📄 tftpd.c
字号:
nak(EBADOP); exit(1); } ecode = (*pf->f_validate)(filename, tp->th_opcode); if (ecode) { nak(ecode); exit(1); } opt = ++cp; if (cp < buf + size && *cp != '\000') goto again; } else { *(argn & 1 ? &val : &opt) = ++cp; if (argn & 1) do_opt(opt, val, &ap); if (cp < buf + size && *cp != '\000') goto again; } if (pf == NULL || pf->f_mode == 0) { nak(EBADOP); exit(1); } isopts = (ap != (ackbuf + 2)); (tp->th_opcode == WRQ ? *pf->f_recv : *pf->f_send) (pf, isopts ? ackbuf : NULL, isopts ? ap - ackbuf : 0); exit(0);}FILE *file;/* * Validate file access. Since we * have no uid or gid, for now require * file to exist and be publicly * 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. */validate_access(filename, mode) char *filename; int mode;{ struct stat stbuf; int fd; char *cp, **dirp; if (*filename != '/') return (EACCESS); /* * prevent tricksters from getting around the directory restrictions */ for (cp = filename + 1; *cp; cp++) if(*cp == '.' && strncmp(cp-1, "/../", 4) == 0) return(EACCESS); for (dirp = dirs; *dirp; dirp++) if (strncmp(filename, *dirp, strlen(*dirp)) == 0) break; if (*dirp==0 && dirp!=dirs) return (EACCESS); if (stat(filename, &stbuf) < 0) return (errno == ENOENT ? ENOTFOUND : EACCESS); if (mode == RRQ) { if ((stbuf.st_mode&(S_IREAD >> 6)) == 0) return (EACCESS); filesize = stbuf.st_size; } else { if ((stbuf.st_mode&(S_IWRITE >> 6)) == 0) return (EACCESS); filesize = 0; } fd = open(filename, mode == RRQ ? 0 : 1); 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(){ timeout += rexmtval; if (timeout >= maxtimeout) exit(1); longjmp(timeoutbuf, 1);}/* * Send the requested file. */sendfile(pf, oap, oacklen) struct formats *pf; struct tftphdr *oap; int oacklen;{ struct tftphdr *dp, *r_init(); register struct tftphdr *ap; /* ack packet */ register int block = 1, size, n; signal(SIGALRM, timer); ap = (struct tftphdr *)ackbuf; if (oap) { timeout = 0; (void)setjmp(timeoutbuf);oack: if (send(peer, oap, oacklen, 0) != oacklen) { syslog(LOG_ERR, "tftpd: write: %m\n"); goto abort; } for ( ; ; ) { alarm(rexmtval); 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) { syslog(LOG_ERR, "tftp: client does not accept " "options\n"); goto abort; } if (ap->th_opcode == ACK) { if (ap->th_block == 0) break; /* Resynchronize with the other side */ (void)synchnet(peer); goto oack; } } } dp = r_init(); 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, 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 (ap->th_block == block) { break; } /* Re-synchronize with the other side */ (void) synchnet(peer); if (ap->th_block == (block -1)) { goto send_data; } } } block++; } while (size == segsize);abort: (void) fclose(file);}voidjustquit(){ exit(0);}/* * Receive a file. */recvfile(pf, oap, oacklen) struct formats *pf; struct tftphdr *oap; int oacklen;{ struct tftphdr *dp, *w_init(); register struct tftphdr *ap; /* ack buffer */ register int block = 0, acksize, n, size; signal(SIGALRM, timer); dp = w_init(); do { timeout = 0; if (!block++ && oap) { ap = (struct tftphdr *)oap; acksize = oacklen; } else { ap = (struct tftphdr *)ackbuf; ap->th_opcode = htons((u_short)ACK); ap->th_block = htons((u_short)block - 1); acksize = 4; } (void) setjmp(timeoutbuf);send_ack: if (send(peer, (char *)ap, acksize, 0) != acksize) { syslog(LOG_ERR, "tftpd: write: %m\n"); goto abort; } write_behind(file, pf->f_convert); for ( ; ; ) { alarm(rexmtval); n = recv(peer, dp, segsize + 4, 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 = (struct tftphdr *)ackbuf; 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, segsize, 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; 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" }, { EOPTNEG, "Failure to negotiate RFC1782 options" }, { -1, 0 }};/* * Send a nak packet (error message). * Error code passed in is one of the * standard TFTP codes, or a UNIX errno * offset by 100. */nak(error) 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");}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -