📄 tftpd.c
字号:
*/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; 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 */ synchnet (peer); if ((u_short) ap->th_block == (u_short) (block - 1)) goto send_data; } } block++; } while (size == SEGSIZE);abort: 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++; 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 */ 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); fclose (file); /* close data file */ ap->th_opcode = htons ((u_short) ACK); /* send the "final" ack */ ap->th_block = htons ((u_short) (block)); 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 */ 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);}static const char usage_str[] = "Usage: tftpd [OPTIONS...]\n" "\n" "Options are:\n" " -l Enable logging\n" " -n Supress negative acknowledgement of\n" " requests for nonexistent relative filenames\n" " --help Display usage instructions\n" " --version Display program version\n";voidusage (void){ printf ("%s\n" "Send bug reports to <%s>\n", usage_str, PACKAGE_BUGREPORT);}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -