diff options
author | 2004-01-27 02:25:30 +0000 | |
---|---|---|
committer | 2004-01-27 02:25:30 +0000 | |
commit | c5a6a5b3f685cf06e32521f149c2362a1a2e206e (patch) | |
tree | 8a1310052844916e6888b254d0ee1af4c21e5984 | |
parent | #define away __extern__ for old gcc and non-gcc; espie@ OK (diff) | |
download | wireguard-openbsd-c5a6a5b3f685cf06e32521f149c2362a1a2e206e.tar.xz wireguard-openbsd-c5a6a5b3f685cf06e32521f149c2362a1a2e206e.zip |
TSIZE & TIMEOUT support; from freebsd via tholo
-rw-r--r-- | include/arpa/tftp.h | 4 | ||||
-rw-r--r-- | libexec/tftpd/tftpd.c | 154 |
2 files changed, 143 insertions, 15 deletions
diff --git a/include/arpa/tftp.h b/include/arpa/tftp.h index 727aacabb4a..62808559899 100644 --- a/include/arpa/tftp.h +++ b/include/arpa/tftp.h @@ -1,4 +1,4 @@ -/* $OpenBSD: tftp.h,v 1.4 2003/06/02 19:34:12 millert Exp $ */ +/* $OpenBSD: tftp.h,v 1.5 2004/01/27 02:25:30 deraadt Exp $ */ /* $NetBSD: tftp.h,v 1.3 1994/10/26 00:56:48 cgd Exp $ */ /* @@ -48,6 +48,7 @@ #define DATA 03 /* data packet */ #define ACK 04 /* acknowledgement */ #define ERROR 05 /* error code */ +#define OACK 06 /* option acknowledgement */ struct tftphdr { u_int16_t th_opcode; /* packet type */ @@ -75,5 +76,6 @@ struct tftphdr { #define EBADID 5 /* unknown transfer ID */ #define EEXISTS 6 /* file already exists */ #define ENOUSER 7 /* no such user */ +#define EOPTNEG 8 /* option negotiation failed */ #endif /* !_TFTP_H_ */ diff --git a/libexec/tftpd/tftpd.c b/libexec/tftpd/tftpd.c index 3985f6f972e..f235aa58748 100644 --- a/libexec/tftpd/tftpd.c +++ b/libexec/tftpd/tftpd.c @@ -1,4 +1,4 @@ -/* $OpenBSD: tftpd.c,v 1.31 2003/09/24 20:40:19 deraadt Exp $ */ +/* $OpenBSD: tftpd.c,v 1.32 2004/01/27 02:25:30 deraadt Exp $ */ /* * Copyright (c) 1983 Regents of the University of California. @@ -37,7 +37,7 @@ char copyright[] = #ifndef lint /*static char sccsid[] = "from: @(#)tftpd.c 5.13 (Berkeley) 2/26/91";*/ -static char rcsid[] = "$OpenBSD: tftpd.c,v 1.31 2003/09/24 20:40:19 deraadt Exp $"; +static char rcsid[] = "$OpenBSD: tftpd.c,v 1.32 2004/01/27 02:25:30 deraadt Exp $"; #endif /* not lint */ /* @@ -69,12 +69,13 @@ static char rcsid[] = "$OpenBSD: tftpd.c,v 1.31 2003/09/24 20:40:19 deraadt Exp #include <pwd.h> #define TIMEOUT 5 +#define MAX_TIMEOUTS 5 extern char *__progname; struct sockaddr_storage s_in; int peer; int rexmtval = TIMEOUT; -int maxtimeout = 5*TIMEOUT; +int max_rexmtval = 2*TIMEOUT; #define PKTSIZE SEGSIZE+4 char buf[PKTSIZE]; @@ -94,7 +95,7 @@ int recvfile(struct formats *pf); int sendfile(struct formats *pf); struct formats { - char *f_mode; + const char *f_mode; int (*f_validate)(char *, int); int (*f_send)(struct formats *); int (*f_recv)(struct formats *); @@ -102,12 +103,26 @@ struct formats { } formats[] = { { "netascii", validate_access, sendfile, recvfile, 1 }, { "octet", validate_access, sendfile, recvfile, 0 }, - { 0 } + { NULL, NULL, NULL, NULL, 0 } +}; +struct options { + const char *o_type; + char *o_request; + int o_reply; /* turn into union if need be */ +} options[] = { + { "tsize", NULL, 0 }, /* OPT_TSIZE */ + { "timeout", NULL, 0 }, /* OPT_TIMEOUT */ + { NULL, NULL, 0 } +}; +enum opt_enum { + OPT_TSIZE = 0, + OPT_TIMEOUT }; int validate_access(char *filename, int mode); void tftp(struct tftphdr *tp, int size); void nak(int error); +void oack(void); int readit(FILE *file, struct tftphdr **dpp, int convert); void read_ahead(FILE *file, int convert); @@ -278,11 +293,12 @@ void tftp(struct tftphdr *tp, int size) { char *cp; - int first = 1, ecode; + int i, first = 1, has_options = 0, ecode; struct formats *pf; - char *filename, *mode = NULL; + char *filename, *mode = NULL, *option, *ccp; + char fnbuf[MAXPATHLEN]; - filename = cp = tp->th_stuff; + cp = tp->th_stuff; again: while (cp < buf + size) { if (*cp == '\0') @@ -293,6 +309,14 @@ again: nak(EBADOP); exit(1); } + i = cp - tp->th_stuff; + if (i >= sizeof(fnbuf)) { + nak(EBADOP); + exit(1); + } + memcpy(fnbuf, tp->th_stuff, i); + fnbuf[i] = '\0'; + filename = fnbuf; if (first) { mode = ++cp; first = 0; @@ -308,7 +332,47 @@ again: nak(EBADOP); exit(1); } + while (++cp < buf + size) { + for (i = 2, ccp = cp; i > 0; ccp++) { + if (ccp >= buf + size) { + /* + * Don't reject the request, just stop trying + * to parse the option and get on with it. + * Some Apple OpenFirmware versions have + * trailing garbage on the end of otherwise + * valid requests. + */ + goto option_fail; + } else if (*ccp == '\0') + i--; + } + for (option = cp; *cp; cp++) + if (isupper(*cp)) + *cp = tolower(*cp); + for (i = 0; options[i].o_type != NULL; i++) + if (strcmp(option, options[i].o_type) == 0) { + options[i].o_request = ++cp; + has_options = 1; + } + cp = ccp-1; + } + +option_fail: + if (options[OPT_TIMEOUT].o_request) { + int to = atoi(options[OPT_TIMEOUT].o_request); + if (to < 1 || to > 255) { + nak(EBADOP); + exit(1); + } + else if (to <= max_rexmtval) + options[OPT_TIMEOUT].o_reply = rexmtval = to; + else + options[OPT_TIMEOUT].o_request = NULL; + } + ecode = (*pf->f_validate)(filename, tp->th_opcode); + if (has_options) + oack(); if (ecode) { nak(ecode); exit(1); @@ -381,6 +445,14 @@ validate_access(char *filename, int mode) return (EACCESS); } } + if (options[OPT_TSIZE].o_request) { + if (mode == RRQ) + options[OPT_TSIZE].o_reply = stbuf.st_size; + else + /* XXX Allows writes of all sizes. */ + options[OPT_TSIZE].o_reply = + atoi(options[OPT_TSIZE].o_request); + } fd = open(filename, mode == RRQ ? O_RDONLY : (O_WRONLY|wmode), 0666); if (fd < 0) return (errno + 100); @@ -401,15 +473,14 @@ validate_access(char *filename, int mode) return (0); } -int timeout; +int timeouts; jmp_buf timeoutbuf; static void timer(int signo) { /* XXX longjmp/signal resource leaks */ - timeout += rexmtval; - if (timeout >= maxtimeout) + if (++timeouts >= MAX_TIMEOUTS) _exit(1); longjmp(timeoutbuf, 1); } @@ -436,7 +507,7 @@ sendfile(struct formats *pf) } dp->th_opcode = htons((u_short)DATA); dp->th_block = htons((u_short)block); - timeout = 0; + timeouts = 0; setjmp(timeoutbuf); send_data: @@ -500,7 +571,7 @@ recvfile(struct formats *pf) dp = w_init(); ap = (struct tftphdr *)ackbuf; do { - timeout = 0; + timeouts = 0; ap->th_opcode = htons((u_short)ACK); ap->th_block = htons((u_short)block); block++; @@ -564,7 +635,7 @@ abort: struct errmsg { int e_code; - char *e_msg; + const char *e_msg; } errmsgs[] = { { EUNDEF, "Undefined error code" }, { ENOTFOUND, "File not found" }, @@ -574,6 +645,7 @@ struct errmsg { { EBADID, "Unknown transfer ID" }, { EEXISTS, "File already exists" }, { ENOUSER, "No such user" }, + { EOPTNEG, "Option negotiation failed" }, { -1, NULL } }; @@ -606,3 +678,57 @@ nak(int error) if (send(peer, buf, length, 0) != length) syslog(LOG_ERR, "nak: %m"); } + +/* + * Send an oack packet (option acknowledgement). + */ +void +oack(void) +{ + struct tftphdr *tp, *ap; + int size, i, n; + char *bp; + + tp = (struct tftphdr *)buf; + bp = buf + 2; + size = sizeof(buf) - 2; + tp->th_opcode = htons((u_short)OACK); + for (i = 0; options[i].o_type != NULL; i++) { + if (options[i].o_request) { + n = snprintf(bp, size, "%s%c%d", options[i].o_type, + 0, options[i].o_reply); + bp += n+1; + size -= n+1; + if (size < 0) { + syslog(LOG_ERR, "oack: buffer overflow"); + exit(1); + } + } + } + size = bp - buf; + ap = (struct tftphdr *)ackbuf; + signal(SIGALRM, timer); + timeouts = 0; + + (void)setjmp(timeoutbuf); + if (send(peer, buf, size, 0) != size) { + syslog(LOG_INFO, "oack: %m"); + exit(1); + } + + for (;;) { + alarm(rexmtval); + n = recv(peer, ackbuf, sizeof (ackbuf), 0); + alarm(0); + if (n < 0) { + syslog(LOG_ERR, "recv: %m"); + exit(1); + } + ap->th_opcode = ntohs((u_short)ap->th_opcode); + ap->th_block = ntohs((u_short)ap->th_block); + if (ap->th_opcode == ERROR) + exit(1); + if (ap->th_opcode == ACK && ap->th_block == 0) + break; + } +} |