📄 tftpd.c
字号:
* readable/writable. * If we were invoked with arguments * from inetd then the file must also be * in one of the given directory prefixes. * Note also, full path name must be * given as we have no login directory. */intvalidate_access(char **filep, int mode){ struct stat stbuf; int fd; struct dirlist *dirp; static char *pathname = 0; char *filename = *filep; /* * Prevent tricksters from getting around the directory restrictions */ if (strstr(filename, "/../")) return (EACCESS); if (*filename == '/') { /* * Allow the request if it's in one of the approved locations. * Special case: check the null prefix ("/") by looking * for length = 1 and relying on the arg. processing that * it's a /. */ for (dirp = dirs; dirp->name != NULL; dirp++) { if (dirp->len == 1 || (!strncmp(filename, dirp->name, dirp->len) && filename[dirp->len] == '/')) break; } /* If directory list is empty, allow access to any file */ if (dirp->name == NULL && dirp != dirs) return (EACCESS); if (stat(filename, &stbuf) < 0) return (errno == ENOENT ? ENOTFOUND : EACCESS); if ((stbuf.st_mode & S_IFMT) != S_IFREG) return (ENOTFOUND); if (mode == RRQ) { if ((stbuf.st_mode & S_IROTH) == 0) return (EACCESS); } else { if ((stbuf.st_mode & S_IWOTH) == 0) return (EACCESS); } } else { int err; /* * Relative file name: search the approved locations for it. * Don't allow write requests or ones that avoid directory * restrictions. */ if (mode != RRQ || !strncmp(filename, "../", 3)) return (EACCESS); /* * If the file exists in one of the directories and isn't * readable, continue looking. However, change the error code * to give an indication that the file exists. */ err = ENOTFOUND; for (dirp = dirs; dirp->name != NULL; dirp++) { if (pathname) free (pathname); pathname = malloc (strlen (dirp->name) + 1 + strlen (filename) + 1); if (! pathname) return ENOMEM; sprintf(pathname, "%s/%s", dirp->name, filename); if (stat(pathname, &stbuf) == 0 && (stbuf.st_mode & S_IFMT) == S_IFREG) { if ((stbuf.st_mode & S_IROTH) != 0) { break; } err = EACCESS; } } if (dirp->name == NULL) return (err); *filep = filename = pathname; } fd = open(filename, mode == RRQ ? O_RDONLY : (O_WRONLY | O_TRUNC)); if (fd < 0) return (errno + 100); file = fdopen(fd, (mode == RRQ)? "r":"w"); if (file == NULL) { return errno+100; } return (0);}int timeout;jmp_buf timeoutbuf;voidtimer(int sig){ timeout += rexmtval; if (timeout >= maxtimeout) exit(1); longjmp(timeoutbuf, 1);}/* * Send the requested file. */voidsend_file(struct formats *pf){ struct tftphdr *dp, *r_init(); register struct tftphdr *ap; /* ack packet */ register int size, n; volatile int block; signal(SIGALRM, timer); dp = r_init(); ap = (struct tftphdr *)ackbuf; block = 1; do { size = readit(file, &dp, pf->f_convert); if (size < 0) { nak(errno + 100); goto abort; } dp->th_opcode = htons((u_short)DATA); dp->th_block = htons((u_short)block); timeout = 0; (void)setjmp(timeoutbuf);send_data: if (send(peer, (const char *)dp, size + 4, 0) != size + 4) { syslog(LOG_ERR, "tftpd: write: %m\n"); goto abort; } read_ahead(file, pf->f_convert); for ( ; ; ) { alarm(rexmtval); /* read the ack */ n = recv(peer, ackbuf, sizeof (ackbuf), 0); alarm(0); if (n < 0) { syslog(LOG_ERR, "tftpd: read: %m\n"); goto abort; } ap->th_opcode = ntohs((u_short)ap->th_opcode); ap->th_block = ntohs((u_short)ap->th_block); if (ap->th_opcode == ERROR) goto abort; if (ap->th_opcode == ACK) { if ((u_short)ap->th_block == (u_short)block) break; /* Re-synchronize with the other side */ (void) synchnet(peer); if ((u_short)ap->th_block == (u_short)(block -1)) goto send_data; } } block++; } while (size == SEGSIZE);abort: (void) fclose(file);}voidjustquit(int sig){ exit(0);}/* * Receive a file. */voidrecvfile(struct formats *pf){ struct tftphdr *dp, *w_init(); register struct tftphdr *ap; /* ack buffer */ register int n, size; volatile int block; signal(SIGALRM, timer); dp = w_init(); ap = (struct tftphdr *)ackbuf; block = 0; do { timeout = 0; ap->th_opcode = htons((u_short)ACK); ap->th_block = htons((u_short)block); block++; (void) setjmp(timeoutbuf);send_ack: if (send(peer, ackbuf, 4, 0) != 4) { syslog(LOG_ERR, "tftpd: write: %m\n"); goto abort; } write_behind(file, pf->f_convert); for ( ; ; ) { alarm(rexmtval); n = recv(peer, (char *)dp, PKTSIZE, 0); alarm(0); if (n < 0) { /* really? */ syslog(LOG_ERR, "tftpd: read: %m\n"); goto abort; } dp->th_opcode = ntohs((u_short)dp->th_opcode); dp->th_block = ntohs((u_short)dp->th_block); if (dp->th_opcode == ERROR) goto abort; if (dp->th_opcode == DATA) { if (dp->th_block == block) { break; /* normal */ } /* Re-synchronize with the other side */ (void) synchnet(peer); if (dp->th_block == (block-1)) goto send_ack; /* rexmit */ } } /* size = write(file, dp->th_data, n - 4); */ size = writeit(file, &dp, n - 4, pf->f_convert); if (size != (n-4)) { /* ahem */ if (size < 0) nak(errno + 100); else nak(ENOSPACE); goto abort; } } while (size == SEGSIZE); write_behind(file, pf->f_convert); (void) fclose(file); /* close data file */ ap->th_opcode = htons((u_short)ACK); /* send the "final" ack */ ap->th_block = htons((u_short)(block)); (void) send(peer, ackbuf, 4, 0); signal(SIGALRM, justquit); /* just quit on timeout */ alarm(rexmtval); n = recv(peer, buf, sizeof (buf), 0); /* normally times out and quits */ alarm(0); if (n >= 4 && /* if read some data */ dp->th_opcode == DATA && /* and got a data block */ block == dp->th_block) { /* then my last ack was lost */ (void) send(peer, ackbuf, 4, 0); /* resend final ack */ }abort: return;}struct errmsg { int e_code; const char *e_msg;} errmsgs[] = { { EUNDEF, "Undefined error code" }, { ENOTFOUND, "File not found" }, { EACCESS, "Access violation" }, { ENOSPACE, "Disk full or allocation exceeded" }, { EBADOP, "Illegal TFTP operation" }, { EBADID, "Unknown transfer ID" }, { EEXISTS, "File already exists" }, { ENOUSER, "No such user" }, { -1, 0 }};static const char *errtomsg(int error){ static char buf[20]; register struct errmsg *pe; if (error == 0) return "success"; for (pe = errmsgs; pe->e_code >= 0; pe++) if (pe->e_code == error) return pe->e_msg; sprintf(buf, "error %d", error); return buf;}/* * Send a nak packet (error message). * Error code passed in is one of the * standard TFTP codes, or a UNIX errno * offset by 100. */static voidnak(int error){ register struct tftphdr *tp; int length; register struct errmsg *pe; tp = (struct tftphdr *)buf; tp->th_opcode = htons((u_short)ERROR); tp->th_code = htons((u_short)error); for (pe = errmsgs; pe->e_code >= 0; pe++) if (pe->e_code == error) break; if (pe->e_code < 0) { pe->e_msg = strerror(error - 100); tp->th_code = EUNDEF; /* set 'undef' errorcode */ } strcpy(tp->th_msg, pe->e_msg); length = strlen(pe->e_msg); tp->th_msg[length] = '\0'; length += 5; if (send(peer, buf, length, 0) != length) syslog(LOG_ERR, "nak: %m\n");}static const char *verifyhost(struct sockaddr_in *fromp){ struct hostent *hp; hp = gethostbyaddr((char *)&fromp->sin_addr, sizeof (fromp->sin_addr), fromp->sin_family); if (hp) return hp->h_name; else return inet_ntoa(fromp->sin_addr);}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -