From c980666bbeddf8625d057a920314a28072ee5f9c Mon Sep 17 00:00:00 2001 From: hpa Date: Mon, 6 May 2002 19:14:14 +0000 Subject: [PATCH 001/184] Fix bogus sizeof() --- tftpd/remap.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tftpd/remap.c b/tftpd/remap.c index 6534b01..86081f3 100644 --- a/tftpd/remap.c +++ b/tftpd/remap.c @@ -146,7 +146,7 @@ static int parseline(char *line, struct rule *r, int lineno) int rxflags = REG_EXTENDED; static int nrule; - memset(r, 0, sizeof r); + memset(r, 0, sizeof *r); r->nrule = nrule; if ( !readescstring(buffer, &line) ) From d2692c9f98a4060b33232c648d5ce9cfffc18e44 Mon Sep 17 00:00:00 2001 From: hpa Date: Tue, 3 Sep 2002 19:02:02 +0000 Subject: [PATCH 002/184] Downgrade client-side errors from LOG_ERR to LOG_WARN --- tftpd/tftpd.c | 158 +++++++++++++++++++++++++++++++++----------------- 1 file changed, 105 insertions(+), 53 deletions(-) diff --git a/tftpd/tftpd.c b/tftpd/tftpd.c index 05337a5..9f4235a 100644 --- a/tftpd/tftpd.c +++ b/tftpd/tftpd.c @@ -79,15 +79,17 @@ int allow_severity = -1; /* Don't log at all */ struct request_info wrap_request; #endif -#define TIMEOUT 5 /* Default timeout (seconds) */ -#define TRIES 4 /* Number of attempts to send each packet */ -#define TIMEOUT_LIMIT (TRIES*(TRIES+1)/2) +#define TIMEOUT 1000 /* Default timeout (ms) */ +#define TRIES 6 /* Number of attempts to send each packet */ +#define TIMEOUT_LIMIT ((1 << TRIES)-1) char *__progname; int peer; -int timeout = TIMEOUT; -int rexmtval = TIMEOUT; +int timeout = TIMEOUT; /* Current timeout value */ +int timeout_quit = 0; +int rexmtval = TIMEOUT; /* Basic timeout value */ int maxtimeout = TIMEOUT_LIMIT*TIMEOUT; +sigjmp_buf timeoutbuf; #define PKTSIZE MAX_SEGSIZE+4 char buf[PKTSIZE]; @@ -121,6 +123,7 @@ int set_blksize(char *, char **); int set_blksize2(char *, char **); int set_tsize(char *, char **); int set_timeout(char *, char **); +int set_utimeout(char *, char **); struct options { const char *o_opt; @@ -130,6 +133,7 @@ struct options { { "blksize2", set_blksize2 }, { "tsize", set_tsize }, { "timeout", set_timeout }, + { "utimeout", set_utimeout }, { NULL, NULL } }; @@ -142,6 +146,17 @@ static void handle_sighup(int sig) } +/* Handle timeout signal or timeout event */ +void +timer(int sig) +{ + (void)sig; /* Suppress unused warning */ + timeout <<= 1; + if (timeout >= maxtimeout || timeout_quit) + exit(0); + siglongjmp(timeoutbuf, 1); +} + static void usage(void) { @@ -189,6 +204,49 @@ set_socket_nonblock(int fd, int flag) } } +/* + * Receive packet with synchronous timeout + */ +static int recv_time(int s, void *rbuf, int len, unsigned int flags, + unsigned long timeout_ms) +{ + fd_set fdset; + struct timeval tmv; + int rv, err; + + for ( ; ; ) { + FD_ZERO(&fdset); + FD_SET(s, &fdset); + + tmv.tv_sec = timeout_ms/1000; + tmv.tv_usec = (timeout_ms%1000)*1000; + + do { + rv = select(s+1, &fdset, NULL, NULL, &tmv); + } while ( rv == -1 && errno == EINTR ); + + if ( rv == 0 ) { + timer(0); /* Should not return */ + return -1; + } + + set_socket_nonblock(s, 1); + rv = recv(s, rbuf, len, flags); + err = errno; + set_socket_nonblock(s, 0); + if ( rv < 0 ) { + if ( E_WOULD_BLOCK(err) || err == EINTR ) { + continue; /* Once again, with feeling... */ + } else { + errno = err; + return rv; + } + } else { + return rv; + } + } +} + int main(int argc, char **argv) { @@ -589,7 +647,6 @@ main(int argc, char **argv) /* Other basic setup */ from.sin_family = AF_INET; - alarm(0); /* Process the request... */ @@ -810,7 +867,9 @@ set_tsize(char *val, char **ret) } /* - * Set the timeout (c.f. RFC2349) + * Set the timeout (c.f. RFC2349). This is supposed + * to be the (default) retransmission timeout, but being an + * integer in seconds it seems a bit limited. */ int set_timeout(char *val, char **ret) @@ -824,14 +883,32 @@ set_timeout(char *val, char **ret) if ( to < 1 || to > 255 || *vend ) return 0; - timeout = to; - rexmtval = to; - maxtimeout = TIMEOUT_LIMIT*to; + rexmtval = timeout = to; + maxtimeout = rexmtval*TIMEOUT_LIMIT; sprintf(*ret = b_ret, "%lu", to); return(1); } +/* Similar, but in microseconds. We allow down to 10 ms. */ +int +set_utimeout(char *val, char **ret) +{ + static char b_ret[4]; + unsigned long to; + char *vend; + + to = strtoul(val, &vend, 10); + + if ( to < 10000UL || to > 255000000UL || *vend ) + return 0; + + rexmtval = timeout = to/1000UL; + maxtimeout = rexmtval*TIMEOUT_LIMIT; + + sprintf(*ret = b_ret, "%lu", to); + return(1); +} /* * Parse RFC2347 style options */ @@ -1030,20 +1107,6 @@ validate_access(char *filename, int mode, struct formats *pf) return (0); } -int timeout; -sigjmp_buf timeoutbuf; - -/* Handle timeout signal */ -void -timer(int sig) -{ - (void)sig; /* Suppress unused warning */ - timeout += rexmtval; - if (timeout >= maxtimeout) - exit(0); - siglongjmp(timeoutbuf, 1); -} - /* * Send the requested file. */ @@ -1058,28 +1121,24 @@ tftp_sendfile(struct formats *pf, struct tftphdr *oap, int oacklen) ap = (struct tftphdr *)ackbuf; if (oap) { - timeout = 0; + timeout = rexmtval; (void)sigsetjmp(timeoutbuf,1); oack: if (send(peer, oap, oacklen, 0) != oacklen) { - syslog(LOG_ERR, "tftpd: oack: %m\n"); + syslog(LOG_WARN, "tftpd: oack: %m\n"); goto abort; } for ( ; ; ) { - set_signal(SIGALRM, timer, SA_RESTART); - alarm(rexmtval); - n = recv(peer, ackbuf, sizeof(ackbuf), 0); - alarm(0); + n = recv_time(peer, ackbuf, sizeof(ackbuf), 0, timeout); if (n < 0) { - syslog(LOG_ERR, "tftpd: read: %m\n"); + syslog(LOG_WARN, "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"); + syslog(LOG_WARN, "tftp: client does not accept options\n"); goto abort; } if (ap->th_opcode == ACK) { @@ -1101,21 +1160,18 @@ tftp_sendfile(struct formats *pf, struct tftphdr *oap, int oacklen) } dp->th_opcode = htons((u_short)DATA); dp->th_block = htons((u_short)block); - timeout = 0; + timeout = rexmtval; (void) sigsetjmp(timeoutbuf,1); if (send(peer, dp, size + 4, 0) != size + 4) { - syslog(LOG_ERR, "tftpd: write: %m"); + syslog(LOG_WARN, "tftpd: write: %m"); goto abort; } read_ahead(file, pf->f_convert); for ( ; ; ) { - set_signal(SIGALRM, timer, SA_RESTART); - alarm(rexmtval); /* read the ack */ - n = recv(peer, ackbuf, sizeof (ackbuf), 0); - alarm(0); + n = recv_time(peer, ackbuf, sizeof (ackbuf), 0, timeout); if (n < 0) { - syslog(LOG_ERR, "tftpd: read(ack): %m"); + syslog(LOG_WARN, "tftpd: read(ack): %m"); goto abort; } ap->th_opcode = ntohs((u_short)ap->th_opcode); @@ -1168,7 +1224,7 @@ tftp_recvfile(struct formats *pf, struct tftphdr *oap, int oacklen) dp = w_init(); do { - timeout = 0; + timeout = rexmtval; if (!block && oap) { ap = (struct tftphdr *)ackbuf; @@ -1181,20 +1237,16 @@ tftp_recvfile(struct formats *pf, struct tftphdr *oap, int oacklen) } block++; (void) sigsetjmp(timeoutbuf,1); - set_signal(SIGALRM, timer, SA_RESTART); send_ack: if (send(peer, ackbuf, acksize, 0) != acksize) { - syslog(LOG_ERR, "tftpd: write(ack): %m"); + syslog(LOG_WARN, "tftpd: write(ack): %m"); goto abort; } write_behind(file, pf->f_convert); for ( ; ; ) { - set_signal(SIGALRM, timer, SA_RESTART); - alarm(rexmtval); - n = recv(peer, dp, PKTSIZE, 0); - alarm(0); + n = recv_time(peer, dp, PKTSIZE, 0, timeout); if (n < 0) { /* really? */ - syslog(LOG_ERR, "tftpd: read: %m"); + syslog(LOG_WARN, "tftpd: read: %m"); goto abort; } dp->th_opcode = ntohs((u_short)dp->th_opcode); @@ -1226,10 +1278,10 @@ tftp_recvfile(struct formats *pf, struct tftphdr *oap, int oacklen) ap->th_block = htons((u_short)(block)); (void) send(peer, ackbuf, 4, 0); - set_signal(SIGALRM, justquit, 0); /* just quit on timeout */ - alarm(rexmtval); - n = recv(peer, buf, sizeof (buf), 0); /* normally times out and quits */ - alarm(0); + timeout_quit = 1; /* just quit on timeout */ + n = recv_time(peer, buf, sizeof (buf), 0, timeout); /* normally times out and quits */ + timeout_quit = 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 */ @@ -1289,5 +1341,5 @@ nak(int error) } if (send(peer, buf, length, 0) != length) - syslog(LOG_ERR, "nak: %m"); + syslog(LOG_WARN, "nak: %m"); } From b5b455d116a27f27ae0749213be32e3ce1d17c94 Mon Sep 17 00:00:00 2001 From: hpa Date: Tue, 3 Sep 2002 19:03:02 +0000 Subject: [PATCH 003/184] Correct LOG_WARN -> LOG_WARNING --- tftpd/tftpd.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/tftpd/tftpd.c b/tftpd/tftpd.c index 9f4235a..5915eaf 100644 --- a/tftpd/tftpd.c +++ b/tftpd/tftpd.c @@ -1125,20 +1125,20 @@ tftp_sendfile(struct formats *pf, struct tftphdr *oap, int oacklen) (void)sigsetjmp(timeoutbuf,1); oack: if (send(peer, oap, oacklen, 0) != oacklen) { - syslog(LOG_WARN, "tftpd: oack: %m\n"); + syslog(LOG_WARNING, "tftpd: oack: %m\n"); goto abort; } for ( ; ; ) { n = recv_time(peer, ackbuf, sizeof(ackbuf), 0, timeout); if (n < 0) { - syslog(LOG_WARN, "tftpd: read: %m\n"); + syslog(LOG_WARNING, "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_WARN, "tftp: client does not accept options\n"); + syslog(LOG_WARNING, "tftp: client does not accept options\n"); goto abort; } if (ap->th_opcode == ACK) { @@ -1164,14 +1164,14 @@ tftp_sendfile(struct formats *pf, struct tftphdr *oap, int oacklen) (void) sigsetjmp(timeoutbuf,1); if (send(peer, dp, size + 4, 0) != size + 4) { - syslog(LOG_WARN, "tftpd: write: %m"); + syslog(LOG_WARNING, "tftpd: write: %m"); goto abort; } read_ahead(file, pf->f_convert); for ( ; ; ) { n = recv_time(peer, ackbuf, sizeof (ackbuf), 0, timeout); if (n < 0) { - syslog(LOG_WARN, "tftpd: read(ack): %m"); + syslog(LOG_WARNING, "tftpd: read(ack): %m"); goto abort; } ap->th_opcode = ntohs((u_short)ap->th_opcode); @@ -1239,14 +1239,14 @@ tftp_recvfile(struct formats *pf, struct tftphdr *oap, int oacklen) (void) sigsetjmp(timeoutbuf,1); send_ack: if (send(peer, ackbuf, acksize, 0) != acksize) { - syslog(LOG_WARN, "tftpd: write(ack): %m"); + syslog(LOG_WARNING, "tftpd: write(ack): %m"); goto abort; } write_behind(file, pf->f_convert); for ( ; ; ) { n = recv_time(peer, dp, PKTSIZE, 0, timeout); if (n < 0) { /* really? */ - syslog(LOG_WARN, "tftpd: read: %m"); + syslog(LOG_WARNING, "tftpd: read: %m"); goto abort; } dp->th_opcode = ntohs((u_short)dp->th_opcode); @@ -1341,5 +1341,5 @@ nak(int error) } if (send(peer, buf, length, 0) != length) - syslog(LOG_WARN, "nak: %m"); + syslog(LOG_WARNING, "nak: %m"); } From 9447780638826810da2fc2d7b369463e8f12fe8f Mon Sep 17 00:00:00 2001 From: hpa Date: Tue, 3 Sep 2002 19:13:17 +0000 Subject: [PATCH 004/184] Cleanup of configure.in; update to version 0.30 --- configure.in | 7 +++---- version | 2 +- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/configure.in b/configure.in index eba4d25..e365811 100644 --- a/configure.in +++ b/configure.in @@ -119,9 +119,9 @@ AH_TEMPLATE([HAVE_SIGSETJMP], PA_SIGSETJMP([AC_DEFINE(HAVE_SIGSETJMP)]) LIBXTRA=false -AC_SEARCH_LIBS(xmalloc, iberty, , LIBXTRA=true LIBOBJS="$LIBOBJS xmalloc.${OBJEXT}") -AC_SEARCH_LIBS(xstrdup, iberty, , LIBXTRA=true LIBOBJS="$LIBOBJS xstrdup.${OBJEXT}") -AC_SEARCH_LIBS(bsd_signal, bsd, , LIBXTRA=true LIBOBJS="$LIBOBJS bsdsignal.${OBJEXT}") +AC_SEARCH_LIBS(xmalloc, iberty, , LIBXTRA=true AC_LIBOBJ(xmalloc)) +AC_SEARCH_LIBS(xstrdup, iberty, , LIBXTRA=true AC_LIBOBJ(xstrdup)) +AC_SEARCH_LIBS(bsd_signal, bsd, , LIBXTRA=true AC_LIBOBJ(bsdsignal)) if $LIBXTRA; then LIBS="../lib/libxtra.a $LIBS" fi @@ -187,7 +187,6 @@ LIBS="$common_libs" AC_SUBST(TFTP_LIBS) AC_SUBST(TFTPD_LIBS) -AC_SUBST(LIBOBJS) AC_SUBST(TFTPDOBJS) AC_PROG_LN_S diff --git a/version b/version index eec15f9..f7c6c31 100644 --- a/version +++ b/version @@ -1 +1 @@ -0.29 +0.30 From e1508afaa203fc855cfd188acebeece69d786ae1 Mon Sep 17 00:00:00 2001 From: hpa Date: Tue, 3 Sep 2002 19:25:21 +0000 Subject: [PATCH 005/184] Update changes in 0.30. --- CHANGES | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/CHANGES b/CHANGES index 43a2de5..77689d7 100644 --- a/CHANGES +++ b/CHANGES @@ -1,5 +1,17 @@ $Id$ +Changes in 0.30: + (Hopefully) better timeout algorithm. + + Add a "utimeout" option; like "timeout" but in microseconds. + + Change the log level of client-side errors to LOG_WARNING. + + autoconf portability improvements. + + Minor bugfixes. + + Changes in 0.29: Posixly correctness. From 725183b5bac23a909b2ebe6f8267f77a7dd1357b Mon Sep 17 00:00:00 2001 From: hpa Date: Wed, 23 Oct 2002 20:47:23 +0000 Subject: [PATCH 006/184] Check that the received address is really AF_INET. --- tftpd/tftpd.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tftpd/tftpd.c b/tftpd/tftpd.c index 5915eaf..052a450 100644 --- a/tftpd/tftpd.c +++ b/tftpd/tftpd.c @@ -543,6 +543,11 @@ main(int argc, char **argv) } } + if ( from.sin_family != AF_INET ) { + syslog(LOG_ERR, "received address was not AF_INET, please check your inetd config"); + exit(EX_PROTOCOL); + } + if ( standalone && myaddr.sin_addr.s_addr == INADDR_ANY ) { /* myrecvfrom() didn't capture the source address; but we might have bound to a specific address, if so we should use it */ From 01bcd42c44dee1e553e528066889433ea14e91eb Mon Sep 17 00:00:00 2001 From: hpa Date: Wed, 23 Oct 2002 20:48:09 +0000 Subject: [PATCH 007/184] Update SYSLINUX mailing list info --- README | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/README b/README index fc25395..91afcb3 100644 --- a/README +++ b/README @@ -21,5 +21,7 @@ installation instructions. This software can be discussed on the SYSLINUX mailing list. To -subscribe, send a message containing the word "subscribe" in the body -to . +subscribe, go to the list subscription page at: + + http://www.zytor.com/mailman/listinfo/syslinux + From ebc8f1f89df9875909b63f84a19ee0a90c70773a Mon Sep 17 00:00:00 2001 From: hpa Date: Wed, 23 Oct 2002 20:59:03 +0000 Subject: [PATCH 008/184] Bump the version number to 0.31 --- version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version b/version index f7c6c31..48b9990 100644 --- a/version +++ b/version @@ -1 +1 @@ -0.30 +0.31 From 82eae1bcd6f7b4edfe6364670a403e40ba7a33f9 Mon Sep 17 00:00:00 2001 From: hpa Date: Wed, 23 Oct 2002 21:21:46 +0000 Subject: [PATCH 009/184] Fix some timeout-related bugs; allow the user to set the default timeout. --- CHANGES | 9 ++++++++ tftpd/tftpd.8.in | 57 +++++++++++++++++++++++++++++++++++------------- tftpd/tftpd.c | 42 ++++++++++++++++++++++------------- 3 files changed, 78 insertions(+), 30 deletions(-) diff --git a/CHANGES b/CHANGES index 77689d7..b39b502 100644 --- a/CHANGES +++ b/CHANGES @@ -1,5 +1,14 @@ $Id$ +Changes in 0.31: + Put in a check to make sure xinetd (in particular) doesn't + pass us an IPv6 socket. + + Fix some problems related to timeout negotiation. + + Allow the user to set the default timeout speed. + + Changes in 0.30: (Hopefully) better timeout algorithm. diff --git a/tftpd/tftpd.8.in b/tftpd/tftpd.8.in index 388da0a..aab8bf6 100644 --- a/tftpd/tftpd.8.in +++ b/tftpd/tftpd.8.in @@ -31,7 +31,7 @@ .\" SUCH DAMAGE. .\" .\"----------------------------------------------------------------------- */ -.TH TFTPD 8 "16 November 2001" "tftp-hpa @@VERSION@@" "UNIX System Manager's Manual" +.TH TFTPD 8 "23 October 2002" "tftp-hpa @@VERSION@@" "System Manager's Manual" .SH NAME .B tftpd \- IPv4 Trivial File Transfer Protocol server @@ -125,6 +125,14 @@ before terminating the server. will then respawn the server when another request comes in. The default is 900 (15 minutes.) .TP +\fB\-T\fP \fItimeout\fP +Determine the default timeout, in microseconds, before the first +packet is retransmitted. This can be modified by the client if the +.B timeout +or +.B utimeout +option is negotiated. The default is 1000000 (1 second.) +.TP \fB\-m\fP \fIremap-file\fP Specify the use of filename remapping. The .I remap-file @@ -149,25 +157,44 @@ exit gracefully. This version of .B tftpd supports RFC 2347 option negotation. Currently implemented options -are -.B blksize -(RFC 2348), -.B blksize2 -(nonstandard), +are: +.TP +\fBblksize\fP (RFC 2348) +Set the transfer block size to anything less than or equal to the +specified option. This version of +.B tftpd +can support any block size up to the theoretical maximum of 65464 +bytes. +.TP +\fBblksize2\fP (nonstandard) +Set the transfer block size to anything less than or equal to the +specified option, but restrict the possible responses to powers of 2. +The maximum is 32768 bytes (the largest power of 2 less than or equal +to 65464.) +.TP +\fBtsize\fP (RFC 2349) +Report the size of the file that is about to be transferred. This +version of +.B tftpd +only supports the .B tsize -(RFC 2349), and -.B timeout -(RFC 2349). The nonstandard -.B blksize2 -TFTP option is functionally identical to the -.B blksize -option, with the additional constraint that the -blocksize is constrained to be a power of 2. +option for binary (octet) mode transfers. +.TP +\fBtimeout\fP (RFC 2349) +Set the time before the server retransmits a packet, in seconds. +.TP +\fButimeout\fP (nonstandard) +Set the time before the server retransmits a packet, in microseconds. +and .PP The .B \-r option can be used to disable specific options; this may be necessary -to work around bugs in specific TFTP client implementations. +to work around bugs in specific TFTP client implementations. For +example, some TFTP clients have been found to request the +.B blksize +option, but crash with an error if they actually get the option +accepted by the server. .SH "FILENAME REMAPPING" The .B \-m diff --git a/tftpd/tftpd.c b/tftpd/tftpd.c index 052a450..2865f35 100644 --- a/tftpd/tftpd.c +++ b/tftpd/tftpd.c @@ -79,17 +79,17 @@ int allow_severity = -1; /* Don't log at all */ struct request_info wrap_request; #endif -#define TIMEOUT 1000 /* Default timeout (ms) */ +#define TIMEOUT 1000000 /* Default timeout (us) */ #define TRIES 6 /* Number of attempts to send each packet */ #define TIMEOUT_LIMIT ((1 << TRIES)-1) -char *__progname; -int peer; -int timeout = TIMEOUT; /* Current timeout value */ -int timeout_quit = 0; -int rexmtval = TIMEOUT; /* Basic timeout value */ -int maxtimeout = TIMEOUT_LIMIT*TIMEOUT; -sigjmp_buf timeoutbuf; +const char *__progname; +int peer; +unsigned long timeout = TIMEOUT; /* Current timeout value */ +unsigned long rexmtval = TIMEOUT; /* Basic timeout value */ +unsigned long maxtimeout = TIMEOUT_LIMIT*TIMEOUT; +int timeout_quit = 0; +sigjmp_buf timeoutbuf; #define PKTSIZE MAX_SEGSIZE+4 char buf[PKTSIZE]; @@ -160,7 +160,7 @@ timer(int sig) static void usage(void) { - syslog(LOG_ERR, "Usage: %s [-vcl][-a address][-m mappings][-u user][-t timeout][-r option...] [-s] [directory ...]", + syslog(LOG_ERR, "Usage: %s [-vcl][-a address][-m mappings][-u user][-t inetd_timeout][-T pkt_timeout][-r option...] [-s] [directory ...]", __progname); exit(EX_USAGE); } @@ -208,7 +208,7 @@ set_socket_nonblock(int fd, int flag) * Receive packet with synchronous timeout */ static int recv_time(int s, void *rbuf, int len, unsigned int flags, - unsigned long timeout_ms) + unsigned long timeout_us) { fd_set fdset; struct timeval tmv; @@ -218,8 +218,8 @@ static int recv_time(int s, void *rbuf, int len, unsigned int flags, FD_ZERO(&fdset); FD_SET(s, &fdset); - tmv.tv_sec = timeout_ms/1000; - tmv.tv_usec = (timeout_ms%1000)*1000; + tmv.tv_sec = timeout_us / 1000000; + tmv.tv_usec = timeout_us % 1000000; do { rv = select(s+1, &fdset, NULL, NULL, &tmv); @@ -278,7 +278,7 @@ main(int argc, char **argv) openlog(__progname, LOG_PID|LOG_NDELAY, LOG_DAEMON); - while ((c = getopt(argc, argv, "cspvVla:u:U:r:t:m:")) != -1) + while ((c = getopt(argc, argv, "cspvVla:u:U:r:t:T:m:")) != -1) switch (c) { case 'c': cancreate = 1; @@ -298,6 +298,18 @@ main(int argc, char **argv) case 't': waittime = atoi(optarg); break; + case 'T': + { + char *vp; + unsigned long tov = strtoul(optarg, &vp, 10); + if ( tov < 10000UL || tov > 255000000UL || *vp ) { + syslog(LOG_ERR, "Bad timeout value: %s", optarg); + exit(EX_USAGE); + } + rexmtval = timeout = tov; + maxtimeout = rexmtval*TIMEOUT_LIMIT; + } + break; case 'u': user = optarg; break; @@ -888,7 +900,7 @@ set_timeout(char *val, char **ret) if ( to < 1 || to > 255 || *vend ) return 0; - rexmtval = timeout = to; + rexmtval = timeout = to*1000000UL; maxtimeout = rexmtval*TIMEOUT_LIMIT; sprintf(*ret = b_ret, "%lu", to); @@ -908,7 +920,7 @@ set_utimeout(char *val, char **ret) if ( to < 10000UL || to > 255000000UL || *vend ) return 0; - rexmtval = timeout = to/1000UL; + rexmtval = timeout = to; maxtimeout = rexmtval*TIMEOUT_LIMIT; sprintf(*ret = b_ret, "%lu", to); From b3ec0c58e674cd8f2bcdd980daa611b24b8ff862 Mon Sep 17 00:00:00 2001 From: hpa Date: Fri, 8 Nov 2002 01:10:08 +0000 Subject: [PATCH 010/184] Better error messages. --- CHANGES | 6 ++++ tftp/tftp.c | 56 ++++++++++++++++++------------- tftpd/remap.c | 26 +++++++++++--- tftpd/remap.h | 2 +- tftpd/tftpd.c | 93 ++++++++++++++++++++++++++------------------------- 5 files changed, 109 insertions(+), 74 deletions(-) diff --git a/CHANGES b/CHANGES index b39b502..b29eaeb 100644 --- a/CHANGES +++ b/CHANGES @@ -1,5 +1,11 @@ $Id$ +Changes in 0.32: + Better error messages; including the capability to send a + custom error message to the client when hitting an "a" rule in + a remapping table. + + Changes in 0.31: Put in a check to make sure xinetd (in particular) doesn't pass us an IPv6 socket. diff --git a/tftp/tftp.c b/tftp/tftp.c index b35473e..b6c1f5f 100644 --- a/tftp/tftp.c +++ b/tftp/tftp.c @@ -65,7 +65,7 @@ int timeout; sigjmp_buf toplevel; sigjmp_buf timeoutbuf; -static void nak(int); +static void nak(int, const char *); static int makerequest(int, const char *, struct tftphdr *, const char *); static void printstats(const char *, unsigned long); static void startclock(void); @@ -107,7 +107,7 @@ tftp_sendfile(int fd, const char *name, const char *mode) /* size = read(fd, dp->th_data, SEGSIZE); */ size = readit(file, &dp, convert); if (size < 0) { - nak(errno + 100); + nak(errno + 100, NULL); break; } dp->th_opcode = htons((u_short)DATA); @@ -274,7 +274,7 @@ send_ack: /* size = write(fd, dp->th_data, n - 4); */ size = writeit(file, &dp, n - 4, convert); if (size < 0) { - nak(errno + 100); + nak(errno + 100, NULL); break; } amount += size; @@ -331,29 +331,37 @@ struct errmsg { * offset by 100. */ static void -nak(int error) +nak(int error, const char *msg) { - struct errmsg *pe; - struct tftphdr *tp; - int length; - - tp = (struct tftphdr *)ackbuf; - 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; + struct errmsg *pe; + struct tftphdr *tp; + int length; + + tp = (struct tftphdr *)ackbuf; + tp->th_opcode = htons((u_short)ERROR); + tp->th_code = htons((u_short)error); + if ( !msg ) { + if ( error >= 100 ) { + msg = strerror(error - 100); + tp->th_code = EUNDEF; + } else { + for (pe = errmsgs; pe->e_code >= 0; pe++) { + if (pe->e_code == error) { + msg = pe->e_msg; + break; } - strcpy(tp->th_msg, pe->e_msg); - length = strlen(pe->e_msg) + 4; - if (trace) - tpacket("sent", tp, length); - if (sendto(f, ackbuf, length, 0, (struct sockaddr *)&peeraddr, - sizeof(peeraddr)) != length) - perror("nak"); + } + } + } + length = strlen(msg)+1; + memcpy(tp->th_msg, msg, length); + length += 4; /* Add space for header */ + + if (trace) + tpacket("sent", tp, length); + if (sendto(f, ackbuf, length, 0, (struct sockaddr *)&peeraddr, + sizeof(peeraddr)) != length) + perror("nak"); } static void diff --git a/tftpd/remap.c b/tftpd/remap.c index 86081f3..0faa7b8 100644 --- a/tftpd/remap.c +++ b/tftpd/remap.c @@ -1,7 +1,7 @@ /* $Id$ */ /* ----------------------------------------------------------------------- * * - * Copyright 2001 H. Peter Anvin - All Rights Reserved + * Copyright 2001-2002 H. Peter Anvin - All Rights Reserved * * This program is free software available under the same license * as the "OpenBSD" operating system, distributed at @@ -104,8 +104,11 @@ static int genmatchstring(char *string, const char *pattern, const char *input, return len; } -/* Extract a string terminated by non-escaped whitespace; ignore leading whitespace */ -/* Consider an unescaped # to be a comment marker, functionally \n */ +/* + * Extract a string terminated by non-escaped whitespace; ignoring + * leading whitespace. Consider an unescaped # to be a comment marker, + * functionally \n. + */ static int readescstring(char *buf, char **str) { char *p = *str; @@ -268,7 +271,8 @@ void freerules(struct rule *r) /* Execute a rule set on a string; returns a malloc'd new string. */ char *rewrite_string(const char *input, const struct rule *rules, - int is_put, match_pattern_callback macrosub) + int is_put, match_pattern_callback macrosub, + const char **errmsg) { char *current = tfstrdup(input); char *newstr; @@ -278,6 +282,9 @@ char *rewrite_string(const char *input, const struct rule *rules, int was_match = 0; int deadman = DEADMAN_MAX_STEPS; + /* Default error */ + *errmsg = "Remap table failure"; + if ( verbosity >= 3 ) { syslog(LOG_INFO, "remap: input: %s", current); } @@ -305,6 +312,17 @@ char *rewrite_string(const char *input, const struct rule *rules, syslog(LOG_INFO, "remap: rule %d: abort: %s", ruleptr->nrule, current); } + if ( ruleptr->pattern[0] ) { + /* Custom error message */ + len = genmatchstring(NULL, ruleptr->pattern, current, + pmatch, macrosub); + newstr = tfmalloc(len+1); + genmatchstring(newstr, ruleptr->pattern, current, + pmatch, macrosub); + *errmsg = newstr; + } else { + *errmsg = NULL; + } free(current); return(NULL); } diff --git a/tftpd/remap.h b/tftpd/remap.h index 123b7d7..5315074 100644 --- a/tftpd/remap.h +++ b/tftpd/remap.h @@ -37,7 +37,7 @@ void freerules(struct rule *); /* Execute a rule set on a string; returns a malloc'd new string. */ char *rewrite_string(const char *, const struct rule *, int, - match_pattern_callback); + match_pattern_callback, const char **); #endif /* WITH_REGEX */ #endif /* TFTPD_REMAP_H */ diff --git a/tftpd/tftpd.c b/tftpd/tftpd.c index 2865f35..7682af5 100644 --- a/tftpd/tftpd.c +++ b/tftpd/tftpd.c @@ -114,7 +114,7 @@ static struct rule *rewrite_rules = NULL; #endif int tftp(struct tftphdr *, int); -void nak(int); +static void nak(int, const char *); void timer(int); void justquit(int); void do_opt(char *, char *, char **); @@ -683,14 +683,14 @@ main(int argc, char **argv) exit(0); } -char *rewrite_access(char *, int); +char *rewrite_access(char *, int, const char **); int validate_access(char *, int, struct formats *); void tftp_sendfile(struct formats *, struct tftphdr *, int); void tftp_recvfile(struct formats *, struct tftphdr *, int); struct formats { const char *f_mode; - char *(*f_rewrite)(char *, int); + char *(*f_rewrite)(char *, int, const char **); int (*f_validate)(char *, int, struct formats *); void (*f_send)(struct formats *, struct tftphdr *, int); void (*f_recv)(struct formats *, struct tftphdr *, int); @@ -712,6 +712,7 @@ tftp(struct tftphdr *tp, int size) struct formats *pf = NULL; char *origfilename; char *filename, *mode = NULL; + const char *maperrmsg; char *val = NULL, *opt = NULL; char *ap = ackbuf + 2; @@ -727,7 +728,7 @@ tftp(struct tftphdr *tp, int size) } while (cp < buf + size && *cp); if ( *cp ) { - nak(EBADOP); /* Corrupt packet - no final NULL */ + nak(EBADOP, "Request not null-terminated"); exit(0); } @@ -742,11 +743,12 @@ tftp(struct tftphdr *tp, int size) break; } if (!pf->f_mode) { - nak(EBADOP); + nak(EBADOP, "Unknown mode"); exit(0); } - if ( !(filename = (*pf->f_rewrite)(origfilename, tp->th_opcode)) ) { - nak(EACCESS); /* File denied by mapping rule */ + if ( !(filename = + (*pf->f_rewrite)(origfilename, tp->th_opcode, &maperrmsg)) ) { + nak(EACCESS, maperrmsg); /* File denied by mapping rule */ exit(0); } if ( verbosity >= 1 ) { @@ -761,7 +763,7 @@ tftp(struct tftphdr *tp, int size) } ecode = (*pf->f_validate)(filename, tp->th_opcode, pf); if (ecode) { - nak(ecode); + nak(ecode, NULL); exit(0); } opt = ++cp; @@ -774,7 +776,7 @@ tftp(struct tftphdr *tp, int size) } if (!pf) { - nak(EBADOP); + nak(EBADOP, "Missing mode"); exit(0); } @@ -946,13 +948,13 @@ do_opt(char *opt, char *val, char **ap) if (po->o_fnc(val, &ret)) { if (*ap + strlen(opt) + strlen(ret) + 2 >= ackbuf + sizeof(ackbuf)) { - nak(ENOSPACE); /* EOPTNEG? */ + nak(EOPTNEG, "Insufficient space for options"); exit(0); } *ap = strrchr(strcpy(strrchr(strcpy(*ap, opt),'\0') + 1, ret),'\0') + 1; } else { - nak(EOPTNEG); + nak(EOPTNEG, "Unsupported option(s) requested"); exit(0); } break; @@ -999,11 +1001,11 @@ rewrite_macros(char macro, char *output) * Modify the filename, if applicable. If it returns NULL, deny the access. */ char * -rewrite_access(char *filename, int mode) +rewrite_access(char *filename, int mode, const char **msg) { if ( rewrite_rules ) { - char *newname = rewrite_string(filename, rewrite_rules, - mode != RRQ, rewrite_macros); + char *newname = rewrite_string(filename, rewrite_rules, mode != RRQ, + rewrite_macros, msg); filename = newname; } return filename; @@ -1011,9 +1013,10 @@ rewrite_access(char *filename, int mode) #else char * -rewrite_access(char *filename, int mode) +rewrite_access(char *filename, int mode, const char **msg) { (void)mode; /* Avoid warning */ + (void)msg; return filename; } #endif @@ -1172,7 +1175,7 @@ tftp_sendfile(struct formats *pf, struct tftphdr *oap, int oacklen) do { size = readit(file, &dp, pf->f_convert); if (size < 0) { - nak(errno + 100); + nak(errno + 100, NULL); goto abort; } dp->th_opcode = htons((u_short)DATA); @@ -1283,8 +1286,8 @@ tftp_recvfile(struct formats *pf, struct tftphdr *oap, int oacklen) /* 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); + if (size < 0) nak(errno + 100, NULL); + else nak(ENOSPACE, NULL); goto abort; } } while (size == segsize); @@ -1308,21 +1311,19 @@ tftp_recvfile(struct formats *pf, struct tftphdr *oap, int oacklen) 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" }, - { EOPTNEG, "Failure to negotiate RFC2347 options" }, - { -1, 0 } +static const char * const errmsgs[] = +{ + "Undefined error code", /* 0 - EUNDEF */ + "File not found", /* 1 - ENOTFOUND */ + "Access denied", /* 2 - EACCESS */ + "Disk full or allocation exceeded", /* 3 - ENOSPACE */ + "Illegal TFTP operation", /* 4 - EBADOP */ + "Unknown transfer ID", /* 5 - EBADID */ + "File already exists", /* 6 - EEXISTS */ + "No such user", /* 7 - ENOUSER */ + "Failure to negotiate RFC2347 options" /* 8 - EOPTNEG */ }; +#define ERR_CNT (sizeof(errmsgs)/sizeof(const char *)) /* * Send a nak packet (error message). @@ -1330,27 +1331,29 @@ struct errmsg { * standard TFTP codes, or a UNIX errno * offset by 100. */ -void -nak(int error) +static void +nak(int error, const char *msg) { struct tftphdr *tp; int length; - 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 */ + if ( !msg ) { + if ( error >= 100 ) { + /* This is a Unix errno+100 */ + msg = strerror(error - 100); + tp->th_code = EUNDEF; + } else { + if ( (unsigned)error >= ERR_CNT ) + tp->th_code = error = EUNDEF; + msg = errmsgs[error]; + } } - strcpy(tp->th_msg, pe->e_msg); - length = strlen(pe->e_msg); - tp->th_msg[length] = '\0'; - length += 5; + length = strlen(msg)+1; + memcpy(tp->th_msg, msg, length); + length += 4; /* Add space for header */ if ( verbosity >= 2 ) { syslog(LOG_INFO, "sending NAK (%d, %s) to %s", From 8036d9e85746c761a67612bf623d0f49a551bdf1 Mon Sep 17 00:00:00 2001 From: hpa Date: Sat, 9 Nov 2002 00:11:38 +0000 Subject: [PATCH 011/184] Missed this on last update --- version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version b/version index 48b9990..00d0c14 100644 --- a/version +++ b/version @@ -1 +1 @@ -0.31 +0.32 From 0f52cd8fa4dd97a581e88d5899b1a2bb97b7d525 Mon Sep 17 00:00:00 2001 From: hpa Date: Sat, 9 Nov 2002 02:22:59 +0000 Subject: [PATCH 012/184] More error message improvements; work around a suspect Solaris compiler bug --- tftpd/recvfrom.c | 9 ++++--- tftpd/tftpd.c | 63 ++++++++++++++++++++++++++++++------------------ 2 files changed, 45 insertions(+), 27 deletions(-) diff --git a/tftpd/recvfrom.c b/tftpd/recvfrom.c index d094a69..9112a6b 100644 --- a/tftpd/recvfrom.c +++ b/tftpd/recvfrom.c @@ -57,7 +57,7 @@ myrecvfrom(int s, void *buf, int len, unsigned int flags, struct sockaddr_in *myaddr) { struct msghdr msg; - struct iovec iov[1]; + struct iovec iov; int n; struct cmsghdr *cmptr; union { @@ -82,15 +82,16 @@ myrecvfrom(int s, void *buf, int len, unsigned int flags, setsockopt(s, IPPROTO_IP, IP_PKTINFO, &on, sizeof(on)); #endif + bzero(&msg, sizeof msg); /* Clear possible system-dependent fields */ msg.msg_control = control_un.control; msg.msg_controllen = sizeof(control_un.control); msg.msg_flags = 0; msg.msg_name = from; msg.msg_namelen = *fromlen; - iov[0].iov_base = buf; - iov[0].iov_len = len; - msg.msg_iov = iov; + iov.iov_base = buf; + iov.iov_len = len; + msg.msg_iov = &iov; msg.msg_iovlen = 1; if ( (n = recvmsg(s, &msg, flags)) < 0 ) diff --git a/tftpd/tftpd.c b/tftpd/tftpd.c index 7682af5..94781a7 100644 --- a/tftpd/tftpd.c +++ b/tftpd/tftpd.c @@ -458,7 +458,7 @@ main(int argc, char **argv) exit(EX_OSERR); } nfd = open("/dev/null", O_RDWR); - if ( nfd >= 0 ) { + if ( nfd >= 3 ) { #ifdef HAVE_DUP2 dup2(nfd, 0); dup2(nfd, 1); @@ -469,7 +469,7 @@ main(int argc, char **argv) close(2); dup(nfd); #endif close(nfd); - } else { + } else if ( nfd < 0 ) { close(0); close(1); close(2); } #ifdef HAVE_SETSID @@ -684,14 +684,14 @@ main(int argc, char **argv) } char *rewrite_access(char *, int, const char **); -int validate_access(char *, int, struct formats *); +int validate_access(char *, int, struct formats *, const char **); void tftp_sendfile(struct formats *, struct tftphdr *, int); void tftp_recvfile(struct formats *, struct tftphdr *, int); struct formats { const char *f_mode; char *(*f_rewrite)(char *, int, const char **); - int (*f_validate)(char *, int, struct formats *); + int (*f_validate)(char *, int, struct formats *, const char **); void (*f_send)(struct formats *, struct tftphdr *, int); void (*f_recv)(struct formats *, struct tftphdr *, int); int f_convert; @@ -707,25 +707,27 @@ struct formats { int tftp(struct tftphdr *tp, int size) { - char *cp; + char *cp, *end; int argn, ecode; struct formats *pf = NULL; char *origfilename; char *filename, *mode = NULL; - const char *maperrmsg; + const char *errmsgptr; char *val = NULL, *opt = NULL; char *ap = ackbuf + 2; - + ((struct tftphdr *)ackbuf)->th_opcode = ntohs(OACK); origfilename = cp = (char *) &(tp->th_stuff); argn = 0; - while ( cp < buf + size && *cp ) { + end = (char *)tp + size; + + while ( cp < end && *cp ) { do { cp++; - } while (cp < buf + size && *cp); + } while (cp < end && *cp); if ( *cp ) { nak(EBADOP, "Request not null-terminated"); @@ -747,8 +749,8 @@ tftp(struct tftphdr *tp, int size) exit(0); } if ( !(filename = - (*pf->f_rewrite)(origfilename, tp->th_opcode, &maperrmsg)) ) { - nak(EACCESS, maperrmsg); /* File denied by mapping rule */ + (*pf->f_rewrite)(origfilename, tp->th_opcode, &errmsgptr)) ) { + nak(EACCESS, errmsgptr); /* File denied by mapping rule */ exit(0); } if ( verbosity >= 1 ) { @@ -761,9 +763,9 @@ tftp(struct tftphdr *tp, int size) tp->th_opcode == WRQ ? "WRQ" : "RRQ", inet_ntoa(from.sin_addr), origfilename, filename); } - ecode = (*pf->f_validate)(filename, tp->th_opcode, pf); + ecode = (*pf->f_validate)(filename, tp->th_opcode, pf, &errmsgptr); if (ecode) { - nak(ecode, NULL); + nak(ecode, errmsgptr); exit(0); } opt = ++cp; @@ -1034,7 +1036,8 @@ FILE *file; * given as we have no login directory. */ int -validate_access(char *filename, int mode, struct formats *pf) +validate_access(char *filename, int mode, + struct formats *pf, const char **errmsg) { struct stat stbuf; int i, len; @@ -1044,10 +1047,14 @@ validate_access(char *filename, int mode, struct formats *pf) char stdio_mode[3]; tsize_ok = 0; + *errmsg = NULL; if (!secure) { - if (*filename != '/') + if (*filename != '/') { + *errmsg = "Only absolute filenames allowed"; return (EACCESS); + } + /* * prevent tricksters from getting around the directory * restrictions @@ -1055,15 +1062,19 @@ validate_access(char *filename, int mode, struct formats *pf) len = strlen(filename); for ( i = 1 ; i < len-3 ; i++ ) { cp = filename + i; - if ( *cp == '.' && memcmp(cp-1, "/../", 4) == 0) + if ( *cp == '.' && memcmp(cp-1, "/../", 4) == 0 ) { + *errmsg = "Reverse path not allowed"; return(EACCESS); + } } for (dirp = dirs; *dirp; dirp++) if (strncmp(filename, *dirp, strlen(*dirp)) == 0) break; - if (*dirp==0 && dirp!=dirs) + if (*dirp==0 && dirp!=dirs) { + *errmsg = "Forbidden directory"; return (EACCESS); + } } /* @@ -1088,7 +1099,7 @@ validate_access(char *filename, int mode, struct formats *pf) case EEXIST: return EEXISTS; default: - return EACCESS; + return errno+100; } } @@ -1096,20 +1107,26 @@ validate_access(char *filename, int mode, struct formats *pf) exit(EX_OSERR); /* This shouldn't happen */ if (mode == RRQ) { - if ( !unixperms && (stbuf.st_mode & (S_IREAD >> 6)) == 0 ) + if ( !unixperms && (stbuf.st_mode & (S_IREAD >> 6)) == 0 ) { + *errmsg = "File must have global read permissions"; return (EACCESS); + } tsize = stbuf.st_size; /* We don't know the tsize if conversion is needed */ tsize_ok = !pf->f_convert; } else { if ( !unixperms ) { - if ( (stbuf.st_mode & (S_IWRITE >> 6)) == 0 ) + if ( (stbuf.st_mode & (S_IWRITE >> 6)) == 0 ) { + *errmsg = "File must have global write permissions"; return (EACCESS); + } /* We didn't get to truncate the file at open() time */ #ifdef HAVE_FTRUNCATE - if ( ftruncate(fd, (off_t)0) ) + if ( ftruncate(fd, (off_t)0) ) { + *errmsg = "Cannot reset file size"; return(EACCESS); + } #endif } tsize = 0; @@ -1138,8 +1155,6 @@ tftp_sendfile(struct formats *pf, struct tftphdr *oap, int oacklen) static u_short block = 1; /* Static to avoid longjmp funnies */ int size, n; - ap = (struct tftphdr *)ackbuf; - if (oap) { timeout = rexmtval; (void)sigsetjmp(timeoutbuf,1); @@ -1154,6 +1169,7 @@ tftp_sendfile(struct formats *pf, struct tftphdr *oap, int oacklen) syslog(LOG_WARNING, "tftpd: read: %m\n"); goto abort; } + ap = (struct tftphdr *)ackbuf; ap->th_opcode = ntohs((u_short)ap->th_opcode); ap->th_block = ntohs((u_short)ap->th_block); @@ -1194,6 +1210,7 @@ tftp_sendfile(struct formats *pf, struct tftphdr *oap, int oacklen) syslog(LOG_WARNING, "tftpd: read(ack): %m"); goto abort; } + ap = (struct tftphdr *)ackbuf; ap->th_opcode = ntohs((u_short)ap->th_opcode); ap->th_block = ntohs((u_short)ap->th_block); From 1f64a5b178af1b9c9ac587bd70b082cb8f597c5c Mon Sep 17 00:00:00 2001 From: hpa Date: Sat, 9 Nov 2002 02:23:39 +0000 Subject: [PATCH 013/184] Now working on 0.33 --- version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version b/version index 00d0c14..94b357e 100644 --- a/version +++ b/version @@ -1 +1 @@ -0.32 +0.33 From 9eef4c82a755307562fa5c1f06f3d2b6752f7a5a Mon Sep 17 00:00:00 2001 From: hpa Date: Sat, 9 Nov 2002 02:24:22 +0000 Subject: [PATCH 014/184] Update for what 0.33 is --- CHANGES | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGES b/CHANGES index b29eaeb..25d6874 100644 --- a/CHANGES +++ b/CHANGES @@ -1,5 +1,11 @@ $Id$ +Changes in 0.33: + Even better error messages. + + Work around a suspect Solaris gcc bug. + + Changes in 0.32: Better error messages; including the capability to send a custom error message to the client when hitting an "a" rule in From 9e952fed98d1fc81e5b9a9810b795036c585c6fb Mon Sep 17 00:00:00 2001 From: hpa Date: Wed, 13 Nov 2002 18:58:23 +0000 Subject: [PATCH 015/184] The original tftp code does a lot of aliasing. --- configure.in | 1 + 1 file changed, 1 insertion(+) diff --git a/configure.in b/configure.in index e365811..2c56170 100644 --- a/configure.in +++ b/configure.in @@ -51,6 +51,7 @@ PA_ADD_CFLAGS(-Wundef) PA_ADD_CFLAGS(-Wshadow) PA_ADD_CFLAGS(-Wsign-compare) PA_ADD_CFLAGS(-pipe) +PA_ADD_CFLAGS(-fno-strict-aliasing) AC_HEADER_STDC AC_CHECK_HEADERS(inttypes.h) From fb83297f030de61581fa494e86bc3388902e6aac Mon Sep 17 00:00:00 2001 From: hpa Date: Fri, 31 Jan 2003 02:17:01 +0000 Subject: [PATCH 016/184] libreadline depends on libtermcap on newer glibc releases, at least... fix autoheader problem --- configure.in | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/configure.in b/configure.in index 2c56170..eee5071 100644 --- a/configure.in +++ b/configure.in @@ -172,13 +172,17 @@ AH_TEMPLATE([WITH_READLINE], PA_WITH_BOOL(readline, 1, [ --without-readline disable the use of readline command-line editing], [ - USE_READLINE=true AC_CHECK_HEADER(readline/readline.h, [ + dnl readline may need libtermcap or somesuch... + AC_SEARCH_LIBS(tputs, [termcap terminfo]) + AC_SEARCH_LIBS(readline, [readline history], [ AC_DEFINE(WITH_READLINE) - ]) + ], + [], + [-ltermcap]) AC_CHECK_HEADERS(readline/history.h) ]) ],:) @@ -194,5 +198,5 @@ AC_PROG_LN_S AC_PROG_RANLIB AC_PROG_INSTALL -AC_CONFIG_HEADER(acconfig.h) +AC_CONFIG_HEADERS(acconfig.h) AC_OUTPUT(MCONFIG) From c11a43fa532ba323d4c301b1015ea2ec55489e5d Mon Sep 17 00:00:00 2001 From: hpa Date: Fri, 31 Jan 2003 02:22:33 +0000 Subject: [PATCH 017/184] Formatting fix --- tftpd/tftpd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tftpd/tftpd.c b/tftpd/tftpd.c index 94781a7..180a02f 100644 --- a/tftpd/tftpd.c +++ b/tftpd/tftpd.c @@ -696,7 +696,7 @@ struct formats { void (*f_recv)(struct formats *, struct tftphdr *, int); int f_convert; } formats[] = { - { "netascii", rewrite_access, validate_access, tftp_sendfile, tftp_recvfile, 1 }, + { "netascii", rewrite_access, validate_access, tftp_sendfile, tftp_recvfile, 1 }, { "octet", rewrite_access, validate_access, tftp_sendfile, tftp_recvfile, 0 }, { NULL, NULL, NULL, NULL, NULL, 0 } }; From cabc969c25d6a66238f29089944499733645a98c Mon Sep 17 00:00:00 2001 From: hpa Date: Fri, 31 Jan 2003 02:24:49 +0000 Subject: [PATCH 018/184] Update CHANGES: configure fix for readline --- CHANGES | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGES b/CHANGES index 25d6874..cfd7e15 100644 --- a/CHANGES +++ b/CHANGES @@ -5,6 +5,8 @@ Changes in 0.33: Work around a suspect Solaris gcc bug. + Configuration fix: readline needs termcap. + Changes in 0.32: Better error messages; including the capability to send a From 8796832723e68c46d186e99d6f06fc0cdaad10c4 Mon Sep 17 00:00:00 2001 From: hpa Date: Mon, 3 Feb 2003 01:29:00 +0000 Subject: [PATCH 019/184] Support running the TFTP client from the command line --- CHANGES | 5 ++ tftp/main.c | 152 +++++++++++++++++++++++++++++++------------------ tftp/tftp.1.in | 8 ++- 3 files changed, 108 insertions(+), 57 deletions(-) diff --git a/CHANGES b/CHANGES index cfd7e15..a6b0dec 100644 --- a/CHANGES +++ b/CHANGES @@ -7,6 +7,11 @@ Changes in 0.33: Configuration fix: readline needs termcap. + Support running the tftp client from the command line. For + example: + + tftp -m binary -c get hostname:file + Changes in 0.32: Better error messages; including the capability to send a diff --git a/tftp/main.c b/tftp/main.c index 7f383fa..661796c 100644 --- a/tftp/main.c +++ b/tftp/main.c @@ -186,70 +186,110 @@ char *xstrdup(const char *); int main(int argc, char *argv[]) { - struct sockaddr_in s_in; - int c; - int pargc; - char **pargv; + struct sockaddr_in s_in; + int o; + int pargc; + int iscmd = 0; + char **pargv; - while ((c = getopt(argc, argv, "Vv")) != -1) { - switch (c) { - case 'v': - verbose = 1; + mode = MODE_DEFAULT; + + while ((o = getopt(argc, argv, "chm:Vv")) != -1) { + switch (o) { + case 'v': + verbose = 1; + break; + case 'V': + /* Print version and configuration to stdout and exit */ + printf("%s\n", TFTP_CONFIG_STR); + exit(0); + case 'm': + { + const struct modes *p; + + for ( p = modes ; p->m_name ; p++ ) { + if (!strcmp(optarg, p->m_name)) break; - case 'V': - /* Print version and configuration to stdout and exit */ - printf("%s\n", TFTP_CONFIG_STR); - exit(0); - default: - fprintf(stderr, "Usage: %s [-v] [host]\n", argv[0]); - exit(EX_USAGE); - } } + if (p->m_name) { + settftpmode(p); + } else { + fprintf(stderr, "%s: invalid mode: %s\n", argv[0], optarg); + exit(EX_USAGE); + } + } + break; + case 'c': + iscmd = 1; + break; + case 'h': + default: + fprintf(stderr, "Usage: %s [-v][-m mode] [-c command|host]\n", argv[0]); + exit(o == 'h' ? 0 : EX_USAGE); + } + } + + pargc = argc - (optind-1); + pargv = argv + (optind-1); + + sp = getservbyname("tftp", "udp"); + if (sp == 0) { + /* Use canned values */ + if (verbose) + fprintf(stderr, "tftp: tftp/udp: unknown service, faking it...\n"); + sp = xmalloc(sizeof(struct servent)); + sp->s_name = (char *)"tftp"; + sp->s_aliases = NULL; + sp->s_port = htons(IPPORT_TFTP); + sp->s_proto = (char *)"udp"; + } + port = sp->s_port; /* Default port */ + f = socket(AF_INET, SOCK_DGRAM, 0); + if (f < 0) { + perror("tftp: socket"); + exit(EX_OSERR); + } + bzero((char *)&s_in, sizeof (s_in)); + s_in.sin_family = AF_INET; + if (bind(f, (struct sockaddr *)&s_in, sizeof (s_in)) < 0) { + perror("tftp: bind"); + exit(EX_OSERR); + } + bsd_signal(SIGINT, intr); + if (pargc > 1) { + if ( iscmd ) { + /* -c specified; execute command and exit */ + struct cmd *c; - pargc = argc - (optind-1); - pargv = argv + (optind-1); - - sp = getservbyname("tftp", "udp"); - if (sp == 0) { - /* Use canned values */ - fprintf(stderr, "tftp: tftp/udp: unknown service, faking it...\n"); - sp = xmalloc(sizeof(struct servent)); - sp->s_name = (char *)"tftp"; - sp->s_aliases = NULL; - sp->s_port = htons(IPPORT_TFTP); - sp->s_proto = (char *)"udp"; - exit(1); - } - f = socket(AF_INET, SOCK_DGRAM, 0); - if (f < 0) { - perror("tftp: socket"); - exit(3); - } - bzero((char *)&s_in, sizeof (s_in)); - s_in.sin_family = AF_INET; - if (bind(f, (struct sockaddr *)&s_in, sizeof (s_in)) < 0) { - perror("tftp: bind"); - exit(1); - } - mode = MODE_DEFAULT; - bsd_signal(SIGINT, intr); - if (pargc > 1) { - if (sigsetjmp(toplevel,1) != 0) - exit(0); - setpeer(pargc, pargv); - } - if (sigsetjmp(toplevel,1) != 0) - (void)putchar('\n'); + if (sigsetjmp(toplevel,1) != 0) + exit(EX_UNAVAILABLE); + c = getcmd(pargv[1]); + if ( c == (struct cmd *)-1 || c == (struct cmd *)0 ) { + fprintf(stderr, "%s: invalid command: %s\n", argv[0], pargv[1]); + exit(EX_USAGE); + } + (*c->handler)(pargc-1, pargv+1); + exit(0); + } else { + /* No -c */ + if (sigsetjmp(toplevel,1) != 0) + exit(0); + setpeer(pargc, pargv); + } + } + if (sigsetjmp(toplevel,1) != 0) + (void)putchar('\n'); + #ifdef WITH_READLINE #ifdef HAVE_READLINE_HISTORY_H - using_history(); + using_history(); #endif #endif - - command(); - - return 0; /* Never reached */ + + command(); + + return 0; /* Never reached */ } char *hostname; @@ -438,7 +478,7 @@ put(int argc, char *argv[]) herror((char *)NULL); return; } - bcopy(hp->h_addr, (caddr_t)&peeraddr.sin_addr, hp->h_length); + bcopy(hp->h_addr, &peeraddr.sin_addr, hp->h_length); peeraddr.sin_family = hp->h_addrtype; connected = 1; hostname = xstrdup(hp->h_name); diff --git a/tftp/tftp.1.in b/tftp/tftp.1.in index 7df6ca0..9994cf4 100644 --- a/tftp/tftp.1.in +++ b/tftp/tftp.1.in @@ -31,7 +31,7 @@ .\" SUCH DAMAGE. .\" .\"----------------------------------------------------------------------- */ -.TH TFTP 1 "13 November 2001" "tftp-hpa @@VERSION@@" "UNIX User's Manual" +.TH TFTP 1 "2 February 2003" "tftp-hpa @@VERSION@@" "User's Manual" .SH NAME .B tftp \- IPv4 Trivial File Transfer Protocol client @@ -55,6 +55,12 @@ as the default host for future transfers (see the command below.) .SH OPTIONS .TP +\fB\-c\fP \fIcommand\fP +Execute \fIcommand\fP as if it had been entered on the tftp prompt. +.TP +\fB\-m\fP \fImode\fP +Set the default transfer mode to \fImode\fP. This is usually used with \-c. +.TP .B \-v Default to verbose mode. .TP From 571deaa36b8ee6a38b27fd296aba6000786ee375 Mon Sep 17 00:00:00 2001 From: hpa Date: Sat, 12 Apr 2003 06:54:58 +0000 Subject: [PATCH 020/184] Apply some code cleanups that apparently fix Solaris 7 gcc problems. --- CHANGES | 5 +++ tftp/tftp.c | 87 ++++++++++++++++++++++++++------------------------- tftpd/tftpd.c | 80 +++++++++++++++++++++++++--------------------- 3 files changed, 93 insertions(+), 79 deletions(-) diff --git a/CHANGES b/CHANGES index a6b0dec..4c2df40 100644 --- a/CHANGES +++ b/CHANGES @@ -1,5 +1,10 @@ $Id$ +Changes in 0.34: + Additional Solaris gcc compiler bug workarounds; these + actually make the code somewhat cleaner. + + Changes in 0.33: Even better error messages. diff --git a/tftp/tftp.c b/tftp/tftp.c index b6c1f5f..0e615e5 100644 --- a/tftp/tftp.c +++ b/tftp/tftp.c @@ -89,6 +89,7 @@ tftp_sendfile(int fd, const char *name, const char *mode) struct sockaddr_in from; int fromlen; FILE *file; + u_short ap_opcode, ap_block; startclock(); /* start stat's clock */ dp = r_init(); /* reset fillbuf/read-ahead code */ @@ -141,17 +142,17 @@ tftp_sendfile(int fd, const char *name, const char *mode) if (trace) tpacket("received", ap, n); /* should verify packet came from server */ - ap->th_opcode = ntohs(ap->th_opcode); - ap->th_block = ntohs(ap->th_block); - if (ap->th_opcode == ERROR) { - printf("Error code %d: %s\n", ap->th_code, + ap_opcode = ntohs((u_short)ap->th_opcode); + ap_block = ntohs((u_short)ap->th_block); + if (ap_opcode == ERROR) { + printf("Error code %d: %s\n", ap_block, ap->th_msg); goto abort; } - if (ap->th_opcode == ACK) { + if (ap_opcode == ACK) { int j; - if (ap->th_block == block) { + if (ap_block == block) { break; } /* On an error, try to synchronize @@ -197,6 +198,7 @@ tftp_recvfile(int fd, const char *name, const char *mode) int fromlen; FILE *file; volatile int convert; /* true if converting crlf -> lf */ + u_short dp_opcode, dp_block; startclock(); dp = w_init(); @@ -214,7 +216,7 @@ tftp_recvfile(int fd, const char *name, const char *mode) firsttrip = 0; } else { ap->th_opcode = htons((u_short)ACK); - ap->th_block = htons((u_short)(block)); + ap->th_block = htons((u_short)block); size = 4; block++; } @@ -246,17 +248,16 @@ send_ack: if (trace) tpacket("received", dp, n); /* should verify client address */ - dp->th_opcode = ntohs(dp->th_opcode); - dp->th_block = ntohs(dp->th_block); - if (dp->th_opcode == ERROR) { - printf("Error code %d: %s\n", dp->th_code, - dp->th_msg); - goto abort; + dp_opcode = ntohs((u_short)dp->th_opcode); + dp_block = ntohs((u_short)dp->th_block); + if (dp_opcode == ERROR) { + printf("Error code %d: %s\n", dp_block, dp->th_msg); + goto abort; } - if (dp->th_opcode == DATA) { + if (dp_opcode == DATA) { int j; - if (dp->th_block == block) { + if (dp_block == block) { break; /* have next packet */ } /* On an error, try to synchronize @@ -266,7 +267,7 @@ send_ack: if (j && trace) { printf("discarded %d packets\n", j); } - if (dp->th_block == (block-1)) { + if (dp_block == (block-1)) { goto send_ack; /* resend ack */ } } @@ -308,21 +309,19 @@ makerequest(int request, const char *name, return (cp - (char *)tp); } -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" }, - { EOPTNEG, "Failure to negotiate RFC2347 options" }, - { -1, 0 } +static const char * const errmsgs[] = +{ + "Undefined error code", /* 0 - EUNDEF */ + "File not found", /* 1 - ENOTFOUND */ + "Access denied", /* 2 - EACCESS */ + "Disk full or allocation exceeded", /* 3 - ENOSPACE */ + "Illegal TFTP operation", /* 4 - EBADOP */ + "Unknown transfer ID", /* 5 - EBADID */ + "File already exists", /* 6 - EEXISTS */ + "No such user", /* 7 - ENOUSER */ + "Failure to negotiate RFC2347 options" /* 8 - EOPTNEG */ }; +#define ERR_CNT (sizeof(errmsgs)/sizeof(const char *)) /* * Send a nak packet (error message). @@ -333,26 +332,28 @@ struct errmsg { static void nak(int error, const char *msg) { - struct errmsg *pe; struct tftphdr *tp; int length; tp = (struct tftphdr *)ackbuf; tp->th_opcode = htons((u_short)ERROR); tp->th_code = htons((u_short)error); - if ( !msg ) { - if ( error >= 100 ) { + + if ( error >= 100 ) { + /* This is a Unix errno+100 */ + if ( !msg ) msg = strerror(error - 100); - tp->th_code = EUNDEF; - } else { - for (pe = errmsgs; pe->e_code >= 0; pe++) { - if (pe->e_code == error) { - msg = pe->e_msg; - break; - } - } - } + error = EUNDEF; + } else { + if ( (unsigned)error >= ERR_CNT ) + error = EUNDEF; + + if ( !msg ) + msg = errmsgs[error]; } + + tp->th_code = htons((u_short)error); + length = strlen(msg)+1; memcpy(tp->th_msg, msg, length); length += 4; /* Add space for header */ @@ -370,7 +371,7 @@ tpacket(const char *s, struct tftphdr *tp, int n) static const char *opcodes[] = { "#0", "RRQ", "WRQ", "DATA", "ACK", "ERROR", "OACK" }; char *cp, *file; - u_short op = ntohs(tp->th_opcode); + u_short op = ntohs((u_short)tp->th_opcode); if (op < RRQ || op > ERROR) printf("%s opcode=%x ", s, op); diff --git a/tftpd/tftpd.c b/tftpd/tftpd.c index 180a02f..fc9a843 100644 --- a/tftpd/tftpd.c +++ b/tftpd/tftpd.c @@ -270,6 +270,7 @@ main(int argc, char **argv) #ifdef WITH_REGEX char *rewrite_file = NULL; #endif + u_short tp_opcode; /* basename() is way too much of a pain from a portability standpoint */ @@ -677,8 +678,8 @@ main(int argc, char **argv) exit(EX_IOERR); } tp = (struct tftphdr *)buf; - tp->th_opcode = ntohs(tp->th_opcode); - if (tp->th_opcode == RRQ || tp->th_opcode == WRQ) + tp_opcode = ntohs(tp->th_opcode); + if (tp_opcode == RRQ || tp_opcode == WRQ) tftp(tp, n); exit(0); } @@ -713,11 +714,12 @@ tftp(struct tftphdr *tp, int size) char *origfilename; char *filename, *mode = NULL; const char *errmsgptr; + u_short tp_opcode = ntohs(tp->th_opcode); char *val = NULL, *opt = NULL; char *ap = ackbuf + 2; - ((struct tftphdr *)ackbuf)->th_opcode = ntohs(OACK); + ((struct tftphdr *)ackbuf)->th_opcode = htons(OACK); origfilename = cp = (char *) &(tp->th_stuff); argn = 0; @@ -749,21 +751,21 @@ tftp(struct tftphdr *tp, int size) exit(0); } if ( !(filename = - (*pf->f_rewrite)(origfilename, tp->th_opcode, &errmsgptr)) ) { + (*pf->f_rewrite)(origfilename, tp_opcode, &errmsgptr)) ) { nak(EACCESS, errmsgptr); /* File denied by mapping rule */ exit(0); } if ( verbosity >= 1 ) { if ( filename == origfilename || !strcmp(filename, origfilename) ) syslog(LOG_NOTICE, "%s from %s filename %s\n", - tp->th_opcode == WRQ ? "WRQ" : "RRQ", + tp_opcode == WRQ ? "WRQ" : "RRQ", inet_ntoa(from.sin_addr), filename); else syslog(LOG_NOTICE, "%s from %s filename %s remapped to %s\n", - tp->th_opcode == WRQ ? "WRQ" : "RRQ", + tp_opcode == WRQ ? "WRQ" : "RRQ", inet_ntoa(from.sin_addr), origfilename, filename); } - ecode = (*pf->f_validate)(filename, tp->th_opcode, pf, &errmsgptr); + ecode = (*pf->f_validate)(filename, tp_opcode, pf, &errmsgptr); if (ecode) { nak(ecode, errmsgptr); exit(0); @@ -783,12 +785,12 @@ tftp(struct tftphdr *tp, int size) } if ( ap != (ackbuf+2) ) { - if ( tp->th_opcode == WRQ ) + if ( tp_opcode == WRQ ) (*pf->f_recv)(pf, (struct tftphdr *)ackbuf, ap-ackbuf); else (*pf->f_send)(pf, (struct tftphdr *)ackbuf, ap-ackbuf); } else { - if (tp->th_opcode == WRQ) + if (tp_opcode == WRQ) (*pf->f_recv)(pf, NULL, 0); else (*pf->f_send)(pf, NULL, 0); @@ -1153,6 +1155,7 @@ tftp_sendfile(struct formats *pf, struct tftphdr *oap, int oacklen) struct tftphdr *dp; struct tftphdr *ap; /* ack packet */ static u_short block = 1; /* Static to avoid longjmp funnies */ + u_short ap_opcode, ap_block; int size, n; if (oap) { @@ -1170,15 +1173,15 @@ tftp_sendfile(struct formats *pf, struct tftphdr *oap, int oacklen) goto abort; } ap = (struct tftphdr *)ackbuf; - ap->th_opcode = ntohs((u_short)ap->th_opcode); - ap->th_block = ntohs((u_short)ap->th_block); + ap_opcode = ntohs((u_short)ap->th_opcode); + ap_block = ntohs((u_short)ap->th_block); - if (ap->th_opcode == ERROR) { + if (ap_opcode == ERROR) { syslog(LOG_WARNING, "tftp: client does not accept options\n"); goto abort; } - if (ap->th_opcode == ACK) { - if (ap->th_block == 0) + if (ap_opcode == ACK) { + if (ap_block == 0) break; /* Resynchronize with the other side */ (void)synchnet(peer); @@ -1211,14 +1214,14 @@ tftp_sendfile(struct formats *pf, struct tftphdr *oap, int oacklen) goto abort; } ap = (struct tftphdr *)ackbuf; - ap->th_opcode = ntohs((u_short)ap->th_opcode); - ap->th_block = ntohs((u_short)ap->th_block); + ap_opcode = ntohs((u_short)ap->th_opcode); + ap_block = ntohs((u_short)ap->th_block); - if (ap->th_opcode == ERROR) + if (ap_opcode == ERROR) goto abort; - if (ap->th_opcode == ACK) { - if (ap->th_block == block) { + if (ap_opcode == ACK) { + if (ap_block == block) { break; } /* Re-synchronize with the other side */ @@ -1258,6 +1261,7 @@ tftp_recvfile(struct formats *pf, struct tftphdr *oap, int oacklen) static struct tftphdr *ap; /* ack buffer */ static u_short block = 0; static int acksize; + u_short dp_opcode, dp_block; dp = w_init(); do { @@ -1286,17 +1290,17 @@ tftp_recvfile(struct formats *pf, struct tftphdr *oap, int oacklen) syslog(LOG_WARNING, "tftpd: read: %m"); 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) + dp_opcode = ntohs((u_short)dp->th_opcode); + dp_block = ntohs((u_short)dp->th_block); + if (dp_opcode == ERROR) goto abort; - if (dp->th_opcode == DATA) { - if (dp->th_block == block) { + if (dp_opcode == DATA) { + if (dp_block == block) { break; /* normal */ } /* Re-synchronize with the other side */ (void) synchnet(peer); - if (dp->th_block == (block-1)) + if (dp_block == (block-1)) goto send_ack; /* rexmit */ } } @@ -1320,8 +1324,8 @@ tftp_recvfile(struct formats *pf, struct tftphdr *oap, int oacklen) timeout_quit = 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 */ + dp_opcode == DATA && /* and got a data block */ + block == dp_block) { /* then my last ack was lost */ (void) send(peer, ackbuf, 4, 0); /* resend final ack */ } abort: @@ -1356,18 +1360,22 @@ nak(int error, const char *msg) tp = (struct tftphdr *)buf; tp->th_opcode = htons((u_short)ERROR); - tp->th_code = htons((u_short)error); - if ( !msg ) { - if ( error >= 100 ) { - /* This is a Unix errno+100 */ + + if ( error >= 100 ) { + /* This is a Unix errno+100 */ + if ( !msg ) msg = strerror(error - 100); - tp->th_code = EUNDEF; - } else { - if ( (unsigned)error >= ERR_CNT ) - tp->th_code = error = EUNDEF; + error = EUNDEF; + } else { + if ( (unsigned)error >= ERR_CNT ) + error = EUNDEF; + + if ( !msg ) msg = errmsgs[error]; - } } + + tp->th_code = htons((u_short)error); + length = strlen(msg)+1; memcpy(tp->th_msg, msg, length); length += 4; /* Add space for header */ From b3941f251adc0d31d307839fd3b97d0d595cf16e Mon Sep 17 00:00:00 2001 From: hpa Date: Sat, 12 Apr 2003 06:56:57 +0000 Subject: [PATCH 021/184] Prepare for 0.34 --- version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version b/version index 94b357e..61d2f35 100644 --- a/version +++ b/version @@ -1 +1 @@ -0.33 +0.34 From 530706aebef96e25dd4bf27416df9d78c83f3bed Mon Sep 17 00:00:00 2001 From: hpa Date: Sat, 23 Aug 2003 00:31:20 +0000 Subject: [PATCH 022/184] #include if it exists; Add option to control the max blksize negotiated. --- CHANGES | 5 +++++ config.h | 10 ++++++++++ configure.in | 5 ++++- tftpd/tftpd.8.in | 9 +++++++++ tftpd/tftpd.c | 27 ++++++++++++++++++++------- version | 2 +- 6 files changed, 49 insertions(+), 9 deletions(-) diff --git a/CHANGES b/CHANGES index 4c2df40..e2379e0 100644 --- a/CHANGES +++ b/CHANGES @@ -1,5 +1,10 @@ $Id$ +Changes in 0.35: + Add an option to control the maximum value of blksize + negotiated. + + Changes in 0.34: Additional Solaris gcc compiler bug workarounds; these actually make the code somewhat cleaner. diff --git a/config.h b/config.h index ef0d408..67dc7ae 100644 --- a/config.h +++ b/config.h @@ -96,7 +96,17 @@ #include #include +#ifdef HAVE_SYS_SOCKET_H #include +#else +#ifdef HAVE_WINSOCK2_H +#include +#else +#ifdef HAVE_WINSOCK_H +#include +#endif +#endif +#endif /* Test for EAGAIN/EWOULDBLOCK */ #ifdef EAGAIN diff --git a/configure.in b/configure.in index eee5071..e9bee9f 100644 --- a/configure.in +++ b/configure.in @@ -77,6 +77,9 @@ AC_CHECK_HEADERS(arpa/inet.h) AC_HEADER_TIME dnl This is needed on some versions of FreeBSD... AC_CHECK_HEADERS(machine/param.h) +AC_CHECK_HEADERS(sys/socket.h) +AC_CHECK_HEADERS(winsock2.h) +AC_CHECK_HEADERS(winsock.h) AC_CHECK_TYPES(intmax_t) AC_CHECK_TYPES(long long) @@ -89,7 +92,7 @@ AC_TYPE_PID_T AC_TYPE_MODE_T AC_TYPE_SIZE_T -AC_SEARCH_LIBS(socket, socket, , [AC_MSG_ERROR(socket library not found)]) +AC_SEARCH_LIBS(socket, [socket ws2_32 wsock32], , [AC_MSG_ERROR(socket library not found)]) AC_SEARCH_LIBS(gethostbyname, [nsl resolv], , [AC_MSG_ERROR(gethostbyname not found)]) AC_SEARCH_LIBS(inet_aton, [nsl resolv], , [AC_MSG_ERROR(inet_aton not found)]) AC_SEARCH_LIBS(herror, [nsl resolv], , [AC_MSG_ERROR(herror not found)]) diff --git a/tftpd/tftpd.8.in b/tftpd/tftpd.8.in index aab8bf6..6462ea0 100644 --- a/tftpd/tftpd.8.in +++ b/tftpd/tftpd.8.in @@ -150,6 +150,15 @@ This flag can be specified multiple times for even higher verbosity. Indicate that a specific RFC 2347 TFTP option should never be accepted. .TP +\fB\-B\fP \fImax-block-size\fP +Specifies the maximum permitted block size. The permitted range for +this parameter is from 512 to 65464. Some embedded clients request +large block sizes and yet do not handle fragmented packets correctly; +for these clients, it is recommended to set this value to the smallest +MTU on your network minus 32 bytes (20 bytes for IP, 8 for UDP, and 4 +for TFTP; less if you use IP options on your network.) For example, +on a standard Ethernet (MTU 1500) a value of 1468 is reasonable. +.TP .B \-V Print the version number and configuration to standard output, then exit gracefully. diff --git a/tftpd/tftpd.c b/tftpd/tftpd.c index fc9a843..6b5b0db 100644 --- a/tftpd/tftpd.c +++ b/tftpd/tftpd.c @@ -92,8 +92,10 @@ int timeout_quit = 0; sigjmp_buf timeoutbuf; #define PKTSIZE MAX_SEGSIZE+4 -char buf[PKTSIZE]; -char ackbuf[PKTSIZE]; +char buf[PKTSIZE]; +char ackbuf[PKTSIZE]; +unsigned int max_blksize = MAX_SEGSIZE; + struct sockaddr_in from; int fromlen; off_t tsize; @@ -279,7 +281,7 @@ main(int argc, char **argv) openlog(__progname, LOG_PID|LOG_NDELAY, LOG_DAEMON); - while ((c = getopt(argc, argv, "cspvVla:u:U:r:t:T:m:")) != -1) + while ((c = getopt(argc, argv, "cspvVla:B:u:U:r:t:T:m:")) != -1) switch (c) { case 'c': cancreate = 1; @@ -299,6 +301,17 @@ main(int argc, char **argv) case 't': waittime = atoi(optarg); break; + case 'B': + { + char *vp; + max_blksize = (unsigned int)strtoul(optarg, &vp, 10); + if ( max_blksize < 512 || max_blksize > MAX_SEGSIZE || *vp ) { + syslog(LOG_ERR, "Bad maximum blocksize value (range 512-%d): %s", + MAX_SEGSIZE, optarg); + exit(EX_USAGE); + } + } + break; case 'T': { char *vp; @@ -817,8 +830,8 @@ set_blksize(char *val, char **ret) if (sz < 8) return(0); - else if (sz > MAX_SEGSIZE) - sz = MAX_SEGSIZE; + else if (sz > max_blksize) + sz = max_blksize; segsize = sz; sprintf(*ret = b_ret, "%u", sz); @@ -845,8 +858,8 @@ set_blksize2(char *val, char **ret) if (sz < 8) return(0); - else if (sz > MAX_SEGSIZE) - sz = MAX_SEGSIZE; + else if (sz > max_blksize) + sz = max_blksize; /* Convert to a power of two */ if ( sz & (sz-1) ) { diff --git a/version b/version index 61d2f35..c74e8a0 100644 --- a/version +++ b/version @@ -1 +1 @@ -0.34 +0.35 From 454370c052fade3a73f3d18da4141fb646d206d9 Mon Sep 17 00:00:00 2001 From: hpa Date: Sat, 23 Aug 2003 00:33:15 +0000 Subject: [PATCH 023/184] Stray crap edit --- tftpd/tftpd.8.in | 1 - 1 file changed, 1 deletion(-) diff --git a/tftpd/tftpd.8.in b/tftpd/tftpd.8.in index 6462ea0..d3ee998 100644 --- a/tftpd/tftpd.8.in +++ b/tftpd/tftpd.8.in @@ -194,7 +194,6 @@ Set the time before the server retransmits a packet, in seconds. .TP \fButimeout\fP (nonstandard) Set the time before the server retransmits a packet, in microseconds. -and .PP The .B \-r From b38a61cf7011efd095494c4894c319fa965f7bb9 Mon Sep 17 00:00:00 2001 From: hpa Date: Mon, 5 Jan 2004 19:41:42 +0000 Subject: [PATCH 024/184] Fixes to help tru64 --- Makefile | 2 +- aclocal.m4 | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index dd860e6..d696eb2 100644 --- a/Makefile +++ b/Makefile @@ -46,7 +46,7 @@ release: $(MAKE) distclean MCONFIG: configure MCONFIG.in acconfig.h.in - ./configure + if test -x config.status; then ./config.status; else ./configure; fi acconfig.h: MCONFIG : Generated by side effect diff --git a/aclocal.m4 b/aclocal.m4 index 3fd7844..f53cea9 100644 --- a/aclocal.m4 +++ b/aclocal.m4 @@ -20,7 +20,7 @@ AC_DEFUN(PA_ADD_CFLAGS, [AC_MSG_CHECKING([if $CC accepts $1]) pa_add_cflags__old_cflags="$CFLAGS" CFLAGS="$CFLAGS $1" - AC_TRY_COMPILE([#include ], + AC_TRY_LINK([#include ], [printf("Hello, World!\n");], AC_MSG_RESULT([yes]), AC_MSG_RESULT([no]) From 0eea078aece1b1b945c7f74dc3230dc9834826a6 Mon Sep 17 00:00:00 2001 From: hpa Date: Thu, 8 Jan 2004 20:47:00 +0000 Subject: [PATCH 025/184] Command-line parsing that doesn't depend on getopt() --- tftp/main.c | 134 +++++++++++++++++++++++++++++-------------------- tftp/tftp.1.in | 6 ++- tftp/tftp.c | 16 +++--- 3 files changed, 92 insertions(+), 64 deletions(-) diff --git a/tftp/main.c b/tftp/main.c index 661796c..961c0c4 100644 --- a/tftp/main.c +++ b/tftp/main.c @@ -183,54 +183,79 @@ char *tail(char *); char *xstrdup(const char *); +const char *program; + +static inline void usage(int errcode) +{ + fprintf(stderr, "Usage: %s [-v][-m mode] [host [port]] [-c command]\n", program); + exit(errcode); +} + int main(int argc, char *argv[]) { struct sockaddr_in s_in; - int o; - int pargc; - int iscmd = 0; + int arg; + static int pargc, peerargc; + static int iscmd = 0; char **pargv; + const char *optx; + char *peerargv[3]; + + program = argv[0]; mode = MODE_DEFAULT; - - while ((o = getopt(argc, argv, "chm:Vv")) != -1) { - switch (o) { - case 'v': - verbose = 1; - break; - case 'V': - /* Print version and configuration to stdout and exit */ - printf("%s\n", TFTP_CONFIG_STR); - exit(0); - case 'm': - { - const struct modes *p; - for ( p = modes ; p->m_name ; p++ ) { - if (!strcmp(optarg, p->m_name)) - break; - } - if (p->m_name) { - settftpmode(p); - } else { - fprintf(stderr, "%s: invalid mode: %s\n", argv[0], optarg); - exit(EX_USAGE); + peerargv[0] = argv[0]; + peerargc = 1; + + for ( arg = 1 ; !iscmd && arg < argc ; arg++ ) { + if ( argv[arg][0] == '-' ) { + for ( optx = &argv[arg][1] ; *optx ; optx++ ) { + switch ( *optx ) { + case 'v': + verbose = 1; + break; + case 'V': + /* Print version and configuration to stdout and exit */ + printf("%s\n", TFTP_CONFIG_STR); + exit(0); + case 'm': + if ( ++arg >= argc ) + usage(EX_USAGE); + { + const struct modes *p; + + for ( p = modes ; p->m_name ; p++ ) { + if (!strcmp(argv[arg], p->m_name)) + break; + } + if (p->m_name) { + settftpmode(p); + } else { + fprintf(stderr, "%s: invalid mode: %s\n", argv[0], argv[arg]); + exit(EX_USAGE); + } + } + break; + case 'c': + iscmd = 1; + break; + case 'h': + default: + usage(*optx == 'h' ? 0 : EX_USAGE); } } - break; - case 'c': - iscmd = 1; - break; - case 'h': - default: - fprintf(stderr, "Usage: %s [-v][-m mode] [-c command|host]\n", argv[0]); - exit(o == 'h' ? 0 : EX_USAGE); + } else { + if ( peerargc >= 3 ) + usage(EX_USAGE); + + peerargv[peerargc++] = argv[arg]; } } - pargc = argc - (optind-1); - pargv = argv + (optind-1); + pargv = argv + arg; + pargc = argc - arg; sp = getservbyname("tftp", "udp"); if (sp == 0) { @@ -256,27 +281,28 @@ main(int argc, char *argv[]) exit(EX_OSERR); } bsd_signal(SIGINT, intr); - if (pargc > 1) { - if ( iscmd ) { - /* -c specified; execute command and exit */ - struct cmd *c; - if (sigsetjmp(toplevel,1) != 0) - exit(EX_UNAVAILABLE); + if ( peerargc ) { + /* Set peer */ + if (sigsetjmp(toplevel,1) != 0) + exit(EX_NOHOST); + setpeer(peerargc, peerargv); + } - c = getcmd(pargv[1]); - if ( c == (struct cmd *)-1 || c == (struct cmd *)0 ) { - fprintf(stderr, "%s: invalid command: %s\n", argv[0], pargv[1]); - exit(EX_USAGE); - } - (*c->handler)(pargc-1, pargv+1); - exit(0); - } else { - /* No -c */ - if (sigsetjmp(toplevel,1) != 0) - exit(0); - setpeer(pargc, pargv); - } + if ( iscmd && pargc ) { + /* -c specified; execute command and exit */ + struct cmd *c; + + if (sigsetjmp(toplevel,1) != 0) + exit(EX_UNAVAILABLE); + + c = getcmd(pargv[0]); + if ( c == (struct cmd *)-1 || c == (struct cmd *)0 ) { + fprintf(stderr, "%s: invalid command: %s\n", argv[0], pargv[1]); + exit(EX_USAGE); + } + (*c->handler)(pargc, pargv); + exit(0); } if (sigsetjmp(toplevel,1) != 0) (void)putchar('\n'); diff --git a/tftp/tftp.1.in b/tftp/tftp.1.in index 9994cf4..9ba9a85 100644 --- a/tftp/tftp.1.in +++ b/tftp/tftp.1.in @@ -37,8 +37,9 @@ \- IPv4 Trivial File Transfer Protocol client .SH SYNOPSIS .B tftp -.RI [ options... ] -.RI [ host ] +[ \fIoptions...\fP ] +[\fIhost\fP [\fIport\fP]] +[\fB\-c\fP \fIcommand\fP] .br .SH DESCRIPTION .B tftp @@ -57,6 +58,7 @@ command below.) .TP \fB\-c\fP \fIcommand\fP Execute \fIcommand\fP as if it had been entered on the tftp prompt. +Must be specified last on the command line. .TP \fB\-m\fP \fImode\fP Set the default transfer mode to \fImode\fP. This is usually used with \-c. diff --git a/tftp/tftp.c b/tftp/tftp.c index 0e615e5..475d25b 100644 --- a/tftp/tftp.c +++ b/tftp/tftp.c @@ -421,14 +421,14 @@ static void printstats(const char *direction, unsigned long amount) { double delta; - /* compute delta in 1/10's second units */ - delta = ((tstop.tv_sec*10.)+(tstop.tv_usec/100000)) - - ((tstart.tv_sec*10.)+(tstart.tv_usec/100000)); - delta = delta/10.; /* back to seconds */ - printf("%s %lu bytes in %.1f seconds", direction, amount, delta); - if (verbose) - printf(" [%.0f bits/sec]", (amount*8.)/delta); - putchar('\n'); + + delta = (tstop.tv_sec+(tstop.tv_usec/100000.0)) - + (tstart.tv_sec+(tstart.tv_usec/100000.0)); + if (verbose) { + printf("%s %lu bytes in %.1f seconds", direction, amount, delta); + printf(" [%.0f bit/s]", (amount*8.)/delta); + putchar('\n'); + } } static void From 9c3ab465f2f289ad08a5d4d5eee2b30015f5edfd Mon Sep 17 00:00:00 2001 From: hpa Date: Thu, 8 Jan 2004 20:48:30 +0000 Subject: [PATCH 026/184] Remove broken Cygwin workaround --- tftpd/tftpd.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/tftpd/tftpd.c b/tftpd/tftpd.c index 6b5b0db..af6f253 100644 --- a/tftpd/tftpd.c +++ b/tftpd/tftpd.c @@ -487,9 +487,7 @@ main(int argc, char **argv) close(0); close(1); close(2); } #ifdef HAVE_SETSID -#ifndef __CYGWIN__ /* Kills the process on Cygwin? */ setsid(); -#endif #endif } } else { From d05bd6fa96cd93d8cfc4c9936bf4e874e349e80f Mon Sep 17 00:00:00 2001 From: hpa Date: Thu, 8 Jan 2004 20:48:51 +0000 Subject: [PATCH 027/184] Makefile/configure cleanups; prepare for release --- CHANGES | 16 ++++++++++++++++ Makefile | 28 ++++++++++++++++------------ config.h | 2 +- configure.in | 4 ++-- 4 files changed, 35 insertions(+), 15 deletions(-) diff --git a/CHANGES b/CHANGES index e2379e0..c76e123 100644 --- a/CHANGES +++ b/CHANGES @@ -4,6 +4,22 @@ Changes in 0.35: Add an option to control the maximum value of blksize negotiated. + Removed workaround for obsolete Cygwin problem. + + Don't use getopt() -- the -c option doesn't work correctly + since it depends on the ordering of arguments and options. It + is now possible to do: + + tftp -m binary hostname -c get filename + + This was previous possible by doing: + + tftp -m binary -c get hostname:filename + + ... but it seemed that was counterintuitive to people. + + Somewhat improved configure scripts. + Changes in 0.34: Additional Solaris gcc compiler bug workarounds; these diff --git a/Makefile b/Makefile index d696eb2..16ac441 100644 --- a/Makefile +++ b/Makefile @@ -3,10 +3,10 @@ SUB = lib tftp tftpd -%.build: MCONFIG acconfig.h version.h +%.build: MCONFIG aconfig.h version.h $(MAKE) -C $(patsubst %.build, %, $@) -%.install: MCONFIG acconfig.h version.h +%.install: MCONFIG aconfig.h version.h $(MAKE) -C $(patsubst %.install, %, $@) install %.clean: @@ -30,36 +30,40 @@ localclean: distclean: localdistclean $(patsubst %, %.distclean, $(SUB)) localdistclean: localclean - rm -f MCONFIG config.status config.log acconfig.h *~ \#* + rm -f MCONFIG config.status config.log aconfig.h *~ \#* rm -rf *.cache find . -type f \( -name \*.orig -o -name \*.rej \) | xargs rm -f spotless: distclean - rm -f configure acconfig.h.in + rm -f configure aconfig.h.in -autoconf: configure acconfig.h.in +autoconf: configure aconfig.h.in -config: MCONFIG acconfig.h +config: MCONFIG aconfig.h release: $(MAKE) autoconf $(MAKE) distclean -MCONFIG: configure MCONFIG.in acconfig.h.in - if test -x config.status; then ./config.status; else ./configure; fi +MCONFIG: configure MCONFIG.in aconfig.h.in + if test -x config.status; then \ + ./config.status --recheck && ./config.status ; \ + else \ + ./configure ; \ + fi -acconfig.h: MCONFIG +aconfig.h: MCONFIG : Generated by side effect # Adding "configure" to the dependencies serializes this with running # autoconf, because there are apparently race conditions between # autoconf and autoheader. -acconfig.h.in: configure.in configure aclocal.m4 - rm -f acconfig.h.in acconfig.h +aconfig.h.in: configure.in configure aclocal.m4 + rm -f aconfig.h.in aconfig.h autoheader configure: configure.in aclocal.m4 - rm -f MCONFIG configure config.log acconfig.h config.cache + rm -rf MCONFIG configure config.log aconfig.h *.cache autoconf version.h: version diff --git a/config.h b/config.h index 67dc7ae..6a99b73 100644 --- a/config.h +++ b/config.h @@ -19,7 +19,7 @@ #define CONFIG_H 1 /* Must be included before we include any system headers! */ -#include "acconfig.h" +#include "aconfig.h" /* autogenerated configuration header */ /* Standard includes */ diff --git a/configure.in b/configure.in index e9bee9f..ea1753c 100644 --- a/configure.in +++ b/configure.in @@ -14,7 +14,7 @@ AC_PROG_CC dnl dnl Feature-test macros. These need to be set in CFLAGS, rather in -dnl acconfig.h, or "configure" will run in a different environment than +dnl aconfig.h, or "configure" will run in a different environment than dnl we eventually we build in. dnl @@ -201,5 +201,5 @@ AC_PROG_LN_S AC_PROG_RANLIB AC_PROG_INSTALL -AC_CONFIG_HEADERS(acconfig.h) +AC_CONFIG_HEADERS(aconfig.h) AC_OUTPUT(MCONFIG) From 0740f97365591cde7f67972557ab838821cef789 Mon Sep 17 00:00:00 2001 From: hpa Date: Wed, 14 Jan 2004 18:37:56 +0000 Subject: [PATCH 028/184] Create a sensible solution to the libxtra path problem. --- MCONFIG.in | 4 ++++ configure.in | 15 ++++++++++++--- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/MCONFIG.in b/MCONFIG.in index 68aab37..5788e81 100644 --- a/MCONFIG.in +++ b/MCONFIG.in @@ -15,6 +15,10 @@ ## Basic Makefile definitions ## +# Source and object root +SRCROOT = @SRCROOT@ +OBJROOT = @OBJROOT@ + # Prefixes prefix = @prefix@ exec_prefix = @exec_prefix@ diff --git a/configure.in b/configure.in index ea1753c..2ec102c 100644 --- a/configure.in +++ b/configure.in @@ -122,12 +122,18 @@ AH_TEMPLATE([HAVE_SIGSETJMP], [Define if we have sigsetjmp, siglongjmp and sigjmp_buf.]) PA_SIGSETJMP([AC_DEFINE(HAVE_SIGSETJMP)]) +dnl +dnl Get common paths +dnl +SRCROOT=`cd $srcdir && pwd` +OBJROOT=`pwd` + LIBXTRA=false AC_SEARCH_LIBS(xmalloc, iberty, , LIBXTRA=true AC_LIBOBJ(xmalloc)) AC_SEARCH_LIBS(xstrdup, iberty, , LIBXTRA=true AC_LIBOBJ(xstrdup)) AC_SEARCH_LIBS(bsd_signal, bsd, , LIBXTRA=true AC_LIBOBJ(bsdsignal)) if $LIBXTRA; then - LIBS="../lib/libxtra.a $LIBS" + XTRALIBS="$OBJROOT/lib/libxtra.a $XTRALIBS" fi dnl @@ -162,7 +168,7 @@ PA_WITH_BOOL(remap, 1, ]) ],:) -TFTPD_LIBS="$LIBS" +TFTPD_LIBS="$LIBS $XTRALIBS" LIBS="$common_libs" dnl @@ -190,9 +196,12 @@ PA_WITH_BOOL(readline, 1, ]) ],:) -TFTP_LIBS="$LIBS" +TFTP_LIBS="$LIBS $XTRALIBS" LIBS="$common_libs" +AC_SUBST(SRCROOT) +AC_SUBST(OBJROOT) + AC_SUBST(TFTP_LIBS) AC_SUBST(TFTPD_LIBS) AC_SUBST(TFTPDOBJS) From 59672620321a3c104ef680392eaa7de7e5a7ac9f Mon Sep 17 00:00:00 2001 From: hpa Date: Thu, 15 Jan 2004 20:15:46 +0000 Subject: [PATCH 029/184] Handle the use of install-sh making sure we absolutize the path. --- configure.in | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/configure.in b/configure.in index 2ec102c..4bb0e2c 100644 --- a/configure.in +++ b/configure.in @@ -208,7 +208,17 @@ AC_SUBST(TFTPDOBJS) AC_PROG_LN_S AC_PROG_RANLIB + +dnl +dnl Make sure the install program has an absolute path if it +dnl has a path at all. autoconf doesn't do this "in order +dnl to not pollute the cache." Sigh. +dnl Note: the $ needs to be double-quoted for reasons unknown. +dnl AC_PROG_INSTALL +[if echo "$INSTALL" | grep '^[^/].*/' > /dev/null 2>&1; then + INSTALL='\${SRCROOT}'/"$INSTALL" +fi] AC_CONFIG_HEADERS(aconfig.h) AC_OUTPUT(MCONFIG) From c4a04c3da2a684b6b43cb91160d0efa6ae7d54f6 Mon Sep 17 00:00:00 2001 From: hpa Date: Thu, 15 Jan 2004 20:16:29 +0000 Subject: [PATCH 030/184] Prepare for 0.36 release. --- CHANGES | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGES b/CHANGES index c76e123..5aae695 100644 --- a/CHANGES +++ b/CHANGES @@ -1,5 +1,9 @@ $Id$ +Changes in 0.36: + Portability fixes. + + Changes in 0.35: Add an option to control the maximum value of blksize negotiated. From 0a85ac914b8223a2275aacc90e76a73b38a9e01c Mon Sep 17 00:00:00 2001 From: hpa Date: Thu, 15 Jan 2004 20:16:46 +0000 Subject: [PATCH 031/184] Update version for release --- version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version b/version index c74e8a0..2037dfa 100644 --- a/version +++ b/version @@ -1 +1 @@ -0.35 +0.36 From 5fbd57b61a4d9646930c706f176218423a17cc6a Mon Sep 17 00:00:00 2001 From: hpa Date: Thu, 15 Jan 2004 20:21:42 +0000 Subject: [PATCH 032/184] Use the correct magic for long long --- config.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/config.h b/config.h index 6a99b73..a3370ff 100644 --- a/config.h +++ b/config.h @@ -134,9 +134,9 @@ #ifdef HAVE_LONG_LONG typedef long long intmax_t; typedef unsigned long long uintmax_t; -#define PRIdMAX "Ld" -#define PRIuMAX "Lu" -#define PRIxMAX "Lx" +#define PRIdMAX "lld" +#define PRIuMAX "llu" +#define PRIxMAX "llx" #define INTMAX_C(x) (x##LL) #define UINTMAX_C(x) (x##ULL) #else From 2cba51056f17f8698dd41327d12661c3b5602276 Mon Sep 17 00:00:00 2001 From: hpa Date: Sun, 13 Jun 2004 21:11:24 +0000 Subject: [PATCH 033/184] Fix a pathology where a client sending ACKs for the wrong packet can prevent proper retransmission. --- CHANGES | 5 +++++ tftpd/tftpd.c | 35 ++++++++++++++++++++++++----------- 2 files changed, 29 insertions(+), 11 deletions(-) diff --git a/CHANGES b/CHANGES index 5aae695..5a7a296 100644 --- a/CHANGES +++ b/CHANGES @@ -1,5 +1,10 @@ $Id$ +Changes in 0.37: + Fix a pathology where a client sending ACKs for the wrong + packet can prevent proper retransmission. + + Changes in 0.36: Portability fixes. diff --git a/tftpd/tftpd.c b/tftpd/tftpd.c index af6f253..fbadadf 100644 --- a/tftpd/tftpd.c +++ b/tftpd/tftpd.c @@ -207,25 +207,37 @@ set_socket_nonblock(int fd, int flag) } /* - * Receive packet with synchronous timeout + * Receive packet with synchronous timeout; timeout is adjusted + * to account for time spent waiting. */ static int recv_time(int s, void *rbuf, int len, unsigned int flags, - unsigned long timeout_us) + unsigned long *timeout_us_p) { fd_set fdset; - struct timeval tmv; + struct timeval tmv, t0, t1; int rv, err; + unsigned long timeout_us = *timeout_us_p; + unsigned long timeout_left, dt; + + gettimeofday(&t0, NULL); + timeout_left = timeout_us; for ( ; ; ) { FD_ZERO(&fdset); FD_SET(s, &fdset); - tmv.tv_sec = timeout_us / 1000000; - tmv.tv_usec = timeout_us % 1000000; - do { + tmv.tv_sec = timeout_left / 1000000; + tmv.tv_usec = timeout_left % 1000000; + rv = select(s+1, &fdset, NULL, NULL, &tmv); - } while ( rv == -1 && errno == EINTR ); + err = errno; + + gettimeofday(&t1, NULL); + + dt = (t1.tv_sec - t0.tv_sec)*1000000 + (t1.tv_usec - t0.tv_usec); + *timeout_us_p = timeout_left = ( dt >= timeout_us ) ? 1 : (timeout_us - dt); + } while ( rv == -1 && err == EINTR ); if ( rv == 0 ) { timer(0); /* Should not return */ @@ -236,6 +248,7 @@ static int recv_time(int s, void *rbuf, int len, unsigned int flags, rv = recv(s, rbuf, len, flags); err = errno; set_socket_nonblock(s, 0); + if ( rv < 0 ) { if ( E_WOULD_BLOCK(err) || err == EINTR ) { continue; /* Once again, with feeling... */ @@ -1178,7 +1191,7 @@ tftp_sendfile(struct formats *pf, struct tftphdr *oap, int oacklen) goto abort; } for ( ; ; ) { - n = recv_time(peer, ackbuf, sizeof(ackbuf), 0, timeout); + n = recv_time(peer, ackbuf, sizeof(ackbuf), 0, &timeout); if (n < 0) { syslog(LOG_WARNING, "tftpd: read: %m\n"); goto abort; @@ -1219,7 +1232,7 @@ tftp_sendfile(struct formats *pf, struct tftphdr *oap, int oacklen) } read_ahead(file, pf->f_convert); for ( ; ; ) { - n = recv_time(peer, ackbuf, sizeof (ackbuf), 0, timeout); + n = recv_time(peer, ackbuf, sizeof (ackbuf), 0, &timeout); if (n < 0) { syslog(LOG_WARNING, "tftpd: read(ack): %m"); goto abort; @@ -1296,7 +1309,7 @@ tftp_recvfile(struct formats *pf, struct tftphdr *oap, int oacklen) } write_behind(file, pf->f_convert); for ( ; ; ) { - n = recv_time(peer, dp, PKTSIZE, 0, timeout); + n = recv_time(peer, dp, PKTSIZE, 0, &timeout); if (n < 0) { /* really? */ syslog(LOG_WARNING, "tftpd: read: %m"); goto abort; @@ -1331,7 +1344,7 @@ tftp_recvfile(struct formats *pf, struct tftphdr *oap, int oacklen) (void) send(peer, ackbuf, 4, 0); timeout_quit = 1; /* just quit on timeout */ - n = recv_time(peer, buf, sizeof (buf), 0, timeout); /* normally times out and quits */ + n = recv_time(peer, buf, sizeof (buf), 0, &timeout); /* normally times out and quits */ timeout_quit = 0; if (n >= 4 && /* if read some data */ From 5f27c18a36cfc47f3abdde04887a7ae82a279c0b Mon Sep 17 00:00:00 2001 From: hpa Date: Tue, 20 Jul 2004 16:43:27 +0000 Subject: [PATCH 034/184] Configuration fixes --- configure.in | 11 ++++------- version | 2 +- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/configure.in b/configure.in index 4bb0e2c..fc56a73 100644 --- a/configure.in +++ b/configure.in @@ -128,13 +128,10 @@ dnl SRCROOT=`cd $srcdir && pwd` OBJROOT=`pwd` -LIBXTRA=false -AC_SEARCH_LIBS(xmalloc, iberty, , LIBXTRA=true AC_LIBOBJ(xmalloc)) -AC_SEARCH_LIBS(xstrdup, iberty, , LIBXTRA=true AC_LIBOBJ(xstrdup)) -AC_SEARCH_LIBS(bsd_signal, bsd, , LIBXTRA=true AC_LIBOBJ(bsdsignal)) -if $LIBXTRA; then - XTRALIBS="$OBJROOT/lib/libxtra.a $XTRALIBS" -fi +AC_SEARCH_LIBS(xmalloc, iberty, , [AC_LIBOBJ(xmalloc)]) +AC_SEARCH_LIBS(xstrdup, iberty, , [AC_LIBOBJ(xstrdup)]) +AC_SEARCH_LIBS(bsd_signal, bsd, , [AC_LIBOBJ(bsdsignal)]) +XTRALIBS="$OBJROOT/lib/libxtra.a $XTRALIBS" dnl dnl These libraries apply to the server only diff --git a/version b/version index 2037dfa..c128d4d 100644 --- a/version +++ b/version @@ -1 +1 @@ -0.36 +0.37 From f9f549c866e046c411e585f906483997e630bc87 Mon Sep 17 00:00:00 2001 From: hpa Date: Tue, 20 Jul 2004 16:44:51 +0000 Subject: [PATCH 035/184] Update to 0.38 --- version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version b/version index c128d4d..1cbc812 100644 --- a/version +++ b/version @@ -1 +1 @@ -0.37 +0.38 From 490ecf7d4a465ac20d3ddee47b7f2ed0d0ca6f74 Mon Sep 17 00:00:00 2001 From: hpa Date: Tue, 20 Jul 2004 16:46:13 +0000 Subject: [PATCH 036/184] Portability fixes. --- CHANGES | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGES b/CHANGES index 5a7a296..298f10b 100644 --- a/CHANGES +++ b/CHANGES @@ -1,5 +1,9 @@ $Id$ +Changes in 0.38: + Portability fixes. + + Changes in 0.37: Fix a pathology where a client sending ACKs for the wrong packet can prevent proper retransmission. From 0ca2afec32266b18f059e838c3f1e204e0ee9b94 Mon Sep 17 00:00:00 2001 From: hpa Date: Fri, 3 Sep 2004 00:42:33 +0000 Subject: [PATCH 037/184] Fix handling of getopt variables. --- config.h | 7 +++++++ configure.in | 1 + 2 files changed, 8 insertions(+) diff --git a/config.h b/config.h index a3370ff..6e2125b 100644 --- a/config.h +++ b/config.h @@ -108,6 +108,13 @@ #endif #endif +#ifdef HAVE_GETOPT_H +#include +#else +extern char *optarg; +extern int optind, opterr, optopt; +#endif + /* Test for EAGAIN/EWOULDBLOCK */ #ifdef EAGAIN #if defined(EWOULDBLOCK) && (EWOULDBLOCK != EAGAIN) diff --git a/configure.in b/configure.in index fc56a73..117006c 100644 --- a/configure.in +++ b/configure.in @@ -58,6 +58,7 @@ AC_CHECK_HEADERS(inttypes.h) AC_CHECK_HEADERS(stdint.h) PA_CHECK_INTTYPES_H_SANE AC_CHECK_HEADERS(fcntl.h) +AC_CHECK_HEADERS(getopt.h) AC_CHECK_HEADERS(grp.h) AC_CHECK_HEADERS(libgen.h) AC_CHECK_HEADERS(memory.h) From bc8e3b32ee5048db22833454af2623e953a2a200 Mon Sep 17 00:00:00 2001 From: hpa Date: Fri, 3 Sep 2004 23:46:08 +0000 Subject: [PATCH 038/184] Next version will be 0.39 --- version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version b/version index 1cbc812..751b1ea 100644 --- a/version +++ b/version @@ -1 +1 @@ -0.38 +0.39 From 17b5188b655164c1ff711b070bf6021b6d3a3a74 Mon Sep 17 00:00:00 2001 From: hpa Date: Tue, 14 Sep 2004 22:38:46 +0000 Subject: [PATCH 039/184] - Fix bug in the handling of timeouts. - Add support for \U..\E and \L..\E. - Add support for inverse rules. --- CHANGES | 7 +++++ tftpd/remap.c | 78 ++++++++++++++++++++++++++++++++++++++++-------- tftpd/tftpd.8.in | 22 ++++++++++++-- tftpd/tftpd.c | 11 +++++-- 4 files changed, 101 insertions(+), 17 deletions(-) diff --git a/CHANGES b/CHANGES index 298f10b..171a7ef 100644 --- a/CHANGES +++ b/CHANGES @@ -1,5 +1,12 @@ $Id$ +Changes in 0.39: + Support Perl-style \U...\E and \L...\E, as well as allow + matching rules to be inverted (execute if rule *doesn't* + match.) + + Fix a timeout bug. + Changes in 0.38: Portability fixes. diff --git a/tftpd/remap.c b/tftpd/remap.c index 0faa7b8..229ece5 100644 --- a/tftpd/remap.c +++ b/tftpd/remap.c @@ -1,7 +1,7 @@ /* $Id$ */ /* ----------------------------------------------------------------------- * * - * Copyright 2001-2002 H. Peter Anvin - All Rights Reserved + * Copyright 2001-2004 H. Peter Anvin - All Rights Reserved * * This program is free software available under the same license * as the "OpenBSD" operating system, distributed at @@ -33,6 +33,7 @@ #define RULE_ABORT 0x10 /* Terminate processing with an error */ #define RULE_GETONLY 0x20 /* Applicable to GET only */ #define RULE_PUTONLY 0x40 /* Applicable to PUT only */ +#define RULE_INVERSE 0x80 /* Execute if regex *doesn't* match */ struct rule { struct rule *next; @@ -42,12 +43,28 @@ struct rule { const char *pattern; }; +static int xform_null(int c) +{ + return c; +} + +static int xform_toupper(int c) +{ + return toupper(c); +} + +static int xform_tolower(int c) +{ + return tolower(c); +} + /* Do \-substitution. Call with string == NULL to get length only. */ static int genmatchstring(char *string, const char *pattern, const char *input, const regmatch_t *pmatch, match_pattern_callback macrosub) { + int (*xform)(int) = xform_null; int len = 0; - int n, mlen; + int n, mlen, sublen; int endbytes; /* Get section before match; note pmatch[0] is the whole match */ @@ -60,9 +77,13 @@ static int genmatchstring(char *string, const char *pattern, const char *input, /* Transform matched section */ while ( *pattern ) { + mlen = 0; + if ( *pattern == '\\' && pattern[1] != '\0' ) { char macro = pattern[1]; - if ( macro >= '0' && macro <= '9' ) { + switch ( macro ) { + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': n = pattern[1] - '0'; if ( pmatch[n].rm_so != -1 ) { @@ -73,24 +94,41 @@ static int genmatchstring(char *string, const char *pattern, const char *input, string += mlen; } } - } else { - int sublen; + break; + + case 'L': + xform = xform_tolower; + break; + + case 'U': + xform = xform_toupper; + break; + + case 'E': + xform = xform_null; + break; + + default: if ( macrosub && (sublen = macrosub(macro, string)) >= 0 ) { - len += sublen; - if ( string ) - string += sublen; + while ( sublen-- ) { + len++; + if ( string ) { + *string = xform(*string); + string++; + } + } } else { len++; if ( string ) - *string++ = pattern[1]; + *string++ = xform(pattern[1]); } } pattern += 2; } else { len++; if ( string ) - *string++ = *pattern; + *string++ = xform(*pattern); pattern++; } } @@ -181,6 +219,9 @@ static int parseline(char *line, struct rule *r, int lineno) case 'P': r->rule_flags |= RULE_PUTONLY; break; + case '~': + r->rule_flags |= RULE_INVERSE; + break; default: syslog(LOG_ERR, "Remap command \"%s\" on line %d contains invalid char \"%c\"", buffer, lineno, *p); @@ -193,6 +234,11 @@ static int parseline(char *line, struct rule *r, int lineno) if ( !(r->rule_flags & RULE_REWRITE) ) r->rule_flags &= ~RULE_GLOBAL; + if ( r->rule_flags & (RULE_INVERSE|RULE_REWRITE) ) { + syslog(LOG_ERR, "r rules cannot be inverted, line %d: %s\n", lineno, line); + return -1; /* Error */ + } + /* Read and compile the regex */ if ( !readescstring(buffer, &line) ) { syslog(LOG_ERR, "No regex on remap line %d: %s\n", lineno, line); @@ -303,10 +349,18 @@ char *rewrite_string(const char *input, const struct rule *rules, } do { - if ( regexec(&ruleptr->rx, current, 10, pmatch, 0) == 0 ) { + if ( regexec(&ruleptr->rx, current, 10, pmatch, 0) == + (ruleptr->rule_flags & RULE_INVERSE ? REG_NOMATCH : 0) ) { /* Match on this rule */ was_match = 1; - + + if ( ruleptr->rule_flags & RULE_INVERSE ) { + /* No actual match, so clear out the pmatch array */ + int i; + for ( i = 0 ; i < 10 ; i++ ) + pmatch[i].rm_so = pmatch[i].rm_eo = -1; + } + if ( ruleptr->rule_flags & RULE_ABORT ) { if ( verbosity >= 3 ) { syslog(LOG_INFO, "remap: rule %d: abort: %s", diff --git a/tftpd/tftpd.8.in b/tftpd/tftpd.8.in index d3ee998..644639b 100644 --- a/tftpd/tftpd.8.in +++ b/tftpd/tftpd.8.in @@ -31,7 +31,7 @@ .\" SUCH DAMAGE. .\" .\"----------------------------------------------------------------------- */ -.TH TFTPD 8 "23 October 2002" "tftp-hpa @@VERSION@@" "System Manager's Manual" +.TH TFTPD 8 "3 September 2004" "tftp-hpa @@VERSION@@" "System Manager's Manual" .SH NAME .B tftpd \- IPv4 Trivial File Transfer Protocol server @@ -261,6 +261,15 @@ This rule applies to GET (RRQ) requests only. .TP .B P This rule applies to PUT (WRQ) requests only. +.TP +.B ~ +Inverse the sense of this rule, i.e. execute the +.I operation +only if the +.I regex +.I doesn't +match. Cannot used together with +.BR r . .PP The following escape sequences are recognized as part of the .IR "replacement pattern" : @@ -289,8 +298,17 @@ Literal backslash. \fB\\\fP\fIwhitespace\fP Literal whitespace. .TP -\fB\\#\fI +\fB\\#\fP Literal hash mark. +.TP +\fB\\U\fP +Turns all subsequent letters to upper case. +.TP +\fB\\L\fP +Turns all subsequent letters to lower case. +.TP +\fB\\E\fP +Cancels the effect of \fB\\U\fP or \fB\\L\fP. .PP If the mapping file is changed, you need to send .B SIGHUP diff --git a/tftpd/tftpd.c b/tftpd/tftpd.c index fbadadf..9c26726 100644 --- a/tftpd/tftpd.c +++ b/tftpd/tftpd.c @@ -1180,18 +1180,20 @@ tftp_sendfile(struct formats *pf, struct tftphdr *oap, int oacklen) struct tftphdr *ap; /* ack packet */ static u_short block = 1; /* Static to avoid longjmp funnies */ u_short ap_opcode, ap_block; + unsigned long r_timeout; int size, n; if (oap) { timeout = rexmtval; (void)sigsetjmp(timeoutbuf,1); oack: + r_timeout = timeout; if (send(peer, oap, oacklen, 0) != oacklen) { syslog(LOG_WARNING, "tftpd: oack: %m\n"); goto abort; } for ( ; ; ) { - n = recv_time(peer, ackbuf, sizeof(ackbuf), 0, &timeout); + n = recv_time(peer, ackbuf, sizeof(ackbuf), 0, &r_timeout); if (n < 0) { syslog(LOG_WARNING, "tftpd: read: %m\n"); goto abort; @@ -1226,13 +1228,14 @@ tftp_sendfile(struct formats *pf, struct tftphdr *oap, int oacklen) timeout = rexmtval; (void) sigsetjmp(timeoutbuf,1); + r_timeout = timeout; if (send(peer, dp, size + 4, 0) != size + 4) { syslog(LOG_WARNING, "tftpd: write: %m"); goto abort; } read_ahead(file, pf->f_convert); for ( ; ; ) { - n = recv_time(peer, ackbuf, sizeof (ackbuf), 0, &timeout); + n = recv_time(peer, ackbuf, sizeof (ackbuf), 0, &r_timeout); if (n < 0) { syslog(LOG_WARNING, "tftpd: read(ack): %m"); goto abort; @@ -1286,6 +1289,7 @@ tftp_recvfile(struct formats *pf, struct tftphdr *oap, int oacklen) static u_short block = 0; static int acksize; u_short dp_opcode, dp_block; + unsigned long r_timeout; dp = w_init(); do { @@ -1303,13 +1307,14 @@ tftp_recvfile(struct formats *pf, struct tftphdr *oap, int oacklen) block++; (void) sigsetjmp(timeoutbuf,1); send_ack: + r_timeout = timeout; if (send(peer, ackbuf, acksize, 0) != acksize) { syslog(LOG_WARNING, "tftpd: write(ack): %m"); goto abort; } write_behind(file, pf->f_convert); for ( ; ; ) { - n = recv_time(peer, dp, PKTSIZE, 0, &timeout); + n = recv_time(peer, dp, PKTSIZE, 0, &r_timeout); if (n < 0) { /* really? */ syslog(LOG_WARNING, "tftpd: read: %m"); goto abort; From 1405c06baba58f4fb5630d7b0c2c1fc2262e45e4 Mon Sep 17 00:00:00 2001 From: hpa Date: Tue, 14 Sep 2004 22:48:36 +0000 Subject: [PATCH 040/184] Add spec file for rpm -ta --- CHANGES | 3 + Makefile | 7 +- tftp-xinetd | 18 ++++ tftp.spec.in | 228 +++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 255 insertions(+), 1 deletion(-) create mode 100644 tftp-xinetd create mode 100644 tftp.spec.in diff --git a/CHANGES b/CHANGES index 171a7ef..71e93f1 100644 --- a/CHANGES +++ b/CHANGES @@ -7,6 +7,9 @@ Changes in 0.39: Fix a timeout bug. + Add an RPM spec file. + + Changes in 0.38: Portability fixes. diff --git a/Makefile b/Makefile index 16ac441..0c73fcf 100644 --- a/Makefile +++ b/Makefile @@ -35,7 +35,7 @@ localdistclean: localclean find . -type f \( -name \*.orig -o -name \*.rej \) | xargs rm -f spotless: distclean - rm -f configure aconfig.h.in + rm -f configure aconfig.h.in tftp.spec autoconf: configure aconfig.h.in @@ -43,6 +43,7 @@ config: MCONFIG aconfig.h release: $(MAKE) autoconf + $(MAKE) tftp.spec $(MAKE) distclean MCONFIG: configure MCONFIG.in aconfig.h.in @@ -68,3 +69,7 @@ configure: configure.in aclocal.m4 version.h: version echo \#define VERSION \"tftp-hpa `cat version`\" > version.h + +tftp.spec: tftp.spec.in version + sed -e "s/@@VERSION@@/`cat version`/g" < $< > $@ || rm -f $@ + diff --git a/tftp-xinetd b/tftp-xinetd new file mode 100644 index 0000000..982fe09 --- /dev/null +++ b/tftp-xinetd @@ -0,0 +1,18 @@ +# default: off +# description: The tftp server serves files using the trivial file transfer \ +# protocol. The tftp protocol is often used to boot diskless \ +# workstations, download configuration files to network-aware printers, \ +# and to start the installation process for some operating systems. +service tftp +{ + socket_type = dgram + protocol = udp + wait = yes + user = root + server = /usr/sbin/in.tftpd + server_args = -s /tftpboot + disable = yes + per_source = 11 + cps = 100 2 + flags = IPv4 +} diff --git a/tftp.spec.in b/tftp.spec.in new file mode 100644 index 0000000..c8453a2 --- /dev/null +++ b/tftp.spec.in @@ -0,0 +1,228 @@ +Summary: The client for the Trivial File Transfer Protocol (TFTP). +Name: tftp +Version: @@VERSION@@ +Release: 3 +Copyright: BSD +Group: Applications/Internet +Source0: http://www.kernel.org/pub/software/network/tftp/tftp-hpa-%{tftp_hpa_version}.tar.gz +BuildPreReq: tcp_wrappers +BuildRoot: %{_tmppath}/%{name}-root + +%description +The Trivial File Transfer Protocol (TFTP) is normally used only for +booting diskless workstations. The tftp package provides the user +interface for TFTP, which allows users to transfer files to and from a +remote machine. This program and TFTP provide very little security, +and should not be enabled unless it is expressly needed. + +%package server +Group: System Environment/Daemons +Summary: The server for the Trivial File Transfer Protocol (TFTP). +Requires: xinetd + +%description server +The Trivial File Transfer Protocol (TFTP) is normally used only for +booting diskless workstations. The tftp-server package provides the +server for TFTP, which allows users to transfer files to and from a +remote machine. TFTP provides very little security, and should not be +enabled unless it is expressly needed. The TFTP server is run from +/etc/xinetd.d/tftp, and is disabled by default on Red Hat Linux systems. + +%prep +%setup -q -n tftp-hpa-%{version} + +%build + +%configure +make %{?_smp_mflags} + +%install +rm -rf ${RPM_BUILD_ROOT} +mkdir -p ${RPM_BUILD_ROOT}%{_bindir} +mkdir -p ${RPM_BUILD_ROOT}%{_mandir}/man{1,8} +mkdir -p ${RPM_BUILD_ROOT}%{_sbindir} + +make INSTALLROOT=${RPM_BUILD_ROOT} \ + SBINDIR=%{_sbindir} MANDIR=%{_mandir} \ + install +install -m755 -d ${RPM_BUILD_ROOT}%{_sysconfdir}/xinetd.d/ ${RPM_BUILD_ROOT}/tftpboot +install -m644 tftp-xinetd ${RPM_BUILD_ROOT}%{_sysconfdir}/xinetd.d/tftp + +%post server +/sbin/service xinetd reload > /dev/null 2>&1 || : + +%postun server +if [ $1 = 0 ]; then + /sbin/service xinetd reload > /dev/null 2>&1 || : +fi + +%clean +rm -rf ${RPM_BUILD_ROOT} + +%files +%defattr(-,root,root) +%{_bindir}/tftp +%{_mandir}/man1/* + +%files server +%defattr(-,root,root) +%config(noreplace) %{_sysconfdir}/xinetd.d/tftp +%dir /tftpboot +%{_sbindir}/in.tftpd +%{_mandir}/man8/* + +%changelog +* Tue Sep 14 2004 H. Peter Anvin +- removed completely broken "Malta" patch. +- integrated into build machinery so rpm -ta works. + +* Fri Feb 13 2004 Elliot Lee +- rebuilt + +* Wed Jun 04 2003 Elliot Lee +- rebuilt + +* Fri Apr 11 2003 Elliot Lee +- 0.33 +- Add /tftpboot directory (#88204) + +* Mon Feb 24 2003 Elliot Lee +- rebuilt + +* Sun Feb 23 2003 Tim Powers +- add BuildPreReq on tcp_wrappers + +* Wed Jan 22 2003 Tim Powers +- rebuilt + +* Mon Nov 11 2002 Elliot Lee 0.32-1 +- Update to 0.32 + +* Wed Oct 23 2002 Elliot Lee 0.30-1 +- Fix #55789 +- Update to 0.30 + +* Thu Jun 27 2002 Elliot Lee +- Try applying HJ's patch from #65476 + +* Fri Jun 21 2002 Tim Powers +- automated rebuild + +* Mon Jun 17 2002 Elliot Lee +- Update to 0.29 + +* Thu May 23 2002 Tim Powers +- automated rebuild + +* Wed Jan 09 2002 Tim Powers +- automated rebuild + +* Tue Dec 18 2001 Elliot Lee 0.17-15 +- Add patch4: netkit-tftp-0.17-defaultport.patch for bug #57562 +- Update to tftp-hpa-0.28 (bug #56131) +- Remove include/arpa/tftp.h to fix #57259 +- Add resource limits in tftp-xinetd (#56722) + +* Sun Jun 24 2001 Elliot Lee +- Bump release + rebuild. + +* Tue Jun 12 2001 Helge Deller (0.17-13) +- updated tftp-hpa source to tftp-hpa-0.17 +- tweaked specfile with different defines for tftp-netkit and tftp-hpa version +- use hpa's tftpd.8 man page instead of the netkits one + +* Mon May 07 2001 Helge Deller +- rebuilt in 7.1.x + +* Wed Apr 18 2001 Helge Deller +- fix tftp client's put problems (#29529) +- update to tftp-hpa-0.16 + +* Wed Apr 4 2001 Jakub Jelinek +- don't let configure to guess compiler, it can pick up egcs + +* Thu Feb 08 2001 Helge Deller +- changed "wait" in xinetd file to "yes" (hpa-tftpd forks and exits) (#26467) +- fixed hpa-tftpd to handle files greater than 32MB (#23725) +- added "-l" flag to hpa-tftpd for file-logging (#26467) +- added description for "-l" to the man-page + +* Thu Feb 08 2001 Helge Deller +- updated tftp client to 0.17 stable (#19640), +- drop dependency on xinetd for tftp client (#25051), + +* Wed Jan 17 2001 Jeff Johnson +- xinetd shouldn't wait on tftp (which forks) (#23923). + +* Sat Jan 6 2001 Jeff Johnson +- fix to permit tftp put's (#18128). +- startup as root with chroot to /tftpboot with early reversion to nobody + is preferable to starting as nobody w/o ability to chroot. +- %%post is needed by server, not client. Add %%postun for erasure as well. + +* Wed Aug 23 2000 Nalin Dahyabhai +- default to being disabled + +* Thu Aug 17 2000 Jeff Johnson +- correct group. + +* Tue Jul 25 2000 Nalin Dahyabhai +- change user from root to nobody + +* Sat Jul 22 2000 Jeff Johnson +- update to tftp-hpa-0.14 (#14003). +- add server_args (#14003). +- remove -D_BSD_SOURCE (#14003). + +* Fri Jul 21 2000 Nalin Dahyabhai +- cook up an xinetd config file for tftpd + +* Wed Jul 12 2000 Prospector +- automatic rebuild + +* Sun Jun 18 2000 Jeff Johnson +- FHS packaging. +- update to 0.17. + +* Fri May 5 2000 Matt Wilson +- use _BSD_SOURCE for hpa's tftpd so we get BSD signal semantics. + +* Fri Feb 11 2000 Bill Nottingham +- fix description + +* Wed Feb 9 2000 Jeff Johnson +- compress man pages (again). + +* Wed Feb 02 2000 Cristian Gafton +- man pages are compressed +- fix description and summary + +* Tue Jan 4 2000 Bill Nottingham +- split client and server + +* Tue Dec 21 1999 Jeff Johnson +- update to 0.16. + +* Sat Aug 28 1999 Jeff Johnson +- update to 0.15. + +* Wed Apr 7 1999 Jeff Johnson +- tftpd should truncate file when overwriting (#412) + +* Sun Mar 21 1999 Cristian Gafton +- auto rebuild in the new build environment (release 22) + +* Mon Mar 15 1999 Jeff Johnson +- compile for 6.0. + +* Fri Aug 7 1998 Jeff Johnson +- build root + +* Mon Apr 27 1998 Prospector System +- translations modified for de, fr, tr + +* Mon Sep 22 1997 Erik Troan +- added check for getpwnam() failure + +* Tue Jul 15 1997 Erik Troan +- initial build From 28a2be925af99269fb0d8bb95807627d8040ed1a Mon Sep 17 00:00:00 2001 From: hpa Date: Tue, 14 Sep 2004 22:51:39 +0000 Subject: [PATCH 041/184] Fix undefined macro --- tftp.spec.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tftp.spec.in b/tftp.spec.in index c8453a2..05e96e2 100644 --- a/tftp.spec.in +++ b/tftp.spec.in @@ -4,7 +4,7 @@ Version: @@VERSION@@ Release: 3 Copyright: BSD Group: Applications/Internet -Source0: http://www.kernel.org/pub/software/network/tftp/tftp-hpa-%{tftp_hpa_version}.tar.gz +Source0: http://www.kernel.org/pub/software/network/tftp/tftp-hpa-%{version}.tar.gz BuildPreReq: tcp_wrappers BuildRoot: %{_tmppath}/%{name}-root From 6299024e7a50cd36d95d9ef812b1bfe80baa4f67 Mon Sep 17 00:00:00 2001 From: hpa Date: Tue, 14 Sep 2004 22:54:17 +0000 Subject: [PATCH 042/184] Make release = 1 --- tftp.spec.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tftp.spec.in b/tftp.spec.in index 05e96e2..820a6fb 100644 --- a/tftp.spec.in +++ b/tftp.spec.in @@ -1,7 +1,7 @@ Summary: The client for the Trivial File Transfer Protocol (TFTP). Name: tftp Version: @@VERSION@@ -Release: 3 +Release: 1 Copyright: BSD Group: Applications/Internet Source0: http://www.kernel.org/pub/software/network/tftp/tftp-hpa-%{version}.tar.gz From 9630aae0061ae3fc1f4fe9c82ca055610d1cf073 Mon Sep 17 00:00:00 2001 From: hpa Date: Thu, 23 Sep 2004 02:22:02 +0000 Subject: [PATCH 043/184] Correct bug which caused "r" rules to get incorrectly rejected as "ri" rules --- CHANGES | 5 +++++ tftpd/remap.c | 3 ++- version | 2 +- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/CHANGES b/CHANGES index 71e93f1..8d0cd03 100644 --- a/CHANGES +++ b/CHANGES @@ -1,5 +1,10 @@ $Id$ +Changes in 0.40: + Fix bug which would cause "r" remapping rules to be + incorrectly rejected. + + Changes in 0.39: Support Perl-style \U...\E and \L...\E, as well as allow matching rules to be inverted (execute if rule *doesn't* diff --git a/tftpd/remap.c b/tftpd/remap.c index 229ece5..3fa0fc0 100644 --- a/tftpd/remap.c +++ b/tftpd/remap.c @@ -234,7 +234,8 @@ static int parseline(char *line, struct rule *r, int lineno) if ( !(r->rule_flags & RULE_REWRITE) ) r->rule_flags &= ~RULE_GLOBAL; - if ( r->rule_flags & (RULE_INVERSE|RULE_REWRITE) ) { + if ( (r->rule_flags & (RULE_INVERSE|RULE_REWRITE)) == + (RULE_INVERSE|RULE_REWRITE) ) { syslog(LOG_ERR, "r rules cannot be inverted, line %d: %s\n", lineno, line); return -1; /* Error */ } diff --git a/version b/version index 751b1ea..744f3d7 100644 --- a/version +++ b/version @@ -1 +1 @@ -0.39 +0.40 From d86d06e13101c58a6706f86f960a3a008b605645 Mon Sep 17 00:00:00 2001 From: hpa Date: Fri, 8 Oct 2004 23:25:35 +0000 Subject: [PATCH 044/184] Change cvs-real -> cvs --- release.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/release.sh b/release.sh index c97c66b..8481bc3 100755 --- a/release.sh +++ b/release.sh @@ -4,7 +4,7 @@ # Script for generating a release # -CVS='cvs-real -d hpa@terminus.zytor.com:/home/hpa/cvsroot' +CVS='cvs -d hpa@terminus.zytor.com:/home/hpa/cvsroot' MODULE=tftp PACKAGE=tftp-hpa From d1f0406637db0aa1a5441ef297e3d41e812b5c7f Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Sun, 25 Sep 2005 17:12:56 -0700 Subject: [PATCH 045/184] New release script --- release.sh | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/release.sh b/release.sh index 8481bc3..288ae77 100755 --- a/release.sh +++ b/release.sh @@ -1,11 +1,8 @@ -#!/bin/sh -x -# $Id$ +#!/bin/sh -xe # # Script for generating a release # -CVS='cvs -d hpa@terminus.zytor.com:/home/hpa/cvsroot' -MODULE=tftp PACKAGE=tftp-hpa if [ -z "$1" ]; then @@ -14,22 +11,24 @@ if [ -z "$1" ]; then fi release="$1" -cvsrelease=$PACKAGE-`echo "$release" | tr '.' '_'` +releasetag=$PACKAGE-$release releasedir=$PACKAGE-$release -echo $release > version -$CVS commit -m 'Update version for release' version +GIT_DIR=`cd "${GIT_DIR-.git}" && pwd` +export GIT_DIR -$CVS tag -F $cvsrelease +echo $release > version +cg-commit -m 'Update version for release' +rm -f "$GIT_DIR"/refs/tags/$releasetag +cg-tag $releasetag here=`pwd` tmpdir=/var/tmp/release.$$ rm -rf $tmpdir -mkdir $tmpdir +mkdir -p $tmpdir cd $tmpdir -$CVS export -r $cvsrelease $MODULE -mv $MODULE $releasedir +cg-export -r $releasetag $releasedir cd $releasedir make release rm -f release.sh From 547d611d537134f479e5e3ccfe3cc7cf86c595db Mon Sep 17 00:00:00 2001 From: Peter Anvin Date: Mon, 26 Sep 2005 10:47:27 -0700 Subject: [PATCH 046/184] Don't build empty libxtra; Solaris can't build an empty archive --- configure.in | 12 ++++++++---- lib/Makefile | 4 ++++ 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/configure.in b/configure.in index 117006c..5166ca4 100644 --- a/configure.in +++ b/configure.in @@ -129,10 +129,14 @@ dnl SRCROOT=`cd $srcdir && pwd` OBJROOT=`pwd` -AC_SEARCH_LIBS(xmalloc, iberty, , [AC_LIBOBJ(xmalloc)]) -AC_SEARCH_LIBS(xstrdup, iberty, , [AC_LIBOBJ(xstrdup)]) -AC_SEARCH_LIBS(bsd_signal, bsd, , [AC_LIBOBJ(bsdsignal)]) -XTRALIBS="$OBJROOT/lib/libxtra.a $XTRALIBS" +XTRA=false +AC_SEARCH_LIBS(xmalloc, iberty, , [XTRA=true ; AC_LIBOBJ(xmalloc)]) +AC_SEARCH_LIBS(xstrdup, iberty, , [XTRA=true ; AC_LIBOBJ(xstrdup)]) +AC_SEARCH_LIBS(bsd_signal, bsd, , [XTRA=true ; AC_LIBOBJ(bsdsignal)]) +if $XTRA +then + XTRALIBS="$OBJROOT/lib/libxtra.a $XTRALIBS" +fi dnl dnl These libraries apply to the server only diff --git a/lib/Makefile b/lib/Makefile index 615a3fa..a43ce19 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -7,7 +7,11 @@ SRCROOT = .. -include ../MCONFIG include ../MRULES +ifeq ($(LIBOBJS),) +all: +else all: libxtra.a +endif install: From 759ff9e29b45466387027ca2bc83e167f99146e8 Mon Sep 17 00:00:00 2001 From: Peter Anvin Date: Mon, 26 Sep 2005 10:48:05 -0700 Subject: [PATCH 047/184] Update version for release --- version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version b/version index 744f3d7..d0a1915 100644 --- a/version +++ b/version @@ -1 +1 @@ -0.40 +0.40.1 From cbf26dca811026d69d775d0953da2645044a0d8c Mon Sep 17 00:00:00 2001 From: Peter Anvin Date: Thu, 13 Oct 2005 10:38:08 -0700 Subject: [PATCH 048/184] 0.41 is the next version --- version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version b/version index d0a1915..a7d6788 100644 --- a/version +++ b/version @@ -1 +1 @@ -0.40.1 +0.41 From 8a2f69609767fd71d9f88367e6088c07e8ff3b66 Mon Sep 17 00:00:00 2001 From: Peter Anvin Date: Thu, 13 Oct 2005 10:41:10 -0700 Subject: [PATCH 049/184] Handle transformations with matches --- tftpd/remap.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tftpd/remap.c b/tftpd/remap.c index 3fa0fc0..dda5632 100644 --- a/tftpd/remap.c +++ b/tftpd/remap.c @@ -90,8 +90,9 @@ static int genmatchstring(char *string, const char *pattern, const char *input, mlen = pmatch[n].rm_eo - pmatch[n].rm_so; len += mlen; if ( string ) { - memcpy(string, input+pmatch[n].rm_so, mlen); - string += mlen; + const char *p = input+pmatch[n].rm_so; + while ( mlen-- ) + *string++ = xform(*p++); } } break; From b52fe8501752d98a57f41b8364d331001cd5efc0 Mon Sep 17 00:00:00 2001 From: Peter Anvin Date: Thu, 13 Oct 2005 10:42:12 -0700 Subject: [PATCH 050/184] Update history --- CHANGES | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGES b/CHANGES index 8d0cd03..374f628 100644 --- a/CHANGES +++ b/CHANGES @@ -1,4 +1,11 @@ -$Id$ +Changes in 0.41: + Fix bug by which patterns of the form \U\1 weren't converted + correctly. + + +Changes in 0.40.1: + Solaris build fix. + Changes in 0.40: Fix bug which would cause "r" remapping rules to be From a3af5d93a34518dda96cd9f7359f9ba5b2cda21d Mon Sep 17 00:00:00 2001 From: Peter Anvin Date: Thu, 13 Oct 2005 10:48:11 -0700 Subject: [PATCH 051/184] Only commit version if necessary --- release.sh | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/release.sh b/release.sh index 288ae77..f1a83a0 100755 --- a/release.sh +++ b/release.sh @@ -17,8 +17,11 @@ releasedir=$PACKAGE-$release GIT_DIR=`cd "${GIT_DIR-.git}" && pwd` export GIT_DIR -echo $release > version -cg-commit -m 'Update version for release' +echo $release > version.new +if ! cmp -s version version.new ; then + mv -f version.new version + cg-commit -m 'Update version for release' version +fi rm -f "$GIT_DIR"/refs/tags/$releasetag cg-tag $releasetag From 4cee9cc9ee46f1d68a879a9860e32dcd4bc5f7e0 Mon Sep 17 00:00:00 2001 From: Peter Anvin Date: Thu, 13 Oct 2005 10:48:34 -0700 Subject: [PATCH 052/184] Remove version.new if superfluous --- release.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/release.sh b/release.sh index f1a83a0..27d617b 100755 --- a/release.sh +++ b/release.sh @@ -21,6 +21,8 @@ echo $release > version.new if ! cmp -s version version.new ; then mv -f version.new version cg-commit -m 'Update version for release' version +else + rm -f version.new fi rm -f "$GIT_DIR"/refs/tags/$releasetag cg-tag $releasetag From 7976458c8ba5a08a38a5b04c3a8c4f613ccd4ceb Mon Sep 17 00:00:00 2001 From: Peter Anvin Date: Thu, 13 Oct 2005 11:16:23 -0700 Subject: [PATCH 053/184] Define _GNU_SOURCE --- configure.in | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/configure.in b/configure.in index 5166ca4..faff15e 100644 --- a/configure.in +++ b/configure.in @@ -24,8 +24,7 @@ CFLAGS="$CFLAGS -D_XOPEN_SOURCE" CFLAGS="$CFLAGS -D__EXTENSIONS__" dnl Needed on some glibc systems -CFLAGS="$CFLAGS -D_BSD_SOURCE" -CFLAGS="$CFLAGS -D_ISO9X_SOURCE" +CFLAGS="$CFLAGS -D_GNU_SOURCE" dnl Needed on Digital Unix CFLAGS="$CFLAGS -D_OSF_SOURCE" From 25d63c415a548ac400acc63f687b25f35262e948 Mon Sep 17 00:00:00 2001 From: Peter Anvin Date: Thu, 13 Oct 2005 11:16:40 -0700 Subject: [PATCH 054/184] Disable path MTU discovery by default. It's useless for TFTP. --- tftpd/tftpd.c | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/tftpd/tftpd.c b/tftpd/tftpd.c index 9c26726..8c3033e 100644 --- a/tftpd/tftpd.c +++ b/tftpd/tftpd.c @@ -187,7 +187,7 @@ read_remap_rules(const char *file) } #endif -static inline void +static void set_socket_nonblock(int fd, int flag) { int err; @@ -206,6 +206,16 @@ set_socket_nonblock(int fd, int flag) } } +static void +pmtu_discovery_off(int fd) +{ +#if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT) + int pmtu = IP_PMTUDISC_DONT; + + setsockopt(fd, IPPROTO_IP, IP_MTU_DISCOVER, &pmtu, sizeof(pmtu)); +#endif +} + /* * Receive packet with synchronous timeout; timeout is adjusted * to account for time spent waiting. @@ -508,6 +518,9 @@ main(int argc, char **argv) close(1); close(2); } + /* Disable path MTU discovery */ + pmtu_discovery_off(0); + /* This means we don't want to wait() for children */ #ifdef SA_NOCLDWAIT set_signal(SIGCHLD, SIG_IGN, SA_NOCLDSTOP|SA_NOCLDWAIT); @@ -701,6 +714,10 @@ main(int argc, char **argv) syslog(LOG_ERR, "connect: %m"); exit(EX_IOERR); } + + /* Disable path MTU discovery */ + pmtu_discovery_off(0); + tp = (struct tftphdr *)buf; tp_opcode = ntohs(tp->th_opcode); if (tp_opcode == RRQ || tp_opcode == WRQ) From ae1305e2d56d0a90d6e25f04006412f73cea8ba2 Mon Sep 17 00:00:00 2001 From: Peter Anvin Date: Thu, 13 Oct 2005 11:30:45 -0700 Subject: [PATCH 055/184] Local port range functionality hack --- tftpd/tftpd.c | 53 +++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 43 insertions(+), 10 deletions(-) diff --git a/tftpd/tftpd.c b/tftpd/tftpd.c index 8c3033e..f859afd 100644 --- a/tftpd/tftpd.c +++ b/tftpd/tftpd.c @@ -104,11 +104,12 @@ int tsize_ok; int ndirs; const char **dirs; -int secure = 0; -int cancreate = 0; -int unixperms = 0; - -int verbosity = 0; +int secure = 0; +int cancreate = 0; +int unixperms = 0; +int portrange = 0; +unsigned int portrange_from, portrange_to; +int verbosity = 0; struct formats; #ifdef WITH_REGEX @@ -303,8 +304,10 @@ main(int argc, char **argv) __progname = (p && p[1]) ? p+1 : argv[0]; openlog(__progname, LOG_PID|LOG_NDELAY, LOG_DAEMON); + + srand(time(NULL) ^ getpid()); - while ((c = getopt(argc, argv, "cspvVla:B:u:U:r:t:T:m:")) != -1) + while ((c = getopt(argc, argv, "cspvVla:B:u:U:r:t:T:R:m:")) != -1) switch (c) { case 'c': cancreate = 1; @@ -347,6 +350,16 @@ main(int argc, char **argv) maxtimeout = rexmtval*TIMEOUT_LIMIT; } break; + case 'R': + { + if ( sscanf(optarg, "%u:%u", &portrange_from, &portrange_to) != 2 || + portrange_from > portrange_to || portrange_to >= 65535 ) { + syslog(LOG_ERR, "Bad port range: %s", optarg); + exit(EX_USAGE); + } + portrange = 1; + } + break; case 'u': user = optarg; break; @@ -705,11 +718,31 @@ main(int argc, char **argv) /* Process the request... */ - myaddr.sin_port = htons(0); /* We want a new local port */ - if (bind(peer, (struct sockaddr *)&myaddr, sizeof myaddr) < 0) { - syslog(LOG_ERR, "bind: %m"); - exit(EX_IOERR); + while(1) { + unsigned int port; + + if ( portrange ) { + /* Pick a (pseudo)random port in the relevant range */ + port = portrange_from + rand() % (portrange_to-portrange_from+1); + } else { + port = 0; /* Let the kernel pick a port */ + } + + + myaddr.sin_port = htons(port); + + if (bind(peer, (struct sockaddr *)&myaddr, sizeof myaddr) < 0) { + if ( (errno == EINVAL || errno == EADDRINUSE) && portrange ) + continue; /* Should not happen in normal operation, but try again */ + + syslog(LOG_ERR, "bind: %m"); + exit(EX_IOERR); + } else { + break; + } } + + if (connect(peer, (struct sockaddr *)&from, sizeof from) < 0) { syslog(LOG_ERR, "connect: %m"); exit(EX_IOERR); From 4187af0e6f0c79e3892bc55ea1bf7b594f00381e Mon Sep 17 00:00:00 2001 From: Peter Anvin Date: Thu, 13 Oct 2005 11:41:37 -0700 Subject: [PATCH 056/184] Cleaner version implementing port range --- tftpd/tftpd.c | 60 ++++++++++++++++++++++++++++++--------------------- 1 file changed, 35 insertions(+), 25 deletions(-) diff --git a/tftpd/tftpd.c b/tftpd/tftpd.c index f859afd..30d20a3 100644 --- a/tftpd/tftpd.c +++ b/tftpd/tftpd.c @@ -273,6 +273,37 @@ static int recv_time(int s, void *rbuf, int len, unsigned int flags, } } +static int +pick_port_bind(int sockfd, struct sockaddr_in *myaddr) +{ + unsigned int port, firstport; + + firstport = portrange + ? portrange_from + rand() % (portrange_to-portrange_from+1) + : 0; + + port = firstport; + + do { + myaddr->sin_port = htons(port); + + if (bind(sockfd, (struct sockaddr *)myaddr, sizeof *myaddr) < 0) { + if ( portrange && (errno == EINVAL || errno == EADDRINUSE) ) + continue; /* Should not happen in normal operation, but try again */ + + return -1; + } else { + return 0; + } + + port++; + if ( port > portrange_to ) + port = portrange_from; + } while ( port != firstport ); + + return -1; +} + int main(int argc, char **argv) { @@ -717,32 +748,11 @@ main(int argc, char **argv) from.sin_family = AF_INET; /* Process the request... */ - - while(1) { - unsigned int port; - - if ( portrange ) { - /* Pick a (pseudo)random port in the relevant range */ - port = portrange_from + rand() % (portrange_to-portrange_from+1); - } else { - port = 0; /* Let the kernel pick a port */ - } - - - myaddr.sin_port = htons(port); - - if (bind(peer, (struct sockaddr *)&myaddr, sizeof myaddr) < 0) { - if ( (errno == EINVAL || errno == EADDRINUSE) && portrange ) - continue; /* Should not happen in normal operation, but try again */ - - syslog(LOG_ERR, "bind: %m"); - exit(EX_IOERR); - } else { - break; - } + if (pick_port_bind(peer, &myaddr) < 0) { + syslog(LOG_ERR, "bind: %m"); + exit(EX_IOERR); } - - + if (connect(peer, (struct sockaddr *)&from, sizeof from) < 0) { syslog(LOG_ERR, "connect: %m"); exit(EX_IOERR); From ccb6289984f7046145f399c1bfd6566fe8ee11d0 Mon Sep 17 00:00:00 2001 From: Peter Anvin Date: Thu, 13 Oct 2005 15:20:41 -0700 Subject: [PATCH 057/184] Actually do loop... --- tftpd/tftpd.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/tftpd/tftpd.c b/tftpd/tftpd.c index 30d20a3..5e2a26e 100644 --- a/tftpd/tftpd.c +++ b/tftpd/tftpd.c @@ -288,10 +288,12 @@ pick_port_bind(int sockfd, struct sockaddr_in *myaddr) myaddr->sin_port = htons(port); if (bind(sockfd, (struct sockaddr *)myaddr, sizeof *myaddr) < 0) { - if ( portrange && (errno == EINVAL || errno == EADDRINUSE) ) - continue; /* Should not happen in normal operation, but try again */ + /* Some versions of Linux return EINVAL instead of EADDRINUSE */ + if ( !portrange || !(errno != EINVAL && errno != EADDRINUSE) ) + return -1; - return -1; + /* Normally, we shouldn't have to loop, but some situations involving + aborted transfers make it possible. */ } else { return 0; } From 5336233982fc9ca9b21bc2148d006584bbe959c2 Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Thu, 16 Feb 2006 08:17:59 -0800 Subject: [PATCH 058/184] Spell IP_RECVDSTADDR correctly --- tftpd/recvfrom.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tftpd/recvfrom.c b/tftpd/recvfrom.c index 9112a6b..22440ce 100644 --- a/tftpd/recvfrom.c +++ b/tftpd/recvfrom.c @@ -1,7 +1,6 @@ -/* $Id$ */ /* ----------------------------------------------------------------------- * * - * Copyright 2001 H. Peter Anvin - All Rights Reserved + * Copyright 2001-2006 H. Peter Anvin - All Rights Reserved * * This program is free software available under the same license * as the "OpenBSD" operating system, distributed at @@ -110,7 +109,7 @@ myrecvfrom(int s, void *buf, int len, unsigned int flags, for ( cmptr = CMSG_FIRSTHDR(&msg) ; cmptr != NULL ; cmptr = CMSG_NXTHDR(&msg, cmptr) ) { -#ifdef IP_RECVSTDADDR +#ifdef IP_RECVDSTADDR if ( cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVDSTADDR ) { memcpy(&myaddr->sin_addr, CMSG_DATA(cmptr), From 30d10d7e22ef6808d3d94fffdf8975fb22268728 Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Thu, 16 Feb 2006 08:18:15 -0800 Subject: [PATCH 059/184] 0.42 is the next version --- version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version b/version index a7d6788..f4bb45b 100644 --- a/version +++ b/version @@ -1 +1 @@ -0.41 +0.42 From 8890d206f44619726c9bfe902500be535f5c18d6 Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Thu, 16 Feb 2006 08:30:46 -0800 Subject: [PATCH 060/184] Document changes since 0.41 --- CHANGES | 11 +++++++++++ tftpd/tftpd.8.in | 6 +++++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/CHANGES b/CHANGES index 374f628..3fb99b8 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,14 @@ +Changes in 0.42: + Try to disable path MTU discovery for TFTP connections (it's + useless anyway.) + + Add a hack to allow the admin to specify a range of local port + numbers to use. + + Fix local IP number handling on systems which present + IP_RECVDSTADDR in recvmsg(). + + Changes in 0.41: Fix bug by which patterns of the form \U\1 weren't converted correctly. diff --git a/tftpd/tftpd.8.in b/tftpd/tftpd.8.in index 644639b..ea1697c 100644 --- a/tftpd/tftpd.8.in +++ b/tftpd/tftpd.8.in @@ -31,7 +31,7 @@ .\" SUCH DAMAGE. .\" .\"----------------------------------------------------------------------- */ -.TH TFTPD 8 "3 September 2004" "tftp-hpa @@VERSION@@" "System Manager's Manual" +.TH TFTPD 8 "16 February 2006" "tftp-hpa @@VERSION@@" "System Manager's Manual" .SH NAME .B tftpd \- IPv4 Trivial File Transfer Protocol server @@ -159,6 +159,10 @@ MTU on your network minus 32 bytes (20 bytes for IP, 8 for UDP, and 4 for TFTP; less if you use IP options on your network.) For example, on a standard Ethernet (MTU 1500) a value of 1468 is reasonable. .TP +\fB\-R\fP \fIport:port\fP +Force the server port number (the Transaction ID) to be in the +specified range of port numbers. +.TP .B \-V Print the version number and configuration to standard output, then exit gracefully. From b3892e801f74c0fc6fae81fe7eb48e8b75a82c56 Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Thu, 16 Feb 2006 09:29:35 -0800 Subject: [PATCH 061/184] Correct the logic when to loop for the port range --- tftpd/tftpd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tftpd/tftpd.c b/tftpd/tftpd.c index 5e2a26e..24fe904 100644 --- a/tftpd/tftpd.c +++ b/tftpd/tftpd.c @@ -289,7 +289,7 @@ pick_port_bind(int sockfd, struct sockaddr_in *myaddr) if (bind(sockfd, (struct sockaddr *)myaddr, sizeof *myaddr) < 0) { /* Some versions of Linux return EINVAL instead of EADDRINUSE */ - if ( !portrange || !(errno != EINVAL && errno != EADDRINUSE) ) + if ( !(portrange && (errno == EINVAL || errno == EADDRINUSE)) ) return -1; /* Normally, we shouldn't have to loop, but some situations involving From 22e2fe6dc65b5241db389e0b08b90c135e5b7494 Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Thu, 16 Feb 2006 09:42:30 -0800 Subject: [PATCH 062/184] Allow testing of the release mechanism --- release.sh | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/release.sh b/release.sh index 27d617b..d2e9e23 100755 --- a/release.sh +++ b/release.sh @@ -17,15 +17,19 @@ releasedir=$PACKAGE-$release GIT_DIR=`cd "${GIT_DIR-.git}" && pwd` export GIT_DIR -echo $release > version.new -if ! cmp -s version version.new ; then - mv -f version.new version - cg-commit -m 'Update version for release' version +if [ x"$release" = x'test' ]; then + releasetag=HEAD else - rm -f version.new + echo $release > version.new + if ! cmp -s version version.new ; then + mv -f version.new version + cg-commit -m 'Update version for release' version + else + rm -f version.new + fi + rm -f "$GIT_DIR"/refs/tags/$releasetag + cg-tag $releasetag fi -rm -f "$GIT_DIR"/refs/tags/$releasetag -cg-tag $releasetag here=`pwd` From 293301fb2ca5a15df3d5dbb020d821aefd224c63 Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Thu, 16 Feb 2006 09:42:48 -0800 Subject: [PATCH 063/184] s/Copyright/License/ --- tftp.spec.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tftp.spec.in b/tftp.spec.in index 820a6fb..7a8c7b7 100644 --- a/tftp.spec.in +++ b/tftp.spec.in @@ -2,7 +2,7 @@ Summary: The client for the Trivial File Transfer Protocol (TFTP). Name: tftp Version: @@VERSION@@ Release: 1 -Copyright: BSD +License: BSD Group: Applications/Internet Source0: http://www.kernel.org/pub/software/network/tftp/tftp-hpa-%{version}.tar.gz BuildPreReq: tcp_wrappers From ebeb6a63f382b955af72375aa1e4abe3f988df5b Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Thu, 16 Feb 2006 09:45:12 -0800 Subject: [PATCH 064/184] Actually allow tests of the release mechanism... --- release.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/release.sh b/release.sh index d2e9e23..4472469 100755 --- a/release.sh +++ b/release.sh @@ -18,7 +18,9 @@ GIT_DIR=`cd "${GIT_DIR-.git}" && pwd` export GIT_DIR if [ x"$release" = x'test' ]; then + release=`cat version` releasetag=HEAD + releasedir=$PACKAGE-$release else echo $release > version.new if ! cmp -s version version.new ; then From 0b821ec893407500011430a94387d90a85acbb46 Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Wed, 27 Sep 2006 15:54:41 -0700 Subject: [PATCH 065/184] Fix double-free error in tftp client. --- tftp/main.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/tftp/main.c b/tftp/main.c index 961c0c4..9db1c87 100644 --- a/tftp/main.c +++ b/tftp/main.c @@ -304,8 +304,6 @@ main(int argc, char *argv[]) (*c->handler)(pargc, pargv); exit(0); } - if (sigsetjmp(toplevel,1) != 0) - (void)putchar('\n'); #ifdef WITH_READLINE #ifdef HAVE_READLINE_HISTORY_H @@ -313,6 +311,8 @@ main(int argc, char *argv[]) #endif #endif + if (sigsetjmp(toplevel,1) != 0) + (void)putchar('\n'); command(); return 0; /* Never reached */ @@ -336,8 +336,10 @@ getmoreargs(const char *partial, const char *mprompt) elen = strlen(eline); - if (line) + if (line) { free(line); + line = NULL; + } line = xmalloc(len+elen+1); strcpy(line, partial); strcpy(line+len, eline); @@ -734,8 +736,10 @@ command(void) for (;;) { #ifdef WITH_READLINE - if ( line ) + if ( line ) { free(line); + line = NULL; + } line = readline(prompt); if ( !line ) exit(0); /* EOF */ From 9b969ddd0e539231963012717edc66b45a036cda Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Wed, 27 Sep 2006 15:55:23 -0700 Subject: [PATCH 066/184] Push for version 0.43 --- CHANGES | 4 ++++ version | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGES b/CHANGES index 3fb99b8..a2e7aad 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,7 @@ +Changes in 0.43: + Fix double-free error on ^c in client. + + Changes in 0.42: Try to disable path MTU discovery for TFTP connections (it's useless anyway.) diff --git a/version b/version index f4bb45b..68f3790 100644 --- a/version +++ b/version @@ -1 +1 @@ -0.42 +0.43 From a548569a3e833a86d80e5a29f5a1bf183754e40c Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Wed, 4 Oct 2006 10:18:51 -0700 Subject: [PATCH 067/184] Next version is 0.43 --- version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version b/version index f4bb45b..68f3790 100644 --- a/version +++ b/version @@ -1 +1 @@ -0.42 +0.43 From 40133b212b38082200e0cdda7baa8b89b7ff6801 Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Wed, 4 Oct 2006 10:19:06 -0700 Subject: [PATCH 068/184] Move common code to a common/ subdirectory --- MCONFIG.in | 4 ++-- Makefile | 2 +- common/Makefile | 25 +++++++++++++++++++++++++ {tftp => common}/tftpsubs.c | 0 {tftp => common}/tftpsubs.h | 0 tftp/Makefile | 4 ++-- tftp/main.c | 2 +- tftp/tftp.c | 2 +- tftpd/Makefile | 10 ++-------- tftpd/recvfrom.c | 2 +- tftpd/tftpd.c | 2 +- 11 files changed, 36 insertions(+), 17 deletions(-) create mode 100644 common/Makefile rename {tftp => common}/tftpsubs.c (100%) rename {tftp => common}/tftpsubs.h (100%) diff --git a/MCONFIG.in b/MCONFIG.in index 5788e81..35e9af5 100644 --- a/MCONFIG.in +++ b/MCONFIG.in @@ -55,8 +55,8 @@ CFLAGS = @CFLAGS@ -I$(SRCROOT) LDFLAGS = @LDFLAGS@ # Libraries (client and server) -TFTP_LIBS = @TFTP_LIBS@ -TFTPD_LIBS = @TFTPD_LIBS@ +TFTP_LIBS = ../common/libcommon.a @TFTP_LIBS@ +TFTPD_LIBS = ../common/libcommon.a @TFTPD_LIBS@ # Additional library we need to build LIBOBJS = @LIBOBJS@ diff --git a/Makefile b/Makefile index 0c73fcf..8f78839 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ # You can do "make SUB=blah" to make only a few, or edit here, or both # You can also run make directly in the subdirs you want. -SUB = lib tftp tftpd +SUB = lib common tftp tftpd %.build: MCONFIG aconfig.h version.h $(MAKE) -C $(patsubst %.build, %, $@) diff --git a/common/Makefile b/common/Makefile new file mode 100644 index 0000000..a825213 --- /dev/null +++ b/common/Makefile @@ -0,0 +1,25 @@ +SRCROOT = .. +VERSION = $(shell cat ../version) + +-include ../MCONFIG +include ../MRULES + +OBJS = tftpsubs.$(O) +LIB = libcommon.a + +all: $(LIB) + +$(LIB): $(OBJS) + -rm -f $(LIB) + $(AR) $(LIB) $(OBJS) + $(RANLIB) $(LIB) + +$(OBJS): tftpsubs.h + +install: + +clean: + rm -f *.o *.obj *.exe $(LIB) + +distclean: clean + rm -f *~ diff --git a/tftp/tftpsubs.c b/common/tftpsubs.c similarity index 100% rename from tftp/tftpsubs.c rename to common/tftpsubs.c diff --git a/tftp/tftpsubs.h b/common/tftpsubs.h similarity index 100% rename from tftp/tftpsubs.h rename to common/tftpsubs.h diff --git a/tftp/Makefile b/tftp/Makefile index 0ff0451..20f4c18 100644 --- a/tftp/Makefile +++ b/tftp/Makefile @@ -4,14 +4,14 @@ VERSION = $(shell cat ../version) -include ../MCONFIG include ../MRULES -OBJS = tftp.$(O) main.$(O) tftpsubs.$(O) +OBJS = tftp.$(O) main.$(O) all: tftp$(X) tftp.1 tftp$(X): $(OBJS) $(CC) $(LDFLAGS) $^ $(TFTP_LIBS) -o $@ -$(OBJS): tftpsubs.h +$(OBJS): ../common/tftpsubs.h tftp.1: tftp.1.in ../version sed -e 's/@@VERSION@@/$(VERSION)/g' < $< > $@ diff --git a/tftp/main.c b/tftp/main.c index 961c0c4..f58cf45 100644 --- a/tftp/main.c +++ b/tftp/main.c @@ -34,7 +34,7 @@ * SUCH DAMAGE. */ -#include "tftpsubs.h" +#include "common/tftpsubs.h" #ifndef lint static const char *copyright UNUSED = diff --git a/tftp/tftp.c b/tftp/tftp.c index 475d25b..97b10dc 100644 --- a/tftp/tftp.c +++ b/tftp/tftp.c @@ -36,7 +36,7 @@ * SUCH DAMAGE. */ -#include "tftpsubs.h" +#include "common/tftpsubs.h" #ifndef lint /* static char sccsid[] = "@(#)tftp.c 8.1 (Berkeley) 6/6/93"; */ diff --git a/tftpd/Makefile b/tftpd/Makefile index 0729a26..a05335d 100644 --- a/tftpd/Makefile +++ b/tftpd/Makefile @@ -4,20 +4,14 @@ VERSION = $(shell cat ../version) -include ../MCONFIG include ../MRULES -OBJS = tftpd.$(O) tftpsubs.$(O) recvfrom.$(O) misc.$(O) $(TFTPDOBJS) +OBJS = tftpd.$(O) recvfrom.$(O) misc.$(O) $(TFTPDOBJS) all: tftpd$(X) tftpd.8 tftpd$(X): $(OBJS) $(CC) $(LDFLAGS) $^ $(TFTPD_LIBS) -o $@ -tftpsubs.c: - $(LN_S) -f ../tftp/tftpsubs.c . - -tftpsubs.h: - $(LN_S) -f ../tftp/tftpsubs.h . - -$(OBJS): tftpsubs.h +$(OBJS): ../common/tftpsubs.h tftpd.8: tftpd.8.in ../version sed -e 's/@@VERSION@@/$(VERSION)/g' < $< > $@ diff --git a/tftpd/recvfrom.c b/tftpd/recvfrom.c index 22440ce..0b3456e 100644 --- a/tftpd/recvfrom.c +++ b/tftpd/recvfrom.c @@ -19,7 +19,7 @@ #include "config.h" /* Must be included first! */ #include "recvfrom.h" -#include "tftpsubs.h" +#include "common/tftpsubs.h" #ifdef HAVE_MACHINE_PARAM_H #include /* Needed on some versions of FreeBSD */ #endif diff --git a/tftpd/tftpd.c b/tftpd/tftpd.c index 24fe904..47b85d9 100644 --- a/tftpd/tftpd.c +++ b/tftpd/tftpd.c @@ -62,7 +62,7 @@ static const char *rcsid UNUSED = #include #include -#include "tftpsubs.h" +#include "common/tftpsubs.h" #include "recvfrom.h" #include "remap.h" From 14993bc916942d01e5fb9c5f4c084121dfdfd41c Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Wed, 4 Oct 2006 21:54:04 -0700 Subject: [PATCH 069/184] Introduce socklen_t; detect non-local (e.g. broadcast) receive addresses --- common/tftpsubs.c | 2 +- config.h | 7 ++++++- configure.in | 9 +++++---- tftp/tftp.c | 6 ++---- tftpd/recvfrom.c | 47 ++++++++++++++++++++++++++++++++++++++++++++++- tftpd/recvfrom.h | 5 ++--- tftpd/tftpd.c | 12 ++++++------ 7 files changed, 68 insertions(+), 20 deletions(-) diff --git a/common/tftpsubs.c b/common/tftpsubs.c index 3dd34bd..9103106 100644 --- a/common/tftpsubs.c +++ b/common/tftpsubs.c @@ -250,7 +250,7 @@ synchnet(int f) /* socket to flush */ int pktcount = 0; char rbuf[PKTSIZE]; struct sockaddr_in from; - int fromlen; + socklen_t fromlen; fd_set socketset; struct timeval notime; diff --git a/config.h b/config.h index 6e2125b..8869134 100644 --- a/config.h +++ b/config.h @@ -1,6 +1,6 @@ /* -*- c -*- ------------------------------------------------------------- * * - * Copyright 2001 H. Peter Anvin - All Rights Reserved + * Copyright 2001-2006 H. Peter Anvin - All Rights Reserved * * This program is free software available under the same license * as the "OpenBSD" operating system, distributed at @@ -217,6 +217,11 @@ typedef unsigned long u_long; #endif #endif +/* socklen_t */ +#ifndef HAVE_SOCKLEN_T +typedef int socklen_t; +#endif + /* sysexits.h */ #ifdef HAVE_SYSEXITS_H diff --git a/configure.in b/configure.in index faff15e..c40d4c8 100644 --- a/configure.in +++ b/configure.in @@ -81,16 +81,17 @@ AC_CHECK_HEADERS(sys/socket.h) AC_CHECK_HEADERS(winsock2.h) AC_CHECK_HEADERS(winsock.h) +AC_TYPE_OFF_T +AC_TYPE_PID_T +AC_TYPE_MODE_T +AC_TYPE_SIZE_T AC_CHECK_TYPES(intmax_t) AC_CHECK_TYPES(long long) AC_CHECK_TYPES(uint16_t) AC_CHECK_TYPES(uint32_t) AC_CHECK_TYPES(u_short) AC_CHECK_TYPES(u_long) -AC_TYPE_OFF_T -AC_TYPE_PID_T -AC_TYPE_MODE_T -AC_TYPE_SIZE_T +AC_CHECK_TYPES(socklen_t) AC_SEARCH_LIBS(socket, [socket ws2_32 wsock32], , [AC_MSG_ERROR(socket library not found)]) AC_SEARCH_LIBS(gethostbyname, [nsl resolv], , [AC_MSG_ERROR(gethostbyname not found)]) diff --git a/tftp/tftp.c b/tftp/tftp.c index 97b10dc..8dfec29 100644 --- a/tftp/tftp.c +++ b/tftp/tftp.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /* $OpenBSD: tftp.c,v 1.4 1997/08/06 06:43:45 deraadt Exp $ */ /* $NetBSD: tftp.c,v 1.5 1995/04/29 05:55:25 cgd Exp $ */ @@ -87,7 +85,7 @@ tftp_sendfile(int fd, const char *name, const char *mode) volatile int size, convert; volatile off_t amount; struct sockaddr_in from; - int fromlen; + socklen_t fromlen; FILE *file; u_short ap_opcode, ap_block; @@ -195,7 +193,7 @@ tftp_recvfile(int fd, const char *name, const char *mode) volatile int size, firsttrip; volatile unsigned long amount; struct sockaddr_in from; - int fromlen; + socklen_t fromlen; FILE *file; volatile int convert; /* true if converting crlf -> lf */ u_short dp_opcode, dp_block; diff --git a/tftpd/recvfrom.c b/tftpd/recvfrom.c index 0b3456e..ba8d648 100644 --- a/tftpd/recvfrom.c +++ b/tftpd/recvfrom.c @@ -50,9 +50,50 @@ struct in_pktinfo { # define CMSG_SPACE(size) (sizeof(struct cmsghdr) + (size)) #endif +/* + * Check to see if this is a valid local address. If so, we should + * end up having the same local and remote address when trying to + * bind to it. + */ +static int address_is_local(const struct sockaddr_in *addr) +{ + struct sockaddr_in sin; + int sockfd = -1; + int e; + int rv = 0; + socklen_t addrlen; + + /* Multicast or universal broadcast address? */ + if (ntohl(addr->sin_addr.s_addr) >= (224UL << 24)) + return 0; + + sockfd = socket(PF_INET, SOCK_DGRAM, 0); + if (sockfd < 0) + goto err; + + if (connect(sockfd, (const struct sockaddr *)addr, sizeof *addr)) + goto err; + + addrlen = sizeof sin; + if (getsockname(sockfd, (struct sockaddr *)&sin, &addrlen)) + goto err; + + rv = sin.sin_addr.s_addr == addr->sin_addr.s_addr; + + err: + e = errno; + + if (sockfd >= 0) + close(sockfd); + + errno = e; + return rv; +} + + int myrecvfrom(int s, void *buf, int len, unsigned int flags, - struct sockaddr *from, int *fromlen, + struct sockaddr *from, socklen_t *fromlen, struct sockaddr_in *myaddr) { struct msghdr msg; @@ -128,6 +169,10 @@ myrecvfrom(int s, void *buf, int len, unsigned int flags, } } + /* If the address is not a valid local address, then bind to any address... */ + if (address_is_local(myaddr) != 1) + myaddr->sin_addr.s_addr = INADDR_ANY; + return n; } diff --git a/tftpd/recvfrom.h b/tftpd/recvfrom.h index 1177981..35ad05b 100644 --- a/tftpd/recvfrom.h +++ b/tftpd/recvfrom.h @@ -1,7 +1,6 @@ -/* $Id$ */ /* ----------------------------------------------------------------------- * * - * Copyright 2001 H. Peter Anvin - All Rights Reserved + * Copyright 2001-2006 H. Peter Anvin - All Rights Reserved * * This program is free software available under the same license * as the "OpenBSD" operating system, distributed at @@ -20,5 +19,5 @@ int myrecvfrom(int s, void *buf, int len, unsigned int flags, - struct sockaddr *from, int *fromlen, + struct sockaddr *from, socklen_t *fromlen, struct sockaddr_in *myaddr); diff --git a/tftpd/tftpd.c b/tftpd/tftpd.c index 47b85d9..6018850 100644 --- a/tftpd/tftpd.c +++ b/tftpd/tftpd.c @@ -96,13 +96,13 @@ char buf[PKTSIZE]; char ackbuf[PKTSIZE]; unsigned int max_blksize = MAX_SEGSIZE; -struct sockaddr_in from; -int fromlen; -off_t tsize; -int tsize_ok; +struct sockaddr_in from; +socklen_t fromlen; +off_t tsize; +int tsize_ok; -int ndirs; -const char **dirs; +int ndirs; +const char **dirs; int secure = 0; int cancreate = 0; From 5f64014add3ac0030945f52755b37ba6ccb65ecc Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Thu, 5 Oct 2006 14:22:35 -0700 Subject: [PATCH 070/184] Document 0.43 changes --- CHANGES | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGES b/CHANGES index a2e7aad..90743f6 100644 --- a/CHANGES +++ b/CHANGES @@ -1,6 +1,12 @@ Changes in 0.43: Fix double-free error on ^c in client. + Try to deal with clients that send TFTP requests to broadcasts + (apparently some recent Sun boxes do this instead of using the + address told by DHCP. Bad Sun! Bad Sun!) + + Portability fixes. + Changes in 0.42: Try to disable path MTU discovery for TFTP connections (it's From ec5973ac5fa6beb57629ef38f3f602d5f8880edd Mon Sep 17 00:00:00 2001 From: Zack Bartel Date: Mon, 13 Nov 2006 17:33:29 -0800 Subject: [PATCH 071/184] Moved pick_port_bind() to the libcommon common code so both client and server can use it. Client can now specify a range of ephemeral ports (transaction id) --- common/tftpsubs.c | 38 ++++++++++++++++++++++++++++++++++++++ common/tftpsubs.h | 3 +++ tftp/main.c | 15 ++++++++++++++- tftpd/tftpd.c | 34 +--------------------------------- 4 files changed, 56 insertions(+), 34 deletions(-) diff --git a/common/tftpsubs.c b/common/tftpsubs.c index 9103106..b2eae71 100644 --- a/common/tftpsubs.c +++ b/common/tftpsubs.c @@ -272,3 +272,41 @@ synchnet(int f) /* socket to flush */ return pktcount; /* Return packets drained */ } + + +int pick_port_bind(int sockfd, struct sockaddr_in *myaddr, unsigned int port_range_from, unsigned int port_range_to) +{ + unsigned int port, firstport; + int port_range = 0; + + if (port_range_from != 0 && port_range_to != 0) { + port_range = 1; + } + + firstport = port_range + ? port_range_from + rand() % (port_range_to-port_range_from+1) + : 0; + + port = firstport; + + do { + myaddr->sin_port = htons(port); + + if (bind(sockfd, (struct sockaddr *)myaddr, sizeof *myaddr) < 0) { + /* Some versions of Linux return EINVAL instead of EADDRINUSE */ + if ( !(port_range && (errno == EINVAL || errno == EADDRINUSE)) ) + return -1; + + /* Normally, we shouldn't have to loop, but some situations involving + aborted transfers make it possible. */ + } else { + return 0; + } + + port++; + if ( port > port_range_to ) + port = port_range_from; + } while ( port != firstport ); + + return -1; +} diff --git a/common/tftpsubs.h b/common/tftpsubs.h index 645c6b9..359ba1a 100644 --- a/common/tftpsubs.h +++ b/common/tftpsubs.h @@ -74,3 +74,6 @@ extern char *xstrdup(const char *); void (*bsd_signal(int, void (*)(int)))(int); #endif + + +int pick_port_bind(int sockfd, struct sockaddr_in *myaddr, unsigned int from, unsigned int to); diff --git a/tftp/main.c b/tftp/main.c index 3a8315f..c3ec3a0 100644 --- a/tftp/main.c +++ b/tftp/main.c @@ -102,6 +102,9 @@ const char *prompt = "tftp> "; sigjmp_buf toplevel; void intr(int); struct servent *sp; +int portrange = 0; +unsigned int portrange_from = 0; +unsigned int portrange_to = 0; void get (int, char **); void help (int, char **); @@ -241,6 +244,16 @@ main(int argc, char *argv[]) case 'c': iscmd = 1; break; + case 'R': + if ( ++arg >= argc ) + usage(EX_USAGE); + if ( sscanf(argv[arg], "%u:%u", &portrange_from, &portrange_to) != 2 || + portrange_from > portrange_to || portrange_to > 65535 ) { + fprintf(stderr, "Bad port range: %s\n", argv[arg]); + exit(EX_USAGE); + } + portrange = 1; + break; case 'h': default: usage(*optx == 'h' ? 0 : EX_USAGE); @@ -276,7 +289,7 @@ main(int argc, char *argv[]) } bzero((char *)&s_in, sizeof (s_in)); s_in.sin_family = AF_INET; - if (bind(f, (struct sockaddr *)&s_in, sizeof (s_in)) < 0) { + if (pick_port_bind(f, &s_in, portrange_from, portrange_to)) { perror("tftp: bind"); exit(EX_OSERR); } diff --git a/tftpd/tftpd.c b/tftpd/tftpd.c index 6018850..09d8733 100644 --- a/tftpd/tftpd.c +++ b/tftpd/tftpd.c @@ -273,38 +273,6 @@ static int recv_time(int s, void *rbuf, int len, unsigned int flags, } } -static int -pick_port_bind(int sockfd, struct sockaddr_in *myaddr) -{ - unsigned int port, firstport; - - firstport = portrange - ? portrange_from + rand() % (portrange_to-portrange_from+1) - : 0; - - port = firstport; - - do { - myaddr->sin_port = htons(port); - - if (bind(sockfd, (struct sockaddr *)myaddr, sizeof *myaddr) < 0) { - /* Some versions of Linux return EINVAL instead of EADDRINUSE */ - if ( !(portrange && (errno == EINVAL || errno == EADDRINUSE)) ) - return -1; - - /* Normally, we shouldn't have to loop, but some situations involving - aborted transfers make it possible. */ - } else { - return 0; - } - - port++; - if ( port > portrange_to ) - port = portrange_from; - } while ( port != firstport ); - - return -1; -} int main(int argc, char **argv) @@ -750,7 +718,7 @@ main(int argc, char **argv) from.sin_family = AF_INET; /* Process the request... */ - if (pick_port_bind(peer, &myaddr) < 0) { + if (pick_port_bind(peer, &myaddr, portrange_from, portrange_to) < 0) { syslog(LOG_ERR, "bind: %m"); exit(EX_IOERR); } From ad475989a50152d84514f503c3a790c64e499e7a Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Mon, 13 Nov 2006 20:54:07 -0800 Subject: [PATCH 072/184] Document changes --- CHANGES | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGES b/CHANGES index 90743f6..d67f713 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,8 @@ +Changes in 0.44: + Allow the client to specify a range of local port numbers, + just like the server can. + + Changes in 0.43: Fix double-free error on ^c in client. From 1cb44ce918315db0547a6642b87f9bf2330a13d9 Mon Sep 17 00:00:00 2001 From: "Dyks, Axel (XL)" Date: Sun, 26 Nov 2006 11:13:27 +0100 Subject: [PATCH 073/184] tftp-hpa does not reload the remap file when receiving SIGHUP ... because the signal handling code is enclosed in "#ifdef HAVE_REGEX" instead of "#ifdef WITH_REGEX" The following patch against head of "network/tftp/tftp-hap.git" on "kernel.org" fixes this issues (you are using blanks for indention and not tabs. right?). Cheers Axel --- tftpd/tftpd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tftpd/tftpd.c b/tftpd/tftpd.c index 09d8733..8ea41d5 100644 --- a/tftpd/tftpd.c +++ b/tftpd/tftpd.c @@ -555,7 +555,7 @@ main(int argc, char **argv) if ( caught_sighup ) { caught_sighup = 0; if ( standalone ) { -#ifdef HAVE_REGEX +#ifdef WITH_REGEX if ( rewrite_file ) { freerules(rewrite_rules); rewrite_rules = read_remap_rules(rewrite_file); From 7390c7ceca4786db18c34bfbb6f24b945591e2cf Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Tue, 5 Dec 2006 14:39:28 -0800 Subject: [PATCH 074/184] Update list of changes for 0.44. --- CHANGES | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGES b/CHANGES index d67f713..1c1986b 100644 --- a/CHANGES +++ b/CHANGES @@ -2,6 +2,8 @@ Changes in 0.44: Allow the client to specify a range of local port numbers, just like the server can. + Fix sending SIGHUP to update the regular expression table. + Changes in 0.43: Fix double-free error on ^c in client. From e031e3bc9acc09b7d4e899cee98daa72fb0f3d0b Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Tue, 5 Dec 2006 14:39:40 -0800 Subject: [PATCH 075/184] Update version for release --- version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version b/version index 68f3790..d2bc220 100644 --- a/version +++ b/version @@ -1 +1 @@ -0.43 +0.44 From 899b7d500be12d17aaea1e9dcfdbc6577e42b7f5 Mon Sep 17 00:00:00 2001 From: Craig Johnston Date: Mon, 11 Dec 2006 09:23:50 -0800 Subject: [PATCH 076/184] Add option to TFTP client to ignore ':' in file name I have encountered a situation where I would like to use tftp-hpa to retrieve a file that resides within an absolute path containing a ':' character. Ala, "tftp foobar -c get C:2/tftpdir/myfile". Since the tftp client automatically converts the host:file syntax, I get an error "tftp: C: Unknown host". I made a chage to the tftp client code to add a literal mode (-l), that prevents the special treatment of the ':' character. I've attached a patch set for main.c and tftp.1.in for your consideration. I'm not sure how many folks run into this, but it may be somewhat common for VxWorks and perhaps MSDOS users. Thanks, Craig Johnston --- tftp/main.c | 28 ++++++++++++++++++++++------ tftp/tftp.1.in | 14 ++++++++++++-- 2 files changed, 34 insertions(+), 8 deletions(-) diff --git a/tftp/main.c b/tftp/main.c index c3ec3a0..6bc7c44 100644 --- a/tftp/main.c +++ b/tftp/main.c @@ -89,6 +89,7 @@ int f; u_short port; int trace; int verbose; +int literal; int connected; const struct modes *mode; #ifdef WITH_READLINE @@ -119,6 +120,7 @@ void settimeout (int, char **); void settrace (int, char **); void setverbose (int, char **); void status (int, char **); +void setliteral (int, char **); static void command (void); @@ -157,6 +159,9 @@ struct cmd cmdtab[] = { { "trace", "toggle packet tracing", settrace }, + { "literal", + "toggle literal mode, ignore ':' in file name", + setliteral }, { "status", "show current status", status }, @@ -190,7 +195,7 @@ const char *program; static inline void usage(int errcode) { - fprintf(stderr, "Usage: %s [-v][-m mode] [host [port]] [-c command]\n", program); + fprintf(stderr, "Usage: %s [-v][-l][-m mode] [host [port]] [-c command]\n", program); exit(errcode); } @@ -223,6 +228,9 @@ main(int argc, char *argv[]) /* Print version and configuration to stdout and exit */ printf("%s\n", TFTP_CONFIG_STR); exit(0); + case 'l': + literal = 1; + break; case 'm': if ( ++arg >= argc ) usage(EX_USAGE); @@ -502,7 +510,7 @@ put(int argc, char *argv[]) return; } targ = argv[argc - 1]; - if (strchr(argv[argc - 1], ':')) { + if (!literal && strchr(argv[argc - 1], ':')) { struct hostent *hp; for (n = 1; n < argc - 1; n++) @@ -591,14 +599,14 @@ get(int argc, char *argv[]) } if (!connected) { for (n = 1; n < argc ; n++) - if (strchr(argv[n], ':') == 0) { + if (literal || strchr(argv[n], ':') == 0) { getusage(argv[0]); return; } } for (n = 1; n < argc ; n++) { src = strchr(argv[n], ':'); - if (src == NULL) + if (literal || src == NULL) src = argv[n]; else { struct hostent *hp; @@ -699,6 +707,14 @@ settimeout(int argc, char *argv[]) maxtimeout = t; } +void +setliteral(int argc, char *argv[]) +{ + (void)argc; (void)argv; /* Quiet unused warning */ + literal = !literal; + printf("Literal mode %s.\n", literal ? "on" : "off"); +} + void status(int argc, char *argv[]) { @@ -707,8 +723,8 @@ status(int argc, char *argv[]) printf("Connected to %s.\n", hostname); else printf("Not connected.\n"); - printf("Mode: %s Verbose: %s Tracing: %s\n", mode->m_mode, - verbose ? "on" : "off", trace ? "on" : "off"); + printf("Mode: %s Verbose: %s Tracing: %s Literal: %s\n", mode->m_mode, + verbose ? "on" : "off", trace ? "on" : "off", literal ? "on" : "off"); printf("Rexmt-interval: %d seconds, Max-timeout: %d seconds\n", rexmtval, maxtimeout); } diff --git a/tftp/tftp.1.in b/tftp/tftp.1.in index 9ba9a85..c591a2e 100644 --- a/tftp/tftp.1.in +++ b/tftp/tftp.1.in @@ -60,6 +60,9 @@ command below.) Execute \fIcommand\fP as if it had been entered on the tftp prompt. Must be specified last on the command line. .TP +.B \-l +Default to literal mode. Used to avoid special processing of ':' in a file name. +.TP \fB\-m\fP \fImode\fP Set the default transfer mode to \fImode\fP. This is usually used with \-c. .TP @@ -119,7 +122,12 @@ host, if the host has already been specified, or a string of the form .I "host:filename" to specify both a host and filename at the same time. If the latter form is used, the last hostname specified becomes the default for -future transfers. +future transfers. Enable +.B literal +mode to prevent special treatment of the ':' character (e.g. C:\\dir\\file). +.TP +.B literal +Toggle literal mode. When set, this mode prevents special treatment of ':' in filenames. .TP \fBmode\fP \fItransfer-mode\fP Specify the mode for transfers; @@ -151,7 +159,9 @@ form is used, the hostname specified becomes the default for future transfers. If the remote-directory form is used, the remote host is assumed to be a UNIX system or another system using .B / -as directory separator. +as directory separator. Enable +.B literal +mode to prevent special treatment of the ':' character (e.g. C:\\dir\\file). .TP .B quit Exit From df877df45244dd3bcad78f8991d877ef1ec2cde9 Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Tue, 12 Dec 2006 14:37:27 -0800 Subject: [PATCH 077/184] Documentation updates. --- CHANGES | 5 +++++ tftp/tftp.1.in | 9 +++++++-- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/CHANGES b/CHANGES index 1c1986b..98b0fe3 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,8 @@ +Changes in 0.45: + Add -l (literal) option to the client, to override the special + treatment of the colon (:) character as a hostname separator. + + Changes in 0.44: Allow the client to specify a range of local port numbers, just like the server can. diff --git a/tftp/tftp.1.in b/tftp/tftp.1.in index c591a2e..41606fe 100644 --- a/tftp/tftp.1.in +++ b/tftp/tftp.1.in @@ -31,7 +31,7 @@ .\" SUCH DAMAGE. .\" .\"----------------------------------------------------------------------- */ -.TH TFTP 1 "2 February 2003" "tftp-hpa @@VERSION@@" "User's Manual" +.TH TFTP 1 "12 December 2006" "tftp-hpa @@VERSION@@" "User's Manual" .SH NAME .B tftp \- IPv4 Trivial File Transfer Protocol client @@ -61,11 +61,16 @@ Execute \fIcommand\fP as if it had been entered on the tftp prompt. Must be specified last on the command line. .TP .B \-l -Default to literal mode. Used to avoid special processing of ':' in a file name. +Default to literal mode. Used to avoid special processing of ':' in a +file name. .TP \fB\-m\fP \fImode\fP Set the default transfer mode to \fImode\fP. This is usually used with \-c. .TP +\fB\-R\fP \fIport:port\fP +Force the originating port number to be in the specified range of port +numbers. +.TP .B \-v Default to verbose mode. .TP From ab12c128ff11db82d7865a99b6ab994cbcb95249 Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Tue, 12 Dec 2006 14:38:27 -0800 Subject: [PATCH 078/184] Update version for release --- version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version b/version index d2bc220..4911aab 100644 --- a/version +++ b/version @@ -1 +1 @@ -0.44 +0.45 From 982c1f2697f6051be22dfaf9311c6ab3eae44f91 Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Mon, 8 Jan 2007 16:30:10 -0800 Subject: [PATCH 079/184] printf() and htonl() type matching issue On older systems, htonl() returns unsigned long, on newer systems it returns uint32_t. Cast to unsigned long and use %08lX for the format, that seems to be the easiest solution. --- tftpd/tftpd.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tftpd/tftpd.c b/tftpd/tftpd.c index 8ea41d5..7e0d1f0 100644 --- a/tftpd/tftpd.c +++ b/tftpd/tftpd.c @@ -1046,8 +1046,7 @@ rewrite_macros(char macro, char *output) case 'x': if ( output ) - sprintf(output, "%08X", - ntohl(from.sin_addr.s_addr)); + sprintf(output, "%08lX", (unsigned long)ntohl(from.sin_addr.s_addr)); return 8; default: From 4d02d827fb36787f4a35b731b97d08ecddcb2950 Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Mon, 8 Jan 2007 16:48:46 -0800 Subject: [PATCH 080/184] Make sure we include when checking for socklen_t socklen_t is often only defined when is included, but this is not autoconf default; make sure we include it first. --- configure.in | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/configure.in b/configure.in index c40d4c8..4b6ef76 100644 --- a/configure.in +++ b/configure.in @@ -33,8 +33,6 @@ CFLAGS="$CFLAGS -D_XOPEN_SOURCE_EXTENDED" AC_C_CONST AC_C_INLINE -AC_SYS_LARGEFILE - PA_ADD_CFLAGS(-W) PA_ADD_CFLAGS(-Wall) PA_ADD_CFLAGS(-Wpointer-arith) @@ -81,6 +79,8 @@ AC_CHECK_HEADERS(sys/socket.h) AC_CHECK_HEADERS(winsock2.h) AC_CHECK_HEADERS(winsock.h) +AC_SYS_LARGEFILE + AC_TYPE_OFF_T AC_TYPE_PID_T AC_TYPE_MODE_T @@ -91,7 +91,16 @@ AC_CHECK_TYPES(uint16_t) AC_CHECK_TYPES(uint32_t) AC_CHECK_TYPES(u_short) AC_CHECK_TYPES(u_long) -AC_CHECK_TYPES(socklen_t) + +dnl +dnl isn't among the list of standard headers that autoconf checks, +dnl but POSIX requires for socklen_t to be defined. +dnl +AC_CHECK_TYPES(socklen_t,,,[ +#ifdef HAVE_SYS_SOCKET_H +# include +#endif +]) AC_SEARCH_LIBS(socket, [socket ws2_32 wsock32], , [AC_MSG_ERROR(socket library not found)]) AC_SEARCH_LIBS(gethostbyname, [nsl resolv], , [AC_MSG_ERROR(gethostbyname not found)]) From 2b36598746a2b1c52fc9df4c6cadd527cf12f0ff Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Mon, 8 Jan 2007 16:53:19 -0800 Subject: [PATCH 081/184] When specifying includes, need to specify ALL includes It seems that overriding the default includes overrides ALL includes; so we need to specify them all. Sigh. --- configure.in | 37 ++++++++++++++++++++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/configure.in b/configure.in index 4b6ef76..fa95f4f 100644 --- a/configure.in +++ b/configure.in @@ -96,7 +96,42 @@ dnl dnl isn't among the list of standard headers that autoconf checks, dnl but POSIX requires for socklen_t to be defined. dnl -AC_CHECK_TYPES(socklen_t,,,[ +AC_CHECK_TYPES(socklen_t,,, +[ +#include +#if HAVE_SYS_TYPES_H +# include +#endif +#if HAVE_SYS_STAT_H +# include +#endif +#if STDC_HEADERS +# include +# include +#else +# if HAVE_STDLIB_H +# include +# endif +#endif +#if HAVE_STRING_H +# if !STDC_HEADERS && HAVE_MEMORY_H +# include +# endif +# include +#endif +#if HAVE_STRINGS_H +# include +#endif +#if HAVE_INTTYPES_H +# include +#else +# if HAVE_STDINT_H +# include +# endif +#endif +#if HAVE_UNISTD_H +# include +#endif #ifdef HAVE_SYS_SOCKET_H # include #endif From 625002c097cc2b6d0cd29f5c0773f81e093b2883 Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Mon, 8 Jan 2007 16:54:17 -0800 Subject: [PATCH 082/184] Document changes --- CHANGES | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGES b/CHANGES index 98b0fe3..4b01abe 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,7 @@ +Changes in 0.46: + Minor portability improvements. + + Changes in 0.45: Add -l (literal) option to the client, to override the special treatment of the colon (:) character as a hostname separator. From 0db17eedd3c56b4f5be829041d39d5fe02b080c3 Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Mon, 8 Jan 2007 16:54:29 -0800 Subject: [PATCH 083/184] Update version for release --- version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version b/version index 4911aab..2c5f97e 100644 --- a/version +++ b/version @@ -1 +1 @@ -0.45 +0.46 From 6124dcbe2d7c5915bd08117e572469754ec331eb Mon Sep 17 00:00:00 2001 From: Mike Frysinger Date: Sat, 13 Jan 2007 10:24:27 -0800 Subject: [PATCH 084/184] [patch] fix parallel building of tftp-hpa if you try to build tftp-hpa in parallel, it may fail as the tftp and tftpd subdirs may try to link before the libcommon.a has a chance to be generated in the common subdir trivial patch attached to address this -mike --- Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 8f78839..9ff12d8 100644 --- a/Makefile +++ b/Makefile @@ -17,8 +17,8 @@ SUB = lib common tftp tftpd all: MCONFIG $(patsubst %, %.build, $(SUB)) -tftp.build: lib.build -tftpd.build: lib.build +tftp.build: lib.build common.build +tftpd.build: lib.build common.build install: MCONFIG $(patsubst %, %.install, $(SUB)) From 059de7ce20e446987f4a7c16e60760b86d383860 Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Mon, 15 Jan 2007 01:11:26 -0800 Subject: [PATCH 085/184] Use replacement library functions to daemonize, rather than #ifdef hell --- configure.in | 9 +++++---- lib/daemon.c | 37 +++++++++++++++++++++++++++++++++++++ lib/dup2.c | 25 +++++++++++++++++++++++++ tftpd/tftpd.c | 32 +++----------------------------- 4 files changed, 70 insertions(+), 33 deletions(-) create mode 100644 lib/daemon.c create mode 100644 lib/dup2.c diff --git a/configure.in b/configure.in index fa95f4f..4d85216 100644 --- a/configure.in +++ b/configure.in @@ -142,7 +142,6 @@ AC_SEARCH_LIBS(gethostbyname, [nsl resolv], , [AC_MSG_ERROR(gethostbyname not fo AC_SEARCH_LIBS(inet_aton, [nsl resolv], , [AC_MSG_ERROR(inet_aton not found)]) AC_SEARCH_LIBS(herror, [nsl resolv], , [AC_MSG_ERROR(herror not found)]) -AC_CHECK_FUNCS(dup2) AC_CHECK_FUNCS(fcntl) AC_CHECK_FUNCS(setsid) AC_CHECK_FUNCS(recvmsg) @@ -174,9 +173,11 @@ SRCROOT=`cd $srcdir && pwd` OBJROOT=`pwd` XTRA=false -AC_SEARCH_LIBS(xmalloc, iberty, , [XTRA=true ; AC_LIBOBJ(xmalloc)]) -AC_SEARCH_LIBS(xstrdup, iberty, , [XTRA=true ; AC_LIBOBJ(xstrdup)]) -AC_SEARCH_LIBS(bsd_signal, bsd, , [XTRA=true ; AC_LIBOBJ(bsdsignal)]) +AC_SEARCH_LIBS(xmalloc, iberty, , [XTRA=true; AC_LIBOBJ(xmalloc)]) +AC_SEARCH_LIBS(xstrdup, iberty, , [XTRA=true; AC_LIBOBJ(xstrdup)]) +AC_SEARCH_LIBS(bsd_signal, bsd, , [XTRA=true; AC_LIBOBJ(bsdsignal)]) +AC_CHECK_FUNCS(daemon, , [XTRA=true; AC_LIBOBJ(daemon)]) +AC_CHECK_FUNCS(dup2, , [XTRA=true; AC_LIBOBJ(dup2)]) if $XTRA then XTRALIBS="$OBJROOT/lib/libxtra.a $XTRALIBS" diff --git a/lib/daemon.c b/lib/daemon.c new file mode 100644 index 0000000..c3106b5 --- /dev/null +++ b/lib/daemon.c @@ -0,0 +1,37 @@ +/* + * daemon.c - "daemonize" a process + */ + +#include "config.h" + +int daemon(int nochdir, int noclose) +{ + int nullfd; + pid_t f; + + if (!nochdir) { + if (chdir("/")) + return -1; + } + + if (!noclose) { + if ((nullfd = open("/dev/null", O_RDWR)) < 0 || + dup2(nullfd, 0) < 0 || + dup2(nullfd, 1) < 0 || + dup2(nullfd, 2) < 0) + return -1; + close(nullfd); + } + + f = fork(); + if (f < 0) + return -1; + else if (f > 0) + _exit(0); + +#ifdef HAVE_SETSID + return setsid(); +#else + return 0; +#endif +} diff --git a/lib/dup2.c b/lib/dup2.c new file mode 100644 index 0000000..bdf3325 --- /dev/null +++ b/lib/dup2.c @@ -0,0 +1,25 @@ +/* + * dup2.c + * + * Ersatz dup2() for really ancient systems + */ + +#include "config.h" + +int dup2(int oldfd, int newfd) +{ + int rv, nfd; + + close(newfd); + + nfd = rv = dup(oldfd); + + if (rv >= 0 && rv != newfd) { + rv = dup2(oldfd, newfd); + close(nfd); + } + + return rv; +} + + diff --git a/tftpd/tftpd.c b/tftpd/tftpd.c index 7e0d1f0..ee65b10 100644 --- a/tftpd/tftpd.c +++ b/tftpd/tftpd.c @@ -1,5 +1,3 @@ -/* tftp-hpa: $Id$ */ - /* $OpenBSD: tftpd.c,v 1.13 1999/06/23 17:01:36 deraadt Exp $ */ /* @@ -499,33 +497,9 @@ main(int argc, char **argv) } /* Daemonize this process */ - { - pid_t f = fork(); - int nfd; - if ( f > 0 ) - exit(0); - if ( f < 0 ) { - syslog(LOG_ERR, "cannot fork: %m"); - exit(EX_OSERR); - } - nfd = open("/dev/null", O_RDWR); - if ( nfd >= 3 ) { -#ifdef HAVE_DUP2 - dup2(nfd, 0); - dup2(nfd, 1); - dup2(nfd, 2); -#else - close(0); dup(nfd); - close(1); dup(nfd); - close(2); dup(nfd); -#endif - close(nfd); - } else if ( nfd < 0 ) { - close(0); close(1); close(2); - } -#ifdef HAVE_SETSID - setsid(); -#endif + if (daemon(0, 0) < 0) { + syslog(LOG_ERR, "cannot daemonize: %m"); + exit(EX_OSERR); } } else { /* 0 is our socket descriptor */ From d9938a7d8331996372effa56d38a28ac2c5474c7 Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Mon, 15 Jan 2007 01:12:52 -0800 Subject: [PATCH 086/184] Add -L option to not daemonize process --- tftpd/tftpd.8.in | 7 +++++++ tftpd/tftpd.c | 9 +++++++-- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/tftpd/tftpd.8.in b/tftpd/tftpd.8.in index ea1697c..e645d37 100644 --- a/tftpd/tftpd.8.in +++ b/tftpd/tftpd.8.in @@ -59,6 +59,11 @@ option is ignored, and the option can be used to specify a specific local address or port to listen to. .TP +.B \-L +Similar to +.B \-l +but do not detach from the foreground process. +.TP \fB\-a\fP \fI[address][:port]\fP Specify a specific .I address @@ -66,6 +71,8 @@ and .I port to listen to when called with the .B \-l +or +.B \-L option. The default is to listen to the .I tftp port specified in diff --git a/tftpd/tftpd.c b/tftpd/tftpd.c index ee65b10..a1f30f6 100644 --- a/tftpd/tftpd.c +++ b/tftpd/tftpd.c @@ -283,6 +283,7 @@ main(int argc, char **argv) int n; int fd = 0; int standalone = 0; /* Standalone (listen) mode */ + int nodaemon = 0; /* Do not detach process */ char *address = NULL; /* Address to listen to */ pid_t pid; mode_t my_umask = 0; @@ -306,7 +307,7 @@ main(int argc, char **argv) srand(time(NULL) ^ getpid()); - while ((c = getopt(argc, argv, "cspvVla:B:u:U:r:t:T:R:m:")) != -1) + while ((c = getopt(argc, argv, "cspvVlLa:B:u:U:r:t:T:R:m:")) != -1) switch (c) { case 'c': cancreate = 1; @@ -320,6 +321,10 @@ main(int argc, char **argv) case 'l': standalone = 1; break; + case 'L': + standalone = 1; + nodaemon = 1; + break; case 'a': address = optarg; break; @@ -497,7 +502,7 @@ main(int argc, char **argv) } /* Daemonize this process */ - if (daemon(0, 0) < 0) { + if (!nodaemon && daemon(0, 0) < 0) { syslog(LOG_ERR, "cannot daemonize: %m"); exit(EX_OSERR); } From d15a61abe6d0b6d5cc89ff4fdfb271393decf4b2 Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Mon, 15 Jan 2007 01:20:41 -0800 Subject: [PATCH 087/184] Prototype cleanup; use "config.h" where appropriate --- common/tftpsubs.h | 14 +------------- config.h | 15 +++++++++++++++ lib/bsdsignal.c | 6 +----- lib/xmalloc.c | 3 +-- lib/xstrdup.c | 4 +--- 5 files changed, 19 insertions(+), 23 deletions(-) diff --git a/common/tftpsubs.h b/common/tftpsubs.h index 359ba1a..f986afc 100644 --- a/common/tftpsubs.h +++ b/common/tftpsubs.h @@ -62,18 +62,6 @@ int writeit(FILE *, struct tftphdr **, int, int); extern int segsize; #define MAX_SEGSIZE 65464 -/* - * Prototype for xmalloc/xstrdup - */ -extern void *xmalloc(size_t); -extern char *xstrdup(const char *); - -/* - * Signal-related stuff - */ -void (*bsd_signal(int, void (*)(int)))(int); +int pick_port_bind(int sockfd, struct sockaddr_in *myaddr, unsigned int from, unsigned int to); #endif - - -int pick_port_bind(int sockfd, struct sockaddr_in *myaddr, unsigned int from, unsigned int to); diff --git a/config.h b/config.h index 8869134..e4c5cc2 100644 --- a/config.h +++ b/config.h @@ -287,6 +287,21 @@ typedef int socklen_t; #define EOPTNEG 8 #endif +/* Prototypes for libxtra functions */ + +void *xmalloc(size_t); +char *xstrdup(const char *); + +#ifndef HAVE_BSD_SIGNAL +void (*bsd_signal(int, void (*)(int)))(int); +#endif +#ifndef HAVE_DUP2 +int dup2(int, int); +#endif +#ifndef HAVE_DAEMON +int daemon(int, int); +#endif + /* tftp-hpa version and configuration strings */ #include "version.h" diff --git a/lib/bsdsignal.c b/lib/bsdsignal.c index bc3ceb5..92de999 100644 --- a/lib/bsdsignal.c +++ b/lib/bsdsignal.c @@ -4,11 +4,7 @@ * Use sigaction() to simulate BSD signal() */ -#include -#include -#include - -void (*bsd_signal(int, void (*)(int)))(int); +#include "config.h" void (*bsd_signal(int signum, void (*handler)(int)))(int) { diff --git a/lib/xmalloc.c b/lib/xmalloc.c index 6e8ae24..f234a46 100644 --- a/lib/xmalloc.c +++ b/lib/xmalloc.c @@ -5,8 +5,7 @@ * */ -#include -#include +#include "config.h" void *xmalloc(size_t size) { diff --git a/lib/xstrdup.c b/lib/xstrdup.c index 5d65b7e..036b3b2 100644 --- a/lib/xstrdup.c +++ b/lib/xstrdup.c @@ -5,9 +5,7 @@ * */ -#include -#include -#include +#include "config.h" char *xstrdup(const char *s) { From ecf79bc97fa15aefdfd08e8fcb8ed2444c18b916 Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Sat, 27 Jan 2007 11:11:22 -0800 Subject: [PATCH 088/184] Document user-visible changes --- CHANGES | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGES b/CHANGES index 4b01abe..8607ecf 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,10 @@ +Changes in 0.47: + Add -L option to the server to run standalone without + detaching from the shell. + + Parallel make fix. + + Changes in 0.46: Minor portability improvements. From 077eac1fd62a6c9ca087278efb10cc6a6802d4bb Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Sat, 27 Jan 2007 11:11:27 -0800 Subject: [PATCH 089/184] Update version for release --- version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version b/version index 2c5f97e..4f009b9 100644 --- a/version +++ b/version @@ -1 +1 @@ -0.46 +0.47 From c7ecc59f86767b07498d770d4953b37fa205415d Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Tue, 30 Jan 2007 15:38:04 -0800 Subject: [PATCH 090/184] When running in secure mode (-s), we must not chdir while daemonizing When running in secure mode (-s), we must not chdir while daemonizing. Thanks to Adrian Urquhart for spotting this bug. --- tftpd/tftpd.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tftpd/tftpd.c b/tftpd/tftpd.c index a1f30f6..92a6889 100644 --- a/tftpd/tftpd.c +++ b/tftpd/tftpd.c @@ -502,7 +502,9 @@ main(int argc, char **argv) } /* Daemonize this process */ - if (!nodaemon && daemon(0, 0) < 0) { + /* Note: when running in secure mode (-s), we must not chroot, since + we are already in the proper directory. */ + if (!nodaemon && daemon(secure, 0) < 0) { syslog(LOG_ERR, "cannot daemonize: %m"); exit(EX_OSERR); } From 0eacf8823f8f6576e8f7184f01049740c1f726bc Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Tue, 30 Jan 2007 15:51:00 -0800 Subject: [PATCH 091/184] Document fixing -l -s. --- CHANGES | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGES b/CHANGES index 8607ecf..c5daef1 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,7 @@ +Changes in 0.48: + Unbreak -l -s in the server, which was broken in 0.47. + + Changes in 0.47: Add -L option to the server to run standalone without detaching from the shell. From 1f57b0edd5e26dcd51b33e24d559efe4b09656db Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Tue, 30 Jan 2007 15:51:04 -0800 Subject: [PATCH 092/184] Update version for release --- version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version b/version index 4f009b9..8ae5e61 100644 --- a/version +++ b/version @@ -1 +1 @@ -0.47 +0.48 From 80f86e5a04f443a0c4cde4176b389e1090727a8b Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Tue, 30 Jan 2007 16:01:24 -0800 Subject: [PATCH 093/184] Get rid of $Id$ tags and update copyright dates --- INSTALL.tftp | 2 -- MCONFIG.in | 3 +-- README.security | 4 ---- aclocal.m4 | 3 +-- configure.in | 1 - tftpd/misc.c | 3 +-- tftpd/remap.c | 3 +-- tftpd/remap.h | 3 +-- 8 files changed, 5 insertions(+), 17 deletions(-) diff --git a/INSTALL.tftp b/INSTALL.tftp index 4bd87a5..914094f 100644 --- a/INSTALL.tftp +++ b/INSTALL.tftp @@ -1,5 +1,3 @@ -$Id$ - Specific installation instructions ================================== diff --git a/MCONFIG.in b/MCONFIG.in index 35e9af5..716bedc 100644 --- a/MCONFIG.in +++ b/MCONFIG.in @@ -1,13 +1,12 @@ ## -*- makefile -*- ------------------------------------------------------ ## -## Copyright 2001 H. Peter Anvin - All Rights Reserved +## Copyright 2001-2007 H. Peter Anvin - All Rights Reserved ## ## This program is free software available under the same license ## as the "OpenBSD" operating system, distributed at ## http://www.openbsd.org/. ## ## ----------------------------------------------------------------------- -## $Id$ ## ## MCONFIG.in diff --git a/README.security b/README.security index 58eab7f..644babb 100644 --- a/README.security +++ b/README.security @@ -1,7 +1,3 @@ -$Id$ - - ======================================= - Starting in version 0.27, tftp-hpa has the option of a "use Unix permissions" mode. In this mode, tftpd can access any file accessible by the tftpd effective user, specified via the -u option. This means diff --git a/aclocal.m4 b/aclocal.m4 index f53cea9..7b29e86 100644 --- a/aclocal.m4 +++ b/aclocal.m4 @@ -1,7 +1,6 @@ -dnl $Id$ dnl ----------------------------------------------------------------------- dnl -dnl Copyright 1999-2002 H. Peter Anvin - All Rights Reserved +dnl Copyright 1999-2007 H. Peter Anvin - All Rights Reserved dnl dnl This program is free software; you can redistribute it and/or modify dnl it under the terms of the GNU General Public License as published by diff --git a/configure.in b/configure.in index 4d85216..71b746f 100644 --- a/configure.in +++ b/configure.in @@ -3,7 +3,6 @@ dnl autoconf input file to generate MCONFIG dnl AC_PREREQ(2.52) -AC_REVISION([$Id$]) AC_INIT(MCONFIG.in) AC_PREFIX_DEFAULT(/usr) diff --git a/tftpd/misc.c b/tftpd/misc.c index d3c49cd..4f88978 100644 --- a/tftpd/misc.c +++ b/tftpd/misc.c @@ -1,7 +1,6 @@ -/* $Id$ */ /* ----------------------------------------------------------------------- * * - * Copyright 2001 H. Peter Anvin - All Rights Reserved + * Copyright 2001-2007 H. Peter Anvin - All Rights Reserved * * This program is free software available under the same license * as the "OpenBSD" operating system, distributed at diff --git a/tftpd/remap.c b/tftpd/remap.c index dda5632..bb10fd0 100644 --- a/tftpd/remap.c +++ b/tftpd/remap.c @@ -1,7 +1,6 @@ -/* $Id$ */ /* ----------------------------------------------------------------------- * * - * Copyright 2001-2004 H. Peter Anvin - All Rights Reserved + * Copyright 2001-2007 H. Peter Anvin - All Rights Reserved * * This program is free software available under the same license * as the "OpenBSD" operating system, distributed at diff --git a/tftpd/remap.h b/tftpd/remap.h index 5315074..a00094c 100644 --- a/tftpd/remap.h +++ b/tftpd/remap.h @@ -1,7 +1,6 @@ -/* $Id$ */ /* ----------------------------------------------------------------------- * * - * Copyright 2001 H. Peter Anvin - All Rights Reserved + * Copyright 2001-2007 H. Peter Anvin - All Rights Reserved * * This program is free software available under the same license * as the "OpenBSD" operating system, distributed at From bbb17208a930ab5c7e8239358635f25734877e68 Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Wed, 3 Oct 2007 17:41:38 -0700 Subject: [PATCH 094/184] Use autoconf features to deal with feature-test macros. --- configure.in | 23 ++--------------------- 1 file changed, 2 insertions(+), 21 deletions(-) diff --git a/configure.in b/configure.in index 71b746f..97856f9 100644 --- a/configure.in +++ b/configure.in @@ -2,33 +2,14 @@ dnl dnl autoconf input file to generate MCONFIG dnl -AC_PREREQ(2.52) +AC_PREREQ(2.59) AC_INIT(MCONFIG.in) AC_PREFIX_DEFAULT(/usr) +AC_USE_SYSTEM_EXTENSIONS AC_ISC_POSIX -AC_AIX -AC_MINIX AC_PROG_CC -dnl -dnl Feature-test macros. These need to be set in CFLAGS, rather in -dnl aconfig.h, or "configure" will run in a different environment than -dnl we eventually we build in. -dnl - -dnl Needed on Solaris/cc or Solaris/gcc -CFLAGS="$CFLAGS -D_XPG4_2" -CFLAGS="$CFLAGS -D_XOPEN_SOURCE" -CFLAGS="$CFLAGS -D__EXTENSIONS__" - -dnl Needed on some glibc systems -CFLAGS="$CFLAGS -D_GNU_SOURCE" - -dnl Needed on Digital Unix -CFLAGS="$CFLAGS -D_OSF_SOURCE" -CFLAGS="$CFLAGS -D_XOPEN_SOURCE_EXTENDED" - AC_C_CONST AC_C_INLINE From 62533e74411a579e5d24e33b50c5ba750b88ae35 Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Mon, 10 Dec 2007 17:37:52 -0800 Subject: [PATCH 095/184] AC_USE_SYSTEM_EXTENSIONS is a 2.61 feature, not 2.59 --- configure.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.in b/configure.in index 97856f9..ddd11da 100644 --- a/configure.in +++ b/configure.in @@ -2,7 +2,7 @@ dnl dnl autoconf input file to generate MCONFIG dnl -AC_PREREQ(2.59) +AC_PREREQ(2.61) AC_INIT(MCONFIG.in) AC_PREFIX_DEFAULT(/usr) From 22accddda0b5a47cb2f6ace54a359e102c31ec02 Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Tue, 8 Jul 2008 17:14:44 -0400 Subject: [PATCH 096/184] Reformat the source code The source code was a mix of different styles; normalize on NASM style; basically K&R style with 4 space indentation. --- common/tftpsubs.c | 356 ++++---- common/tftpsubs.h | 13 +- config.h | 8 +- lib/bsdsignal.c | 27 +- lib/daemon.c | 43 +- lib/dup2.c | 18 +- lib/xmalloc.c | 12 +- lib/xstrdup.c | 12 +- tftp/extern.h | 4 +- tftp/main.c | 1356 ++++++++++++++-------------- tftp/tftp.c | 629 +++++++------ tftpd/misc.c | 51 +- tftpd/recvfrom.c | 222 ++--- tftpd/recvfrom.h | 6 +- tftpd/remap.c | 655 +++++++------- tftpd/remap.h | 11 +- tftpd/tftpd.c | 2189 ++++++++++++++++++++++----------------------- tftpd/tftpd.h | 2 +- 18 files changed, 2794 insertions(+), 2820 deletions(-) diff --git a/common/tftpsubs.c b/common/tftpsubs.c index b2eae71..b266efe 100644 --- a/common/tftpsubs.c +++ b/common/tftpsubs.c @@ -41,9 +41,8 @@ #ifndef lint /* static char sccsid[] = "@(#)tftpsubs.c 8.1 (Berkeley) 6/6/93"; */ /* static char rcsid[] = "$OpenBSD: tftpsubs.c,v 1.2 1996/06/26 05:40:36 deraadt Exp $"; */ -static const char *rcsid UNUSED = -"tftp-hpa: $Id$"; -#endif /* not lint */ +static const char *rcsid UNUSED = "tftp-hpa: $Id$"; +#endif /* not lint */ /* Simple minded read-ahead/write-behind subroutines for tftp user and server. Written originally with multiple buffers in mind, but current @@ -58,127 +57,130 @@ static const char *rcsid UNUSED = #include -#define PKTSIZE MAX_SEGSIZE+4 /* should be moved to tftp.h */ +#define PKTSIZE MAX_SEGSIZE+4 /* should be moved to tftp.h */ -int segsize = SEGSIZE; /* Default segsize */ +int segsize = SEGSIZE; /* Default segsize */ struct bf { - int counter; /* size of data in buffer, or flag */ - char buf[PKTSIZE]; /* room for data packet */ + int counter; /* size of data in buffer, or flag */ + char buf[PKTSIZE]; /* room for data packet */ } bfs[2]; - /* Values for bf.counter */ + /* Values for bf.counter */ #define BF_ALLOC -3 /* alloc'd but not yet filled */ #define BF_FREE -2 /* free */ /* [-1 .. segsize] = size of data in the data buffer */ -static int nextone; /* index of next buffer to use */ -static int current; /* index of buffer in use */ +static int nextone; /* index of next buffer to use */ +static int current; /* index of buffer in use */ - /* control flags for crlf conversions */ -int newline = 0; /* fillbuf: in middle of newline expansion */ -int prevchar = -1; /* putbuf: previous char (cr check) */ + /* control flags for crlf conversions */ +int newline = 0; /* fillbuf: in middle of newline expansion */ +int prevchar = -1; /* putbuf: previous char (cr check) */ static struct tftphdr *rw_init(int); -struct tftphdr *w_init() { return rw_init(0); } /* write-behind */ -struct tftphdr *r_init() { return rw_init(1); } /* read-ahead */ +struct tftphdr *w_init() +{ + return rw_init(0); +} /* write-behind */ + +struct tftphdr *r_init() +{ + return rw_init(1); +} /* read-ahead */ /* init for either read-ahead or write-behind */ /* x == zero for write-behind, one for read-head */ -static struct tftphdr * -rw_init(int x) +static struct tftphdr *rw_init(int x) { - newline = 0; /* init crlf flag */ - prevchar = -1; - bfs[0].counter = BF_ALLOC; /* pass out the first buffer */ - current = 0; - bfs[1].counter = BF_FREE; - nextone = x; /* ahead or behind? */ - return (struct tftphdr *)bfs[0].buf; + newline = 0; /* init crlf flag */ + prevchar = -1; + bfs[0].counter = BF_ALLOC; /* pass out the first buffer */ + current = 0; + bfs[1].counter = BF_FREE; + nextone = x; /* ahead or behind? */ + return (struct tftphdr *)bfs[0].buf; } - /* Have emptied current buffer by sending to net and getting ack. Free it and return next buffer filled with data. */ -int -readit(FILE *file, struct tftphdr **dpp, int convert) +int readit(FILE * file, struct tftphdr **dpp, int convert) { - struct bf *b; + struct bf *b; - bfs[current].counter = BF_FREE; /* free old one */ - current = !current; /* "incr" current */ + bfs[current].counter = BF_FREE; /* free old one */ + current = !current; /* "incr" current */ - b = &bfs[current]; /* look at new buffer */ - if (b->counter == BF_FREE) /* if it's empty */ - read_ahead(file, convert); /* fill it */ -/* assert(b->counter != BF_FREE);*//* check */ - *dpp = (struct tftphdr *)b->buf; /* set caller's ptr */ - return b->counter; + b = &bfs[current]; /* look at new buffer */ + if (b->counter == BF_FREE) /* if it's empty */ + read_ahead(file, convert); /* fill it */ + /* assert(b->counter != BF_FREE);*//* check */ + *dpp = (struct tftphdr *)b->buf; /* set caller's ptr */ + return b->counter; } /* * fill the input buffer, doing ascii conversions if requested * conversions are lf -> cr,lf and cr -> cr, nul */ -void -read_ahead(FILE *file, int convert) +void read_ahead(FILE * file, int convert) { - int i; - char *p; - int c; - struct bf *b; - struct tftphdr *dp; + int i; + char *p; + int c; + struct bf *b; + struct tftphdr *dp; - b = &bfs[nextone]; /* look at "next" buffer */ - if (b->counter != BF_FREE) /* nop if not free */ - return; - nextone = !nextone; /* "incr" next buffer ptr */ + b = &bfs[nextone]; /* look at "next" buffer */ + if (b->counter != BF_FREE) /* nop if not free */ + return; + nextone = !nextone; /* "incr" next buffer ptr */ - dp = (struct tftphdr *)b->buf; + dp = (struct tftphdr *)b->buf; - if (convert == 0) { - b->counter = read(fileno(file), dp->th_data, segsize); - return; - } + if (convert == 0) { + b->counter = read(fileno(file), dp->th_data, segsize); + return; + } - p = dp->th_data; - for (i = 0 ; i < segsize; i++) { - if (newline) { - if (prevchar == '\n') - c = '\n'; /* lf to cr,lf */ - else c = '\0'; /* cr to cr,nul */ - newline = 0; - } - else { - c = getc(file); - if (c == EOF) break; - if (c == '\n' || c == '\r') { - prevchar = c; - c = '\r'; - newline = 1; - } - } - *p++ = c; - } - b->counter = (int)(p - dp->th_data); + p = dp->th_data; + for (i = 0; i < segsize; i++) { + if (newline) { + if (prevchar == '\n') + c = '\n'; /* lf to cr,lf */ + else + c = '\0'; /* cr to cr,nul */ + newline = 0; + } else { + c = getc(file); + if (c == EOF) + break; + if (c == '\n' || c == '\r') { + prevchar = c; + c = '\r'; + newline = 1; + } + } + *p++ = c; + } + b->counter = (int)(p - dp->th_data); } /* Update count associated with the buffer, get new buffer from the queue. Calls write_behind only if next buffer not available. */ -int -writeit(FILE *file, struct tftphdr **dpp, int ct, int convert) +int writeit(FILE * file, struct tftphdr **dpp, int ct, int convert) { - bfs[current].counter = ct; /* set size of data to write */ - current = !current; /* switch to other buffer */ - if (bfs[current].counter != BF_FREE) /* if not free */ - (void)write_behind(file, convert); /* flush it */ - bfs[current].counter = BF_ALLOC; /* mark as alloc'd */ - *dpp = (struct tftphdr *)bfs[current].buf; - return ct; /* this is a lie of course */ + bfs[current].counter = ct; /* set size of data to write */ + current = !current; /* switch to other buffer */ + if (bfs[current].counter != BF_FREE) /* if not free */ + (void)write_behind(file, convert); /* flush it */ + bfs[current].counter = BF_ALLOC; /* mark as alloc'd */ + *dpp = (struct tftphdr *)bfs[current].buf; + return ct; /* this is a lie of course */ } /* @@ -187,52 +189,50 @@ writeit(FILE *file, struct tftphdr **dpp, int ct, int convert) * Note spec is undefined if we get CR as last byte of file or a * CR followed by anything else. In this case we leave it alone. */ -int -write_behind(FILE *file, int convert) +int write_behind(FILE * file, int convert) { - char *buf; - int count; - int ct; - char *p; - int c; /* current character */ - struct bf *b; - struct tftphdr *dp; + char *buf; + int count; + int ct; + char *p; + int c; /* current character */ + struct bf *b; + struct tftphdr *dp; - b = &bfs[nextone]; - if (b->counter < -1) /* anything to flush? */ - return 0; /* just nop if nothing to do */ + b = &bfs[nextone]; + if (b->counter < -1) /* anything to flush? */ + return 0; /* just nop if nothing to do */ - count = b->counter; /* remember byte count */ - b->counter = BF_FREE; /* reset flag */ - dp = (struct tftphdr *)b->buf; - nextone = !nextone; /* incr for next time */ - buf = dp->th_data; + count = b->counter; /* remember byte count */ + b->counter = BF_FREE; /* reset flag */ + dp = (struct tftphdr *)b->buf; + nextone = !nextone; /* incr for next time */ + buf = dp->th_data; - if (count <= 0) return -1; /* nak logic? */ + if (count <= 0) + return -1; /* nak logic? */ - if (convert == 0) - return write(fileno(file), buf, count); + if (convert == 0) + return write(fileno(file), buf, count); - p = buf; - ct = count; - while (ct--) { /* loop over the buffer */ - c = *p++; /* pick up a character */ - if (prevchar == '\r') { /* if prev char was cr */ - if (c == '\n') /* if have cr,lf then just */ - fseek(file, -1, 1); /* smash lf on top of the cr */ - else - if (c == '\0') /* if have cr,nul then */ - goto skipit; /* just skip over the putc */ - /* else just fall through and allow it */ - } - putc(c, file); -skipit: - prevchar = c; - } - return count; + p = buf; + ct = count; + while (ct--) { /* loop over the buffer */ + c = *p++; /* pick up a character */ + if (prevchar == '\r') { /* if prev char was cr */ + if (c == '\n') /* if have cr,lf then just */ + fseek(file, -1, 1); /* smash lf on top of the cr */ + else if (c == '\0') /* if have cr,nul then */ + goto skipit; /* just skip over the putc */ + /* else just fall through and allow it */ + } + putc(c, file); + skipit: + prevchar = c; + } + return count; } - /* When an error has occurred, it is possible that the two sides * are out of synch. Ie: that what I think is the other side's * response to packet N is really their response to packet N-1. @@ -244,69 +244,69 @@ skipit: * when trace is active). */ -int -synchnet(int f) /* socket to flush */ -{ - int pktcount = 0; - char rbuf[PKTSIZE]; - struct sockaddr_in from; - socklen_t fromlen; - fd_set socketset; - struct timeval notime; - - while ( 1 ) { - notime.tv_sec = notime.tv_usec = 0; - - FD_ZERO(&socketset); - FD_SET(f, &socketset); - - if ( select(f, &socketset, NULL, NULL, ¬ime) <= 0 ) - break; /* Nothing to read */ - - /* Otherwise drain the packet */ - pktcount++; - fromlen = sizeof from; - (void) recvfrom(f, rbuf, sizeof (rbuf), 0, - (struct sockaddr *)&from, &fromlen); - } +int synchnet(int f) +{ /* socket to flush */ + int pktcount = 0; + char rbuf[PKTSIZE]; + struct sockaddr_in from; + socklen_t fromlen; + fd_set socketset; + struct timeval notime; - return pktcount; /* Return packets drained */ -} + while (1) { + notime.tv_sec = notime.tv_usec = 0; + FD_ZERO(&socketset); + FD_SET(f, &socketset); -int pick_port_bind(int sockfd, struct sockaddr_in *myaddr, unsigned int port_range_from, unsigned int port_range_to) -{ - unsigned int port, firstport; - int port_range = 0; + if (select(f, &socketset, NULL, NULL, ¬ime) <= 0) + break; /* Nothing to read */ - if (port_range_from != 0 && port_range_to != 0) { - port_range = 1; - } - - firstport = port_range - ? port_range_from + rand() % (port_range_to-port_range_from+1) - : 0; - - port = firstport; - - do { - myaddr->sin_port = htons(port); - - if (bind(sockfd, (struct sockaddr *)myaddr, sizeof *myaddr) < 0) { - /* Some versions of Linux return EINVAL instead of EADDRINUSE */ - if ( !(port_range && (errno == EINVAL || errno == EADDRINUSE)) ) - return -1; - - /* Normally, we shouldn't have to loop, but some situations involving - aborted transfers make it possible. */ - } else { - return 0; + /* Otherwise drain the packet */ + pktcount++; + fromlen = sizeof from; + (void)recvfrom(f, rbuf, sizeof(rbuf), 0, + (struct sockaddr *)&from, &fromlen); } - port++; - if ( port > port_range_to ) - port = port_range_from; - } while ( port != firstport ); - - return -1; + return pktcount; /* Return packets drained */ +} + +int pick_port_bind(int sockfd, struct sockaddr_in *myaddr, + unsigned int port_range_from, + unsigned int port_range_to) +{ + unsigned int port, firstport; + int port_range = 0; + + if (port_range_from != 0 && port_range_to != 0) { + port_range = 1; + } + + firstport = port_range + ? port_range_from + rand() % (port_range_to - port_range_from + 1) + : 0; + + port = firstport; + + do { + myaddr->sin_port = htons(port); + + if (bind(sockfd, (struct sockaddr *)myaddr, sizeof *myaddr) < 0) { + /* Some versions of Linux return EINVAL instead of EADDRINUSE */ + if (!(port_range && (errno == EINVAL || errno == EADDRINUSE))) + return -1; + + /* Normally, we shouldn't have to loop, but some situations involving + aborted transfers make it possible. */ + } else { + return 0; + } + + port++; + if (port > port_range_to) + port = port_range_from; + } while (port != firstport); + + return -1; } diff --git a/common/tftpsubs.h b/common/tftpsubs.h index f986afc..f1269fd 100644 --- a/common/tftpsubs.h +++ b/common/tftpsubs.h @@ -50,18 +50,19 @@ struct tftphdr; struct tftphdr *r_init(void); -void read_ahead(FILE *, int); -int readit(FILE *, struct tftphdr **, int); +void read_ahead(FILE *, int); +int readit(FILE *, struct tftphdr **, int); -int synchnet(int); +int synchnet(int); struct tftphdr *w_init(void); -int write_behind(FILE *, int); -int writeit(FILE *, struct tftphdr **, int, int); +int write_behind(FILE *, int); +int writeit(FILE *, struct tftphdr **, int, int); extern int segsize; #define MAX_SEGSIZE 65464 -int pick_port_bind(int sockfd, struct sockaddr_in *myaddr, unsigned int from, unsigned int to); +int pick_port_bind(int sockfd, struct sockaddr_in *myaddr, + unsigned int from, unsigned int to); #endif diff --git a/config.h b/config.h index e4c5cc2..9c973b7 100644 --- a/config.h +++ b/config.h @@ -1,5 +1,5 @@ /* -*- c -*- ------------------------------------------------------------- * - * + * * Copyright 2001-2006 H. Peter Anvin - All Rights Reserved * * This program is free software available under the same license @@ -19,7 +19,7 @@ #define CONFIG_H 1 /* Must be included before we include any system headers! */ -#include "aconfig.h" /* autogenerated configuration header */ +#include "aconfig.h" /* autogenerated configuration header */ /* Standard includes */ @@ -54,7 +54,7 @@ #ifdef HAVE_STRINGS_H #include -#endif +#endif #ifdef HAVE_INTTYPES_H #ifdef INTTYPES_H_IS_SANE @@ -293,7 +293,7 @@ void *xmalloc(size_t); char *xstrdup(const char *); #ifndef HAVE_BSD_SIGNAL -void (*bsd_signal(int, void (*)(int)))(int); +void (*bsd_signal(int, void (*)(int))) (int); #endif #ifndef HAVE_DUP2 int dup2(int, int); diff --git a/lib/bsdsignal.c b/lib/bsdsignal.c index 92de999..0aae136 100644 --- a/lib/bsdsignal.c +++ b/lib/bsdsignal.c @@ -6,23 +6,22 @@ #include "config.h" -void (*bsd_signal(int signum, void (*handler)(int)))(int) -{ - struct sigaction action, oldaction; +void (*bsd_signal(int signum, void (*handler) (int))) (int) { + struct sigaction action, oldaction; - memset(&action, 0, sizeof action); - action.sa_handler = handler; - sigemptyset(&action.sa_mask); - sigaddset(&action.sa_mask, signum); - action.sa_flags = SA_RESTART; - - if (sigaction(signum, &action, &oldaction) == -1) { + memset(&action, 0, sizeof action); + action.sa_handler = handler; + sigemptyset(&action.sa_mask); + sigaddset(&action.sa_mask, signum); + action.sa_flags = SA_RESTART; + + if (sigaction(signum, &action, &oldaction) == -1) { #ifdef SIG_ERR - return SIG_ERR; + return SIG_ERR; #else - return NULL; + return NULL; #endif - } + } - return oldaction.sa_handler; + return oldaction.sa_handler; } diff --git a/lib/daemon.c b/lib/daemon.c index c3106b5..0eb39c9 100644 --- a/lib/daemon.c +++ b/lib/daemon.c @@ -6,32 +6,31 @@ int daemon(int nochdir, int noclose) { - int nullfd; - pid_t f; - - if (!nochdir) { - if (chdir("/")) - return -1; - } + int nullfd; + pid_t f; - if (!noclose) { - if ((nullfd = open("/dev/null", O_RDWR)) < 0 || - dup2(nullfd, 0) < 0 || - dup2(nullfd, 1) < 0 || - dup2(nullfd, 2) < 0) - return -1; - close(nullfd); - } + if (!nochdir) { + if (chdir("/")) + return -1; + } - f = fork(); - if (f < 0) - return -1; - else if (f > 0) - _exit(0); + if (!noclose) { + if ((nullfd = open("/dev/null", O_RDWR)) < 0 || + dup2(nullfd, 0) < 0 || + dup2(nullfd, 1) < 0 || dup2(nullfd, 2) < 0) + return -1; + close(nullfd); + } + + f = fork(); + if (f < 0) + return -1; + else if (f > 0) + _exit(0); #ifdef HAVE_SETSID - return setsid(); + return setsid(); #else - return 0; + return 0; #endif } diff --git a/lib/dup2.c b/lib/dup2.c index bdf3325..bba45c4 100644 --- a/lib/dup2.c +++ b/lib/dup2.c @@ -8,18 +8,16 @@ int dup2(int oldfd, int newfd) { - int rv, nfd; + int rv, nfd; - close(newfd); + close(newfd); - nfd = rv = dup(oldfd); + nfd = rv = dup(oldfd); - if (rv >= 0 && rv != newfd) { - rv = dup2(oldfd, newfd); - close(nfd); - } + if (rv >= 0 && rv != newfd) { + rv = dup2(oldfd, newfd); + close(nfd); + } - return rv; + return rv; } - - diff --git a/lib/xmalloc.c b/lib/xmalloc.c index f234a46..30704f3 100644 --- a/lib/xmalloc.c +++ b/lib/xmalloc.c @@ -9,12 +9,12 @@ void *xmalloc(size_t size) { - void *p = malloc(size); + void *p = malloc(size); - if ( !p ) { - fprintf(stderr, "Out of memory!\n"); - exit(128); - } + if (!p) { + fprintf(stderr, "Out of memory!\n"); + exit(128); + } - return p; + return p; } diff --git a/lib/xstrdup.c b/lib/xstrdup.c index 036b3b2..05e3054 100644 --- a/lib/xstrdup.c +++ b/lib/xstrdup.c @@ -9,12 +9,12 @@ char *xstrdup(const char *s) { - char *p = strdup(s); + char *p = strdup(s); - if ( !p ) { - fprintf(stderr, "Out of memory!\n"); - exit(128); - } + if (!p) { + fprintf(stderr, "Out of memory!\n"); + exit(128); + } - return p; + return p; } diff --git a/tftp/extern.h b/tftp/extern.h index 401608e..bfe9a97 100644 --- a/tftp/extern.h +++ b/tftp/extern.h @@ -41,7 +41,7 @@ #ifndef RECVFILE_H #define RECVFILE_H -void tftp_recvfile (int, const char *, const char *); -void tftp_sendfile (int, const char *, const char *); +void tftp_recvfile(int, const char *, const char *); +void tftp_sendfile(int, const char *, const char *); #endif diff --git a/tftp/main.c b/tftp/main.c index 6bc7c44..bd041a8 100644 --- a/tftp/main.c +++ b/tftp/main.c @@ -37,14 +37,12 @@ #include "common/tftpsubs.h" #ifndef lint -static const char *copyright UNUSED = -"@(#) Copyright (c) 1983, 1993\n\ +static const char *copyright UNUSED = "@(#) Copyright (c) 1983, 1993\n\ The Regents of the University of California. All rights reserved.\n"; /* static char sccsid[] = "@(#)main.c 8.1 (Berkeley) 6/6/93"; */ /* static char rcsid[] = "$OpenBSD: main.c,v 1.4 1997/01/17 07:13:30 millert Exp $"; */ -static const char *rcsid UNUSED = - "tftp-hpa $Id$"; -#endif /* not lint */ +static const char *rcsid UNUSED = "tftp-hpa $Id$"; +#endif /* not lint */ /* Many bug fixes are from Jim Guyton */ @@ -63,131 +61,132 @@ static const char *rcsid UNUSED = #include "extern.h" -#define TIMEOUT 5 /* secs between rexmt's */ -#define LBUFLEN 200 /* size of input buffer */ +#define TIMEOUT 5 /* secs between rexmt's */ +#define LBUFLEN 200 /* size of input buffer */ struct modes { - const char *m_name; - const char *m_mode; - int m_openflags; + const char *m_name; + const char *m_mode; + int m_openflags; }; static const struct modes modes[] = { - { "netascii", "netascii", O_TEXT }, - { "ascii", "netascii", O_TEXT }, - { "octet", "octet", O_BINARY }, - { "binary", "octet", O_BINARY }, - { "image", "octet", O_BINARY }, - { 0, 0, 0 } + {"netascii", "netascii", O_TEXT}, + {"ascii", "netascii", O_TEXT}, + {"octet", "octet", O_BINARY}, + {"binary", "octet", O_BINARY}, + {"image", "octet", O_BINARY}, + {0, 0, 0} }; + #define MODE_OCTET (&modes[2]) #define MODE_NETASCII (&modes[0]) #define MODE_DEFAULT MODE_NETASCII -struct sockaddr_in peeraddr; -int f; +struct sockaddr_in peeraddr; +int f; u_short port; -int trace; -int verbose; -int literal; -int connected; +int trace; +int verbose; +int literal; +int connected; const struct modes *mode; #ifdef WITH_READLINE -char *line = NULL; +char *line = NULL; #else -char line[LBUFLEN]; +char line[LBUFLEN]; #endif -int margc; -char *margv[20]; +int margc; +char *margv[20]; const char *prompt = "tftp> "; -sigjmp_buf toplevel; -void intr(int); -struct servent *sp; +sigjmp_buf toplevel; +void intr(int); +struct servent *sp; int portrange = 0; unsigned int portrange_from = 0; -unsigned int portrange_to = 0; +unsigned int portrange_to = 0; -void get (int, char **); -void help (int, char **); -void modecmd (int, char **); -void put (int, char **); -void quit (int, char **); -void setascii (int, char **); -void setbinary (int, char **); -void setpeer (int, char **); -void setrexmt (int, char **); -void settimeout (int, char **); -void settrace (int, char **); -void setverbose (int, char **); -void status (int, char **); -void setliteral (int, char **); +void get(int, char **); +void help(int, char **); +void modecmd(int, char **); +void put(int, char **); +void quit(int, char **); +void setascii(int, char **); +void setbinary(int, char **); +void setpeer(int, char **); +void setrexmt(int, char **); +void settimeout(int, char **); +void settrace(int, char **); +void setverbose(int, char **); +void status(int, char **); +void setliteral(int, char **); -static void command (void); +static void command(void); -static void getusage (char *); -static void makeargv (void); -static void putusage (char *); -static void settftpmode (const struct modes *); +static void getusage(char *); +static void makeargv(void); +static void putusage(char *); +static void settftpmode(const struct modes *); #define HELPINDENT (sizeof("connect")) struct cmd { - const char *name; - const char *help; - void (*handler) (int, char **); + const char *name; + const char *help; + void (*handler) (int, char **); }; struct cmd cmdtab[] = { - { "connect", - "connect to remote tftp", - setpeer }, - { "mode", - "set file transfer mode", - modecmd }, - { "put", - "send file", - put }, - { "get", - "receive file", - get }, - { "quit", - "exit tftp", - quit }, - { "verbose", - "toggle verbose mode", - setverbose }, - { "trace", - "toggle packet tracing", - settrace }, - { "literal", - "toggle literal mode, ignore ':' in file name", - setliteral }, - { "status", - "show current status", - status }, - { "binary", - "set mode to octet", - setbinary }, - { "ascii", - "set mode to netascii", - setascii }, - { "rexmt", - "set per-packet transmission timeout", - setrexmt }, - { "timeout", - "set total retransmission timeout", - settimeout }, - { "?", - "print help information", - help }, - { "help", - "print help information", - help }, - { 0, 0, 0 } + {"connect", + "connect to remote tftp", + setpeer}, + {"mode", + "set file transfer mode", + modecmd}, + {"put", + "send file", + put}, + {"get", + "receive file", + get}, + {"quit", + "exit tftp", + quit}, + {"verbose", + "toggle verbose mode", + setverbose}, + {"trace", + "toggle packet tracing", + settrace}, + {"literal", + "toggle literal mode, ignore ':' in file name", + setliteral}, + {"status", + "show current status", + status}, + {"binary", + "set mode to octet", + setbinary}, + {"ascii", + "set mode to netascii", + setascii}, + {"rexmt", + "set per-packet transmission timeout", + setrexmt}, + {"timeout", + "set total retransmission timeout", + settimeout}, + {"?", + "print help information", + help}, + {"help", + "print help information", + help}, + {0, 0, 0} }; -struct cmd *getcmd(char *); -char *tail(char *); +struct cmd *getcmd(char *); +char *tail(char *); char *xstrdup(const char *); @@ -195,722 +194,717 @@ const char *program; static inline void usage(int errcode) { - fprintf(stderr, "Usage: %s [-v][-l][-m mode] [host [port]] [-c command]\n", program); - exit(errcode); + fprintf(stderr, + "Usage: %s [-v][-l][-m mode] [host [port]] [-c command]\n", + program); + exit(errcode); } -int -main(int argc, char *argv[]) +int main(int argc, char *argv[]) { - struct sockaddr_in s_in; - int arg; - static int pargc, peerargc; - static int iscmd = 0; - char **pargv; - const char *optx; - char *peerargv[3]; + struct sockaddr_in s_in; + int arg; + static int pargc, peerargc; + static int iscmd = 0; + char **pargv; + const char *optx; + char *peerargv[3]; - program = argv[0]; + program = argv[0]; - mode = MODE_DEFAULT; + mode = MODE_DEFAULT; - peerargv[0] = argv[0]; - peerargc = 1; + peerargv[0] = argv[0]; + peerargc = 1; - for ( arg = 1 ; !iscmd && arg < argc ; arg++ ) { - if ( argv[arg][0] == '-' ) { - for ( optx = &argv[arg][1] ; *optx ; optx++ ) { - switch ( *optx ) { - case 'v': - verbose = 1; - break; - case 'V': - /* Print version and configuration to stdout and exit */ - printf("%s\n", TFTP_CONFIG_STR); - exit(0); - case 'l': - literal = 1; - break; - case 'm': - if ( ++arg >= argc ) - usage(EX_USAGE); - { - const struct modes *p; - - for ( p = modes ; p->m_name ; p++ ) { - if (!strcmp(argv[arg], p->m_name)) - break; - } - if (p->m_name) { - settftpmode(p); - } else { - fprintf(stderr, "%s: invalid mode: %s\n", argv[0], argv[arg]); - exit(EX_USAGE); - } - } - break; - case 'c': - iscmd = 1; - break; - case 'R': - if ( ++arg >= argc ) - usage(EX_USAGE); - if ( sscanf(argv[arg], "%u:%u", &portrange_from, &portrange_to) != 2 || - portrange_from > portrange_to || portrange_to > 65535 ) { - fprintf(stderr, "Bad port range: %s\n", argv[arg]); - exit(EX_USAGE); - } - portrange = 1; - break; - case 'h': - default: - usage(*optx == 'h' ? 0 : EX_USAGE); - } - } - } else { - if ( peerargc >= 3 ) - usage(EX_USAGE); + for (arg = 1; !iscmd && arg < argc; arg++) { + if (argv[arg][0] == '-') { + for (optx = &argv[arg][1]; *optx; optx++) { + switch (*optx) { + case 'v': + verbose = 1; + break; + case 'V': + /* Print version and configuration to stdout and exit */ + printf("%s\n", TFTP_CONFIG_STR); + exit(0); + case 'l': + literal = 1; + break; + case 'm': + if (++arg >= argc) + usage(EX_USAGE); + { + const struct modes *p; - peerargv[peerargc++] = argv[arg]; + for (p = modes; p->m_name; p++) { + if (!strcmp(argv[arg], p->m_name)) + break; + } + if (p->m_name) { + settftpmode(p); + } else { + fprintf(stderr, "%s: invalid mode: %s\n", + argv[0], argv[arg]); + exit(EX_USAGE); + } + } + break; + case 'c': + iscmd = 1; + break; + case 'R': + if (++arg >= argc) + usage(EX_USAGE); + if (sscanf + (argv[arg], "%u:%u", &portrange_from, + &portrange_to) != 2 + || portrange_from > portrange_to + || portrange_to > 65535) { + fprintf(stderr, "Bad port range: %s\n", argv[arg]); + exit(EX_USAGE); + } + portrange = 1; + break; + case 'h': + default: + usage(*optx == 'h' ? 0 : EX_USAGE); + } + } + } else { + if (peerargc >= 3) + usage(EX_USAGE); + + peerargv[peerargc++] = argv[arg]; + } } - } - - pargv = argv + arg; - pargc = argc - arg; - - sp = getservbyname("tftp", "udp"); - if (sp == 0) { - /* Use canned values */ - if (verbose) - fprintf(stderr, "tftp: tftp/udp: unknown service, faking it...\n"); - sp = xmalloc(sizeof(struct servent)); - sp->s_name = (char *)"tftp"; - sp->s_aliases = NULL; - sp->s_port = htons(IPPORT_TFTP); - sp->s_proto = (char *)"udp"; - } - port = sp->s_port; /* Default port */ - f = socket(AF_INET, SOCK_DGRAM, 0); - if (f < 0) { - perror("tftp: socket"); - exit(EX_OSERR); - } - bzero((char *)&s_in, sizeof (s_in)); - s_in.sin_family = AF_INET; - if (pick_port_bind(f, &s_in, portrange_from, portrange_to)) { - perror("tftp: bind"); - exit(EX_OSERR); - } - bsd_signal(SIGINT, intr); - if ( peerargc ) { - /* Set peer */ - if (sigsetjmp(toplevel,1) != 0) - exit(EX_NOHOST); - setpeer(peerargc, peerargv); - } + pargv = argv + arg; + pargc = argc - arg; - if ( iscmd && pargc ) { - /* -c specified; execute command and exit */ - struct cmd *c; - - if (sigsetjmp(toplevel,1) != 0) - exit(EX_UNAVAILABLE); - - c = getcmd(pargv[0]); - if ( c == (struct cmd *)-1 || c == (struct cmd *)0 ) { - fprintf(stderr, "%s: invalid command: %s\n", argv[0], pargv[1]); - exit(EX_USAGE); - } - (*c->handler)(pargc, pargv); - exit(0); - } - + sp = getservbyname("tftp", "udp"); + if (sp == 0) { + /* Use canned values */ + if (verbose) + fprintf(stderr, + "tftp: tftp/udp: unknown service, faking it...\n"); + sp = xmalloc(sizeof(struct servent)); + sp->s_name = (char *)"tftp"; + sp->s_aliases = NULL; + sp->s_port = htons(IPPORT_TFTP); + sp->s_proto = (char *)"udp"; + } + port = sp->s_port; /* Default port */ + f = socket(AF_INET, SOCK_DGRAM, 0); + if (f < 0) { + perror("tftp: socket"); + exit(EX_OSERR); + } + bzero((char *)&s_in, sizeof(s_in)); + s_in.sin_family = AF_INET; + if (pick_port_bind(f, &s_in, portrange_from, portrange_to)) { + perror("tftp: bind"); + exit(EX_OSERR); + } + bsd_signal(SIGINT, intr); + + if (peerargc) { + /* Set peer */ + if (sigsetjmp(toplevel, 1) != 0) + exit(EX_NOHOST); + setpeer(peerargc, peerargv); + } + + if (iscmd && pargc) { + /* -c specified; execute command and exit */ + struct cmd *c; + + if (sigsetjmp(toplevel, 1) != 0) + exit(EX_UNAVAILABLE); + + c = getcmd(pargv[0]); + if (c == (struct cmd *)-1 || c == (struct cmd *)0) { + fprintf(stderr, "%s: invalid command: %s\n", argv[0], + pargv[1]); + exit(EX_USAGE); + } + (*c->handler) (pargc, pargv); + exit(0); + } #ifdef WITH_READLINE #ifdef HAVE_READLINE_HISTORY_H - using_history(); + using_history(); #endif #endif - - if (sigsetjmp(toplevel,1) != 0) - (void)putchar('\n'); - command(); - - return 0; /* Never reached */ + + if (sigsetjmp(toplevel, 1) != 0) + (void)putchar('\n'); + command(); + + return 0; /* Never reached */ } -char *hostname; +char *hostname; /* Called when a command is incomplete; modifies the global variable "line" */ -static void -getmoreargs(const char *partial, const char *mprompt) +static void getmoreargs(const char *partial, const char *mprompt) { #ifdef WITH_READLINE - char *eline; - int len, elen; - - len = strlen(partial); - eline = readline(mprompt); - if (!eline) - exit(0); /* EOF */ - - elen = strlen(eline); + char *eline; + int len, elen; - if (line) { - free(line); - line = NULL; - } - line = xmalloc(len+elen+1); - strcpy(line, partial); - strcpy(line+len, eline); - free(eline); + len = strlen(partial); + eline = readline(mprompt); + if (!eline) + exit(0); /* EOF */ + + elen = strlen(eline); + + if (line) { + free(line); + line = NULL; + } + line = xmalloc(len + elen + 1); + strcpy(line, partial); + strcpy(line + len, eline); + free(eline); #ifdef HAVE_READLINE_HISTORY_H - add_history(line); + add_history(line); #endif #else - int len = strlen(partial); - - strcpy(line, partial); - fputs(mprompt, stdout); - if ( fgets(line+len, LBUFLEN-len, stdin) == 0 ) - if ( feof(stdin) ) - exit(0); /* EOF */ + int len = strlen(partial); + + strcpy(line, partial); + fputs(mprompt, stdout); + if (fgets(line + len, LBUFLEN - len, stdin) == 0) + if (feof(stdin)) + exit(0); /* EOF */ #endif } -void -setpeer(int argc, char *argv[]) +void setpeer(int argc, char *argv[]) { - struct hostent *host; + struct hostent *host; - if (argc < 2) { - getmoreargs("connect ", "(to) "); - makeargv(); - argc = margc; - argv = margv; - } - if ((argc < 2) || (argc > 3)) { - printf("usage: %s host-name [port]\n", argv[0]); - return; - } + if (argc < 2) { + getmoreargs("connect ", "(to) "); + makeargv(); + argc = margc; + argv = margv; + } + if ((argc < 2) || (argc > 3)) { + printf("usage: %s host-name [port]\n", argv[0]); + return; + } - host = gethostbyname(argv[1]); - if (host == 0) { - connected = 0; - printf("%s: unknown host\n", argv[1]); - return; - } - peeraddr.sin_family = host->h_addrtype; - bcopy(host->h_addr, &peeraddr.sin_addr, host->h_length); - hostname = xstrdup(host->h_name); + host = gethostbyname(argv[1]); + if (host == 0) { + connected = 0; + printf("%s: unknown host\n", argv[1]); + return; + } + peeraddr.sin_family = host->h_addrtype; + bcopy(host->h_addr, &peeraddr.sin_addr, host->h_length); + hostname = xstrdup(host->h_name); - port = sp->s_port; - if (argc == 3) { - struct servent *usp; - usp = getservbyname(argv[2], "udp"); - if ( usp ) { - port = usp->s_port; - } else { - unsigned long myport; - char *ep; - myport = strtoul(argv[2], &ep, 10); - if (*ep || myport > 65535UL) { - printf("%s: bad port number\n", argv[2]); - connected = 0; - return; - } - port = htons((u_short)myport); - } - } + port = sp->s_port; + if (argc == 3) { + struct servent *usp; + usp = getservbyname(argv[2], "udp"); + if (usp) { + port = usp->s_port; + } else { + unsigned long myport; + char *ep; + myport = strtoul(argv[2], &ep, 10); + if (*ep || myport > 65535UL) { + printf("%s: bad port number\n", argv[2]); + connected = 0; + return; + } + port = htons((u_short) myport); + } + } - if (verbose) { - printf("Connected to %s (%s), port %u\n", - hostname, inet_ntoa(peeraddr.sin_addr), - (unsigned int)ntohs(port)); - } - connected = 1; + if (verbose) { + printf("Connected to %s (%s), port %u\n", + hostname, inet_ntoa(peeraddr.sin_addr), + (unsigned int)ntohs(port)); + } + connected = 1; } -void -modecmd(int argc, char *argv[]) +void modecmd(int argc, char *argv[]) { - const struct modes *p; - const char *sep; + const struct modes *p; + const char *sep; - if (argc < 2) { - printf("Using %s mode to transfer files.\n", mode->m_mode); - return; - } - if (argc == 2) { - for (p = modes; p->m_name; p++) - if (strcmp(argv[1], p->m_name) == 0) - break; - if (p->m_name) { - settftpmode(p); - return; - } - printf("%s: unknown mode\n", argv[1]); - /* drop through and print usage message */ - } + if (argc < 2) { + printf("Using %s mode to transfer files.\n", mode->m_mode); + return; + } + if (argc == 2) { + for (p = modes; p->m_name; p++) + if (strcmp(argv[1], p->m_name) == 0) + break; + if (p->m_name) { + settftpmode(p); + return; + } + printf("%s: unknown mode\n", argv[1]); + /* drop through and print usage message */ + } - printf("usage: %s [", argv[0]); - sep = " "; - for (p = modes; p->m_name; p++) { - printf("%s%s", sep, p->m_name); - if (*sep == ' ') - sep = " | "; - } - printf(" ]\n"); - return; + printf("usage: %s [", argv[0]); + sep = " "; + for (p = modes; p->m_name; p++) { + printf("%s%s", sep, p->m_name); + if (*sep == ' ') + sep = " | "; + } + printf(" ]\n"); + return; } -void -setbinary(int argc, char *argv[]) -{ - (void)argc; (void)argv; /* Quiet unused warning */ - settftpmode(MODE_OCTET); -} - -void -setascii(int argc, char *argv[]) +void setbinary(int argc, char *argv[]) { - (void)argc; (void)argv; /* Quiet unused warning */ - settftpmode(MODE_NETASCII); + (void)argc; + (void)argv; /* Quiet unused warning */ + settftpmode(MODE_OCTET); } -static void -settftpmode(const struct modes *newmode) +void setascii(int argc, char *argv[]) { - mode = newmode; - if (verbose) - printf("mode set to %s\n", mode->m_mode); + (void)argc; + (void)argv; /* Quiet unused warning */ + settftpmode(MODE_NETASCII); } +static void settftpmode(const struct modes *newmode) +{ + mode = newmode; + if (verbose) + printf("mode set to %s\n", mode->m_mode); +} /* * Send file(s). */ -void -put(int argc, char *argv[]) +void put(int argc, char *argv[]) { - int fd; - int n; - char *cp, *targ; + int fd; + int n; + char *cp, *targ; - if (argc < 2) { - getmoreargs("send ", "(file) "); - makeargv(); - argc = margc; - argv = margv; - } - if (argc < 2) { - putusage(argv[0]); - return; - } - targ = argv[argc - 1]; - if (!literal && strchr(argv[argc - 1], ':')) { - struct hostent *hp; + if (argc < 2) { + getmoreargs("send ", "(file) "); + makeargv(); + argc = margc; + argv = margv; + } + if (argc < 2) { + putusage(argv[0]); + return; + } + targ = argv[argc - 1]; + if (!literal && strchr(argv[argc - 1], ':')) { + struct hostent *hp; - for (n = 1; n < argc - 1; n++) - if (strchr(argv[n], ':')) { - putusage(argv[0]); - return; - } - cp = argv[argc - 1]; - targ = strchr(cp, ':'); - *targ++ = 0; - hp = gethostbyname(cp); - if (hp == NULL) { - fprintf(stderr, "tftp: %s: ", cp); - herror((char *)NULL); - return; - } - bcopy(hp->h_addr, &peeraddr.sin_addr, hp->h_length); - peeraddr.sin_family = hp->h_addrtype; - connected = 1; - hostname = xstrdup(hp->h_name); - } - if (!connected) { - printf("No target machine specified.\n"); - return; - } - if (argc < 4) { - cp = argc == 2 ? tail(targ) : argv[1]; - fd = open(cp, O_RDONLY|mode->m_openflags); - if (fd < 0) { - fprintf(stderr, "tftp: "); perror(cp); - return; - } - if (verbose) - printf("putting %s to %s:%s [%s]\n", - cp, hostname, targ, mode->m_mode); - peeraddr.sin_port = port; - tftp_sendfile(fd, targ, mode->m_mode); - return; - } - /* this assumes the target is a directory */ - /* on a remote unix system. hmmmm. */ - cp = strchr(targ, '\0'); - *cp++ = '/'; - for (n = 1; n < argc - 1; n++) { - strcpy(cp, tail(argv[n])); - fd = open(argv[n], O_RDONLY|mode->m_openflags); - if (fd < 0) { - fprintf(stderr, "tftp: "); perror(argv[n]); - continue; - } - if (verbose) - printf("putting %s to %s:%s [%s]\n", - argv[n], hostname, targ, mode->m_mode); - peeraddr.sin_port = port; - tftp_sendfile(fd, targ, mode->m_mode); - } + for (n = 1; n < argc - 1; n++) + if (strchr(argv[n], ':')) { + putusage(argv[0]); + return; + } + cp = argv[argc - 1]; + targ = strchr(cp, ':'); + *targ++ = 0; + hp = gethostbyname(cp); + if (hp == NULL) { + fprintf(stderr, "tftp: %s: ", cp); + herror((char *)NULL); + return; + } + bcopy(hp->h_addr, &peeraddr.sin_addr, hp->h_length); + peeraddr.sin_family = hp->h_addrtype; + connected = 1; + hostname = xstrdup(hp->h_name); + } + if (!connected) { + printf("No target machine specified.\n"); + return; + } + if (argc < 4) { + cp = argc == 2 ? tail(targ) : argv[1]; + fd = open(cp, O_RDONLY | mode->m_openflags); + if (fd < 0) { + fprintf(stderr, "tftp: "); + perror(cp); + return; + } + if (verbose) + printf("putting %s to %s:%s [%s]\n", + cp, hostname, targ, mode->m_mode); + peeraddr.sin_port = port; + tftp_sendfile(fd, targ, mode->m_mode); + return; + } + /* this assumes the target is a directory */ + /* on a remote unix system. hmmmm. */ + cp = strchr(targ, '\0'); + *cp++ = '/'; + for (n = 1; n < argc - 1; n++) { + strcpy(cp, tail(argv[n])); + fd = open(argv[n], O_RDONLY | mode->m_openflags); + if (fd < 0) { + fprintf(stderr, "tftp: "); + perror(argv[n]); + continue; + } + if (verbose) + printf("putting %s to %s:%s [%s]\n", + argv[n], hostname, targ, mode->m_mode); + peeraddr.sin_port = port; + tftp_sendfile(fd, targ, mode->m_mode); + } } -static void -putusage(char *s) +static void putusage(char *s) { - printf("usage: %s file ... host:target, or\n", s); - printf(" %s file ... target (when already connected)\n", s); + printf("usage: %s file ... host:target, or\n", s); + printf(" %s file ... target (when already connected)\n", s); } /* * Receive file(s). */ -void -get(int argc, char *argv[]) +void get(int argc, char *argv[]) { - int fd; - int n; - char *cp; - char *src; + int fd; + int n; + char *cp; + char *src; - if (argc < 2) { - getmoreargs("get ", "(files) "); - makeargv(); - argc = margc; - argv = margv; - } - if (argc < 2) { - getusage(argv[0]); - return; - } - if (!connected) { - for (n = 1; n < argc ; n++) - if (literal || strchr(argv[n], ':') == 0) { - getusage(argv[0]); - return; - } - } - for (n = 1; n < argc ; n++) { - src = strchr(argv[n], ':'); - if (literal || src == NULL) - src = argv[n]; - else { - struct hostent *hp; + if (argc < 2) { + getmoreargs("get ", "(files) "); + makeargv(); + argc = margc; + argv = margv; + } + if (argc < 2) { + getusage(argv[0]); + return; + } + if (!connected) { + for (n = 1; n < argc; n++) + if (literal || strchr(argv[n], ':') == 0) { + getusage(argv[0]); + return; + } + } + for (n = 1; n < argc; n++) { + src = strchr(argv[n], ':'); + if (literal || src == NULL) + src = argv[n]; + else { + struct hostent *hp; - *src++ = 0; - hp = gethostbyname(argv[n]); - if (hp == NULL) { - fprintf(stderr, "tftp: %s: ", argv[n]); - herror((char *)NULL); - continue; - } - bcopy(hp->h_addr, (caddr_t)&peeraddr.sin_addr, - hp->h_length); - peeraddr.sin_family = hp->h_addrtype; - connected = 1; - hostname = xstrdup(hp->h_name); - } - if (argc < 4) { - cp = argc == 3 ? argv[2] : tail(src); - fd = open(cp, O_WRONLY|O_CREAT|O_TRUNC|mode->m_openflags, 0666); - if (fd < 0) { - fprintf(stderr, "tftp: "); perror(cp); - return; - } - if (verbose) - printf("getting from %s:%s to %s [%s]\n", - hostname, src, cp, mode->m_mode); - peeraddr.sin_port = port; - tftp_recvfile(fd, src, mode->m_mode); - break; - } - cp = tail(src); /* new .. jdg */ - fd = open(cp, O_WRONLY|O_CREAT|O_TRUNC|mode->m_openflags, 0666); - if (fd < 0) { - fprintf(stderr, "tftp: "); perror(cp); - continue; - } - if (verbose) - printf("getting from %s:%s to %s [%s]\n", - hostname, src, cp, mode->m_mode); - peeraddr.sin_port = port; - tftp_recvfile(fd, src, mode->m_mode); - } + *src++ = 0; + hp = gethostbyname(argv[n]); + if (hp == NULL) { + fprintf(stderr, "tftp: %s: ", argv[n]); + herror((char *)NULL); + continue; + } + bcopy(hp->h_addr, (caddr_t) & peeraddr.sin_addr, hp->h_length); + peeraddr.sin_family = hp->h_addrtype; + connected = 1; + hostname = xstrdup(hp->h_name); + } + if (argc < 4) { + cp = argc == 3 ? argv[2] : tail(src); + fd = open(cp, O_WRONLY | O_CREAT | O_TRUNC | mode->m_openflags, + 0666); + if (fd < 0) { + fprintf(stderr, "tftp: "); + perror(cp); + return; + } + if (verbose) + printf("getting from %s:%s to %s [%s]\n", + hostname, src, cp, mode->m_mode); + peeraddr.sin_port = port; + tftp_recvfile(fd, src, mode->m_mode); + break; + } + cp = tail(src); /* new .. jdg */ + fd = open(cp, O_WRONLY | O_CREAT | O_TRUNC | mode->m_openflags, + 0666); + if (fd < 0) { + fprintf(stderr, "tftp: "); + perror(cp); + continue; + } + if (verbose) + printf("getting from %s:%s to %s [%s]\n", + hostname, src, cp, mode->m_mode); + peeraddr.sin_port = port; + tftp_recvfile(fd, src, mode->m_mode); + } } -static void -getusage(char *s) +static void getusage(char *s) { - printf("usage: %s host:file host:file ... file, or\n", s); - printf(" %s file file ... file if connected\n", s); + printf("usage: %s host:file host:file ... file, or\n", s); + printf(" %s file file ... file if connected\n", s); } -int rexmtval = TIMEOUT; +int rexmtval = TIMEOUT; -void -setrexmt(int argc, char *argv[]) +void setrexmt(int argc, char *argv[]) { - int t; + int t; - if (argc < 2) { - getmoreargs("rexmt-timeout ", "(value) "); - makeargv(); - argc = margc; - argv = margv; - } - if (argc != 2) { - printf("usage: %s value\n", argv[0]); - return; - } - t = atoi(argv[1]); - if (t < 0) - printf("%s: bad value\n", argv[1]); - else - rexmtval = t; + if (argc < 2) { + getmoreargs("rexmt-timeout ", "(value) "); + makeargv(); + argc = margc; + argv = margv; + } + if (argc != 2) { + printf("usage: %s value\n", argv[0]); + return; + } + t = atoi(argv[1]); + if (t < 0) + printf("%s: bad value\n", argv[1]); + else + rexmtval = t; } -int maxtimeout = 5 * TIMEOUT; +int maxtimeout = 5 * TIMEOUT; -void -settimeout(int argc, char *argv[]) +void settimeout(int argc, char *argv[]) { - int t; + int t; - if (argc < 2) { - getmoreargs("maximum-timeout ", "(value) "); - makeargv(); - argc = margc; - argv = margv; - } - if (argc != 2) { - printf("usage: %s value\n", argv[0]); - return; - } - t = atoi(argv[1]); - if (t < 0) - printf("%s: bad value\n", argv[1]); - else - maxtimeout = t; + if (argc < 2) { + getmoreargs("maximum-timeout ", "(value) "); + makeargv(); + argc = margc; + argv = margv; + } + if (argc != 2) { + printf("usage: %s value\n", argv[0]); + return; + } + t = atoi(argv[1]); + if (t < 0) + printf("%s: bad value\n", argv[1]); + else + maxtimeout = t; } -void -setliteral(int argc, char *argv[]) +void setliteral(int argc, char *argv[]) { - (void)argc; (void)argv; /* Quiet unused warning */ - literal = !literal; - printf("Literal mode %s.\n", literal ? "on" : "off"); + (void)argc; + (void)argv; /* Quiet unused warning */ + literal = !literal; + printf("Literal mode %s.\n", literal ? "on" : "off"); } -void -status(int argc, char *argv[]) +void status(int argc, char *argv[]) { - (void)argc; (void)argv; /* Quiet unused warning */ - if (connected) - printf("Connected to %s.\n", hostname); - else - printf("Not connected.\n"); - printf("Mode: %s Verbose: %s Tracing: %s Literal: %s\n", mode->m_mode, - verbose ? "on" : "off", trace ? "on" : "off", literal ? "on" : "off"); - printf("Rexmt-interval: %d seconds, Max-timeout: %d seconds\n", - rexmtval, maxtimeout); + (void)argc; + (void)argv; /* Quiet unused warning */ + if (connected) + printf("Connected to %s.\n", hostname); + else + printf("Not connected.\n"); + printf("Mode: %s Verbose: %s Tracing: %s Literal: %s\n", mode->m_mode, + verbose ? "on" : "off", trace ? "on" : "off", + literal ? "on" : "off"); + printf("Rexmt-interval: %d seconds, Max-timeout: %d seconds\n", + rexmtval, maxtimeout); } -void -intr(int sig) +void intr(int sig) { - (void)sig; /* Quiet unused warning */ + (void)sig; /* Quiet unused warning */ - bsd_signal(SIGALRM, SIG_IGN); - alarm(0); - siglongjmp(toplevel, -1); + bsd_signal(SIGALRM, SIG_IGN); + alarm(0); + siglongjmp(toplevel, -1); } -char * -tail(char *filename) +char *tail(char *filename) { - char *s; - - while (*filename) { - s = strrchr(filename, '/'); - if (s == NULL) - break; - if (s[1]) - return (s + 1); - *s = '\0'; - } - return (filename); + char *s; + + while (*filename) { + s = strrchr(filename, '/'); + if (s == NULL) + break; + if (s[1]) + return (s + 1); + *s = '\0'; + } + return (filename); } /* * Command parser. */ -static void -command(void) +static void command(void) { - struct cmd *c; + struct cmd *c; - for (;;) { + for (;;) { #ifdef WITH_READLINE - if ( line ) { - free(line); - line = NULL; - } - line = readline(prompt); - if ( !line ) - exit(0); /* EOF */ + if (line) { + free(line); + line = NULL; + } + line = readline(prompt); + if (!line) + exit(0); /* EOF */ #else - fputs(prompt, stdout); - if (fgets(line, LBUFLEN, stdin) == 0) { - if (feof(stdin)) { - exit(0); - } else { - continue; - } - } + fputs(prompt, stdout); + if (fgets(line, LBUFLEN, stdin) == 0) { + if (feof(stdin)) { + exit(0); + } else { + continue; + } + } #endif - if ((line[0] == 0) || (line[0] == '\n')) - continue; + if ((line[0] == 0) || (line[0] == '\n')) + continue; #ifdef WITH_READLINE #ifdef HAVE_READLINE_HISTORY_H - add_history(line); + add_history(line); #endif #endif - makeargv(); - if (margc == 0) - continue; + makeargv(); + if (margc == 0) + continue; - c = getcmd(margv[0]); - if (c == (struct cmd *)-1) { - printf("?Ambiguous command\n"); - continue; - } - if (c == 0) { - printf("?Invalid command\n"); - continue; - } - (*c->handler)(margc, margv); - } + c = getcmd(margv[0]); + if (c == (struct cmd *)-1) { + printf("?Ambiguous command\n"); + continue; + } + if (c == 0) { + printf("?Invalid command\n"); + continue; + } + (*c->handler) (margc, margv); + } } -struct cmd * -getcmd(char *name) +struct cmd *getcmd(char *name) { - const char *p; - char *q; - struct cmd *c, *found; - int nmatches, longest; + const char *p; + char *q; + struct cmd *c, *found; + int nmatches, longest; - longest = 0; - nmatches = 0; - found = 0; - for (c = cmdtab; (p = c->name) != NULL; c++) { - for (q = name; *q == *p++; q++) - if (*q == 0) /* exact match? */ - return (c); - if (!*q) { /* the name was a prefix */ - if (q - name > longest) { - longest = q - name; - nmatches = 1; - found = c; - } else if (q - name == longest) - nmatches++; - } - } - if (nmatches > 1) - return ((struct cmd *)-1); - return (found); + longest = 0; + nmatches = 0; + found = 0; + for (c = cmdtab; (p = c->name) != NULL; c++) { + for (q = name; *q == *p++; q++) + if (*q == 0) /* exact match? */ + return (c); + if (!*q) { /* the name was a prefix */ + if (q - name > longest) { + longest = q - name; + nmatches = 1; + found = c; + } else if (q - name == longest) + nmatches++; + } + } + if (nmatches > 1) + return ((struct cmd *)-1); + return (found); } /* * Slice a string up into argc/argv. */ -static void -makeargv(void) +static void makeargv(void) { - char *cp; - char **argp = margv; + char *cp; + char **argp = margv; - margc = 0; - for (cp = line; *cp;) { - while (isspace(*cp)) - cp++; - if (*cp == '\0') - break; - *argp++ = cp; - margc += 1; - while (*cp != '\0' && !isspace(*cp)) - cp++; - if (*cp == '\0') - break; - *cp++ = '\0'; - } - *argp++ = 0; + margc = 0; + for (cp = line; *cp;) { + while (isspace(*cp)) + cp++; + if (*cp == '\0') + break; + *argp++ = cp; + margc += 1; + while (*cp != '\0' && !isspace(*cp)) + cp++; + if (*cp == '\0') + break; + *cp++ = '\0'; + } + *argp++ = 0; } -void -quit(int argc, char *argv[]) +void quit(int argc, char *argv[]) { - (void)argc; (void)argv; /* Quiet unused warning */ - exit(0); + (void)argc; + (void)argv; /* Quiet unused warning */ + exit(0); } /* * Help command. */ -void -help(int argc, char *argv[]) +void help(int argc, char *argv[]) { - struct cmd *c; + struct cmd *c; - printf("%s\n", VERSION); + printf("%s\n", VERSION); - if (argc == 1) { - printf("Commands may be abbreviated. Commands are:\n\n"); - for (c = cmdtab; c->name; c++) - printf("%-*s\t%s\n", (int)HELPINDENT, c->name, c->help); - return; - } - while (--argc > 0) { - char *arg; - arg = *++argv; - c = getcmd(arg); - if (c == (struct cmd *)-1) - printf("?Ambiguous help command %s\n", arg); - else if (c == (struct cmd *)0) - printf("?Invalid help command %s\n", arg); - else - printf("%s\n", c->help); - } + if (argc == 1) { + printf("Commands may be abbreviated. Commands are:\n\n"); + for (c = cmdtab; c->name; c++) + printf("%-*s\t%s\n", (int)HELPINDENT, c->name, c->help); + return; + } + while (--argc > 0) { + char *arg; + arg = *++argv; + c = getcmd(arg); + if (c == (struct cmd *)-1) + printf("?Ambiguous help command %s\n", arg); + else if (c == (struct cmd *)0) + printf("?Invalid help command %s\n", arg); + else + printf("%s\n", c->help); + } } -void -settrace(int argc, char *argv[]) +void settrace(int argc, char *argv[]) { - (void)argc; (void)argv; /* Quiet unused warning */ + (void)argc; + (void)argv; /* Quiet unused warning */ - trace = !trace; - printf("Packet tracing %s.\n", trace ? "on" : "off"); + trace = !trace; + printf("Packet tracing %s.\n", trace ? "on" : "off"); } -void -setverbose(int argc, char *argv[]) +void setverbose(int argc, char *argv[]) { - (void)argc; (void)argv; /* Quiet unused warning */ + (void)argc; + (void)argv; /* Quiet unused warning */ - verbose = !verbose; - printf("Verbose mode %s.\n", verbose ? "on" : "off"); + verbose = !verbose; + printf("Verbose mode %s.\n", verbose ? "on" : "off"); } diff --git a/tftp/tftp.c b/tftp/tftp.c index 8dfec29..41c6fbd 100644 --- a/tftp/tftp.c +++ b/tftp/tftp.c @@ -39,9 +39,8 @@ #ifndef lint /* static char sccsid[] = "@(#)tftp.c 8.1 (Berkeley) 6/6/93"; */ /* static char rcsid[] = "$OpenBSD: tftp.c,v 1.4 1997/08/06 06:43:45 deraadt Exp $"; */ -static const char *rcsid UNUSED = -"tftp-hpa $Id$"; -#endif /* not lint */ +static const char *rcsid UNUSED = "tftp-hpa $Id$"; +#endif /* not lint */ /* Many bug fixes are from Jim Guyton */ @@ -50,18 +49,18 @@ static const char *rcsid UNUSED = */ #include "extern.h" -extern struct sockaddr_in peeraddr; /* filled in by main */ -extern int f; /* the opened socket */ -extern int trace; -extern int verbose; -extern int rexmtval; -extern int maxtimeout; +extern struct sockaddr_in peeraddr; /* filled in by main */ +extern int f; /* the opened socket */ +extern int trace; +extern int verbose; +extern int rexmtval; +extern int maxtimeout; #define PKTSIZE SEGSIZE+4 -char ackbuf[PKTSIZE]; -int timeout; -sigjmp_buf toplevel; -sigjmp_buf timeoutbuf; +char ackbuf[PKTSIZE]; +int timeout; +sigjmp_buf toplevel; +sigjmp_buf timeoutbuf; static void nak(int, const char *); static int makerequest(int, const char *, struct tftphdr *, const char *); @@ -74,251 +73,247 @@ static void tpacket(const char *, struct tftphdr *, int); /* * Send the requested file. */ -void -tftp_sendfile(int fd, const char *name, const char *mode) +void tftp_sendfile(int fd, const char *name, const char *mode) { - struct tftphdr *ap; /* data and ack packets */ - struct tftphdr *dp; - int n; - volatile int is_request; - volatile u_short block; - volatile int size, convert; - volatile off_t amount; - struct sockaddr_in from; - socklen_t fromlen; - FILE *file; - u_short ap_opcode, ap_block; + struct tftphdr *ap; /* data and ack packets */ + struct tftphdr *dp; + int n; + volatile int is_request; + volatile u_short block; + volatile int size, convert; + volatile off_t amount; + struct sockaddr_in from; + socklen_t fromlen; + FILE *file; + u_short ap_opcode, ap_block; - startclock(); /* start stat's clock */ - dp = r_init(); /* reset fillbuf/read-ahead code */ - ap = (struct tftphdr *)ackbuf; - convert = !strcmp(mode, "netascii"); - file = fdopen(fd, convert ? "rt" : "rb"); - block = 0; - is_request = 1; /* First packet is the actual WRQ */ - amount = 0; + startclock(); /* start stat's clock */ + dp = r_init(); /* reset fillbuf/read-ahead code */ + ap = (struct tftphdr *)ackbuf; + convert = !strcmp(mode, "netascii"); + file = fdopen(fd, convert ? "rt" : "rb"); + block = 0; + is_request = 1; /* First packet is the actual WRQ */ + amount = 0; - bsd_signal(SIGALRM, timer); - do { - if (is_request) { - size = makerequest(WRQ, name, dp, mode) - 4; - } else { - /* size = read(fd, dp->th_data, SEGSIZE); */ - size = readit(file, &dp, convert); - if (size < 0) { - nak(errno + 100, NULL); - break; - } - dp->th_opcode = htons((u_short)DATA); - dp->th_block = htons((u_short)block); - } - timeout = 0; - (void) sigsetjmp(timeoutbuf,1); + bsd_signal(SIGALRM, timer); + do { + if (is_request) { + size = makerequest(WRQ, name, dp, mode) - 4; + } else { + /* size = read(fd, dp->th_data, SEGSIZE); */ + size = readit(file, &dp, convert); + if (size < 0) { + nak(errno + 100, NULL); + break; + } + dp->th_opcode = htons((u_short) DATA); + dp->th_block = htons((u_short) block); + } + timeout = 0; + (void)sigsetjmp(timeoutbuf, 1); - if (trace) - tpacket("sent", dp, size + 4); - n = sendto(f, dp, size + 4, 0, - (struct sockaddr *)&peeraddr, sizeof(peeraddr)); - if (n != size + 4) { - perror("tftp: sendto"); - goto abort; - } - read_ahead(file, convert); - for ( ; ; ) { - alarm(rexmtval); - do { - fromlen = sizeof(from); - n = recvfrom(f, ackbuf, sizeof(ackbuf), 0, - (struct sockaddr *)&from, &fromlen); - } while (n <= 0); - alarm(0); - if (n < 0) { - perror("tftp: recvfrom"); - goto abort; - } - peeraddr.sin_port = from.sin_port; /* added */ - if (trace) - tpacket("received", ap, n); - /* should verify packet came from server */ - ap_opcode = ntohs((u_short)ap->th_opcode); - ap_block = ntohs((u_short)ap->th_block); - if (ap_opcode == ERROR) { - printf("Error code %d: %s\n", ap_block, - ap->th_msg); - goto abort; - } - if (ap_opcode == ACK) { - int j; + if (trace) + tpacket("sent", dp, size + 4); + n = sendto(f, dp, size + 4, 0, + (struct sockaddr *)&peeraddr, sizeof(peeraddr)); + if (n != size + 4) { + perror("tftp: sendto"); + goto abort; + } + read_ahead(file, convert); + for (;;) { + alarm(rexmtval); + do { + fromlen = sizeof(from); + n = recvfrom(f, ackbuf, sizeof(ackbuf), 0, + (struct sockaddr *)&from, &fromlen); + } while (n <= 0); + alarm(0); + if (n < 0) { + perror("tftp: recvfrom"); + goto abort; + } + peeraddr.sin_port = from.sin_port; /* added */ + if (trace) + tpacket("received", ap, n); + /* should verify packet came from server */ + ap_opcode = ntohs((u_short) ap->th_opcode); + ap_block = ntohs((u_short) ap->th_block); + if (ap_opcode == ERROR) { + printf("Error code %d: %s\n", ap_block, ap->th_msg); + goto abort; + } + if (ap_opcode == ACK) { + int j; - if (ap_block == block) { - break; - } - /* On an error, try to synchronize - * both sides. - */ - j = synchnet(f); - if (j && trace) { - printf("discarded %d packets\n", - j); - } - /* - * RFC1129/RFC1350: We MUST NOT re-send the DATA - * packet in response to an invalid ACK. Doing so - * would cause the Sorcerer's Apprentice bug. - */ - } - } - if ( !is_request ) - amount += size; - is_request = 0; - block++; - } while (size == SEGSIZE || block == 1); -abort: - fclose(file); - stopclock(); - if (amount > 0) - printstats("Sent", amount); + if (ap_block == block) { + break; + } + /* On an error, try to synchronize + * both sides. + */ + j = synchnet(f); + if (j && trace) { + printf("discarded %d packets\n", j); + } + /* + * RFC1129/RFC1350: We MUST NOT re-send the DATA + * packet in response to an invalid ACK. Doing so + * would cause the Sorcerer's Apprentice bug. + */ + } + } + if (!is_request) + amount += size; + is_request = 0; + block++; + } while (size == SEGSIZE || block == 1); + abort: + fclose(file); + stopclock(); + if (amount > 0) + printstats("Sent", amount); } /* * Receive a file. */ -void -tftp_recvfile(int fd, const char *name, const char *mode) +void tftp_recvfile(int fd, const char *name, const char *mode) { - struct tftphdr *ap; - struct tftphdr *dp; - int n; - volatile u_short block; - volatile int size, firsttrip; - volatile unsigned long amount; - struct sockaddr_in from; - socklen_t fromlen; - FILE *file; - volatile int convert; /* true if converting crlf -> lf */ - u_short dp_opcode, dp_block; + struct tftphdr *ap; + struct tftphdr *dp; + int n; + volatile u_short block; + volatile int size, firsttrip; + volatile unsigned long amount; + struct sockaddr_in from; + socklen_t fromlen; + FILE *file; + volatile int convert; /* true if converting crlf -> lf */ + u_short dp_opcode, dp_block; - startclock(); - dp = w_init(); - ap = (struct tftphdr *)ackbuf; - convert = !strcmp(mode, "netascii"); - file = fdopen(fd, convert ?"wt":"wb"); - block = 1; - firsttrip = 1; - amount = 0; + startclock(); + dp = w_init(); + ap = (struct tftphdr *)ackbuf; + convert = !strcmp(mode, "netascii"); + file = fdopen(fd, convert ? "wt" : "wb"); + block = 1; + firsttrip = 1; + amount = 0; - bsd_signal(SIGALRM, timer); - do { - if (firsttrip) { - size = makerequest(RRQ, name, ap, mode); - firsttrip = 0; - } else { - ap->th_opcode = htons((u_short)ACK); - ap->th_block = htons((u_short)block); - size = 4; - block++; - } - timeout = 0; - (void) sigsetjmp(timeoutbuf,1); -send_ack: - if (trace) - tpacket("sent", ap, size); - if (sendto(f, ackbuf, size, 0, (struct sockaddr *)&peeraddr, - sizeof(peeraddr)) != size) { - alarm(0); - perror("tftp: sendto"); - goto abort; - } - write_behind(file, convert); - for ( ; ; ) { - alarm(rexmtval); - do { - fromlen = sizeof(from); - n = recvfrom(f, dp, PKTSIZE, 0, - (struct sockaddr *)&from, &fromlen); - } while (n <= 0); - alarm(0); - if (n < 0) { - perror("tftp: recvfrom"); - goto abort; - } - peeraddr.sin_port = from.sin_port; /* added */ - if (trace) - tpacket("received", dp, n); - /* should verify client address */ - dp_opcode = ntohs((u_short)dp->th_opcode); - dp_block = ntohs((u_short)dp->th_block); - if (dp_opcode == ERROR) { - printf("Error code %d: %s\n", dp_block, dp->th_msg); - goto abort; - } - if (dp_opcode == DATA) { - int j; + bsd_signal(SIGALRM, timer); + do { + if (firsttrip) { + size = makerequest(RRQ, name, ap, mode); + firsttrip = 0; + } else { + ap->th_opcode = htons((u_short) ACK); + ap->th_block = htons((u_short) block); + size = 4; + block++; + } + timeout = 0; + (void)sigsetjmp(timeoutbuf, 1); + send_ack: + if (trace) + tpacket("sent", ap, size); + if (sendto(f, ackbuf, size, 0, (struct sockaddr *)&peeraddr, + sizeof(peeraddr)) != size) { + alarm(0); + perror("tftp: sendto"); + goto abort; + } + write_behind(file, convert); + for (;;) { + alarm(rexmtval); + do { + fromlen = sizeof(from); + n = recvfrom(f, dp, PKTSIZE, 0, + (struct sockaddr *)&from, &fromlen); + } while (n <= 0); + alarm(0); + if (n < 0) { + perror("tftp: recvfrom"); + goto abort; + } + peeraddr.sin_port = from.sin_port; /* added */ + if (trace) + tpacket("received", dp, n); + /* should verify client address */ + dp_opcode = ntohs((u_short) dp->th_opcode); + dp_block = ntohs((u_short) dp->th_block); + if (dp_opcode == ERROR) { + printf("Error code %d: %s\n", dp_block, dp->th_msg); + goto abort; + } + if (dp_opcode == DATA) { + int j; - if (dp_block == block) { - break; /* have next packet */ - } - /* On an error, try to synchronize - * both sides. - */ - j = synchnet(f); - if (j && trace) { - printf("discarded %d packets\n", j); - } - if (dp_block == (block-1)) { - goto send_ack; /* resend ack */ - } - } - } - /* size = write(fd, dp->th_data, n - 4); */ - size = writeit(file, &dp, n - 4, convert); - if (size < 0) { - nak(errno + 100, NULL); - break; - } - amount += size; - } while (size == SEGSIZE); -abort: /* ok to ack, since user */ - ap->th_opcode = htons((u_short)ACK); /* has seen err msg */ - ap->th_block = htons((u_short)block); - (void) sendto(f, ackbuf, 4, 0, (struct sockaddr *)&peeraddr, - sizeof(peeraddr)); - write_behind(file, convert); /* flush last buffer */ - fclose(file); - stopclock(); - if (amount > 0) - printstats("Received", amount); + if (dp_block == block) { + break; /* have next packet */ + } + /* On an error, try to synchronize + * both sides. + */ + j = synchnet(f); + if (j && trace) { + printf("discarded %d packets\n", j); + } + if (dp_block == (block - 1)) { + goto send_ack; /* resend ack */ + } + } + } + /* size = write(fd, dp->th_data, n - 4); */ + size = writeit(file, &dp, n - 4, convert); + if (size < 0) { + nak(errno + 100, NULL); + break; + } + amount += size; + } while (size == SEGSIZE); + abort: /* ok to ack, since user */ + ap->th_opcode = htons((u_short) ACK); /* has seen err msg */ + ap->th_block = htons((u_short) block); + (void)sendto(f, ackbuf, 4, 0, (struct sockaddr *)&peeraddr, + sizeof(peeraddr)); + write_behind(file, convert); /* flush last buffer */ + fclose(file); + stopclock(); + if (amount > 0) + printstats("Received", amount); } static int makerequest(int request, const char *name, - struct tftphdr *tp, const char *mode) + struct tftphdr *tp, const char *mode) { - char *cp; + char *cp; - tp->th_opcode = htons((u_short)request); - cp = (char *) &(tp->th_stuff); - strcpy(cp, name); - cp += strlen(name); - *cp++ = '\0'; - strcpy(cp, mode); - cp += strlen(mode); - *cp++ = '\0'; - return (cp - (char *)tp); + tp->th_opcode = htons((u_short) request); + cp = (char *)&(tp->th_stuff); + strcpy(cp, name); + cp += strlen(name); + *cp++ = '\0'; + strcpy(cp, mode); + cp += strlen(mode); + *cp++ = '\0'; + return (cp - (char *)tp); } -static const char * const errmsgs[] = -{ - "Undefined error code", /* 0 - EUNDEF */ - "File not found", /* 1 - ENOTFOUND */ - "Access denied", /* 2 - EACCESS */ - "Disk full or allocation exceeded", /* 3 - ENOSPACE */ - "Illegal TFTP operation", /* 4 - EBADOP */ - "Unknown transfer ID", /* 5 - EBADID */ - "File already exists", /* 6 - EEXISTS */ - "No such user", /* 7 - ENOUSER */ - "Failure to negotiate RFC2347 options" /* 8 - EOPTNEG */ +static const char *const errmsgs[] = { + "Undefined error code", /* 0 - EUNDEF */ + "File not found", /* 1 - ENOTFOUND */ + "Access denied", /* 2 - EACCESS */ + "Disk full or allocation exceeded", /* 3 - ENOSPACE */ + "Illegal TFTP operation", /* 4 - EBADOP */ + "Unknown transfer ID", /* 5 - EBADID */ + "File already exists", /* 6 - EEXISTS */ + "No such user", /* 7 - ENOUSER */ + "Failure to negotiate RFC2347 options" /* 8 - EOPTNEG */ }; + #define ERR_CNT (sizeof(errmsgs)/sizeof(const char *)) /* @@ -327,121 +322,115 @@ static const char * const errmsgs[] = * standard TFTP codes, or a UNIX errno * offset by 100. */ -static void -nak(int error, const char *msg) +static void nak(int error, const char *msg) { - struct tftphdr *tp; - int length; - - tp = (struct tftphdr *)ackbuf; - tp->th_opcode = htons((u_short)ERROR); - tp->th_code = htons((u_short)error); + struct tftphdr *tp; + int length; - if ( error >= 100 ) { - /* This is a Unix errno+100 */ - if ( !msg ) - msg = strerror(error - 100); - error = EUNDEF; - } else { - if ( (unsigned)error >= ERR_CNT ) - error = EUNDEF; - - if ( !msg ) - msg = errmsgs[error]; - } + tp = (struct tftphdr *)ackbuf; + tp->th_opcode = htons((u_short) ERROR); + tp->th_code = htons((u_short) error); - tp->th_code = htons((u_short)error); + if (error >= 100) { + /* This is a Unix errno+100 */ + if (!msg) + msg = strerror(error - 100); + error = EUNDEF; + } else { + if ((unsigned)error >= ERR_CNT) + error = EUNDEF; - length = strlen(msg)+1; - memcpy(tp->th_msg, msg, length); - length += 4; /* Add space for header */ + if (!msg) + msg = errmsgs[error]; + } - if (trace) - tpacket("sent", tp, length); - if (sendto(f, ackbuf, length, 0, (struct sockaddr *)&peeraddr, - sizeof(peeraddr)) != length) - perror("nak"); + tp->th_code = htons((u_short) error); + + length = strlen(msg) + 1; + memcpy(tp->th_msg, msg, length); + length += 4; /* Add space for header */ + + if (trace) + tpacket("sent", tp, length); + if (sendto(f, ackbuf, length, 0, (struct sockaddr *)&peeraddr, + sizeof(peeraddr)) != length) + perror("nak"); } -static void -tpacket(const char *s, struct tftphdr *tp, int n) +static void tpacket(const char *s, struct tftphdr *tp, int n) { - static const char *opcodes[] = - { "#0", "RRQ", "WRQ", "DATA", "ACK", "ERROR", "OACK" }; - char *cp, *file; - u_short op = ntohs((u_short)tp->th_opcode); + static const char *opcodes[] = + { "#0", "RRQ", "WRQ", "DATA", "ACK", "ERROR", "OACK" }; + char *cp, *file; + u_short op = ntohs((u_short) tp->th_opcode); - if (op < RRQ || op > ERROR) - printf("%s opcode=%x ", s, op); - else - printf("%s %s ", s, opcodes[op]); - switch (op) { + if (op < RRQ || op > ERROR) + printf("%s opcode=%x ", s, op); + else + printf("%s %s ", s, opcodes[op]); + switch (op) { - case RRQ: - case WRQ: - n -= 2; - file = cp = (char *) &(tp->th_stuff); - cp = strchr(cp, '\0'); - printf("\n", file, cp + 1); - break; + case RRQ: + case WRQ: + n -= 2; + file = cp = (char *)&(tp->th_stuff); + cp = strchr(cp, '\0'); + printf("\n", file, cp + 1); + break; - case DATA: - printf("\n", ntohs(tp->th_block), n - 4); - break; + case DATA: + printf("\n", ntohs(tp->th_block), n - 4); + break; - case ACK: - printf("\n", ntohs(tp->th_block)); - break; + case ACK: + printf("\n", ntohs(tp->th_block)); + break; - case ERROR: - printf("\n", ntohs(tp->th_code), tp->th_msg); - break; - } + case ERROR: + printf("\n", ntohs(tp->th_code), tp->th_msg); + break; + } } struct timeval tstart; struct timeval tstop; -static void -startclock(void) +static void startclock(void) { - (void)gettimeofday(&tstart, NULL); + (void)gettimeofday(&tstart, NULL); } -static void -stopclock(void) +static void stopclock(void) { - (void)gettimeofday(&tstop, NULL); + (void)gettimeofday(&tstop, NULL); } -static void -printstats(const char *direction, unsigned long amount) +static void printstats(const char *direction, unsigned long amount) { - double delta; + double delta; - delta = (tstop.tv_sec+(tstop.tv_usec/100000.0)) - - (tstart.tv_sec+(tstart.tv_usec/100000.0)); - if (verbose) { - printf("%s %lu bytes in %.1f seconds", direction, amount, delta); - printf(" [%.0f bit/s]", (amount*8.)/delta); - putchar('\n'); - } + delta = (tstop.tv_sec + (tstop.tv_usec / 100000.0)) - + (tstart.tv_sec + (tstart.tv_usec / 100000.0)); + if (verbose) { + printf("%s %lu bytes in %.1f seconds", direction, amount, delta); + printf(" [%.0f bit/s]", (amount * 8.) / delta); + putchar('\n'); + } } -static void -timer(int sig) +static void timer(int sig) { - int save_errno = errno; + int save_errno = errno; - (void)sig; /* Shut up unused warning */ + (void)sig; /* Shut up unused warning */ - timeout += rexmtval; - if (timeout >= maxtimeout) { - printf("Transfer timed out.\n"); - errno = save_errno; - siglongjmp(toplevel, -1); - } - errno = save_errno; - siglongjmp(timeoutbuf, 1); + timeout += rexmtval; + if (timeout >= maxtimeout) { + printf("Transfer timed out.\n"); + errno = save_errno; + siglongjmp(toplevel, -1); + } + errno = save_errno; + siglongjmp(timeoutbuf, 1); } diff --git a/tftpd/misc.c b/tftpd/misc.c index 4f88978..07684dd 100644 --- a/tftpd/misc.c +++ b/tftpd/misc.c @@ -1,5 +1,5 @@ /* ----------------------------------------------------------------------- * - * + * * Copyright 2001-2007 H. Peter Anvin - All Rights Reserved * * This program is free software available under the same license @@ -14,7 +14,7 @@ * Minor help routines. */ -#include "config.h" /* Must be included first! */ +#include "config.h" /* Must be included first! */ #include #include "tftpd.h" @@ -22,19 +22,19 @@ * Set the signal handler and flags. Basically a user-friendly * wrapper around sigaction(). */ -void set_signal(int signum, void (*handler)(int), int flags) +void set_signal(int signum, void (*handler) (int), int flags) { - struct sigaction sa; + struct sigaction sa; - memset(&sa, 0, sizeof sa); - sa.sa_handler = handler; - sigemptyset(&sa.sa_mask); - sa.sa_flags = flags; - - if ( sigaction(signum, &sa, NULL) ) { - syslog(LOG_ERR, "sigaction: %m"); - exit(EX_OSERR); - } + memset(&sa, 0, sizeof sa); + sa.sa_handler = handler; + sigemptyset(&sa.sa_mask); + sa.sa_flags = flags; + + if (sigaction(signum, &sa, NULL)) { + syslog(LOG_ERR, "sigaction: %m"); + exit(EX_OSERR); + } } /* @@ -42,14 +42,14 @@ void set_signal(int signum, void (*handler)(int), int flags) */ void *tfmalloc(size_t size) { - void *p = malloc(size); + void *p = malloc(size); - if ( !p ) { - syslog(LOG_ERR, "malloc: %m"); - exit(EX_OSERR); - } + if (!p) { + syslog(LOG_ERR, "malloc: %m"); + exit(EX_OSERR); + } - return p; + return p; } /* @@ -57,13 +57,12 @@ void *tfmalloc(size_t size) */ char *tfstrdup(const char *str) { - char *p = strdup(str); + char *p = strdup(str); - if ( !p ) { - syslog(LOG_ERR, "strdup: %m"); - exit(EX_OSERR); - } + if (!p) { + syslog(LOG_ERR, "strdup: %m"); + exit(EX_OSERR); + } - return p; + return p; } - diff --git a/tftpd/recvfrom.c b/tftpd/recvfrom.c index ba8d648..fee6d03 100644 --- a/tftpd/recvfrom.c +++ b/tftpd/recvfrom.c @@ -1,5 +1,5 @@ /* ----------------------------------------------------------------------- * - * + * * Copyright 2001-2006 H. Peter Anvin - All Rights Reserved * * This program is free software available under the same license @@ -17,11 +17,11 @@ * */ -#include "config.h" /* Must be included first! */ +#include "config.h" /* Must be included first! */ #include "recvfrom.h" #include "common/tftpsubs.h" #ifdef HAVE_MACHINE_PARAM_H -#include /* Needed on some versions of FreeBSD */ +#include /* Needed on some versions of FreeBSD */ #endif #if defined(HAVE_RECVMSG) && defined(HAVE_MSGHDR_MSG_CONTROL) @@ -33,12 +33,12 @@ # ifdef __linux__ /* Assume this version of glibc simply lacks the definition */ struct in_pktinfo { - int ipi_ifindex; - struct in_addr ipi_spec_dst; - struct in_addr ipi_addr; + int ipi_ifindex; + struct in_addr ipi_spec_dst; + struct in_addr ipi_addr; }; # else -# undef IP_PKTINFO /* No definition, no way to get it */ +# undef IP_PKTINFO /* No definition, no way to get it */ # endif # endif #endif @@ -57,141 +57,141 @@ struct in_pktinfo { */ static int address_is_local(const struct sockaddr_in *addr) { - struct sockaddr_in sin; - int sockfd = -1; - int e; - int rv = 0; - socklen_t addrlen; + struct sockaddr_in sin; + int sockfd = -1; + int e; + int rv = 0; + socklen_t addrlen; - /* Multicast or universal broadcast address? */ - if (ntohl(addr->sin_addr.s_addr) >= (224UL << 24)) - return 0; + /* Multicast or universal broadcast address? */ + if (ntohl(addr->sin_addr.s_addr) >= (224UL << 24)) + return 0; - sockfd = socket(PF_INET, SOCK_DGRAM, 0); - if (sockfd < 0) - goto err; + sockfd = socket(PF_INET, SOCK_DGRAM, 0); + if (sockfd < 0) + goto err; - if (connect(sockfd, (const struct sockaddr *)addr, sizeof *addr)) - goto err; + if (connect(sockfd, (const struct sockaddr *)addr, sizeof *addr)) + goto err; - addrlen = sizeof sin; - if (getsockname(sockfd, (struct sockaddr *)&sin, &addrlen)) - goto err; + addrlen = sizeof sin; + if (getsockname(sockfd, (struct sockaddr *)&sin, &addrlen)) + goto err; - rv = sin.sin_addr.s_addr == addr->sin_addr.s_addr; + rv = sin.sin_addr.s_addr == addr->sin_addr.s_addr; - err: - e = errno; - - if (sockfd >= 0) - close(sockfd); + err: + e = errno; - errno = e; - return rv; + if (sockfd >= 0) + close(sockfd); + + errno = e; + return rv; } - int myrecvfrom(int s, void *buf, int len, unsigned int flags, - struct sockaddr *from, socklen_t *fromlen, - struct sockaddr_in *myaddr) + struct sockaddr *from, socklen_t * fromlen, + struct sockaddr_in *myaddr) { - struct msghdr msg; - struct iovec iov; - int n; - struct cmsghdr *cmptr; - union { - struct cmsghdr cm; + struct msghdr msg; + struct iovec iov; + int n; + struct cmsghdr *cmptr; + union { + struct cmsghdr cm; #ifdef IP_PKTINFO - char control[CMSG_SPACE(sizeof(struct in_addr)) + - CMSG_SPACE(sizeof(struct in_pktinfo))]; + char control[CMSG_SPACE(sizeof(struct in_addr)) + + CMSG_SPACE(sizeof(struct in_pktinfo))]; #else - char control[CMSG_SPACE(sizeof(struct in_addr))]; + char control[CMSG_SPACE(sizeof(struct in_addr))]; #endif - } control_un; - int on = 1; + } control_un; + int on = 1; #ifdef IP_PKTINFO - struct in_pktinfo pktinfo; + struct in_pktinfo pktinfo; #endif - /* Try to enable getting the return address */ + /* Try to enable getting the return address */ #ifdef IP_RECVDSTADDR - setsockopt(s, IPPROTO_IP, IP_RECVDSTADDR, &on, sizeof(on)); + setsockopt(s, IPPROTO_IP, IP_RECVDSTADDR, &on, sizeof(on)); #endif #ifdef IP_PKTINFO - setsockopt(s, IPPROTO_IP, IP_PKTINFO, &on, sizeof(on)); + setsockopt(s, IPPROTO_IP, IP_PKTINFO, &on, sizeof(on)); #endif - bzero(&msg, sizeof msg); /* Clear possible system-dependent fields */ - msg.msg_control = control_un.control; - msg.msg_controllen = sizeof(control_un.control); - msg.msg_flags = 0; + bzero(&msg, sizeof msg); /* Clear possible system-dependent fields */ + msg.msg_control = control_un.control; + msg.msg_controllen = sizeof(control_un.control); + msg.msg_flags = 0; - msg.msg_name = from; - msg.msg_namelen = *fromlen; - iov.iov_base = buf; - iov.iov_len = len; - msg.msg_iov = &iov; - msg.msg_iovlen = 1; + msg.msg_name = from; + msg.msg_namelen = *fromlen; + iov.iov_base = buf; + iov.iov_len = len; + msg.msg_iov = &iov; + msg.msg_iovlen = 1; - if ( (n = recvmsg(s, &msg, flags)) < 0 ) - return n; /* Error */ + if ((n = recvmsg(s, &msg, flags)) < 0) + return n; /* Error */ - *fromlen = msg.msg_namelen; + *fromlen = msg.msg_namelen; + + if (myaddr) { + bzero(myaddr, sizeof(struct sockaddr_in)); + myaddr->sin_family = AF_INET; + + if (msg.msg_controllen < sizeof(struct cmsghdr) || + (msg.msg_flags & MSG_CTRUNC)) + return n; /* No information available */ + + for (cmptr = CMSG_FIRSTHDR(&msg); cmptr != NULL; + cmptr = CMSG_NXTHDR(&msg, cmptr)) { + +#ifdef IP_RECVDSTADDR + if (cmptr->cmsg_level == IPPROTO_IP && + cmptr->cmsg_type == IP_RECVDSTADDR) { + memcpy(&myaddr->sin_addr, CMSG_DATA(cmptr), + sizeof(struct in_addr)); + } +#endif + +#ifdef IP_PKTINFO + if (cmptr->cmsg_level == IPPROTO_IP && + cmptr->cmsg_type == IP_PKTINFO) { + memcpy(&pktinfo, CMSG_DATA(cmptr), + sizeof(struct in_pktinfo)); + memcpy(&myaddr->sin_addr, &pktinfo.ipi_addr, + sizeof(struct in_addr)); + } +#endif + + } + } + + /* If the address is not a valid local address, then bind to any address... */ + if (address_is_local(myaddr) != 1) + myaddr->sin_addr.s_addr = INADDR_ANY; + + return n; +} + +#else /* pointless... */ + +int +myrecvfrom(int s, void *buf, int len, unsigned int flags, + struct sockaddr *from, int *fromlen, struct sockaddr_in *myaddr) +{ + /* There is no way we can get the local address, fudge it */ - if ( myaddr ) { bzero(myaddr, sizeof(struct sockaddr_in)); myaddr->sin_family = AF_INET; - if ( msg.msg_controllen < sizeof(struct cmsghdr) || - (msg.msg_flags & MSG_CTRUNC) ) - return n; /* No information available */ + myaddr->sin_port = htons(IPPORT_TFTP); + bzero(&myaddr->sin_addr, sizeof(myaddr->sin_addr)); - for ( cmptr = CMSG_FIRSTHDR(&msg) ; cmptr != NULL ; - cmptr = CMSG_NXTHDR(&msg, cmptr) ) { - -#ifdef IP_RECVDSTADDR - if ( cmptr->cmsg_level == IPPROTO_IP && - cmptr->cmsg_type == IP_RECVDSTADDR ) { - memcpy(&myaddr->sin_addr, CMSG_DATA(cmptr), - sizeof(struct in_addr)); - } -#endif - -#ifdef IP_PKTINFO - if ( cmptr->cmsg_level == IPPROTO_IP && - cmptr->cmsg_type == IP_PKTINFO ) { - memcpy(&pktinfo, CMSG_DATA(cmptr), sizeof(struct in_pktinfo)); - memcpy(&myaddr->sin_addr, &pktinfo.ipi_addr, sizeof(struct in_addr)); - } -#endif - - } - } - - /* If the address is not a valid local address, then bind to any address... */ - if (address_is_local(myaddr) != 1) - myaddr->sin_addr.s_addr = INADDR_ANY; - - return n; -} - -#else /* pointless... */ - -int -myrecvfrom(int s, void *buf, int len, unsigned int flags, - struct sockaddr *from, int *fromlen, - struct sockaddr_in *myaddr) -{ - /* There is no way we can get the local address, fudge it */ - - bzero(myaddr, sizeof(struct sockaddr_in)); - myaddr->sin_family = AF_INET; - - myaddr->sin_port = htons(IPPORT_TFTP); - bzero(&myaddr->sin_addr, sizeof(myaddr->sin_addr)); - - return recvfrom(s,buf,len,flags,from,fromlen); + return recvfrom(s, buf, len, flags, from, fromlen); } #endif diff --git a/tftpd/recvfrom.h b/tftpd/recvfrom.h index 35ad05b..fda65c0 100644 --- a/tftpd/recvfrom.h +++ b/tftpd/recvfrom.h @@ -1,5 +1,5 @@ /* ----------------------------------------------------------------------- * - * + * * Copyright 2001-2006 H. Peter Anvin - All Rights Reserved * * This program is free software available under the same license @@ -19,5 +19,5 @@ int myrecvfrom(int s, void *buf, int len, unsigned int flags, - struct sockaddr *from, socklen_t *fromlen, - struct sockaddr_in *myaddr); + struct sockaddr *from, socklen_t * fromlen, + struct sockaddr_in *myaddr); diff --git a/tftpd/remap.c b/tftpd/remap.c index bb10fd0..cdb9062 100644 --- a/tftpd/remap.c +++ b/tftpd/remap.c @@ -1,5 +1,5 @@ /* ----------------------------------------------------------------------- * - * + * * Copyright 2001-2007 H. Peter Anvin - All Rights Reserved * * This program is free software available under the same license @@ -14,7 +14,7 @@ * Perform regular-expression based filename remapping. */ -#include "config.h" /* Must be included first! */ +#include "config.h" /* Must be included first! */ #include #include #include @@ -23,123 +23,131 @@ #include "remap.h" #define DEADMAN_MAX_STEPS 1024 /* Timeout after this many steps */ -#define MAXLINE 16384 /* Truncate a line at this many bytes */ +#define MAXLINE 16384 /* Truncate a line at this many bytes */ -#define RULE_REWRITE 0x01 /* This is a rewrite rule */ -#define RULE_GLOBAL 0x02 /* Global rule (repeat until no match) */ -#define RULE_EXIT 0x04 /* Exit after matching this rule */ -#define RULE_RESTART 0x08 /* Restart at the top after matching this rule */ -#define RULE_ABORT 0x10 /* Terminate processing with an error */ -#define RULE_GETONLY 0x20 /* Applicable to GET only */ -#define RULE_PUTONLY 0x40 /* Applicable to PUT only */ -#define RULE_INVERSE 0x80 /* Execute if regex *doesn't* match */ +#define RULE_REWRITE 0x01 /* This is a rewrite rule */ +#define RULE_GLOBAL 0x02 /* Global rule (repeat until no match) */ +#define RULE_EXIT 0x04 /* Exit after matching this rule */ +#define RULE_RESTART 0x08 /* Restart at the top after matching this rule */ +#define RULE_ABORT 0x10 /* Terminate processing with an error */ +#define RULE_GETONLY 0x20 /* Applicable to GET only */ +#define RULE_PUTONLY 0x40 /* Applicable to PUT only */ +#define RULE_INVERSE 0x80 /* Execute if regex *doesn't* match */ struct rule { - struct rule *next; - int nrule; - int rule_flags; - regex_t rx; - const char *pattern; + struct rule *next; + int nrule; + int rule_flags; + regex_t rx; + const char *pattern; }; static int xform_null(int c) { - return c; + return c; } static int xform_toupper(int c) { - return toupper(c); + return toupper(c); } static int xform_tolower(int c) { - return tolower(c); + return tolower(c); } /* Do \-substitution. Call with string == NULL to get length only. */ -static int genmatchstring(char *string, const char *pattern, const char *input, - const regmatch_t *pmatch, match_pattern_callback macrosub) +static int genmatchstring(char *string, const char *pattern, + const char *input, const regmatch_t * pmatch, + match_pattern_callback macrosub) { - int (*xform)(int) = xform_null; - int len = 0; - int n, mlen, sublen; - int endbytes; + int (*xform) (int) = xform_null; + int len = 0; + int n, mlen, sublen; + int endbytes; - /* Get section before match; note pmatch[0] is the whole match */ - endbytes = strlen(input) - pmatch[0].rm_eo; - len = pmatch[0].rm_so + endbytes; - if ( string ) { - memcpy(string, input, pmatch[0].rm_so); - string += pmatch[0].rm_so; - } - - /* Transform matched section */ - while ( *pattern ) { - mlen = 0; - - if ( *pattern == '\\' && pattern[1] != '\0' ) { - char macro = pattern[1]; - switch ( macro ) { - case '0': case '1': case '2': case '3': case '4': - case '5': case '6': case '7': case '8': case '9': - n = pattern[1] - '0'; - - if ( pmatch[n].rm_so != -1 ) { - mlen = pmatch[n].rm_eo - pmatch[n].rm_so; - len += mlen; - if ( string ) { - const char *p = input+pmatch[n].rm_so; - while ( mlen-- ) - *string++ = xform(*p++); - } - } - break; - - case 'L': - xform = xform_tolower; - break; - - case 'U': - xform = xform_toupper; - break; - - case 'E': - xform = xform_null; - break; - - default: - if ( macrosub && - (sublen = macrosub(macro, string)) >= 0 ) { - while ( sublen-- ) { - len++; - if ( string ) { - *string = xform(*string); - string++; - } - } - } else { - len++; - if ( string ) - *string++ = xform(pattern[1]); - } - } - pattern += 2; - } else { - len++; - if ( string ) - *string++ = xform(*pattern); - pattern++; + /* Get section before match; note pmatch[0] is the whole match */ + endbytes = strlen(input) - pmatch[0].rm_eo; + len = pmatch[0].rm_so + endbytes; + if (string) { + memcpy(string, input, pmatch[0].rm_so); + string += pmatch[0].rm_so; } - } - /* Copy section after match */ - if ( string ) { - memcpy(string, input+pmatch[0].rm_eo, endbytes); - string[endbytes] = '\0'; - } + /* Transform matched section */ + while (*pattern) { + mlen = 0; - return len; + if (*pattern == '\\' && pattern[1] != '\0') { + char macro = pattern[1]; + switch (macro) { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + n = pattern[1] - '0'; + + if (pmatch[n].rm_so != -1) { + mlen = pmatch[n].rm_eo - pmatch[n].rm_so; + len += mlen; + if (string) { + const char *p = input + pmatch[n].rm_so; + while (mlen--) + *string++ = xform(*p++); + } + } + break; + + case 'L': + xform = xform_tolower; + break; + + case 'U': + xform = xform_toupper; + break; + + case 'E': + xform = xform_null; + break; + + default: + if (macrosub && (sublen = macrosub(macro, string)) >= 0) { + while (sublen--) { + len++; + if (string) { + *string = xform(*string); + string++; + } + } + } else { + len++; + if (string) + *string++ = xform(pattern[1]); + } + } + pattern += 2; + } else { + len++; + if (string) + *string++ = xform(*pattern); + pattern++; + } + } + + /* Copy section after match */ + if (string) { + memcpy(string, input + pmatch[0].rm_eo, endbytes); + string[endbytes] = '\0'; + } + + return len; } /* @@ -149,277 +157,284 @@ static int genmatchstring(char *string, const char *pattern, const char *input, */ static int readescstring(char *buf, char **str) { - char *p = *str; - int wasbs = 0, len = 0; + char *p = *str; + int wasbs = 0, len = 0; - while ( *p && isspace(*p) ) - p++; + while (*p && isspace(*p)) + p++; + + if (!*p) { + *buf = '\0'; + *str = p; + return 0; + } + + while (*p) { + if (!wasbs && (isspace(*p) || *p == '#')) { + *buf = '\0'; + *str = p; + return len; + } + /* Important: two backslashes leave us in the !wasbs state! */ + wasbs = !wasbs && (*p == '\\'); + *buf++ = *p++; + len++; + } - if ( ! *p ) { *buf = '\0'; *str = p; - return 0; - } - - while ( *p ) { - if ( !wasbs && (isspace(*p) || *p == '#') ) { - *buf = '\0'; - *str = p; - return len; - } - /* Important: two backslashes leave us in the !wasbs state! */ - wasbs = !wasbs && ( *p == '\\' ); - *buf++ = *p++; - len++; - } - - *buf = '\0'; - *str = p; - return len; + return len; } /* Parse a line into a set of instructions */ static int parseline(char *line, struct rule *r, int lineno) { - char buffer[MAXLINE]; - char *p; - int rv; - int rxflags = REG_EXTENDED; - static int nrule; + char buffer[MAXLINE]; + char *p; + int rv; + int rxflags = REG_EXTENDED; + static int nrule; - memset(r, 0, sizeof *r); - r->nrule = nrule; + memset(r, 0, sizeof *r); + r->nrule = nrule; - if ( !readescstring(buffer, &line) ) - return 0; /* No rule found */ + if (!readescstring(buffer, &line)) + return 0; /* No rule found */ - for ( p = buffer ; *p ; p++ ) { - switch(*p) { - case 'r': - r->rule_flags |= RULE_REWRITE; - break; - case 'g': - r->rule_flags |= RULE_GLOBAL; - break; - case 'e': - r->rule_flags |= RULE_EXIT; - break; - case 's': - r->rule_flags |= RULE_RESTART; - break; - case 'a': - r->rule_flags |= RULE_ABORT; - break; - case 'i': - rxflags |= REG_ICASE; - break; - case 'G': - r->rule_flags |= RULE_GETONLY; - break; - case 'P': - r->rule_flags |= RULE_PUTONLY; - break; - case '~': - r->rule_flags |= RULE_INVERSE; - break; - default: - syslog(LOG_ERR, "Remap command \"%s\" on line %d contains invalid char \"%c\"", - buffer, lineno, *p); - return -1; /* Error */ - break; + for (p = buffer; *p; p++) { + switch (*p) { + case 'r': + r->rule_flags |= RULE_REWRITE; + break; + case 'g': + r->rule_flags |= RULE_GLOBAL; + break; + case 'e': + r->rule_flags |= RULE_EXIT; + break; + case 's': + r->rule_flags |= RULE_RESTART; + break; + case 'a': + r->rule_flags |= RULE_ABORT; + break; + case 'i': + rxflags |= REG_ICASE; + break; + case 'G': + r->rule_flags |= RULE_GETONLY; + break; + case 'P': + r->rule_flags |= RULE_PUTONLY; + break; + case '~': + r->rule_flags |= RULE_INVERSE; + break; + default: + syslog(LOG_ERR, + "Remap command \"%s\" on line %d contains invalid char \"%c\"", + buffer, lineno, *p); + return -1; /* Error */ + break; + } } - } - /* RULE_GLOBAL only applies when RULE_REWRITE specified */ - if ( !(r->rule_flags & RULE_REWRITE) ) - r->rule_flags &= ~RULE_GLOBAL; + /* RULE_GLOBAL only applies when RULE_REWRITE specified */ + if (!(r->rule_flags & RULE_REWRITE)) + r->rule_flags &= ~RULE_GLOBAL; - if ( (r->rule_flags & (RULE_INVERSE|RULE_REWRITE)) == - (RULE_INVERSE|RULE_REWRITE) ) { - syslog(LOG_ERR, "r rules cannot be inverted, line %d: %s\n", lineno, line); - return -1; /* Error */ - } + if ((r->rule_flags & (RULE_INVERSE | RULE_REWRITE)) == + (RULE_INVERSE | RULE_REWRITE)) { + syslog(LOG_ERR, "r rules cannot be inverted, line %d: %s\n", + lineno, line); + return -1; /* Error */ + } - /* Read and compile the regex */ - if ( !readescstring(buffer, &line) ) { - syslog(LOG_ERR, "No regex on remap line %d: %s\n", lineno, line); - return -1; /* Error */ - } + /* Read and compile the regex */ + if (!readescstring(buffer, &line)) { + syslog(LOG_ERR, "No regex on remap line %d: %s\n", lineno, line); + return -1; /* Error */ + } - if ( (rv = regcomp(&r->rx, buffer, rxflags)) != 0 ) { - char errbuf[BUFSIZ]; - regerror(rv, &r->rx, errbuf, BUFSIZ); - syslog(LOG_ERR, "Bad regex in remap line %d: %s\n", lineno, errbuf); - return -1; /* Error */ - } + if ((rv = regcomp(&r->rx, buffer, rxflags)) != 0) { + char errbuf[BUFSIZ]; + regerror(rv, &r->rx, errbuf, BUFSIZ); + syslog(LOG_ERR, "Bad regex in remap line %d: %s\n", lineno, + errbuf); + return -1; /* Error */ + } - /* Read the rewrite pattern, if any */ - if ( readescstring(buffer, &line) ) { - r->pattern = tfstrdup(buffer); - } else { - r->pattern = ""; - } + /* Read the rewrite pattern, if any */ + if (readescstring(buffer, &line)) { + r->pattern = tfstrdup(buffer); + } else { + r->pattern = ""; + } - nrule++; - return 1; /* Rule found */ + nrule++; + return 1; /* Rule found */ } /* Read a rule file */ -struct rule *parserulefile(FILE *f) +struct rule *parserulefile(FILE * f) { - char line[MAXLINE]; - struct rule *first_rule = NULL; - struct rule **last_rule = &first_rule; - struct rule *this_rule = tfmalloc(sizeof(struct rule)); - int rv; - int lineno = 0; - int err = 0; + char line[MAXLINE]; + struct rule *first_rule = NULL; + struct rule **last_rule = &first_rule; + struct rule *this_rule = tfmalloc(sizeof(struct rule)); + int rv; + int lineno = 0; + int err = 0; - while ( lineno++, fgets(line, MAXLINE, f) ) { - rv = parseline(line, this_rule, lineno); - if ( rv < 0 ) - err = 1; - if ( rv > 0 ) { - *last_rule = this_rule; - last_rule = &this_rule->next; - this_rule = tfmalloc(sizeof(struct rule)); + while (lineno++, fgets(line, MAXLINE, f)) { + rv = parseline(line, this_rule, lineno); + if (rv < 0) + err = 1; + if (rv > 0) { + *last_rule = this_rule; + last_rule = &this_rule->next; + this_rule = tfmalloc(sizeof(struct rule)); + } } - } - free(this_rule); /* Last one is always unused */ + free(this_rule); /* Last one is always unused */ - if ( err ) { - /* Bail on error, we have already logged an error message */ - exit(EX_CONFIG); - } + if (err) { + /* Bail on error, we have already logged an error message */ + exit(EX_CONFIG); + } - return first_rule; + return first_rule; } /* Destroy a rule file data structure */ void freerules(struct rule *r) { - struct rule *next; + struct rule *next; - while ( r ) { - next = r->next; + while (r) { + next = r->next; - regfree(&r->rx); + regfree(&r->rx); - /* "" patterns aren't allocated by malloc() */ - if ( r->pattern && *r->pattern ) - free((void *)r->pattern); - - free(r); + /* "" patterns aren't allocated by malloc() */ + if (r->pattern && *r->pattern) + free((void *)r->pattern); - r = next; - } + free(r); + + r = next; + } } /* Execute a rule set on a string; returns a malloc'd new string. */ char *rewrite_string(const char *input, const struct rule *rules, - int is_put, match_pattern_callback macrosub, - const char **errmsg) + int is_put, match_pattern_callback macrosub, + const char **errmsg) { - char *current = tfstrdup(input); - char *newstr; - const struct rule *ruleptr = rules; - regmatch_t pmatch[10]; - int len; - int was_match = 0; - int deadman = DEADMAN_MAX_STEPS; + char *current = tfstrdup(input); + char *newstr; + const struct rule *ruleptr = rules; + regmatch_t pmatch[10]; + int len; + int was_match = 0; + int deadman = DEADMAN_MAX_STEPS; - /* Default error */ - *errmsg = "Remap table failure"; + /* Default error */ + *errmsg = "Remap table failure"; - if ( verbosity >= 3 ) { - syslog(LOG_INFO, "remap: input: %s", current); - } - - for ( ruleptr = rules ; ruleptr ; ruleptr = ruleptr->next ) { - if ( ((ruleptr->rule_flags & RULE_GETONLY) && is_put) || - ((ruleptr->rule_flags & RULE_PUTONLY) && !is_put) ) { - continue; /* Rule not applicable, try next */ + if (verbosity >= 3) { + syslog(LOG_INFO, "remap: input: %s", current); } - if ( ! deadman-- ) { - syslog(LOG_WARNING, "remap: Breaking loop, input = %s, last = %s", - input, current); - free(current); - return NULL; /* Did not terminate! */ + for (ruleptr = rules; ruleptr; ruleptr = ruleptr->next) { + if (((ruleptr->rule_flags & RULE_GETONLY) && is_put) || + ((ruleptr->rule_flags & RULE_PUTONLY) && !is_put)) { + continue; /* Rule not applicable, try next */ + } + + if (!deadman--) { + syslog(LOG_WARNING, + "remap: Breaking loop, input = %s, last = %s", input, + current); + free(current); + return NULL; /* Did not terminate! */ + } + + do { + if (regexec(&ruleptr->rx, current, 10, pmatch, 0) == + (ruleptr->rule_flags & RULE_INVERSE ? REG_NOMATCH : 0)) { + /* Match on this rule */ + was_match = 1; + + if (ruleptr->rule_flags & RULE_INVERSE) { + /* No actual match, so clear out the pmatch array */ + int i; + for (i = 0; i < 10; i++) + pmatch[i].rm_so = pmatch[i].rm_eo = -1; + } + + if (ruleptr->rule_flags & RULE_ABORT) { + if (verbosity >= 3) { + syslog(LOG_INFO, "remap: rule %d: abort: %s", + ruleptr->nrule, current); + } + if (ruleptr->pattern[0]) { + /* Custom error message */ + len = + genmatchstring(NULL, ruleptr->pattern, current, + pmatch, macrosub); + newstr = tfmalloc(len + 1); + genmatchstring(newstr, ruleptr->pattern, current, + pmatch, macrosub); + *errmsg = newstr; + } else { + *errmsg = NULL; + } + free(current); + return (NULL); + } + + if (ruleptr->rule_flags & RULE_REWRITE) { + len = genmatchstring(NULL, ruleptr->pattern, current, + pmatch, macrosub); + newstr = tfmalloc(len + 1); + genmatchstring(newstr, ruleptr->pattern, current, + pmatch, macrosub); + free(current); + current = newstr; + if (verbosity >= 3) { + syslog(LOG_INFO, "remap: rule %d: rewrite: %s", + ruleptr->nrule, current); + } + } + } else { + break; /* No match, terminate unconditionally */ + } + /* If the rule is global, keep going until no match */ + } while (ruleptr->rule_flags & RULE_GLOBAL); + + if (was_match) { + was_match = 0; + + if (ruleptr->rule_flags & RULE_EXIT) { + if (verbosity >= 3) { + syslog(LOG_INFO, "remap: rule %d: exit", + ruleptr->nrule); + } + return current; /* Exit here, we're done */ + } else if (ruleptr->rule_flags & RULE_RESTART) { + ruleptr = rules; /* Start from the top */ + if (verbosity >= 3) { + syslog(LOG_INFO, "remap: rule %d: restart", + ruleptr->nrule); + } + } + } } - do { - if ( regexec(&ruleptr->rx, current, 10, pmatch, 0) == - (ruleptr->rule_flags & RULE_INVERSE ? REG_NOMATCH : 0) ) { - /* Match on this rule */ - was_match = 1; - - if ( ruleptr->rule_flags & RULE_INVERSE ) { - /* No actual match, so clear out the pmatch array */ - int i; - for ( i = 0 ; i < 10 ; i++ ) - pmatch[i].rm_so = pmatch[i].rm_eo = -1; - } - - if ( ruleptr->rule_flags & RULE_ABORT ) { - if ( verbosity >= 3 ) { - syslog(LOG_INFO, "remap: rule %d: abort: %s", - ruleptr->nrule, current); - } - if ( ruleptr->pattern[0] ) { - /* Custom error message */ - len = genmatchstring(NULL, ruleptr->pattern, current, - pmatch, macrosub); - newstr = tfmalloc(len+1); - genmatchstring(newstr, ruleptr->pattern, current, - pmatch, macrosub); - *errmsg = newstr; - } else { - *errmsg = NULL; - } - free(current); - return(NULL); - } - - if ( ruleptr->rule_flags & RULE_REWRITE ) { - len = genmatchstring(NULL, ruleptr->pattern, current, - pmatch, macrosub); - newstr = tfmalloc(len+1); - genmatchstring(newstr, ruleptr->pattern, current, - pmatch, macrosub); - free(current); - current = newstr; - if ( verbosity >= 3 ) { - syslog(LOG_INFO, "remap: rule %d: rewrite: %s", - ruleptr->nrule, current); - } - } - } else { - break; /* No match, terminate unconditionally */ - } - /* If the rule is global, keep going until no match */ - } while ( ruleptr->rule_flags & RULE_GLOBAL ); - - if ( was_match ) { - was_match = 0; - - if ( ruleptr->rule_flags & RULE_EXIT ) { - if ( verbosity >= 3 ) { - syslog(LOG_INFO, "remap: rule %d: exit", ruleptr->nrule); - } - return current; /* Exit here, we're done */ - } else if ( ruleptr->rule_flags & RULE_RESTART ) { - ruleptr = rules; /* Start from the top */ - if ( verbosity >= 3 ) { - syslog(LOG_INFO, "remap: rule %d: restart", ruleptr->nrule); - } - } + if (verbosity >= 3) { + syslog(LOG_INFO, "remap: done"); } - } - - if ( verbosity >= 3 ) { - syslog(LOG_INFO, "remap: done"); - } - return current; + return current; } diff --git a/tftpd/remap.h b/tftpd/remap.h index a00094c..3830b5c 100644 --- a/tftpd/remap.h +++ b/tftpd/remap.h @@ -1,5 +1,5 @@ /* ----------------------------------------------------------------------- * - * + * * Copyright 2001-2007 H. Peter Anvin - All Rights Reserved * * This program is free software available under the same license @@ -26,7 +26,7 @@ struct rule; macro character is passed as the first argument; the output buffer, if any, is passed as the second argument. The function should return the number of characters output, or -1 on failure. */ -typedef int (*match_pattern_callback)(char, char *); +typedef int (*match_pattern_callback) (char, char *); /* Read a rule file */ struct rule *parserulefile(FILE *); @@ -36,8 +36,7 @@ void freerules(struct rule *); /* Execute a rule set on a string; returns a malloc'd new string. */ char *rewrite_string(const char *, const struct rule *, int, - match_pattern_callback, const char **); - -#endif /* WITH_REGEX */ -#endif /* TFTPD_REMAP_H */ + match_pattern_callback, const char **); +#endif /* WITH_REGEX */ +#endif /* TFTPD_REMAP_H */ diff --git a/tftpd/tftpd.c b/tftpd/tftpd.c index 92a6889..92b7c67 100644 --- a/tftpd/tftpd.c +++ b/tftpd/tftpd.c @@ -33,19 +33,9 @@ * SUCH DAMAGE. */ -#include "config.h" /* Must be included first */ +#include "config.h" /* Must be included first */ #include "tftpd.h" -#ifndef lint -static const char *copyright UNUSED = -"@(#) Copyright (c) 1983 Regents of the University of California.\n\ - All rights reserved.\n"; -/*static char sccsid[] = "from: @(#)tftpd.c 5.13 (Berkeley) 2/26/91";*/ -/*static char rcsid[] = "$OpenBSD: tftpd.c,v 1.13 1999/06/23 17:01:36 deraadt Exp $: tftpd.c,v 1.6 1997/02/16 23:49:21 deraadt Exp $";*/ -static const char *rcsid UNUSED = -"tftp-hpa $Id$"; -#endif /* not lint */ - /* * Trivial file transfer protocol server. * @@ -65,49 +55,49 @@ static const char *rcsid UNUSED = #include "remap.h" #ifdef HAVE_SYS_FILIO_H -#include /* Necessary for FIONBIO on Solaris */ +#include /* Necessary for FIONBIO on Solaris */ #endif #ifdef HAVE_TCPWRAPPERS #include -int deny_severity = LOG_WARNING; -int allow_severity = -1; /* Don't log at all */ +int deny_severity = LOG_WARNING; +int allow_severity = -1; /* Don't log at all */ struct request_info wrap_request; #endif -#define TIMEOUT 1000000 /* Default timeout (us) */ -#define TRIES 6 /* Number of attempts to send each packet */ +#define TIMEOUT 1000000 /* Default timeout (us) */ +#define TRIES 6 /* Number of attempts to send each packet */ #define TIMEOUT_LIMIT ((1 << TRIES)-1) -const char *__progname; -int peer; -unsigned long timeout = TIMEOUT; /* Current timeout value */ -unsigned long rexmtval = TIMEOUT; /* Basic timeout value */ -unsigned long maxtimeout = TIMEOUT_LIMIT*TIMEOUT; -int timeout_quit = 0; -sigjmp_buf timeoutbuf; +const char *__progname; +int peer; +unsigned long timeout = TIMEOUT; /* Current timeout value */ +unsigned long rexmtval = TIMEOUT; /* Basic timeout value */ +unsigned long maxtimeout = TIMEOUT_LIMIT * TIMEOUT; +int timeout_quit = 0; +sigjmp_buf timeoutbuf; #define PKTSIZE MAX_SEGSIZE+4 -char buf[PKTSIZE]; -char ackbuf[PKTSIZE]; -unsigned int max_blksize = MAX_SEGSIZE; +char buf[PKTSIZE]; +char ackbuf[PKTSIZE]; +unsigned int max_blksize = MAX_SEGSIZE; struct sockaddr_in from; -socklen_t fromlen; -off_t tsize; -int tsize_ok; +socklen_t fromlen; +off_t tsize; +int tsize_ok; -int ndirs; -const char **dirs; +int ndirs; +const char **dirs; -int secure = 0; -int cancreate = 0; -int unixperms = 0; -int portrange = 0; -unsigned int portrange_from, portrange_to; -int verbosity = 0; +int secure = 0; +int cancreate = 0; +int unixperms = 0; +int portrange = 0; +unsigned int portrange_from, portrange_to; +int verbosity = 0; struct formats; #ifdef WITH_REGEX @@ -127,91 +117,80 @@ int set_timeout(char *, char **); int set_utimeout(char *, char **); struct options { - const char *o_opt; - int (*o_fnc)(char *, char **); + const char *o_opt; + int (*o_fnc) (char *, char **); } options[] = { - { "blksize", set_blksize }, - { "blksize2", set_blksize2 }, - { "tsize", set_tsize }, - { "timeout", set_timeout }, - { "utimeout", set_utimeout }, - { NULL, NULL } + { + "blksize", set_blksize}, { + "blksize2", set_blksize2}, { + "tsize", set_tsize}, { + "timeout", set_timeout}, { + "utimeout", set_utimeout}, { + NULL, NULL} }; /* Simple handler for SIGHUP */ static volatile sig_atomic_t caught_sighup = 0; static void handle_sighup(int sig) { - (void)sig; /* Suppress unused warning */ - caught_sighup = 1; + (void)sig; /* Suppress unused warning */ + caught_sighup = 1; } - /* Handle timeout signal or timeout event */ -void -timer(int sig) +void timer(int sig) { - (void)sig; /* Suppress unused warning */ - timeout <<= 1; - if (timeout >= maxtimeout || timeout_quit) - exit(0); - siglongjmp(timeoutbuf, 1); + (void)sig; /* Suppress unused warning */ + timeout <<= 1; + if (timeout >= maxtimeout || timeout_quit) + exit(0); + siglongjmp(timeoutbuf, 1); } -static void -usage(void) -{ - syslog(LOG_ERR, "Usage: %s [-vcl][-a address][-m mappings][-u user][-t inetd_timeout][-T pkt_timeout][-r option...] [-s] [directory ...]", - __progname); - exit(EX_USAGE); -} - - #ifdef WITH_REGEX -static struct rule * -read_remap_rules(const char *file) +static struct rule *read_remap_rules(const char *file) { - FILE *f; - struct rule *rulep; + FILE *f; + struct rule *rulep; - f = fopen(file, "rt"); - if ( !f ) { - syslog(LOG_ERR, "Cannot open map file: %s: %m", file); - exit(EX_NOINPUT); - } - rulep = parserulefile(f); - fclose(f); + f = fopen(file, "rt"); + if (!f) { + syslog(LOG_ERR, "Cannot open map file: %s: %m", file); + exit(EX_NOINPUT); + } + rulep = parserulefile(f); + fclose(f); - return rulep; + return rulep; } #endif -static void -set_socket_nonblock(int fd, int flag) +static void set_socket_nonblock(int fd, int flag) { - int err; - int flags; + int err; + int flags; #if defined(HAVE_FCNTL) && defined(HAVE_O_NONBLOCK_DEFINITION) - /* Posixly correct */ - err = ((flags = fcntl(fd, F_GETFL, 0)) < 0) || - (fcntl(fd, F_SETFL, flag ? flags|O_NONBLOCK : flags&~O_NONBLOCK) < 0); + /* Posixly correct */ + err = ((flags = fcntl(fd, F_GETFL, 0)) < 0) || + (fcntl + (fd, F_SETFL, + flag ? flags | O_NONBLOCK : flags & ~O_NONBLOCK) < 0); #else - flags = flag ? 1 : 0; - err = (ioctl(fd, FIONBIO, &flags) < 0); + flags = flag ? 1 : 0; + err = (ioctl(fd, FIONBIO, &flags) < 0); #endif - if ( err ) { - syslog(LOG_ERR, "Cannot set nonblock flag on socket: %m"); - exit(EX_OSERR); - } + if (err) { + syslog(LOG_ERR, "Cannot set nonblock flag on socket: %m"); + exit(EX_OSERR); + } } -static void -pmtu_discovery_off(int fd) +static void pmtu_discovery_off(int fd) { #if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT) - int pmtu = IP_PMTUDISC_DONT; + int pmtu = IP_PMTUDISC_DONT; - setsockopt(fd, IPPROTO_IP, IP_MTU_DISCOVER, &pmtu, sizeof(pmtu)); + setsockopt(fd, IPPROTO_IP, IP_MTU_DISCOVER, &pmtu, sizeof(pmtu)); #endif } @@ -220,617 +199,630 @@ pmtu_discovery_off(int fd) * to account for time spent waiting. */ static int recv_time(int s, void *rbuf, int len, unsigned int flags, - unsigned long *timeout_us_p) + unsigned long *timeout_us_p) { - fd_set fdset; - struct timeval tmv, t0, t1; - int rv, err; - unsigned long timeout_us = *timeout_us_p; - unsigned long timeout_left, dt; + fd_set fdset; + struct timeval tmv, t0, t1; + int rv, err; + unsigned long timeout_us = *timeout_us_p; + unsigned long timeout_left, dt; - gettimeofday(&t0, NULL); - timeout_left = timeout_us; + gettimeofday(&t0, NULL); + timeout_left = timeout_us; - for ( ; ; ) { - FD_ZERO(&fdset); - FD_SET(s, &fdset); - - do { - tmv.tv_sec = timeout_left / 1000000; - tmv.tv_usec = timeout_left % 1000000; - - rv = select(s+1, &fdset, NULL, NULL, &tmv); - err = errno; + for (;;) { + FD_ZERO(&fdset); + FD_SET(s, &fdset); - gettimeofday(&t1, NULL); + do { + tmv.tv_sec = timeout_left / 1000000; + tmv.tv_usec = timeout_left % 1000000; - dt = (t1.tv_sec - t0.tv_sec)*1000000 + (t1.tv_usec - t0.tv_usec); - *timeout_us_p = timeout_left = ( dt >= timeout_us ) ? 1 : (timeout_us - dt); - } while ( rv == -1 && err == EINTR ); - - if ( rv == 0 ) { - timer(0); /* Should not return */ - return -1; + rv = select(s + 1, &fdset, NULL, NULL, &tmv); + err = errno; + + gettimeofday(&t1, NULL); + + dt = (t1.tv_sec - t0.tv_sec) * 1000000 + (t1.tv_usec - + t0.tv_usec); + *timeout_us_p = timeout_left = + (dt >= timeout_us) ? 1 : (timeout_us - dt); + } while (rv == -1 && err == EINTR); + + if (rv == 0) { + timer(0); /* Should not return */ + return -1; + } + + set_socket_nonblock(s, 1); + rv = recv(s, rbuf, len, flags); + err = errno; + set_socket_nonblock(s, 0); + + if (rv < 0) { + if (E_WOULD_BLOCK(err) || err == EINTR) { + continue; /* Once again, with feeling... */ + } else { + errno = err; + return rv; + } + } else { + return rv; + } } - - set_socket_nonblock(s, 1); - rv = recv(s, rbuf, len, flags); - err = errno; - set_socket_nonblock(s, 0); - - if ( rv < 0 ) { - if ( E_WOULD_BLOCK(err) || err == EINTR ) { - continue; /* Once again, with feeling... */ - } else { - errno = err; - return rv; - } - } else { - return rv; - } - } } - -int -main(int argc, char **argv) +int main(int argc, char **argv) { - struct tftphdr *tp; - struct passwd *pw; - struct options *opt; - struct sockaddr_in myaddr; - struct sockaddr_in bindaddr; - int n; - int fd = 0; - int standalone = 0; /* Standalone (listen) mode */ - int nodaemon = 0; /* Do not detach process */ - char *address = NULL; /* Address to listen to */ - pid_t pid; - mode_t my_umask = 0; - int spec_umask = 0; - int c; - int setrv; - int waittime = 900; /* Default time to wait for a connect*/ - const char *user = "nobody"; /* Default user */ - char *p, *ep; + struct tftphdr *tp; + struct passwd *pw; + struct options *opt; + struct sockaddr_in myaddr; + struct sockaddr_in bindaddr; + int n; + int fd = 0; + int standalone = 0; /* Standalone (listen) mode */ + int nodaemon = 0; /* Do not detach process */ + char *address = NULL; /* Address to listen to */ + pid_t pid; + mode_t my_umask = 0; + int spec_umask = 0; + int c; + int setrv; + int waittime = 900; /* Default time to wait for a connect */ + const char *user = "nobody"; /* Default user */ + char *p, *ep; #ifdef WITH_REGEX - char *rewrite_file = NULL; + char *rewrite_file = NULL; #endif - u_short tp_opcode; + u_short tp_opcode; - /* basename() is way too much of a pain from a portability standpoint */ + /* basename() is way too much of a pain from a portability standpoint */ - p = strrchr(argv[0], '/'); - __progname = (p && p[1]) ? p+1 : argv[0]; - - openlog(__progname, LOG_PID|LOG_NDELAY, LOG_DAEMON); + p = strrchr(argv[0], '/'); + __progname = (p && p[1]) ? p + 1 : argv[0]; - srand(time(NULL) ^ getpid()); - - while ((c = getopt(argc, argv, "cspvVlLa:B:u:U:r:t:T:R:m:")) != -1) - switch (c) { - case 'c': - cancreate = 1; - break; - case 's': - secure = 1; - break; - case 'p': - unixperms = 1; - break; - case 'l': - standalone = 1; - break; - case 'L': - standalone = 1; - nodaemon = 1; - break; - case 'a': - address = optarg; - break; - case 't': - waittime = atoi(optarg); - break; - case 'B': - { - char *vp; - max_blksize = (unsigned int)strtoul(optarg, &vp, 10); - if ( max_blksize < 512 || max_blksize > MAX_SEGSIZE || *vp ) { - syslog(LOG_ERR, "Bad maximum blocksize value (range 512-%d): %s", - MAX_SEGSIZE, optarg); - exit(EX_USAGE); - } - } - break; - case 'T': - { - char *vp; - unsigned long tov = strtoul(optarg, &vp, 10); - if ( tov < 10000UL || tov > 255000000UL || *vp ) { - syslog(LOG_ERR, "Bad timeout value: %s", optarg); - exit(EX_USAGE); - } - rexmtval = timeout = tov; - maxtimeout = rexmtval*TIMEOUT_LIMIT; - } - break; - case 'R': - { - if ( sscanf(optarg, "%u:%u", &portrange_from, &portrange_to) != 2 || - portrange_from > portrange_to || portrange_to >= 65535 ) { - syslog(LOG_ERR, "Bad port range: %s", optarg); - exit(EX_USAGE); - } - portrange = 1; - } - break; - case 'u': - user = optarg; - break; - case 'U': - my_umask = strtoul(optarg, &ep, 8); - if ( *ep ) { - syslog(LOG_ERR, "Invalid umask: %s", optarg); - exit(EX_USAGE); - } - spec_umask = 1; - break; - case 'r': - for ( opt = options ; opt->o_opt ; opt++ ) { - if ( !strcasecmp(optarg, opt->o_opt) ) { - opt->o_opt = ""; /* Don't support this option */ - break; - } - } - if ( !opt->o_opt ) { - syslog(LOG_ERR, "Unknown option: %s", optarg); - exit(EX_USAGE); - } - break; + openlog(__progname, LOG_PID | LOG_NDELAY, LOG_DAEMON); + + srand(time(NULL) ^ getpid()); + + while ((c = getopt(argc, argv, "cspvVlLa:B:u:U:r:t:T:R:m:")) != -1) + switch (c) { + case 'c': + cancreate = 1; + break; + case 's': + secure = 1; + break; + case 'p': + unixperms = 1; + break; + case 'l': + standalone = 1; + break; + case 'L': + standalone = 1; + nodaemon = 1; + break; + case 'a': + address = optarg; + break; + case 't': + waittime = atoi(optarg); + break; + case 'B': + { + char *vp; + max_blksize = (unsigned int)strtoul(optarg, &vp, 10); + if (max_blksize < 512 || max_blksize > MAX_SEGSIZE || *vp) { + syslog(LOG_ERR, + "Bad maximum blocksize value (range 512-%d): %s", + MAX_SEGSIZE, optarg); + exit(EX_USAGE); + } + } + break; + case 'T': + { + char *vp; + unsigned long tov = strtoul(optarg, &vp, 10); + if (tov < 10000UL || tov > 255000000UL || *vp) { + syslog(LOG_ERR, "Bad timeout value: %s", optarg); + exit(EX_USAGE); + } + rexmtval = timeout = tov; + maxtimeout = rexmtval * TIMEOUT_LIMIT; + } + break; + case 'R': + { + if (sscanf(optarg, "%u:%u", &portrange_from, &portrange_to) + != 2 || portrange_from > portrange_to + || portrange_to >= 65535) { + syslog(LOG_ERR, "Bad port range: %s", optarg); + exit(EX_USAGE); + } + portrange = 1; + } + break; + case 'u': + user = optarg; + break; + case 'U': + my_umask = strtoul(optarg, &ep, 8); + if (*ep) { + syslog(LOG_ERR, "Invalid umask: %s", optarg); + exit(EX_USAGE); + } + spec_umask = 1; + break; + case 'r': + for (opt = options; opt->o_opt; opt++) { + if (!strcasecmp(optarg, opt->o_opt)) { + opt->o_opt = ""; /* Don't support this option */ + break; + } + } + if (!opt->o_opt) { + syslog(LOG_ERR, "Unknown option: %s", optarg); + exit(EX_USAGE); + } + break; #ifdef WITH_REGEX - case 'm': - if ( rewrite_file ) { - syslog(LOG_ERR, "Multiple -m options"); - exit(EX_USAGE); - } - rewrite_file = optarg; - break; + case 'm': + if (rewrite_file) { + syslog(LOG_ERR, "Multiple -m options"); + exit(EX_USAGE); + } + rewrite_file = optarg; + break; #endif - case 'v': - verbosity++; - break; - case 'V': - /* Print configuration to stdout and exit */ - printf("%s\n", TFTPD_CONFIG_STR); - exit(0); - break; - default: - usage(); - break; + case 'v': + verbosity++; + break; + case 'V': + /* Print configuration to stdout and exit */ + printf("%s\n", TFTPD_CONFIG_STR); + exit(0); + break; + default: + syslog(LOG_ERR, "Unknown option: '%c'", optopt); + break; + } + + dirs = xmalloc((argc - optind + 1) * sizeof(char *)); + for (ndirs = 0; optind != argc; optind++) + dirs[ndirs++] = argv[optind]; + + dirs[ndirs] = NULL; + + if (secure) { + if (ndirs == 0) { + syslog(LOG_ERR, "no -s directory"); + exit(EX_USAGE); + } + if (ndirs > 1) { + syslog(LOG_ERR, "too many -s directories"); + exit(EX_USAGE); + } + if (chdir(dirs[0])) { + syslog(LOG_ERR, "%s: %m", dirs[0]); + exit(EX_NOINPUT); + } } - dirs = xmalloc((argc-optind+1)*sizeof(char *)); - for ( ndirs = 0 ; optind != argc ; optind++ ) - dirs[ndirs++] = argv[optind]; + pw = getpwnam(user); + if (!pw) { + syslog(LOG_ERR, "no user %s: %m", user); + exit(EX_NOUSER); + } - dirs[ndirs] = NULL; - - if (secure) { - if (ndirs == 0) { - syslog(LOG_ERR, "no -s directory"); - exit(EX_USAGE); - } - if (ndirs > 1) { - syslog(LOG_ERR, "too many -s directories"); - exit(EX_USAGE); - } - if (chdir(dirs[0])) { - syslog(LOG_ERR, "%s: %m", dirs[0]); - exit(EX_NOINPUT); - } - } - - pw = getpwnam(user); - if (!pw) { - syslog(LOG_ERR, "no user %s: %m", user); - exit(EX_NOUSER); - } + if (spec_umask || !unixperms) + umask(my_umask); - if ( spec_umask || !unixperms ) - umask(my_umask); - - /* Note: on Cygwin, select() on a nonblocking socket becomes - a nonblocking select. */ + /* Note: on Cygwin, select() on a nonblocking socket becomes + a nonblocking select. */ #ifndef __CYGWIN__ - set_socket_nonblock(fd, 1); -#endif - -#ifdef WITH_REGEX - if ( rewrite_file ) - rewrite_rules = read_remap_rules(rewrite_file); -#endif - - /* If we're running standalone, set up the input port */ - if ( standalone ) { - fd = socket(PF_INET, SOCK_DGRAM, 0); - - memset(&bindaddr, 0, sizeof bindaddr); - bindaddr.sin_family = AF_INET; - bindaddr.sin_addr.s_addr = INADDR_ANY; - bindaddr.sin_port = htons(IPPORT_TFTP); - - if ( address ) { - char *portptr, *eportptr; - struct hostent *hostent; - struct servent *servent; - unsigned long port; - - address = tfstrdup(address); - portptr = strrchr(address, ':'); - if ( portptr ) - *portptr++ = '\0'; - - if ( *address ) { - hostent = gethostbyname(address); - if ( !hostent || hostent->h_addrtype != AF_INET ) { - syslog(LOG_ERR, "cannot resolve local bind address: %s", address); - exit(EX_NOINPUT); - } - memcpy(&bindaddr.sin_addr, hostent->h_addr, hostent->h_length); - } else { - /* Default to using INADDR_ANY */ - } - - if ( portptr && *portptr ) { - servent = getservbyname(portptr, "udp"); - if ( servent ) { - bindaddr.sin_port = servent->s_port; - } else if ( (port = strtoul(portptr, &eportptr, 0)) && !*eportptr ) { - bindaddr.sin_port = htons(port); - } else if ( !strcmp(portptr, "tftp") ) { - /* It's TFTP, we're OK */ - } else { - syslog(LOG_ERR, "cannot resolve local bind port: %s", portptr); - exit(EX_NOINPUT); - } - } - } - - if (bind(fd, (struct sockaddr *)&bindaddr, sizeof bindaddr) < 0) { - syslog(LOG_ERR, "cannot bind to local socket: %m"); - exit(EX_OSERR); - } - - /* Daemonize this process */ - /* Note: when running in secure mode (-s), we must not chroot, since - we are already in the proper directory. */ - if (!nodaemon && daemon(secure, 0) < 0) { - syslog(LOG_ERR, "cannot daemonize: %m"); - exit(EX_OSERR); - } - } else { - /* 0 is our socket descriptor */ - close(1); close(2); - } - - /* Disable path MTU discovery */ - pmtu_discovery_off(0); - - /* This means we don't want to wait() for children */ -#ifdef SA_NOCLDWAIT - set_signal(SIGCHLD, SIG_IGN, SA_NOCLDSTOP|SA_NOCLDWAIT); -#else - set_signal(SIGCHLD, SIG_IGN, SA_NOCLDSTOP); -#endif - - /* Take SIGHUP and use it to set a variable. This - is polled synchronously to make sure we don't - lose packets as a result. */ - set_signal(SIGHUP, handle_sighup, 0); - - while ( 1 ) { - fd_set readset; - struct timeval tv_waittime; - int rv; - - if ( caught_sighup ) { - caught_sighup = 0; - if ( standalone ) { -#ifdef WITH_REGEX - if ( rewrite_file ) { - freerules(rewrite_rules); - rewrite_rules = read_remap_rules(rewrite_file); - } -#endif - } else { - /* Return to inetd for respawn */ - exit(0); - } - } - - FD_ZERO(&readset); - FD_SET(fd, &readset); - tv_waittime.tv_sec = waittime; - tv_waittime.tv_usec = 0; - -#ifdef __CYGWIN__ - /* On Cygwin, select() on a nonblocking socket returns immediately, - with a rv of 0! */ - set_socket_nonblock(fd, 0); -#endif - - /* Never time out if we're in standalone mode */ - rv = select(fd+1, &readset, NULL, NULL, standalone ? NULL : &tv_waittime); - if ( rv == -1 && errno == EINTR ) - continue; /* Signal caught, reloop */ - if ( rv == -1 ) { - syslog(LOG_ERR, "select loop: %m"); - exit(EX_IOERR); - } else if ( rv == 0 ) { - exit(0); /* Timeout, return to inetd */ - } - -#ifdef __CYGWIN__ set_socket_nonblock(fd, 1); -#endif - - fromlen = sizeof (from); - n = myrecvfrom(fd, buf, sizeof (buf), 0, - (struct sockaddr *)&from, &fromlen, - &myaddr); - - if ( n < 0 ) { - if ( E_WOULD_BLOCK(errno) || errno == EINTR ) { - continue; /* Again, from the top */ - } else { - syslog(LOG_ERR, "recvfrom: %m"); - exit(EX_IOERR); - } - } - - if ( from.sin_family != AF_INET ) { - syslog(LOG_ERR, "received address was not AF_INET, please check your inetd config"); - exit(EX_PROTOCOL); - } - - if ( standalone && myaddr.sin_addr.s_addr == INADDR_ANY ) { - /* myrecvfrom() didn't capture the source address; but we might - have bound to a specific address, if so we should use it */ - memcpy(&myaddr.sin_addr, &bindaddr.sin_addr, sizeof bindaddr.sin_addr); - } - - /* - * Now that we have read the request packet from the UDP - * socket, we fork and go back to listening to the socket. - */ - pid = fork(); - if (pid < 0) { - syslog(LOG_ERR, "fork: %m"); - exit(EX_OSERR); /* Return to inetd, just in case */ - } else if ( pid == 0 ) - break; /* Child exit, parent loop */ - } - - /* Child process: handle the actual request here */ - - /* Ignore SIGHUP */ - set_signal(SIGHUP, SIG_IGN, 0); - -#ifdef HAVE_TCPWRAPPERS - /* Verify if this was a legal request for us. This has to be - done before the chroot, while /etc is still accessible. */ - request_init(&wrap_request, - RQ_DAEMON, __progname, - RQ_FILE, fd, - RQ_CLIENT_SIN, &from, - RQ_SERVER_SIN, &myaddr, - 0); - sock_methods(&wrap_request); - if ( hosts_access(&wrap_request) == 0 ) { - if ( deny_severity != -1 ) - syslog(deny_severity, "connection refused from %s", - inet_ntoa(from.sin_addr)); - exit(EX_NOPERM); /* Access denied */ - } else if ( allow_severity != -1 ) { - syslog(allow_severity, "connect from %s", - inet_ntoa(from.sin_addr)); - } #endif - /* Close file descriptors we don't need */ - close(fd); - - /* Get a socket. This has to be done before the chroot(), since - some systems require access to /dev to create a socket. */ - - peer = socket(AF_INET, SOCK_DGRAM, 0); - if (peer < 0) { - syslog(LOG_ERR, "socket: %m"); - exit(EX_IOERR); - } +#ifdef WITH_REGEX + if (rewrite_file) + rewrite_rules = read_remap_rules(rewrite_file); +#endif - /* Set up the supplementary group access list if possible */ - /* /etc/group still need to be accessible at this point */ + /* If we're running standalone, set up the input port */ + if (standalone) { + fd = socket(PF_INET, SOCK_DGRAM, 0); + + memset(&bindaddr, 0, sizeof bindaddr); + bindaddr.sin_family = AF_INET; + bindaddr.sin_addr.s_addr = INADDR_ANY; + bindaddr.sin_port = htons(IPPORT_TFTP); + + if (address) { + char *portptr, *eportptr; + struct hostent *hostent; + struct servent *servent; + unsigned long port; + + address = tfstrdup(address); + portptr = strrchr(address, ':'); + if (portptr) + *portptr++ = '\0'; + + if (*address) { + hostent = gethostbyname(address); + if (!hostent || hostent->h_addrtype != AF_INET) { + syslog(LOG_ERR, + "cannot resolve local bind address: %s", + address); + exit(EX_NOINPUT); + } + memcpy(&bindaddr.sin_addr, hostent->h_addr, + hostent->h_length); + } else { + /* Default to using INADDR_ANY */ + } + + if (portptr && *portptr) { + servent = getservbyname(portptr, "udp"); + if (servent) { + bindaddr.sin_port = servent->s_port; + } else if ((port = strtoul(portptr, &eportptr, 0)) + && !*eportptr) { + bindaddr.sin_port = htons(port); + } else if (!strcmp(portptr, "tftp")) { + /* It's TFTP, we're OK */ + } else { + syslog(LOG_ERR, "cannot resolve local bind port: %s", + portptr); + exit(EX_NOINPUT); + } + } + } + + if (bind(fd, (struct sockaddr *)&bindaddr, sizeof bindaddr) < 0) { + syslog(LOG_ERR, "cannot bind to local socket: %m"); + exit(EX_OSERR); + } + + /* Daemonize this process */ + /* Note: when running in secure mode (-s), we must not chroot, since + we are already in the proper directory. */ + if (!nodaemon && daemon(secure, 0) < 0) { + syslog(LOG_ERR, "cannot daemonize: %m"); + exit(EX_OSERR); + } + } else { + /* 0 is our socket descriptor */ + close(1); + close(2); + } + + /* Disable path MTU discovery */ + pmtu_discovery_off(0); + + /* This means we don't want to wait() for children */ +#ifdef SA_NOCLDWAIT + set_signal(SIGCHLD, SIG_IGN, SA_NOCLDSTOP | SA_NOCLDWAIT); +#else + set_signal(SIGCHLD, SIG_IGN, SA_NOCLDSTOP); +#endif + + /* Take SIGHUP and use it to set a variable. This + is polled synchronously to make sure we don't + lose packets as a result. */ + set_signal(SIGHUP, handle_sighup, 0); + + while (1) { + fd_set readset; + struct timeval tv_waittime; + int rv; + + if (caught_sighup) { + caught_sighup = 0; + if (standalone) { +#ifdef WITH_REGEX + if (rewrite_file) { + freerules(rewrite_rules); + rewrite_rules = read_remap_rules(rewrite_file); + } +#endif + } else { + /* Return to inetd for respawn */ + exit(0); + } + } + + FD_ZERO(&readset); + FD_SET(fd, &readset); + tv_waittime.tv_sec = waittime; + tv_waittime.tv_usec = 0; + +#ifdef __CYGWIN__ + /* On Cygwin, select() on a nonblocking socket returns immediately, + with a rv of 0! */ + set_socket_nonblock(fd, 0); +#endif + + /* Never time out if we're in standalone mode */ + rv = select(fd + 1, &readset, NULL, NULL, + standalone ? NULL : &tv_waittime); + if (rv == -1 && errno == EINTR) + continue; /* Signal caught, reloop */ + if (rv == -1) { + syslog(LOG_ERR, "select loop: %m"); + exit(EX_IOERR); + } else if (rv == 0) { + exit(0); /* Timeout, return to inetd */ + } +#ifdef __CYGWIN__ + set_socket_nonblock(fd, 1); +#endif + + fromlen = sizeof(from); + n = myrecvfrom(fd, buf, sizeof(buf), 0, + (struct sockaddr *)&from, &fromlen, &myaddr); + + if (n < 0) { + if (E_WOULD_BLOCK(errno) || errno == EINTR) { + continue; /* Again, from the top */ + } else { + syslog(LOG_ERR, "recvfrom: %m"); + exit(EX_IOERR); + } + } + + if (from.sin_family != AF_INET) { + syslog(LOG_ERR, + "received address was not AF_INET, please check your inetd config"); + exit(EX_PROTOCOL); + } + + if (standalone && myaddr.sin_addr.s_addr == INADDR_ANY) { + /* myrecvfrom() didn't capture the source address; but we might + have bound to a specific address, if so we should use it */ + memcpy(&myaddr.sin_addr, &bindaddr.sin_addr, + sizeof bindaddr.sin_addr); + } + + /* + * Now that we have read the request packet from the UDP + * socket, we fork and go back to listening to the socket. + */ + pid = fork(); + if (pid < 0) { + syslog(LOG_ERR, "fork: %m"); + exit(EX_OSERR); /* Return to inetd, just in case */ + } else if (pid == 0) + break; /* Child exit, parent loop */ + } + + /* Child process: handle the actual request here */ + + /* Ignore SIGHUP */ + set_signal(SIGHUP, SIG_IGN, 0); + +#ifdef HAVE_TCPWRAPPERS + /* Verify if this was a legal request for us. This has to be + done before the chroot, while /etc is still accessible. */ + request_init(&wrap_request, + RQ_DAEMON, __progname, + RQ_FILE, fd, + RQ_CLIENT_SIN, &from, RQ_SERVER_SIN, &myaddr, 0); + sock_methods(&wrap_request); + if (hosts_access(&wrap_request) == 0) { + if (deny_severity != -1) + syslog(deny_severity, "connection refused from %s", + inet_ntoa(from.sin_addr)); + exit(EX_NOPERM); /* Access denied */ + } else if (allow_severity != -1) { + syslog(allow_severity, "connect from %s", + inet_ntoa(from.sin_addr)); + } +#endif + + /* Close file descriptors we don't need */ + close(fd); + + /* Get a socket. This has to be done before the chroot(), since + some systems require access to /dev to create a socket. */ + + peer = socket(AF_INET, SOCK_DGRAM, 0); + if (peer < 0) { + syslog(LOG_ERR, "socket: %m"); + exit(EX_IOERR); + } + + /* Set up the supplementary group access list if possible */ + /* /etc/group still need to be accessible at this point */ #ifdef HAVE_INITGROUPS - setrv = initgroups(user, pw->pw_gid); - if ( setrv ) { - syslog(LOG_ERR, "cannot set groups for user %s", user); - exit(EX_OSERR); - } + setrv = initgroups(user, pw->pw_gid); + if (setrv) { + syslog(LOG_ERR, "cannot set groups for user %s", user); + exit(EX_OSERR); + } #else #ifdef HAVE_SETGROUPS - if ( setgroups(0, NULL) ) { - syslog(LOG_ERR, "cannot clear group list"); - } -#endif -#endif - - /* Chroot and drop privileges */ - if (secure) { - if (chroot(".")) { - syslog(LOG_ERR, "chroot: %m"); - exit(EX_OSERR); + if (setgroups(0, NULL)) { + syslog(LOG_ERR, "cannot clear group list"); } +#endif +#endif + + /* Chroot and drop privileges */ + if (secure) { + if (chroot(".")) { + syslog(LOG_ERR, "chroot: %m"); + exit(EX_OSERR); + } #ifdef __CYGWIN__ - chdir("/"); /* Cygwin chroot() bug workaround */ + chdir("/"); /* Cygwin chroot() bug workaround */ #endif - } - + } #ifdef HAVE_SETREGID - setrv = setregid(pw->pw_gid, pw->pw_gid); + setrv = setregid(pw->pw_gid, pw->pw_gid); #else - setrv = setegid(pw->pw_gid) || setgid(pw->pw_gid); + setrv = setegid(pw->pw_gid) || setgid(pw->pw_gid); #endif - + #ifdef HAVE_SETREUID - setrv = setrv || setreuid(pw->pw_uid, pw->pw_uid); + setrv = setrv || setreuid(pw->pw_uid, pw->pw_uid); #else - /* Important: setuid() must come first */ - setrv = setrv || setuid(pw->pw_uid) || - (geteuid() != pw->pw_uid && seteuid(pw->pw_uid)); + /* Important: setuid() must come first */ + setrv = setrv || setuid(pw->pw_uid) || + (geteuid() != pw->pw_uid && seteuid(pw->pw_uid)); #endif - - if ( setrv ) { - syslog(LOG_ERR, "cannot drop privileges: %m"); - exit(EX_OSERR); - } - - /* Other basic setup */ - from.sin_family = AF_INET; - - /* Process the request... */ - if (pick_port_bind(peer, &myaddr, portrange_from, portrange_to) < 0) { - syslog(LOG_ERR, "bind: %m"); - exit(EX_IOERR); - } - - if (connect(peer, (struct sockaddr *)&from, sizeof from) < 0) { - syslog(LOG_ERR, "connect: %m"); - exit(EX_IOERR); - } - /* Disable path MTU discovery */ - pmtu_discovery_off(0); + if (setrv) { + syslog(LOG_ERR, "cannot drop privileges: %m"); + exit(EX_OSERR); + } - tp = (struct tftphdr *)buf; - tp_opcode = ntohs(tp->th_opcode); - if (tp_opcode == RRQ || tp_opcode == WRQ) - tftp(tp, n); - exit(0); + /* Other basic setup */ + from.sin_family = AF_INET; + + /* Process the request... */ + if (pick_port_bind(peer, &myaddr, portrange_from, portrange_to) < 0) { + syslog(LOG_ERR, "bind: %m"); + exit(EX_IOERR); + } + + if (connect(peer, (struct sockaddr *)&from, sizeof from) < 0) { + syslog(LOG_ERR, "connect: %m"); + exit(EX_IOERR); + } + + /* Disable path MTU discovery */ + pmtu_discovery_off(0); + + tp = (struct tftphdr *)buf; + tp_opcode = ntohs(tp->th_opcode); + if (tp_opcode == RRQ || tp_opcode == WRQ) + tftp(tp, n); + exit(0); } -char *rewrite_access(char *, int, const char **); -int validate_access(char *, int, struct formats *, const char **); -void tftp_sendfile(struct formats *, struct tftphdr *, int); -void tftp_recvfile(struct formats *, struct tftphdr *, int); +char *rewrite_access(char *, int, const char **); +int validate_access(char *, int, struct formats *, const char **); +void tftp_sendfile(struct formats *, struct tftphdr *, int); +void tftp_recvfile(struct formats *, struct tftphdr *, int); struct formats { - const char *f_mode; - char *(*f_rewrite)(char *, int, const char **); - int (*f_validate)(char *, int, struct formats *, const char **); - void (*f_send)(struct formats *, struct tftphdr *, int); - void (*f_recv)(struct formats *, struct tftphdr *, int); - int f_convert; + const char *f_mode; + char *(*f_rewrite) (char *, int, const char **); + int (*f_validate) (char *, int, struct formats *, const char **); + void (*f_send) (struct formats *, struct tftphdr *, int); + void (*f_recv) (struct formats *, struct tftphdr *, int); + int f_convert; } formats[] = { - { "netascii", rewrite_access, validate_access, tftp_sendfile, tftp_recvfile, 1 }, - { "octet", rewrite_access, validate_access, tftp_sendfile, tftp_recvfile, 0 }, - { NULL, NULL, NULL, NULL, NULL, 0 } + { + "netascii", rewrite_access, validate_access, tftp_sendfile, + tftp_recvfile, 1}, { + "octet", rewrite_access, validate_access, tftp_sendfile, + tftp_recvfile, 0}, { + NULL, NULL, NULL, NULL, NULL, 0} }; /* * Handle initial connection protocol. */ -int -tftp(struct tftphdr *tp, int size) +int tftp(struct tftphdr *tp, int size) { - char *cp, *end; - int argn, ecode; - struct formats *pf = NULL; - char *origfilename; - char *filename, *mode = NULL; - const char *errmsgptr; - u_short tp_opcode = ntohs(tp->th_opcode); - - char *val = NULL, *opt = NULL; - char *ap = ackbuf + 2; + char *cp, *end; + int argn, ecode; + struct formats *pf = NULL; + char *origfilename; + char *filename, *mode = NULL; + const char *errmsgptr; + u_short tp_opcode = ntohs(tp->th_opcode); - ((struct tftphdr *)ackbuf)->th_opcode = htons(OACK); - - origfilename = cp = (char *) &(tp->th_stuff); - argn = 0; - - end = (char *)tp + size; + char *val = NULL, *opt = NULL; + char *ap = ackbuf + 2; - while ( cp < end && *cp ) { - do { - cp++; - } while (cp < end && *cp); - - if ( *cp ) { - nak(EBADOP, "Request not null-terminated"); - exit(0); + ((struct tftphdr *)ackbuf)->th_opcode = htons(OACK); + + origfilename = cp = (char *)&(tp->th_stuff); + argn = 0; + + end = (char *)tp + size; + + while (cp < end && *cp) { + do { + cp++; + } while (cp < end && *cp); + + if (*cp) { + nak(EBADOP, "Request not null-terminated"); + exit(0); + } + + argn++; + if (argn == 1) { + mode = ++cp; + } else if (argn == 2) { + for (cp = mode; *cp; cp++) + *cp = tolower(*cp); + for (pf = formats; pf->f_mode; pf++) { + if (!strcmp(pf->f_mode, mode)) + break; + } + if (!pf->f_mode) { + nak(EBADOP, "Unknown mode"); + exit(0); + } + if (!(filename = + (*pf->f_rewrite) (origfilename, tp_opcode, + &errmsgptr))) { + nak(EACCESS, errmsgptr); /* File denied by mapping rule */ + exit(0); + } + if (verbosity >= 1) { + if (filename == origfilename + || !strcmp(filename, origfilename)) + syslog(LOG_NOTICE, "%s from %s filename %s\n", + tp_opcode == WRQ ? "WRQ" : "RRQ", + inet_ntoa(from.sin_addr), filename); + else + syslog(LOG_NOTICE, + "%s from %s filename %s remapped to %s\n", + tp_opcode == WRQ ? "WRQ" : "RRQ", + inet_ntoa(from.sin_addr), origfilename, + filename); + } + ecode = + (*pf->f_validate) (filename, tp_opcode, pf, &errmsgptr); + if (ecode) { + nak(ecode, errmsgptr); + exit(0); + } + opt = ++cp; + } else if (argn & 1) { + val = ++cp; + } else { + do_opt(opt, val, &ap); + opt = ++cp; + } } - - argn++; - if (argn == 1) { - mode = ++cp; - } else if (argn == 2) { - for (cp = mode; *cp; cp++) - *cp = tolower(*cp); - for (pf = formats; pf->f_mode; pf++) { - if (!strcmp(pf->f_mode, mode)) - break; - } - if (!pf->f_mode) { - nak(EBADOP, "Unknown mode"); - exit(0); - } - if ( !(filename = - (*pf->f_rewrite)(origfilename, tp_opcode, &errmsgptr)) ) { - nak(EACCESS, errmsgptr); /* File denied by mapping rule */ - exit(0); - } - if ( verbosity >= 1 ) { - if ( filename == origfilename || !strcmp(filename, origfilename) ) - syslog(LOG_NOTICE, "%s from %s filename %s\n", - tp_opcode == WRQ ? "WRQ" : "RRQ", - inet_ntoa(from.sin_addr), filename); - else - syslog(LOG_NOTICE, "%s from %s filename %s remapped to %s\n", - tp_opcode == WRQ ? "WRQ" : "RRQ", - inet_ntoa(from.sin_addr), origfilename, filename); - } - ecode = (*pf->f_validate)(filename, tp_opcode, pf, &errmsgptr); - if (ecode) { - nak(ecode, errmsgptr); - exit(0); - } - opt = ++cp; - } else if ( argn & 1 ) { - val = ++cp; + + if (!pf) { + nak(EBADOP, "Missing mode"); + exit(0); + } + + if (ap != (ackbuf + 2)) { + if (tp_opcode == WRQ) + (*pf->f_recv) (pf, (struct tftphdr *)ackbuf, ap - ackbuf); + else + (*pf->f_send) (pf, (struct tftphdr *)ackbuf, ap - ackbuf); } else { - do_opt(opt, val, &ap); - opt = ++cp; + if (tp_opcode == WRQ) + (*pf->f_recv) (pf, NULL, 0); + else + (*pf->f_send) (pf, NULL, 0); } - } - - if (!pf) { - nak(EBADOP, "Missing mode"); - exit(0); - } - - if ( ap != (ackbuf+2) ) { - if ( tp_opcode == WRQ ) - (*pf->f_recv)(pf, (struct tftphdr *)ackbuf, ap-ackbuf); - else - (*pf->f_send)(pf, (struct tftphdr *)ackbuf, ap-ackbuf); - } else { - if (tp_opcode == WRQ) - (*pf->f_recv)(pf, NULL, 0); - else - (*pf->f_send)(pf, NULL, 0); - } - exit(0); /* Request completed */ + exit(0); /* Request completed */ } static int blksize_set; @@ -838,66 +830,64 @@ static int blksize_set; /* * Set a non-standard block size (c.f. RFC2348) */ -int -set_blksize(char *val, char **ret) +int set_blksize(char *val, char **ret) { - static char b_ret[6]; - unsigned int sz; - char *vend; - - sz = (unsigned int)strtoul(val, &vend, 10); - - if ( blksize_set || *vend ) - return 0; - - if (sz < 8) - return(0); - else if (sz > max_blksize) - sz = max_blksize; - - segsize = sz; - sprintf(*ret = b_ret, "%u", sz); - - blksize_set = 1; - - return(1); + static char b_ret[6]; + unsigned int sz; + char *vend; + + sz = (unsigned int)strtoul(val, &vend, 10); + + if (blksize_set || *vend) + return 0; + + if (sz < 8) + return (0); + else if (sz > max_blksize) + sz = max_blksize; + + segsize = sz; + sprintf(*ret = b_ret, "%u", sz); + + blksize_set = 1; + + return (1); } /* * Set a power-of-two block size (nonstandard) */ -int -set_blksize2(char *val, char **ret) +int set_blksize2(char *val, char **ret) { - static char b_ret[6]; - unsigned int sz; - char *vend; - - sz = (unsigned int)strtoul(val, &vend, 10); - - if ( blksize_set || *vend ) - return 0; - - if (sz < 8) - return(0); - else if (sz > max_blksize) - sz = max_blksize; - - /* Convert to a power of two */ - if ( sz & (sz-1) ) { - unsigned int sz1 = 1; - /* Not a power of two - need to convert */ - while ( sz >>= 1 ) - sz1 <<= 1; - sz = sz1; - } - - segsize = sz; - sprintf(*ret = b_ret, "%u", sz); - - blksize_set = 1; - - return(1); + static char b_ret[6]; + unsigned int sz; + char *vend; + + sz = (unsigned int)strtoul(val, &vend, 10); + + if (blksize_set || *vend) + return 0; + + if (sz < 8) + return (0); + else if (sz > max_blksize) + sz = max_blksize; + + /* Convert to a power of two */ + if (sz & (sz - 1)) { + unsigned int sz1 = 1; + /* Not a power of two - need to convert */ + while (sz >>= 1) + sz1 <<= 1; + sz = sz1; + } + + segsize = sz; + sprintf(*ret = b_ret, "%u", sz); + + blksize_set = 1; + + return (1); } /* @@ -905,23 +895,22 @@ set_blksize2(char *val, char **ret) * For netascii mode, we don't know the size ahead of time; * so reject the option. */ -int -set_tsize(char *val, char **ret) +int set_tsize(char *val, char **ret) { - static char b_ret[sizeof(uintmax_t)*CHAR_BIT/3+2]; - uintmax_t sz; - char *vend; + static char b_ret[sizeof(uintmax_t) * CHAR_BIT / 3 + 2]; + uintmax_t sz; + char *vend; - sz = strtoumax(val, &vend, 10); - - if ( !tsize_ok || *vend ) - return 0; - - if (sz == 0) - sz = (uintmax_t)tsize; + sz = strtoumax(val, &vend, 10); - sprintf(*ret = b_ret, "%"PRIuMAX, sz); - return(1); + if (!tsize_ok || *vend) + return 0; + + if (sz == 0) + sz = (uintmax_t) tsize; + + sprintf(*ret = b_ret, "%" PRIuMAX, sz); + return (1); } /* @@ -929,76 +918,74 @@ set_tsize(char *val, char **ret) * to be the (default) retransmission timeout, but being an * integer in seconds it seems a bit limited. */ -int -set_timeout(char *val, char **ret) +int set_timeout(char *val, char **ret) { - static char b_ret[4]; - unsigned long to; - char *vend; + static char b_ret[4]; + unsigned long to; + char *vend; - to = strtoul(val, &vend, 10); + to = strtoul(val, &vend, 10); - if ( to < 1 || to > 255 || *vend ) - return 0; - - rexmtval = timeout = to*1000000UL; - maxtimeout = rexmtval*TIMEOUT_LIMIT; - - sprintf(*ret = b_ret, "%lu", to); - return(1); + if (to < 1 || to > 255 || *vend) + return 0; + + rexmtval = timeout = to * 1000000UL; + maxtimeout = rexmtval * TIMEOUT_LIMIT; + + sprintf(*ret = b_ret, "%lu", to); + return (1); } /* Similar, but in microseconds. We allow down to 10 ms. */ -int -set_utimeout(char *val, char **ret) +int set_utimeout(char *val, char **ret) { - static char b_ret[4]; - unsigned long to; - char *vend; + static char b_ret[4]; + unsigned long to; + char *vend; - to = strtoul(val, &vend, 10); + to = strtoul(val, &vend, 10); - if ( to < 10000UL || to > 255000000UL || *vend ) - return 0; - - rexmtval = timeout = to; - maxtimeout = rexmtval*TIMEOUT_LIMIT; - - sprintf(*ret = b_ret, "%lu", to); - return(1); + if (to < 10000UL || to > 255000000UL || *vend) + return 0; + + rexmtval = timeout = to; + maxtimeout = rexmtval * TIMEOUT_LIMIT; + + sprintf(*ret = b_ret, "%lu", to); + return (1); } + /* * Parse RFC2347 style options */ -void -do_opt(char *opt, char *val, char **ap) +void do_opt(char *opt, char *val, char **ap) { - struct options *po; - char *ret; - - /* Global option-parsing variables initialization */ - blksize_set = 0; - - if ( !*opt ) + struct options *po; + char *ret; + + /* Global option-parsing variables initialization */ + blksize_set = 0; + + if (!*opt) + return; + + for (po = options; po->o_opt; po++) + if (!strcasecmp(po->o_opt, opt)) { + if (po->o_fnc(val, &ret)) { + if (*ap + strlen(opt) + strlen(ret) + 2 >= + ackbuf + sizeof(ackbuf)) { + nak(EOPTNEG, "Insufficient space for options"); + exit(0); + } + *ap = strrchr(strcpy(strrchr(strcpy(*ap, opt), '\0') + 1, + ret), '\0') + 1; + } else { + nak(EOPTNEG, "Unsupported option(s) requested"); + exit(0); + } + break; + } return; - - for (po = options; po->o_opt; po++) - if (!strcasecmp(po->o_opt, opt)) { - if (po->o_fnc(val, &ret)) { - if (*ap + strlen(opt) + strlen(ret) + 2 >= - ackbuf + sizeof(ackbuf)) { - nak(EOPTNEG, "Insufficient space for options"); - exit(0); - } - *ap = strrchr(strcpy(strrchr(strcpy(*ap, opt),'\0') + 1, - ret),'\0') + 1; - } else { - nak(EOPTNEG, "Unsupported option(s) requested"); - exit(0); - } - break; - } - return; } #ifdef WITH_REGEX @@ -1010,52 +997,50 @@ do_opt(char *opt, char *val, char **ap) * * Return -1 on failure. */ -int -rewrite_macros(char macro, char *output); +int rewrite_macros(char macro, char *output); -int -rewrite_macros(char macro, char *output) +int rewrite_macros(char macro, char *output) { - char *p; + char *p; - switch (macro) { - case 'i': - p = inet_ntoa(from.sin_addr); - if ( output ) - strcpy(output, p); - return strlen(p); - - case 'x': - if ( output ) - sprintf(output, "%08lX", (unsigned long)ntohl(from.sin_addr.s_addr)); - return 8; + switch (macro) { + case 'i': + p = inet_ntoa(from.sin_addr); + if (output) + strcpy(output, p); + return strlen(p); - default: - return -1; - } + case 'x': + if (output) + sprintf(output, "%08lX", + (unsigned long)ntohl(from.sin_addr.s_addr)); + return 8; + + default: + return -1; + } } /* * Modify the filename, if applicable. If it returns NULL, deny the access. */ -char * -rewrite_access(char *filename, int mode, const char **msg) +char *rewrite_access(char *filename, int mode, const char **msg) { - if ( rewrite_rules ) { - char *newname = rewrite_string(filename, rewrite_rules, mode != RRQ, - rewrite_macros, msg); - filename = newname; - } - return filename; + if (rewrite_rules) { + char *newname = + rewrite_string(filename, rewrite_rules, mode != RRQ, + rewrite_macros, msg); + filename = newname; + } + return filename; } #else -char * -rewrite_access(char *filename, int mode, const char **msg) +char *rewrite_access(char *filename, int mode, const char **msg) { - (void)mode; /* Avoid warning */ - (void)msg; - return filename; + (void)mode; /* Avoid warning */ + (void)msg; + return filename; } #endif @@ -1073,316 +1058,313 @@ FILE *file; */ int validate_access(char *filename, int mode, - struct formats *pf, const char **errmsg) + struct formats *pf, const char **errmsg) { - struct stat stbuf; - int i, len; - int fd, wmode, rmode; - char *cp; - const char **dirp; - char stdio_mode[3]; - - tsize_ok = 0; - *errmsg = NULL; - - if (!secure) { - if (*filename != '/') { - *errmsg = "Only absolute filenames allowed"; - return (EACCESS); + struct stat stbuf; + int i, len; + int fd, wmode, rmode; + char *cp; + const char **dirp; + char stdio_mode[3]; + + tsize_ok = 0; + *errmsg = NULL; + + if (!secure) { + if (*filename != '/') { + *errmsg = "Only absolute filenames allowed"; + return (EACCESS); + } + + /* + * prevent tricksters from getting around the directory + * restrictions + */ + len = strlen(filename); + for (i = 1; i < len - 3; i++) { + cp = filename + i; + if (*cp == '.' && memcmp(cp - 1, "/../", 4) == 0) { + *errmsg = "Reverse path not allowed"; + return (EACCESS); + } + } + + for (dirp = dirs; *dirp; dirp++) + if (strncmp(filename, *dirp, strlen(*dirp)) == 0) + break; + if (*dirp == 0 && dirp != dirs) { + *errmsg = "Forbidden directory"; + return (EACCESS); + } } /* - * prevent tricksters from getting around the directory - * restrictions + * We use different a different permissions scheme if `cancreate' is + * set. */ - len = strlen(filename); - for ( i = 1 ; i < len-3 ; i++ ) { - cp = filename + i; - if ( *cp == '.' && memcmp(cp-1, "/../", 4) == 0 ) { - *errmsg = "Reverse path not allowed"; - return(EACCESS); - } + wmode = O_WRONLY | + (cancreate ? O_CREAT : 0) | + (unixperms ? O_TRUNC : 0) | (pf->f_convert ? O_TEXT : O_BINARY); + rmode = O_RDONLY | (pf->f_convert ? O_TEXT : O_BINARY); + + fd = open(filename, mode == RRQ ? rmode : wmode, 0666); + if (fd < 0) { + switch (errno) { + case ENOENT: + case ENOTDIR: + return ENOTFOUND; + case ENOSPC: + return ENOSPACE; + case EEXIST: + return EEXISTS; + default: + return errno + 100; + } } - for (dirp = dirs; *dirp; dirp++) - if (strncmp(filename, *dirp, strlen(*dirp)) == 0) - break; - if (*dirp==0 && dirp!=dirs) { - *errmsg = "Forbidden directory"; - return (EACCESS); - } - } - - /* - * We use different a different permissions scheme if `cancreate' is - * set. - */ - wmode = O_WRONLY | - (cancreate ? O_CREAT : 0) | - (unixperms ? O_TRUNC : 0) | - (pf->f_convert ? O_TEXT : O_BINARY); - rmode = O_RDONLY | - (pf->f_convert ? O_TEXT : O_BINARY); + if (fstat(fd, &stbuf) < 0) + exit(EX_OSERR); /* This shouldn't happen */ - fd = open(filename, mode == RRQ ? rmode : wmode, 0666); - if (fd < 0) { - switch (errno) { - case ENOENT: - case ENOTDIR: - return ENOTFOUND; - case ENOSPC: - return ENOSPACE; - case EEXIST: - return EEXISTS; - default: - return errno+100; - } - } + if (mode == RRQ) { + if (!unixperms && (stbuf.st_mode & (S_IREAD >> 6)) == 0) { + *errmsg = "File must have global read permissions"; + return (EACCESS); + } + tsize = stbuf.st_size; + /* We don't know the tsize if conversion is needed */ + tsize_ok = !pf->f_convert; + } else { + if (!unixperms) { + if ((stbuf.st_mode & (S_IWRITE >> 6)) == 0) { + *errmsg = "File must have global write permissions"; + return (EACCESS); + } - if ( fstat(fd, &stbuf) < 0 ) - exit(EX_OSERR); /* This shouldn't happen */ - - if (mode == RRQ) { - if ( !unixperms && (stbuf.st_mode & (S_IREAD >> 6)) == 0 ) { - *errmsg = "File must have global read permissions"; - return (EACCESS); - } - tsize = stbuf.st_size; - /* We don't know the tsize if conversion is needed */ - tsize_ok = !pf->f_convert; - } else { - if ( !unixperms ) { - if ( (stbuf.st_mode & (S_IWRITE >> 6)) == 0 ) { - *errmsg = "File must have global write permissions"; - return (EACCESS); - } - - /* We didn't get to truncate the file at open() time */ + /* We didn't get to truncate the file at open() time */ #ifdef HAVE_FTRUNCATE - if ( ftruncate(fd, (off_t)0) ) { - *errmsg = "Cannot reset file size"; - return(EACCESS); - } + if (ftruncate(fd, (off_t) 0)) { + *errmsg = "Cannot reset file size"; + return (EACCESS); + } #endif + } + tsize = 0; + tsize_ok = 1; } - tsize = 0; - tsize_ok = 1; - } - stdio_mode[0] = (mode == RRQ) ? 'r':'w'; - stdio_mode[1] = (pf->f_convert) ? 't':'b'; - stdio_mode[2] = '\0'; + stdio_mode[0] = (mode == RRQ) ? 'r' : 'w'; + stdio_mode[1] = (pf->f_convert) ? 't' : 'b'; + stdio_mode[2] = '\0'; - file = fdopen(fd, stdio_mode); - if (file == NULL) - exit(EX_OSERR); /* Internal error */ + file = fdopen(fd, stdio_mode); + if (file == NULL) + exit(EX_OSERR); /* Internal error */ - return (0); + return (0); } /* * Send the requested file. */ -void -tftp_sendfile(struct formats *pf, struct tftphdr *oap, int oacklen) +void tftp_sendfile(struct formats *pf, struct tftphdr *oap, int oacklen) { - struct tftphdr *dp; - struct tftphdr *ap; /* ack packet */ - static u_short block = 1; /* Static to avoid longjmp funnies */ - u_short ap_opcode, ap_block; - unsigned long r_timeout; - int size, n; - - if (oap) { - timeout = rexmtval; - (void)sigsetjmp(timeoutbuf,1); - oack: - r_timeout = timeout; - if (send(peer, oap, oacklen, 0) != oacklen) { - syslog(LOG_WARNING, "tftpd: oack: %m\n"); - goto abort; - } - for ( ; ; ) { - n = recv_time(peer, ackbuf, sizeof(ackbuf), 0, &r_timeout); - if (n < 0) { - syslog(LOG_WARNING, "tftpd: read: %m\n"); - goto abort; - } - ap = (struct tftphdr *)ackbuf; - ap_opcode = ntohs((u_short)ap->th_opcode); - ap_block = ntohs((u_short)ap->th_block); - - if (ap_opcode == ERROR) { - syslog(LOG_WARNING, "tftp: client does not accept options\n"); - goto abort; - } - if (ap_opcode == ACK) { - if (ap_block == 0) - break; - /* Resynchronize with the other side */ - (void)synchnet(peer); - goto oack; - } - } - } + struct tftphdr *dp; + struct tftphdr *ap; /* ack packet */ + static u_short block = 1; /* Static to avoid longjmp funnies */ + u_short ap_opcode, ap_block; + unsigned long r_timeout; + int size, n; - dp = r_init(); - do { - size = readit(file, &dp, pf->f_convert); - if (size < 0) { - nak(errno + 100, NULL); - goto abort; + if (oap) { + timeout = rexmtval; + (void)sigsetjmp(timeoutbuf, 1); + oack: + r_timeout = timeout; + if (send(peer, oap, oacklen, 0) != oacklen) { + syslog(LOG_WARNING, "tftpd: oack: %m\n"); + goto abort; + } + for (;;) { + n = recv_time(peer, ackbuf, sizeof(ackbuf), 0, &r_timeout); + if (n < 0) { + syslog(LOG_WARNING, "tftpd: read: %m\n"); + goto abort; + } + ap = (struct tftphdr *)ackbuf; + ap_opcode = ntohs((u_short) ap->th_opcode); + ap_block = ntohs((u_short) ap->th_block); + + if (ap_opcode == ERROR) { + syslog(LOG_WARNING, + "tftp: client does not accept options\n"); + goto abort; + } + if (ap_opcode == ACK) { + if (ap_block == 0) + break; + /* Resynchronize with the other side */ + (void)synchnet(peer); + goto oack; + } + } } - dp->th_opcode = htons((u_short)DATA); - dp->th_block = htons((u_short)block); - timeout = rexmtval; - (void) sigsetjmp(timeoutbuf,1); - - r_timeout = timeout; - if (send(peer, dp, size + 4, 0) != size + 4) { - syslog(LOG_WARNING, "tftpd: write: %m"); - goto abort; - } - read_ahead(file, pf->f_convert); - for ( ; ; ) { - n = recv_time(peer, ackbuf, sizeof (ackbuf), 0, &r_timeout); - if (n < 0) { - syslog(LOG_WARNING, "tftpd: read(ack): %m"); - goto abort; - } - ap = (struct tftphdr *)ackbuf; - ap_opcode = ntohs((u_short)ap->th_opcode); - ap_block = ntohs((u_short)ap->th_block); - - if (ap_opcode == ERROR) - goto abort; - - if (ap_opcode == ACK) { - if (ap_block == block) { - break; - } - /* Re-synchronize with the other side */ - (void) synchnet(peer); - /* - * RFC1129/RFC1350: We MUST NOT re-send the DATA - * packet in response to an invalid ACK. Doing so - * would cause the Sorcerer's Apprentice bug. - */ - } - - } - block++; - } while (size == segsize); - abort: - (void) fclose(file); + + dp = r_init(); + do { + size = readit(file, &dp, pf->f_convert); + if (size < 0) { + nak(errno + 100, NULL); + goto abort; + } + dp->th_opcode = htons((u_short) DATA); + dp->th_block = htons((u_short) block); + timeout = rexmtval; + (void)sigsetjmp(timeoutbuf, 1); + + r_timeout = timeout; + if (send(peer, dp, size + 4, 0) != size + 4) { + syslog(LOG_WARNING, "tftpd: write: %m"); + goto abort; + } + read_ahead(file, pf->f_convert); + for (;;) { + n = recv_time(peer, ackbuf, sizeof(ackbuf), 0, &r_timeout); + if (n < 0) { + syslog(LOG_WARNING, "tftpd: read(ack): %m"); + goto abort; + } + ap = (struct tftphdr *)ackbuf; + ap_opcode = ntohs((u_short) ap->th_opcode); + ap_block = ntohs((u_short) ap->th_block); + + if (ap_opcode == ERROR) + goto abort; + + if (ap_opcode == ACK) { + if (ap_block == block) { + break; + } + /* Re-synchronize with the other side */ + (void)synchnet(peer); + /* + * RFC1129/RFC1350: We MUST NOT re-send the DATA + * packet in response to an invalid ACK. Doing so + * would cause the Sorcerer's Apprentice bug. + */ + } + + } + block++; + } while (size == segsize); + abort: + (void)fclose(file); } /* Bail out signal handler */ -void -justquit(int sig) +void justquit(int sig) { - (void)sig; /* Suppress unused warning */ - exit(0); + (void)sig; /* Suppress unused warning */ + exit(0); } - /* * Receive a file. */ -void -tftp_recvfile(struct formats *pf, struct tftphdr *oap, int oacklen) +void tftp_recvfile(struct formats *pf, struct tftphdr *oap, int oacklen) { - struct tftphdr *dp; - int n, size; - /* These are "static" to avoid longjmp funnies */ - static struct tftphdr *ap; /* ack buffer */ - static u_short block = 0; - static int acksize; - u_short dp_opcode, dp_block; - unsigned long r_timeout; + struct tftphdr *dp; + int n, size; + /* These are "static" to avoid longjmp funnies */ + static struct tftphdr *ap; /* ack buffer */ + static u_short block = 0; + static int acksize; + u_short dp_opcode, dp_block; + unsigned long r_timeout; - dp = w_init(); - do { - timeout = rexmtval; - - if (!block && oap) { - ap = (struct tftphdr *)ackbuf; - acksize = oacklen; - } else { - ap = (struct tftphdr *)ackbuf; - ap->th_opcode = htons((u_short)ACK); - ap->th_block = htons((u_short)block); - acksize = 4; - } - block++; - (void) sigsetjmp(timeoutbuf,1); - send_ack: - r_timeout = timeout; - if (send(peer, ackbuf, acksize, 0) != acksize) { - syslog(LOG_WARNING, "tftpd: write(ack): %m"); - goto abort; - } + dp = w_init(); + do { + timeout = rexmtval; + + if (!block && oap) { + ap = (struct tftphdr *)ackbuf; + acksize = oacklen; + } else { + ap = (struct tftphdr *)ackbuf; + ap->th_opcode = htons((u_short) ACK); + ap->th_block = htons((u_short) block); + acksize = 4; + } + block++; + (void)sigsetjmp(timeoutbuf, 1); + send_ack: + r_timeout = timeout; + if (send(peer, ackbuf, acksize, 0) != acksize) { + syslog(LOG_WARNING, "tftpd: write(ack): %m"); + goto abort; + } + write_behind(file, pf->f_convert); + for (;;) { + n = recv_time(peer, dp, PKTSIZE, 0, &r_timeout); + if (n < 0) { /* really? */ + syslog(LOG_WARNING, "tftpd: read: %m"); + goto abort; + } + dp_opcode = ntohs((u_short) dp->th_opcode); + dp_block = ntohs((u_short) dp->th_block); + if (dp_opcode == ERROR) + goto abort; + if (dp_opcode == DATA) { + if (dp_block == block) { + break; /* normal */ + } + /* Re-synchronize with the other side */ + (void)synchnet(peer); + if (dp_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, NULL); + else + nak(ENOSPACE, NULL); + goto abort; + } + } while (size == segsize); write_behind(file, pf->f_convert); - for ( ; ; ) { - n = recv_time(peer, dp, PKTSIZE, 0, &r_timeout); - if (n < 0) { /* really? */ - syslog(LOG_WARNING, "tftpd: read: %m"); - goto abort; - } - dp_opcode = ntohs((u_short)dp->th_opcode); - dp_block = ntohs((u_short)dp->th_block); - if (dp_opcode == ERROR) - goto abort; - if (dp_opcode == DATA) { - if (dp_block == block) { - break; /* normal */ - } - /* Re-synchronize with the other side */ - (void) synchnet(peer); - if (dp_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, NULL); - else nak(ENOSPACE, NULL); - 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); - - timeout_quit = 1; /* just quit on timeout */ - n = recv_time(peer, buf, sizeof (buf), 0, &timeout); /* normally times out and quits */ - timeout_quit = 0; + (void)fclose(file); /* close data file */ - if (n >= 4 && /* if read some data */ - dp_opcode == DATA && /* and got a data block */ - block == dp_block) { /* then my last ack was lost */ - (void) send(peer, ackbuf, 4, 0); /* resend final ack */ - } - abort: - return; + ap->th_opcode = htons((u_short) ACK); /* send the "final" ack */ + ap->th_block = htons((u_short) (block)); + (void)send(peer, ackbuf, 4, 0); + + timeout_quit = 1; /* just quit on timeout */ + n = recv_time(peer, buf, sizeof(buf), 0, &timeout); /* normally times out and quits */ + timeout_quit = 0; + + if (n >= 4 && /* if read some data */ + dp_opcode == DATA && /* and got a data block */ + block == dp_block) { /* then my last ack was lost */ + (void)send(peer, ackbuf, 4, 0); /* resend final ack */ + } + abort: + return; } -static const char * const errmsgs[] = -{ - "Undefined error code", /* 0 - EUNDEF */ - "File not found", /* 1 - ENOTFOUND */ - "Access denied", /* 2 - EACCESS */ - "Disk full or allocation exceeded", /* 3 - ENOSPACE */ - "Illegal TFTP operation", /* 4 - EBADOP */ - "Unknown transfer ID", /* 5 - EBADID */ - "File already exists", /* 6 - EEXISTS */ - "No such user", /* 7 - ENOUSER */ - "Failure to negotiate RFC2347 options" /* 8 - EOPTNEG */ +static const char *const errmsgs[] = { + "Undefined error code", /* 0 - EUNDEF */ + "File not found", /* 1 - ENOTFOUND */ + "Access denied", /* 2 - EACCESS */ + "Disk full or allocation exceeded", /* 3 - ENOSPACE */ + "Illegal TFTP operation", /* 4 - EBADOP */ + "Unknown transfer ID", /* 5 - EBADID */ + "File already exists", /* 6 - EEXISTS */ + "No such user", /* 7 - ENOUSER */ + "Failure to negotiate RFC2347 options" /* 8 - EOPTNEG */ }; + #define ERR_CNT (sizeof(errmsgs)/sizeof(const char *)) /* @@ -1391,39 +1373,38 @@ static const char * const errmsgs[] = * standard TFTP codes, or a UNIX errno * offset by 100. */ -static void -nak(int error, const char *msg) +static void nak(int error, const char *msg) { - struct tftphdr *tp; - int length; - - tp = (struct tftphdr *)buf; - tp->th_opcode = htons((u_short)ERROR); + struct tftphdr *tp; + int length; - if ( error >= 100 ) { - /* This is a Unix errno+100 */ - if ( !msg ) - msg = strerror(error - 100); - error = EUNDEF; - } else { - if ( (unsigned)error >= ERR_CNT ) - error = EUNDEF; + tp = (struct tftphdr *)buf; + tp->th_opcode = htons((u_short) ERROR); - if ( !msg ) - msg = errmsgs[error]; - } + if (error >= 100) { + /* This is a Unix errno+100 */ + if (!msg) + msg = strerror(error - 100); + error = EUNDEF; + } else { + if ((unsigned)error >= ERR_CNT) + error = EUNDEF; - tp->th_code = htons((u_short)error); + if (!msg) + msg = errmsgs[error]; + } - length = strlen(msg)+1; - memcpy(tp->th_msg, msg, length); - length += 4; /* Add space for header */ - - if ( verbosity >= 2 ) { - syslog(LOG_INFO, "sending NAK (%d, %s) to %s", - error, tp->th_msg, inet_ntoa(from.sin_addr)); - } - - if (send(peer, buf, length, 0) != length) - syslog(LOG_WARNING, "nak: %m"); + tp->th_code = htons((u_short) error); + + length = strlen(msg) + 1; + memcpy(tp->th_msg, msg, length); + length += 4; /* Add space for header */ + + if (verbosity >= 2) { + syslog(LOG_INFO, "sending NAK (%d, %s) to %s", + error, tp->th_msg, inet_ntoa(from.sin_addr)); + } + + if (send(peer, buf, length, 0) != length) + syslog(LOG_WARNING, "nak: %m"); } diff --git a/tftpd/tftpd.h b/tftpd/tftpd.h index 789ee94..4413729 100644 --- a/tftpd/tftpd.h +++ b/tftpd/tftpd.h @@ -1,6 +1,6 @@ /* $Id$ */ /* ----------------------------------------------------------------------- * - * + * * Copyright 2001 H. Peter Anvin - All Rights Reserved * * This program is free software available under the same license From cd22c6ea31e241c3d30ac94da3d56ebf0aba4744 Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Tue, 8 Jul 2008 17:24:11 -0400 Subject: [PATCH 097/184] Formatting cleanup Clean up line break --- tftpd/tftpd.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tftpd/tftpd.c b/tftpd/tftpd.c index 92b7c67..71ca60c 100644 --- a/tftpd/tftpd.c +++ b/tftpd/tftpd.c @@ -223,8 +223,8 @@ static int recv_time(int s, void *rbuf, int len, unsigned int flags, gettimeofday(&t1, NULL); - dt = (t1.tv_sec - t0.tv_sec) * 1000000 + (t1.tv_usec - - t0.tv_usec); + dt = (t1.tv_sec - t0.tv_sec) * 1000000 + + (t1.tv_usec - t0.tv_usec); *timeout_us_p = timeout_left = (dt >= timeout_us) ? 1 : (timeout_us - dt); } while (rv == -1 && err == EINTR); From 9f834757793a56a9e7078dc71b47d6a2d39dbb88 Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Tue, 8 Jul 2008 17:26:18 -0400 Subject: [PATCH 098/184] Formatting cleanup --- tftpd/tftpd.c | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/tftpd/tftpd.c b/tftpd/tftpd.c index 71ca60c..b5798a6 100644 --- a/tftpd/tftpd.c +++ b/tftpd/tftpd.c @@ -1,5 +1,3 @@ -/* $OpenBSD: tftpd.c,v 1.13 1999/06/23 17:01:36 deraadt Exp $ */ - /* * Copyright (c) 1983 Regents of the University of California. * All rights reserved. @@ -120,13 +118,12 @@ struct options { const char *o_opt; int (*o_fnc) (char *, char **); } options[] = { - { - "blksize", set_blksize}, { - "blksize2", set_blksize2}, { - "tsize", set_tsize}, { - "timeout", set_timeout}, { - "utimeout", set_utimeout}, { - NULL, NULL} + {"blksize", set_blksize}, + {"blksize2", set_blksize2}, + {"tsize", set_tsize}, + {"timeout", set_timeout}, + {"utimeout", set_utimeout}, + {NULL, NULL} }; /* Simple handler for SIGHUP */ From 50f22a7ad208a4b1a8819af7f64b97e18f1105ee Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Tue, 8 Jul 2008 17:30:27 -0400 Subject: [PATCH 099/184] Remove long since obsolete ID tags --- common/tftpsubs.c | 11 ----------- common/tftpsubs.h | 7 ------- tftp/extern.h | 7 ------- tftp/main.c | 11 ----------- tftp/tftp.c | 11 ----------- tftpd/tftpd.h | 1 - 6 files changed, 48 deletions(-) diff --git a/common/tftpsubs.c b/common/tftpsubs.c index b266efe..ef05cc4 100644 --- a/common/tftpsubs.c +++ b/common/tftpsubs.c @@ -1,8 +1,3 @@ -/* tftp-hpa: $Id$ */ - -/* $OpenBSD: tftpsubs.c,v 1.2 1996/06/26 05:40:36 deraadt Exp $ */ -/* $NetBSD: tftpsubs.c,v 1.3 1994/12/08 09:51:31 jtc Exp $ */ - /* * Copyright (c) 1983, 1993 * The Regents of the University of California. All rights reserved. @@ -38,12 +33,6 @@ #include "tftpsubs.h" -#ifndef lint -/* static char sccsid[] = "@(#)tftpsubs.c 8.1 (Berkeley) 6/6/93"; */ -/* static char rcsid[] = "$OpenBSD: tftpsubs.c,v 1.2 1996/06/26 05:40:36 deraadt Exp $"; */ -static const char *rcsid UNUSED = "tftp-hpa: $Id$"; -#endif /* not lint */ - /* Simple minded read-ahead/write-behind subroutines for tftp user and server. Written originally with multiple buffers in mind, but current implementation has two buffer logic wired in. diff --git a/common/tftpsubs.h b/common/tftpsubs.h index f1269fd..a5c88e3 100644 --- a/common/tftpsubs.h +++ b/common/tftpsubs.h @@ -1,8 +1,3 @@ -/* $Id$ */ - -/* $OpenBSD: tftpsubs.h,v 1.2 1996/06/26 05:40:37 deraadt Exp $ */ -/* $NetBSD: tftpsubs.h,v 1.2 1994/12/08 09:51:32 jtc Exp $ */ - /* * Copyright (c) 1993 * The Regents of the University of California. All rights reserved. @@ -34,8 +29,6 @@ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * - * @(#)tftpsubs.h 8.1 (Berkeley) 6/6/93 */ /* diff --git a/tftp/extern.h b/tftp/extern.h index bfe9a97..78474fc 100644 --- a/tftp/extern.h +++ b/tftp/extern.h @@ -1,8 +1,3 @@ -/* $Id$ */ - -/* $OpenBSD: extern.h,v 1.2 1996/06/26 05:40:33 deraadt Exp $ */ -/* $NetBSD: extern.h,v 1.2 1994/12/08 09:51:24 jtc Exp $ */ - /* * Copyright (c) 1993 * The Regents of the University of California. All rights reserved. @@ -34,8 +29,6 @@ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * - * @(#)extern.h 8.1 (Berkeley) 6/6/93 */ #ifndef RECVFILE_H diff --git a/tftp/main.c b/tftp/main.c index bd041a8..ec57bc2 100644 --- a/tftp/main.c +++ b/tftp/main.c @@ -1,6 +1,3 @@ -/* $OpenBSD: main.c,v 1.4 1997/01/17 07:13:30 millert Exp $ */ -/* $NetBSD: main.c,v 1.6 1995/05/21 16:54:10 mycroft Exp $ */ - /* * Copyright (c) 1983, 1993 * The Regents of the University of California. All rights reserved. @@ -36,14 +33,6 @@ #include "common/tftpsubs.h" -#ifndef lint -static const char *copyright UNUSED = "@(#) Copyright (c) 1983, 1993\n\ - The Regents of the University of California. All rights reserved.\n"; -/* static char sccsid[] = "@(#)main.c 8.1 (Berkeley) 6/6/93"; */ -/* static char rcsid[] = "$OpenBSD: main.c,v 1.4 1997/01/17 07:13:30 millert Exp $"; */ -static const char *rcsid UNUSED = "tftp-hpa $Id$"; -#endif /* not lint */ - /* Many bug fixes are from Jim Guyton */ /* diff --git a/tftp/tftp.c b/tftp/tftp.c index 41c6fbd..2d26b4b 100644 --- a/tftp/tftp.c +++ b/tftp/tftp.c @@ -1,6 +1,3 @@ -/* $OpenBSD: tftp.c,v 1.4 1997/08/06 06:43:45 deraadt Exp $ */ -/* $NetBSD: tftp.c,v 1.5 1995/04/29 05:55:25 cgd Exp $ */ - /* * Copyright (c) 1983, 1993 * The Regents of the University of California. All rights reserved. @@ -36,14 +33,6 @@ #include "common/tftpsubs.h" -#ifndef lint -/* static char sccsid[] = "@(#)tftp.c 8.1 (Berkeley) 6/6/93"; */ -/* static char rcsid[] = "$OpenBSD: tftp.c,v 1.4 1997/08/06 06:43:45 deraadt Exp $"; */ -static const char *rcsid UNUSED = "tftp-hpa $Id$"; -#endif /* not lint */ - -/* Many bug fixes are from Jim Guyton */ - /* * TFTP User Program -- Protocol Machines */ diff --git a/tftpd/tftpd.h b/tftpd/tftpd.h index 4413729..e1d8bf0 100644 --- a/tftpd/tftpd.h +++ b/tftpd/tftpd.h @@ -1,4 +1,3 @@ -/* $Id$ */ /* ----------------------------------------------------------------------- * * * Copyright 2001 H. Peter Anvin - All Rights Reserved From 3e7043c50fa6fd244ec94a37485b23eb331ede5a Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Tue, 22 Jul 2008 14:44:40 -0400 Subject: [PATCH 100/184] Define DATAROOTDIR in MCONFIG.in to shut up autoconf Shut up autoconf warning by defining DATAROOTDIR in MCONFIG.in. Signed-off-by: H. Peter Anvin --- MCONFIG.in | 3 +++ 1 file changed, 3 insertions(+) diff --git a/MCONFIG.in b/MCONFIG.in index 716bedc..ecff393 100644 --- a/MCONFIG.in +++ b/MCONFIG.in @@ -31,6 +31,9 @@ MANDIR = @mandir@ # System binaries SBINDIR = @sbindir@ +# Data root directory +DATAROOTDIR = @datarootdir@ + # Binary suffixes O = @OBJEXT@ X = @EXEEXT@ From 40e20cc4a54ebf8dd81471d24abc25113baff09f Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Tue, 22 Jul 2008 14:52:34 -0400 Subject: [PATCH 101/184] Remove workaround for autoconf 2.52 Remove obsolete workaround for autoconf 2.52 -- we don't support that version of autoconf anymore. --- aclocal.m4 | 26 ++++++-------------------- 1 file changed, 6 insertions(+), 20 deletions(-) diff --git a/aclocal.m4 b/aclocal.m4 index 7b29e86..0ea5f1d 100644 --- a/aclocal.m4 +++ b/aclocal.m4 @@ -1,6 +1,6 @@ dnl ----------------------------------------------------------------------- dnl -dnl Copyright 1999-2007 H. Peter Anvin - All Rights Reserved +dnl Copyright 1999-2008 H. Peter Anvin - All Rights Reserved dnl dnl This program is free software; you can redistribute it and/or modify dnl it under the terms of the GNU General Public License as published by @@ -70,36 +70,22 @@ dnl Look for definition of struct in_pktinfo, which at least has an dnl ipi_addr member. Some versions of glibc lack struct in_pktinfo; dnl if so we need to include the definition ourselves -- but we only dnl want to do that if absolutely necessary! -dnl -dnl We don't use AC_CHECK_MEMBER() here, since at least in autoconf 2.52 -dnl this is broken for a member of structure type. dnl ------------------------------------------------------------------------ AH_TEMPLATE([HAVE_STRUCT_IN_PKTINFO], [Define if struct in_pktinfo is defined.]) AC_DEFUN(PA_STRUCT_IN_PKTINFO, - [AC_MSG_CHECKING([for definition of struct in_pktinfo]) - AC_TRY_COMPILE( - [ + [AC_CHECK_MEMBER(struct in_pktinfo.ipi_addr, + [AC_DEFINE(HAVE_STRUCT_IN_PKTINFO)], + [], + [ #include #include #include #include #include #include - ], - [ - struct in_pktinfo pktinfo; - int foo = sizeof(struct in_pktinfo); - void *quux = (void *)(&pktinfo.ipi_addr); - ], - [ - AC_DEFINE(HAVE_STRUCT_IN_PKTINFO) - AC_MSG_RESULT(yes) - ], - [ - AC_MSG_RESULT(no) - ])]) + ])]) dnl -------------------------------------------------------------------------- dnl PA_HAVE_TCPWRAPPERS From 57ca2819802b3263da69976a4c3f1496e76f677f Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Tue, 22 Jul 2008 15:07:24 -0400 Subject: [PATCH 102/184] Remove more obsolete $Id$ tags Additional $Id$ tags from the CVS era... --- config.h | 1 - tftp/tftp.1.in | 1 - tftpd/sample.rules | 1 - tftpd/tftpd.8.in | 1 - 4 files changed, 4 deletions(-) diff --git a/config.h b/config.h index 9c973b7..ed1e39a 100644 --- a/config.h +++ b/config.h @@ -7,7 +7,6 @@ * http://www.openbsd.org/. * * ----------------------------------------------------------------------- */ -/* $Id$ */ /* * config.h diff --git a/tftp/tftp.1.in b/tftp/tftp.1.in index 41606fe..8ef1e86 100644 --- a/tftp/tftp.1.in +++ b/tftp/tftp.1.in @@ -1,5 +1,4 @@ .\" -*- nroff -*- --------------------------------------------------------- * -.\" $Id$ .\" .\" Copyright (c) 1990, 1993, 1994 .\" The Regents of the University of California. All rights reserved. diff --git a/tftpd/sample.rules b/tftpd/sample.rules index 7b59e70..55b56be 100644 --- a/tftpd/sample.rules +++ b/tftpd/sample.rules @@ -1,4 +1,3 @@ -# $Id$ # # Sample rule file for the -m (remapping option) # diff --git a/tftpd/tftpd.8.in b/tftpd/tftpd.8.in index e645d37..49cf359 100644 --- a/tftpd/tftpd.8.in +++ b/tftpd/tftpd.8.in @@ -1,5 +1,4 @@ .\" -*- nroff -*- --------------------------------------------------------- * -.\" $Id$ .\" .\" Copyright (c) 1990, 1993, 1994 .\" The Regents of the University of California. All rights reserved. From 7fe0fb941cb8ee2562cae02028df4e80f61548f4 Mon Sep 17 00:00:00 2001 From: Karsten Keil Date: Wed, 23 Jul 2008 17:05:24 +0200 Subject: [PATCH 103/184] IPv6 infrastructure support Add autoconf rules to detect IPv6 availability and some of the neeeded support functions. Add stubs for getaddrinfo and inet_ntop. You can disable IPv6 at compile time with ./configure --without-ipv6 Signed-off-by: Karsten Keil Signed-off-by: H. Peter Anvin --- aclocal.m4 | 89 ++++++++++++++++++++++++++++++++++ config.h | 46 ++++++++++++++++++ configure.in | 53 +++++++++++++++++--- lib/getaddrinfo.c | 121 ++++++++++++++++++++++++++++++++++++++++++++++ lib/inet_ntop.c | 52 ++++++++++++++++++++ 5 files changed, 355 insertions(+), 6 deletions(-) create mode 100644 lib/getaddrinfo.c create mode 100644 lib/inet_ntop.c diff --git a/aclocal.m4 b/aclocal.m4 index 0ea5f1d..c07702c 100644 --- a/aclocal.m4 +++ b/aclocal.m4 @@ -87,6 +87,71 @@ AC_DEFUN(PA_STRUCT_IN_PKTINFO, #include ])]) + +dnl ------------------------------------------------------------------------ +dnl PA_STRUCT_SOCKADDR_IN6 +dnl +dnl Look for definition of struct sockaddr_in6, which at least has an +dnl sin6_addr member +dnl +AH_TEMPLATE([HAVE_STRUCT_SOCKADDR_IN6], +[Define if struct sockaddr_in6 is defined.]) + +AC_DEFUN(PA_STRUCT_SOCKADDR_IN6, + [AC_CHECK_MEMBER(struct sockaddr_in6.sin6_addr, + [ + AC_DEFINE(HAVE_STRUCT_SOCKADDR_IN6) + HAVE_INET6=true; + ], + [ + HAVE_INET6=false; + ], + [ +#include +#include +#include +#include + ])]) + +dnl ------------------------------------------------------------------------ +dnl PA_STRUCT_ADDRINFO +dnl +dnl Look for definition of struct addrinfo, which at least has an +dnl ai_addr member +dnl +AH_TEMPLATE([HAVE_STRUCT_ADDRINFO], +[Define if struct addrinfo is defined.]) + +AC_DEFUN(PA_STRUCT_ADDRINFO, + [AC_CHECK_MEMBER(struct addrinfo.ai_addr, + [AC_DEFINE(HAVE_STRUCT_ADDRINFO)], + [], + [ +#include +#include +#include + ])]) + +dnl ------------------------------------------------------------------------ +dnl PA_STRUCT_IN6_PKTINFO +dnl +dnl Look for definition of struct in6_pktinfo, which at least has an +dnl ipi6_addr member +dnl +AH_TEMPLATE([HAVE_STRUCT_IN6_PKTINFO], +[Define if struct in6_pktinfo is defined.]) + +AC_DEFUN(PA_STRUCT_IN6_PKTINFO, + [AC_CHECK_MEMBER(struct in6_pktinfo.ipi6_addr, + [AC_DEFINE(HAVE_STRUCT_IN6_PKTINFO)], + [], + [ +#include +#include +#include +#include + ])]) + dnl -------------------------------------------------------------------------- dnl PA_HAVE_TCPWRAPPERS dnl @@ -189,3 +254,27 @@ int main() [ AC_MSG_RESULT(no) ])]) + +dnl -------------------------------------------------------------------------- +dnl PA_SEARCH_LIBS_AND_ADD +dnl +dnl PA_SEARCH_LIBS_AND_ADD(function, libraries [,function to add]) +dnl -------------------------------------------------------------------------- + +AC_DEFUN(PA_SEARCH_LIBS_AND_ADD, + [ + AH_TEMPLATE(AS_TR_CPP(HAVE_$1), [Define if $1 function was found]) + AC_SEARCH_LIBS($1, $2, + [ + AC_DEFINE_UNQUOTED(AS_TR_CPP(HAVE_$1)) + pa_add_$1=false; + ], + [ + XTRA=true; + if test $# -eq 3; then + AC_LIBOBJ($3) + else + AC_LIBOBJ($1) + fi + pa_add_$1=true; + ])]) diff --git a/config.h b/config.h index ed1e39a..38be517 100644 --- a/config.h +++ b/config.h @@ -106,6 +106,9 @@ #endif #endif #endif +#ifdef HAVE_NETDB_H +#include +#endif #ifdef HAVE_GETOPT_H #include @@ -301,6 +304,49 @@ int dup2(int, int); int daemon(int, int); #endif +#ifndef HAVE_GETADDRINFO +#ifndef HAVE_STRUCT_ADDRINFO +struct addrinfo { + int ai_flags; + int ai_family; + int ai_socktype; + int ai_protocol; + size_t ai_addrlen; + struct sockaddr *ai_addr; + char *ai_canonname; + struct addrinfo *ai_next; +}; +#endif +int getaddrinfo(const char *, const char *, const struct addrinfo *, + struct addrinfo **); +void freeaddrinfo(struct addrinfo *); +const char *gai_strerror(int); + +#ifndef AI_CANONNAME +#define AI_CANONNAME 0x0002 /* Request for canonical name. */ +#endif + +#ifndef EAI_NONAME +#define EAI_NONAME -2 /* NAME or SERVICE is unknown. */ +#endif +#ifndef EAI_ADDRFAMILY +#define EAI_ADDRFAMILY -9 /* Address family for NAME not supported. */ +#endif +#ifndef EAI_MEMORY +#define EAI_MEMORY -10 /* Memory allocation failure. */ +#endif +#ifndef EAI_SYSTEM +#define EAI_SYSTEM -11 /* System error returned in rrno'. */ +#endif +#endif + +#ifndef HAVE_INET_NTOP +const char *inet_ntop(int, const void *, char *, socklen_t); +#ifndef INET6_ADDRSTRLEN +#define INET6_ADDRSTRLEN 48 +#endif +#endif + /* tftp-hpa version and configuration strings */ #include "version.h" diff --git a/configure.in b/configure.in index ddd11da..91fd889 100644 --- a/configure.in +++ b/configure.in @@ -52,6 +52,7 @@ AC_CHECK_HEADERS(sys/stat.h) AC_CHECK_HEADERS(sys/time.h) AC_CHECK_HEADERS(sys/types.h) AC_CHECK_HEADERS(arpa/inet.h) +AC_CHECK_HEADERS(netdb.h) AC_HEADER_TIME dnl This is needed on some versions of FreeBSD... AC_CHECK_HEADERS(machine/param.h) @@ -118,9 +119,6 @@ AC_CHECK_TYPES(socklen_t,,, ]) AC_SEARCH_LIBS(socket, [socket ws2_32 wsock32], , [AC_MSG_ERROR(socket library not found)]) -AC_SEARCH_LIBS(gethostbyname, [nsl resolv], , [AC_MSG_ERROR(gethostbyname not found)]) -AC_SEARCH_LIBS(inet_aton, [nsl resolv], , [AC_MSG_ERROR(inet_aton not found)]) -AC_SEARCH_LIBS(herror, [nsl resolv], , [AC_MSG_ERROR(herror not found)]) AC_CHECK_FUNCS(fcntl) AC_CHECK_FUNCS(setsid) @@ -137,6 +135,7 @@ AC_CHECK_FUNCS(strtoull) PA_MSGHDR_MSG_CONTROL PA_STRUCT_IN_PKTINFO +PA_STRUCT_ADDRINFO PA_HEADER_DEFINES(fcntl.h, int, O_NONBLOCK) PA_HEADER_DEFINES(fcntl.h, int, O_BINARY) @@ -153,9 +152,30 @@ SRCROOT=`cd $srcdir && pwd` OBJROOT=`pwd` XTRA=false -AC_SEARCH_LIBS(xmalloc, iberty, , [XTRA=true; AC_LIBOBJ(xmalloc)]) -AC_SEARCH_LIBS(xstrdup, iberty, , [XTRA=true; AC_LIBOBJ(xstrdup)]) -AC_SEARCH_LIBS(bsd_signal, bsd, , [XTRA=true; AC_LIBOBJ(bsdsignal)]) +PA_SEARCH_LIBS_AND_ADD(xmalloc, iberty) +PA_SEARCH_LIBS_AND_ADD(xstrdup, iberty) +PA_SEARCH_LIBS_AND_ADD(bsd_signal, bsd, bsdsignal) +PA_SEARCH_LIBS_AND_ADD(getaddrinfo, [nsl resolv]) +if $pa_add_getaddrinfo +then + AC_SEARCH_LIBS(gethostbyname, [nsl resolv], + [AC_SEARCH_LIBS(herror, [nsl resolv], , + [AC_MSG_ERROR(herror not found)])], + [AC_MSG_ERROR(gethostbyname not found)]) +else + AC_SEARCH_LIBS(freeaddrinfo, [nsl resolv], , + [AC_MSG_ERROR(getaddrinfo but not freeaddrinfo found)]) + AC_SEARCH_LIBS(gai_strerror, [nsl resolv], , + [AC_MSG_ERROR(getaddrinfo but not gai_strerror found)]) +fi + +PA_SEARCH_LIBS_AND_ADD(inet_ntop, [nsl resolv]) +if $pa_add_inet_ntop +then + AC_SEARCH_LIBS(inet_ntoa, [nsl resolv], , + [AC_MSG_ERROR(inet_ntoa not found)]) +fi + AC_CHECK_FUNCS(daemon, , [XTRA=true; AC_LIBOBJ(daemon)]) AC_CHECK_FUNCS(dup2, , [XTRA=true; AC_LIBOBJ(dup2)]) if $XTRA @@ -226,6 +246,27 @@ PA_WITH_BOOL(readline, 1, TFTP_LIBS="$LIBS $XTRALIBS" LIBS="$common_libs" +dnl +dnl Check for IPV6 and disable-ipv6 +dnl +PA_STRUCT_SOCKADDR_IN6 +AC_MSG_CHECKING([for IPv6 support]) +PA_WITH_BOOL(ipv6, 1, +[ --without-ipv6 disable the support for IPv6], +[ + if $HAVE_INET6 + then + AC_MSG_RESULT(yes) + AC_DEFINE(HAVE_IPV6, 1, [Define if IPv6 support is enabled.]) + PA_STRUCT_IN6_PKTINFO + else + AC_MSG_RESULT(no) + AC_MSG_WARN([*** we do not have required IPv6 structs - IPv6 will be disabled]) + fi +], +[AC_MSG_RESULT(disabled)]) + + AC_SUBST(SRCROOT) AC_SUBST(OBJROOT) diff --git a/lib/getaddrinfo.c b/lib/getaddrinfo.c new file mode 100644 index 0000000..ef7c9ae --- /dev/null +++ b/lib/getaddrinfo.c @@ -0,0 +1,121 @@ +/* + * getaddrinfo.c + * + * Simple version of getaddrinfo() + * + */ + +#include "config.h" + +extern int errno; +extern int h_errno; + +void freeaddrinfo(struct addrinfo *res) +{ + if (!res) + return; + if (res->ai_next) + freeaddrinfo(res->ai_next); + if (res->ai_addr) + free(res->ai_addr); + if (res->ai_canonname) + free(res->ai_canonname); + free(res); +} + +int getaddrinfo(const char *node, const char *service, const struct addrinfo *hints, + struct addrinfo **res) +{ + struct hostent *host; + struct sockaddr *sa; + int err, size = 0; + + if ((!node) || (!res)) { + errno = EINVAL; + return EAI_SYSTEM; + } + *res = NULL; + /* we do not support service in this version */ + if (service) { + errno = EINVAL; + return EAI_SYSTEM; + } + host = gethostbyname(node); + if (!host) + return EAI_NONAME; + if (hints) { + if (hints->ai_family != AF_UNSPEC) { + if (hints->ai_family != host->h_addrtype) + return EAI_ADDRFAMILY; + } + } + *res = malloc(sizeof(struct addrinfo)); + if (!*res) { + return EAI_MEMORY; + } + memset(*res, 0, sizeof(struct addrinfo)); + (*res)->ai_family = host->h_addrtype; + if (host->h_length) { + if (host->h_addrtype == AF_INET) + size = sizeof(struct sockaddr_in); +#ifdef HAVE_IPV6 + else if (host->h_addrtype == AF_INET6) + size = sizeof(struct sockaddr_in6); +#endif + else { + free(*res); + *res = NULL; + return EAI_ADDRFAMILY; + } + sa = malloc(size); + if (!sa) { + free(*res); + *res = NULL; + return EAI_MEMORY; + } + memset(sa, 0, size); + (*res)->ai_addr = sa; + (*res)->ai_addrlen = size; + sa->sa_family = host->h_addrtype; + if (host->h_addrtype == AF_INET) + memcpy(&((struct sockaddr_in *)sa)->sin_addr, host->h_addr, host->h_length); +#ifdef HAVE_IPV6 + else + memcpy(&((struct sockaddr_in6 *)sa)->sin6_addr, host->h_addr, host->h_length); +#endif + } + if (host->h_name) + (*res)->ai_canonname = strdup(host->h_name); + + /* we only handle the first address entry and do not build a list now */ + return 0; +} + + + +const char *gai_strerror(int errcode) +{ + const char *s = NULL; + + switch(errcode) { + case 0: + s = "no error"; + break; + case EAI_MEMORY: + s = "no memory"; + break; + case EAI_SYSTEM: + s = strerror(errno); + break; + case EAI_NONAME: + s = hstrerror(h_errno); + break; + case EAI_ADDRFAMILY: + s = "address does not match address family"; + break; + default: + s = "unknown error code"; + break; + } + return s; +} diff --git a/lib/inet_ntop.c b/lib/inet_ntop.c new file mode 100644 index 0000000..fe8e560 --- /dev/null +++ b/lib/inet_ntop.c @@ -0,0 +1,52 @@ +/* + * inet_ntop.c + * + * Simple version of inet_ntop() + * + */ + +#include "config.h" + +extern int errno; + +const char *inet_ntop(int af, const void *src, + char *dst, socklen_t cnt) +{ + char *p; + + switch(af) { + case AF_INET: + p = inet_ntoa(*((struct in_addr *)src)); + if (p) { + if (cnt <= strlen(p)) { + errno = ENOSPC; + dst = NULL; + } else + strcpy(dst, p); + } else + dst = NULL; + break; +#ifdef HAVE_IPV6 + case AF_INET6: + if (cnt < 40) { + errno = ENOSPC; + dst = NULL; + } else { + struct in6_addr *a = src; + int i; + + p = (char *)dst; + /* we do not compress :0: to :: */ + for (i = 0; i < 8; i++) + p += sprintf(p, "%x:", ntohs(a->s6_addr16[i])); + p--; + *p = 0; + } + break; +#endif + default: + errno = EAFNOSUPPORT; + dst = NULL; + } + return dst; +} From 28f22b6591c3bca4d6ead5f943107ddfe0d6705c Mon Sep 17 00:00:00 2001 From: Karsten Keil Date: Wed, 23 Jul 2008 18:32:21 +0200 Subject: [PATCH 104/184] Add support for IPv6 in the server and client. Add support for IPv6 in the server and client. You can force the use of IPv4 or IPv6 only with new -4 and -6 commandline options, if IPv6 support was compiled in. Signed-off-by: Karsten Keil Signed-off-by: H. Peter Anvin --- common/tftpsubs.c | 44 +++++- common/tftpsubs.h | 54 +++++++- tftp/main.c | 135 +++++++++++------- tftp/tftp.1.in | 7 +- tftp/tftp.c | 28 ++-- tftpd/recvfrom.c | 140 +++++++++++++------ tftpd/recvfrom.h | 4 +- tftpd/tftpd.8.in | 8 +- tftpd/tftpd.c | 339 ++++++++++++++++++++++++++++++++++++---------- 9 files changed, 574 insertions(+), 185 deletions(-) diff --git a/common/tftpsubs.c b/common/tftpsubs.c index ef05cc4..45f4907 100644 --- a/common/tftpsubs.c +++ b/common/tftpsubs.c @@ -237,7 +237,7 @@ int synchnet(int f) { /* socket to flush */ int pktcount = 0; char rbuf[PKTSIZE]; - struct sockaddr_in from; + union sock_addr from; socklen_t fromlen; fd_set socketset; struct timeval notime; @@ -253,15 +253,15 @@ int synchnet(int f) /* Otherwise drain the packet */ pktcount++; - fromlen = sizeof from; + fromlen = sizeof(from); (void)recvfrom(f, rbuf, sizeof(rbuf), 0, - (struct sockaddr *)&from, &fromlen); + &from.sa, &fromlen); } return pktcount; /* Return packets drained */ } -int pick_port_bind(int sockfd, struct sockaddr_in *myaddr, +int pick_port_bind(int sockfd, union sock_addr *myaddr, unsigned int port_range_from, unsigned int port_range_to) { @@ -279,9 +279,8 @@ int pick_port_bind(int sockfd, struct sockaddr_in *myaddr, port = firstport; do { - myaddr->sin_port = htons(port); - - if (bind(sockfd, (struct sockaddr *)myaddr, sizeof *myaddr) < 0) { + sa_set_port(myaddr, htons(port)); + if (bind(sockfd, &myaddr->sa, SOCKLEN(myaddr)) < 0) { /* Some versions of Linux return EINVAL instead of EADDRINUSE */ if (!(port_range && (errno == EINVAL || errno == EADDRINUSE))) return -1; @@ -299,3 +298,34 @@ int pick_port_bind(int sockfd, struct sockaddr_in *myaddr, return -1; } + +int +set_sock_addr(char *host,union sock_addr *s, char **name) +{ + struct addrinfo *addrResult; + struct addrinfo hints; + int err; + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = s->sa.sa_family; + hints.ai_flags = AI_CANONNAME; + err = getaddrinfo(host, NULL, &hints, &addrResult); + if (err) { + printf("Error : %s\n", gai_strerror(err)); + printf("%s: unknown host\n", host); + return err; + } + if (addrResult == NULL) { + printf("%s: unknown host\n", host); + return EAI_NONAME; + } + memcpy(s, addrResult->ai_addr, addrResult->ai_addrlen); + if (name) { + if (addrResult->ai_canonname) + *name = xstrdup(addrResult->ai_canonname); + else + *name = xstrdup(host); + } + freeaddrinfo(addrResult); + return 0; +} diff --git a/common/tftpsubs.h b/common/tftpsubs.h index a5c88e3..20cf47e 100644 --- a/common/tftpsubs.h +++ b/common/tftpsubs.h @@ -40,6 +40,58 @@ #include "config.h" +union sock_addr { + struct sockaddr sa; + struct sockaddr_in si; +#ifdef HAVE_IPV6 + struct sockaddr_in6 s6; +#endif +}; + +#define SOCKLEN(sock) \ + (((union sock_addr*)sock)->sa.sa_family == AF_INET ? \ + (sizeof(struct sockaddr_in)) : \ + (sizeof(union sock_addr))) + +#ifdef HAVE_IPV6 +#define SOCKPORT(sock) \ + (((union sock_addr*)sock)->sa.sa_family == AF_INET ? \ + ((union sock_addr*)sock)->si.sin_port : \ + ((union sock_addr*)sock)->s6.sin6_port) +#else +#define SOCKPORT(sock) \ + (((union sock_addr*)sock)->si.sin_port) +#endif + +#ifdef HAVE_IPV6 +#define SOCKADDR_P(sock) \ + (((union sock_addr*)sock)->sa.sa_family == AF_INET ? \ + (void *)&((union sock_addr*)sock)->si.sin_addr : \ + (void *)&((union sock_addr*)sock)->s6.sin6_addr) +#else +#define SOCKADDR_P(sock) \ + (void *)&((union sock_addr*)sock)->si.sin_addr +#endif + +static inline int sa_set_port(union sock_addr *s, u_short port) +{ + switch (s->sa.sa_family) { + case AF_INET: + s->si.sin_port = port; + break; +#ifdef HAVE_IPV6 + case AF_INET6: + s->s6.sin6_port = port; + break; +#endif + default: + return -1; + } + return 0; +} + +int set_sock_addr(char *, union sock_addr *, char **); + struct tftphdr; struct tftphdr *r_init(void); @@ -55,7 +107,7 @@ int writeit(FILE *, struct tftphdr **, int, int); extern int segsize; #define MAX_SEGSIZE 65464 -int pick_port_bind(int sockfd, struct sockaddr_in *myaddr, +int pick_port_bind(int sockfd, union sock_addr *myaddr, unsigned int from, unsigned int to); #endif diff --git a/tftp/main.c b/tftp/main.c index ec57bc2..6176b37 100644 --- a/tftp/main.c +++ b/tftp/main.c @@ -40,7 +40,6 @@ */ #include #include -#include #ifdef WITH_READLINE #include #ifdef HAVE_READLINE_HISTORY_H @@ -72,8 +71,16 @@ static const struct modes modes[] = { #define MODE_NETASCII (&modes[0]) #define MODE_DEFAULT MODE_NETASCII -struct sockaddr_in peeraddr; -int f; +#ifdef HAVE_IPV6 +int ai_fam = AF_UNSPEC; +int ai_fam_sock = AF_UNSPEC; +#else +int ai_fam = AF_INET; +int ai_fam_sock = AF_INET; +#endif + +union sock_addr peeraddr; +int f = -1; u_short port; int trace; int verbose; @@ -184,14 +191,18 @@ const char *program; static inline void usage(int errcode) { fprintf(stderr, +#ifdef HAVE_IPV6 + "Usage: %s [-4][-6][-v][-l][-m mode] [host [port]] [-c command]\n", +#else "Usage: %s [-v][-l][-m mode] [host [port]] [-c command]\n", +#endif program); exit(errcode); } int main(int argc, char *argv[]) { - struct sockaddr_in s_in; + union sock_addr sa; int arg; static int pargc, peerargc; static int iscmd = 0; @@ -210,6 +221,14 @@ int main(int argc, char *argv[]) if (argv[arg][0] == '-') { for (optx = &argv[arg][1]; *optx; optx++) { switch (*optx) { +#ifdef HAVE_IPV6 + case '4': + ai_fam = AF_INET; + break; + case '6': + ai_fam = AF_INET6; + break; +#endif case 'v': verbose = 1; break; @@ -268,6 +287,8 @@ int main(int argc, char *argv[]) } } + ai_fam_sock = ai_fam; + pargv = argv + arg; pargc = argc - arg; @@ -283,18 +304,7 @@ int main(int argc, char *argv[]) sp->s_port = htons(IPPORT_TFTP); sp->s_proto = (char *)"udp"; } - port = sp->s_port; /* Default port */ - f = socket(AF_INET, SOCK_DGRAM, 0); - if (f < 0) { - perror("tftp: socket"); - exit(EX_OSERR); - } - bzero((char *)&s_in, sizeof(s_in)); - s_in.sin_family = AF_INET; - if (pick_port_bind(f, &s_in, portrange_from, portrange_to)) { - perror("tftp: bind"); - exit(EX_OSERR); - } + bsd_signal(SIGINT, intr); if (peerargc) { @@ -304,6 +314,21 @@ int main(int argc, char *argv[]) setpeer(peerargc, peerargv); } + if (ai_fam_sock == AF_UNSPEC) + ai_fam_sock = AF_INET; + + f = socket(ai_fam_sock, SOCK_DGRAM, 0); + if (f < 0) { + perror("tftp: socket"); + exit(EX_OSERR); + } + bzero(&sa, sizeof(sa)); + sa.sa.sa_family = ai_fam_sock; + if (pick_port_bind(f, &sa, portrange_from, portrange_to)) { + perror("tftp: bind"); + exit(EX_OSERR); + } + if (iscmd && pargc) { /* -c specified; execute command and exit */ struct cmd *c; @@ -375,7 +400,7 @@ static void getmoreargs(const char *partial, const char *mprompt) void setpeer(int argc, char *argv[]) { - struct hostent *host; + int err; if (argc < 2) { getmoreargs("connect ", "(to) "); @@ -388,16 +413,34 @@ void setpeer(int argc, char *argv[]) return; } - host = gethostbyname(argv[1]); - if (host == 0) { + peeraddr.sa.sa_family = ai_fam; + err = set_sock_addr(argv[1], &peeraddr, &hostname); + if (err) { connected = 0; - printf("%s: unknown host\n", argv[1]); return; } - peeraddr.sin_family = host->h_addrtype; - bcopy(host->h_addr, &peeraddr.sin_addr, host->h_length); - hostname = xstrdup(host->h_name); + ai_fam = peeraddr.sa.sa_family; + if (f == -1) { /* socket not open */ + ai_fam_sock = ai_fam; + } else { /* socket was already open */ + if (ai_fam_sock != ai_fam) { /* need reopen socken for new family */ + union sock_addr sa; + close(f); + ai_fam_sock = ai_fam; + f = socket(ai_fam_sock, SOCK_DGRAM, 0); + if (f < 0) { + perror("tftp: socket"); + exit(EX_OSERR); + } + bzero((char *)&sa, sizeof (sa)); + sa.sa.sa_family = ai_fam_sock; + if (pick_port_bind(f, &sa, portrange_from, portrange_to)) { + perror("tftp: bind"); + exit(EX_OSERR); + } + } + } port = sp->s_port; if (argc == 3) { struct servent *usp; @@ -418,9 +461,13 @@ void setpeer(int argc, char *argv[]) } if (verbose) { + char tmp[INET6_ADDRSTRLEN], *tp; + tp = (char *)inet_ntop(peeraddr.sa.sa_family, SOCKADDR_P(&peeraddr), + tmp, INET6_ADDRSTRLEN); + if (!tp) + tp = (char *)"???"; printf("Connected to %s (%s), port %u\n", - hostname, inet_ntoa(peeraddr.sin_addr), - (unsigned int)ntohs(port)); + hostname, tp, (unsigned int)ntohs(port)); } connected = 1; } @@ -484,7 +531,7 @@ static void settftpmode(const struct modes *newmode) void put(int argc, char *argv[]) { int fd; - int n; + int n, err; char *cp, *targ; if (argc < 2) { @@ -499,8 +546,6 @@ void put(int argc, char *argv[]) } targ = argv[argc - 1]; if (!literal && strchr(argv[argc - 1], ':')) { - struct hostent *hp; - for (n = 1; n < argc - 1; n++) if (strchr(argv[n], ':')) { putusage(argv[0]); @@ -509,16 +554,14 @@ void put(int argc, char *argv[]) cp = argv[argc - 1]; targ = strchr(cp, ':'); *targ++ = 0; - hp = gethostbyname(cp); - if (hp == NULL) { - fprintf(stderr, "tftp: %s: ", cp); - herror((char *)NULL); + peeraddr.sa.sa_family = ai_fam; + err = set_sock_addr(cp, &peeraddr,&hostname); + if (err) { + connected = 0; return; } - bcopy(hp->h_addr, &peeraddr.sin_addr, hp->h_length); - peeraddr.sin_family = hp->h_addrtype; + ai_fam = peeraddr.sa.sa_family; connected = 1; - hostname = xstrdup(hp->h_name); } if (!connected) { printf("No target machine specified.\n"); @@ -535,7 +578,7 @@ void put(int argc, char *argv[]) if (verbose) printf("putting %s to %s:%s [%s]\n", cp, hostname, targ, mode->m_mode); - peeraddr.sin_port = port; + sa_set_port(&peeraddr, port); tftp_sendfile(fd, targ, mode->m_mode); return; } @@ -554,7 +597,7 @@ void put(int argc, char *argv[]) if (verbose) printf("putting %s to %s:%s [%s]\n", argv[n], hostname, targ, mode->m_mode); - peeraddr.sin_port = port; + sa_set_port(&peeraddr, port); tftp_sendfile(fd, targ, mode->m_mode); } } @@ -597,19 +640,15 @@ void get(int argc, char *argv[]) if (literal || src == NULL) src = argv[n]; else { - struct hostent *hp; + int err; *src++ = 0; - hp = gethostbyname(argv[n]); - if (hp == NULL) { - fprintf(stderr, "tftp: %s: ", argv[n]); - herror((char *)NULL); + peeraddr.sa.sa_family = ai_fam; + err = set_sock_addr(argv[n], &peeraddr, &hostname); + if (err) continue; - } - bcopy(hp->h_addr, (caddr_t) & peeraddr.sin_addr, hp->h_length); - peeraddr.sin_family = hp->h_addrtype; + ai_fam = peeraddr.sa.sa_family; connected = 1; - hostname = xstrdup(hp->h_name); } if (argc < 4) { cp = argc == 3 ? argv[2] : tail(src); @@ -623,7 +662,7 @@ void get(int argc, char *argv[]) if (verbose) printf("getting from %s:%s to %s [%s]\n", hostname, src, cp, mode->m_mode); - peeraddr.sin_port = port; + sa_set_port(&peeraddr, port); tftp_recvfile(fd, src, mode->m_mode); break; } @@ -638,7 +677,7 @@ void get(int argc, char *argv[]) if (verbose) printf("getting from %s:%s to %s [%s]\n", hostname, src, cp, mode->m_mode); - peeraddr.sin_port = port; + sa_set_port(&peeraddr, port); tftp_recvfile(fd, src, mode->m_mode); } } diff --git a/tftp/tftp.1.in b/tftp/tftp.1.in index 8ef1e86..8bbb796 100644 --- a/tftp/tftp.1.in +++ b/tftp/tftp.1.in @@ -42,7 +42,7 @@ .br .SH DESCRIPTION .B tftp -is a client for the IPv4 Trivial file Transfer Protocol, which can be +is a client for the Trivial file Transfer Protocol, which can be used to transfer files to and from remote machines, including some very minimalistic, usually embedded, systems. The remote .I host @@ -55,6 +55,11 @@ as the default host for future transfers (see the command below.) .SH OPTIONS .TP +.B \-4 +Connect with IPv4 only, if IPv6 support was compiled in. +.TP +.B \-6 +.TP \fB\-c\fP \fIcommand\fP Execute \fIcommand\fP as if it had been entered on the tftp prompt. Must be specified last on the command line. diff --git a/tftp/tftp.c b/tftp/tftp.c index 2d26b4b..d15da22 100644 --- a/tftp/tftp.c +++ b/tftp/tftp.c @@ -38,8 +38,8 @@ */ #include "extern.h" -extern struct sockaddr_in peeraddr; /* filled in by main */ -extern int f; /* the opened socket */ +extern union sock_addr peeraddr; /* filled in by main */ +extern int f; /* the opened socket */ extern int trace; extern int verbose; extern int rexmtval; @@ -71,7 +71,7 @@ void tftp_sendfile(int fd, const char *name, const char *mode) volatile u_short block; volatile int size, convert; volatile off_t amount; - struct sockaddr_in from; + union sock_addr from; socklen_t fromlen; FILE *file; u_short ap_opcode, ap_block; @@ -105,7 +105,7 @@ void tftp_sendfile(int fd, const char *name, const char *mode) if (trace) tpacket("sent", dp, size + 4); n = sendto(f, dp, size + 4, 0, - (struct sockaddr *)&peeraddr, sizeof(peeraddr)); + &peeraddr.sa, SOCKLEN(&peeraddr)); if (n != size + 4) { perror("tftp: sendto"); goto abort; @@ -116,14 +116,14 @@ void tftp_sendfile(int fd, const char *name, const char *mode) do { fromlen = sizeof(from); n = recvfrom(f, ackbuf, sizeof(ackbuf), 0, - (struct sockaddr *)&from, &fromlen); + &from.sa, &fromlen); } while (n <= 0); alarm(0); if (n < 0) { perror("tftp: recvfrom"); goto abort; } - peeraddr.sin_port = from.sin_port; /* added */ + sa_set_port(&peeraddr, SOCKPORT(&from)); /* added */ if (trace) tpacket("received", ap, n); /* should verify packet came from server */ @@ -176,7 +176,7 @@ void tftp_recvfile(int fd, const char *name, const char *mode) volatile u_short block; volatile int size, firsttrip; volatile unsigned long amount; - struct sockaddr_in from; + union sock_addr from; socklen_t fromlen; FILE *file; volatile int convert; /* true if converting crlf -> lf */ @@ -207,8 +207,8 @@ void tftp_recvfile(int fd, const char *name, const char *mode) send_ack: if (trace) tpacket("sent", ap, size); - if (sendto(f, ackbuf, size, 0, (struct sockaddr *)&peeraddr, - sizeof(peeraddr)) != size) { + if (sendto(f, ackbuf, size, 0, &peeraddr.sa, + SOCKLEN(&peeraddr)) != size) { alarm(0); perror("tftp: sendto"); goto abort; @@ -219,14 +219,14 @@ void tftp_recvfile(int fd, const char *name, const char *mode) do { fromlen = sizeof(from); n = recvfrom(f, dp, PKTSIZE, 0, - (struct sockaddr *)&from, &fromlen); + &from.sa, &fromlen); } while (n <= 0); alarm(0); if (n < 0) { perror("tftp: recvfrom"); goto abort; } - peeraddr.sin_port = from.sin_port; /* added */ + sa_set_port(&peeraddr, SOCKPORT(&from)); /* added */ if (trace) tpacket("received", dp, n); /* should verify client address */ @@ -266,7 +266,7 @@ void tftp_recvfile(int fd, const char *name, const char *mode) ap->th_opcode = htons((u_short) ACK); /* has seen err msg */ ap->th_block = htons((u_short) block); (void)sendto(f, ackbuf, 4, 0, (struct sockaddr *)&peeraddr, - sizeof(peeraddr)); + SOCKLEN(&peeraddr)); write_behind(file, convert); /* flush last buffer */ fclose(file); stopclock(); @@ -341,8 +341,8 @@ static void nak(int error, const char *msg) if (trace) tpacket("sent", tp, length); - if (sendto(f, ackbuf, length, 0, (struct sockaddr *)&peeraddr, - sizeof(peeraddr)) != length) + if (sendto(f, ackbuf, length, 0, &peeraddr.sa, + SOCKLEN(&peeraddr)) != length) perror("nak"); } diff --git a/tftpd/recvfrom.c b/tftpd/recvfrom.c index fee6d03..389ba82 100644 --- a/tftpd/recvfrom.c +++ b/tftpd/recvfrom.c @@ -18,8 +18,8 @@ */ #include "config.h" /* Must be included first! */ -#include "recvfrom.h" #include "common/tftpsubs.h" +#include "recvfrom.h" #ifdef HAVE_MACHINE_PARAM_H #include /* Needed on some versions of FreeBSD */ #endif @@ -55,31 +55,47 @@ struct in_pktinfo { * end up having the same local and remote address when trying to * bind to it. */ -static int address_is_local(const struct sockaddr_in *addr) +static int address_is_local(const union sock_addr *addr) { - struct sockaddr_in sin; + union sock_addr sa; int sockfd = -1; int e; int rv = 0; socklen_t addrlen; /* Multicast or universal broadcast address? */ - if (ntohl(addr->sin_addr.s_addr) >= (224UL << 24)) + if (addr->sa.sa_family == AF_INET) { + if (ntohl(addr->si.sin_addr.s_addr) >= (224UL << 24)) + return 0; + } +#ifdef HAVE_IPV6 + else if (addr->sa.sa_family == AF_INET6) { + if (IN6_IS_ADDR_MULTICAST(&addr->s6.sin6_addr)) + return 0; + } +#endif + else return 0; - sockfd = socket(PF_INET, SOCK_DGRAM, 0); + sockfd = socket(addr->sa.sa_family, SOCK_DGRAM, 0); if (sockfd < 0) goto err; - if (connect(sockfd, (const struct sockaddr *)addr, sizeof *addr)) + if (connect(sockfd, &addr->sa, SOCKLEN(addr))) goto err; - addrlen = sizeof sin; - if (getsockname(sockfd, (struct sockaddr *)&sin, &addrlen)) + addrlen = SOCKLEN(addr); + if (getsockname(sockfd, (struct sockaddr *)&sa, &addrlen)) goto err; - rv = sin.sin_addr.s_addr == addr->sin_addr.s_addr; - + if (addr->sa.sa_family == AF_INET) + rv = sa.si.sin_addr.s_addr == addr->si.sin_addr.s_addr; +#ifdef HAVE_IPV6 + else if (addr->sa.sa_family == AF_INET6) + rv = IN6_ARE_ADDR_EQUAL(&sa.s6.sin6_addr, &addr->s6.sin6_addr); +#endif + else + rv = 0; err: e = errno; @@ -93,7 +109,7 @@ static int address_is_local(const struct sockaddr_in *addr) int myrecvfrom(int s, void *buf, int len, unsigned int flags, struct sockaddr *from, socklen_t * fromlen, - struct sockaddr_in *myaddr) + union sock_addr *myaddr) { struct msghdr msg; struct iovec iov; @@ -106,24 +122,42 @@ myrecvfrom(int s, void *buf, int len, unsigned int flags, CMSG_SPACE(sizeof(struct in_pktinfo))]; #else char control[CMSG_SPACE(sizeof(struct in_addr))]; +#endif +#ifdef HAVE_IPV6 +#ifdef HAVE_STRUCT_IN6_PKTINFO + char control6[CMSG_SPACE(sizeof(struct in6_addr)) + + CMSG_SPACE(sizeof(struct in6_pktinfo))]; +#else + char control6[CMSG_SPACE(sizeof(struct in6_addr))]; +#endif #endif } control_un; int on = 1; #ifdef IP_PKTINFO struct in_pktinfo pktinfo; #endif +#ifdef HAVE_STRUCT_IN6_PKTINFO + struct in6_pktinfo pktinfo6; +#endif /* Try to enable getting the return address */ #ifdef IP_RECVDSTADDR - setsockopt(s, IPPROTO_IP, IP_RECVDSTADDR, &on, sizeof(on)); + if (from->sa_family == AF_INET) + setsockopt(s, IPPROTO_IP, IP_RECVDSTADDR, &on, sizeof(on)); #endif #ifdef IP_PKTINFO - setsockopt(s, IPPROTO_IP, IP_PKTINFO, &on, sizeof(on)); + if (from->sa_family == AF_INET) + setsockopt(s, IPPROTO_IP, IP_PKTINFO, &on, sizeof(on)); +#endif +#ifdef HAVE_IPV6 +#ifdef IPV6_RECVPKTINFO + if (from->sa_family == AF_INET6) + setsockopt(s, IPPROTO_IPV6, IPV6_RECVPKTINFO, &on, sizeof(on)); +#endif #endif - bzero(&msg, sizeof msg); /* Clear possible system-dependent fields */ msg.msg_control = control_un.control; - msg.msg_controllen = sizeof(control_un.control); + msg.msg_controllen = sizeof(control_un); msg.msg_flags = 0; msg.msg_name = from; @@ -139,8 +173,8 @@ myrecvfrom(int s, void *buf, int len, unsigned int flags, *fromlen = msg.msg_namelen; if (myaddr) { - bzero(myaddr, sizeof(struct sockaddr_in)); - myaddr->sin_family = AF_INET; + bzero(myaddr, sizeof(*myaddr)); + myaddr->sa.sa_family = from->sa_family; if (msg.msg_controllen < sizeof(struct cmsghdr) || (msg.msg_flags & MSG_CTRUNC)) @@ -149,31 +183,61 @@ myrecvfrom(int s, void *buf, int len, unsigned int flags, for (cmptr = CMSG_FIRSTHDR(&msg); cmptr != NULL; cmptr = CMSG_NXTHDR(&msg, cmptr)) { + if (from->sa_family == AF_INET) { + myaddr->sa.sa_family = AF_INET; #ifdef IP_RECVDSTADDR - if (cmptr->cmsg_level == IPPROTO_IP && - cmptr->cmsg_type == IP_RECVDSTADDR) { - memcpy(&myaddr->sin_addr, CMSG_DATA(cmptr), - sizeof(struct in_addr)); - } + if (cmptr->cmsg_level == IPPROTO_IP && + cmptr->cmsg_type == IP_RECVDSTADDR) { + memcpy(&myaddr->si.sin_addr, CMSG_DATA(cmptr), + sizeof(struct in_addr)); + } #endif #ifdef IP_PKTINFO - if (cmptr->cmsg_level == IPPROTO_IP && - cmptr->cmsg_type == IP_PKTINFO) { - memcpy(&pktinfo, CMSG_DATA(cmptr), - sizeof(struct in_pktinfo)); - memcpy(&myaddr->sin_addr, &pktinfo.ipi_addr, - sizeof(struct in_addr)); + if (cmptr->cmsg_level == IPPROTO_IP && + cmptr->cmsg_type == IP_PKTINFO) { + memcpy(&pktinfo, CMSG_DATA(cmptr), + sizeof(struct in_pktinfo)); + memcpy(&myaddr->si.sin_addr, &pktinfo.ipi_addr, + sizeof(struct in_addr)); + } +#endif } +#ifdef HAVE_IPV6 + else if (from->sa_family == AF_INET6) { + myaddr->sa.sa_family = AF_INET6; +#ifdef IP6_RECVDSTADDR + if (cmptr->cmsg_level == IPPROTO_IPV6 && + cmptr->cmsg_type == IPV6_RECVDSTADDR ) + memcpy(&myaddr->s6.sin6_addr, CMSG_DATA(cmptr), + sizeof(struct in6_addr)); #endif +#ifdef HAVE_STRUCT_IN6_PKTINFO + if (cmptr->cmsg_level == IPPROTO_IPV6 && + (cmptr->cmsg_type == IPV6_RECVPKTINFO || + cmptr->cmsg_type == IPV6_PKTINFO)) { + memcpy(&pktinfo6, CMSG_DATA(cmptr), + sizeof(struct in6_pktinfo)); + memcpy(&myaddr->s6.sin6_addr, &pktinfo6.ipi6_addr, + sizeof(struct in6_addr)); + } +#endif + } +#endif + } + /* If the address is not a valid local address, + * then bind to any address... + */ + if (address_is_local(myaddr) != 1) { + if (myaddr->sa.sa_family == AF_INET) + ((struct sockaddr_in *)myaddr)->sin_addr.s_addr = INADDR_ANY; +#ifdef HAVE_IPV6 + else if (myaddr->sa.sa_family == AF_INET6) + memset(&myaddr->s6.sin6_addr, 0, sizeof(struct in6_addr)); +#endif } } - - /* If the address is not a valid local address, then bind to any address... */ - if (address_is_local(myaddr) != 1) - myaddr->sin_addr.s_addr = INADDR_ANY; - return n; } @@ -181,15 +245,13 @@ myrecvfrom(int s, void *buf, int len, unsigned int flags, int myrecvfrom(int s, void *buf, int len, unsigned int flags, - struct sockaddr *from, int *fromlen, struct sockaddr_in *myaddr) + struct sockaddr *from, int *fromlen, union sock_addr *myaddr) { /* There is no way we can get the local address, fudge it */ - bzero(myaddr, sizeof(struct sockaddr_in)); - myaddr->sin_family = AF_INET; - - myaddr->sin_port = htons(IPPORT_TFTP); - bzero(&myaddr->sin_addr, sizeof(myaddr->sin_addr)); + bzero(myaddr, sizeof(*myaddr)); + myaddr->sa.sa_family = from->sa_family; + sa_set_port(myaddr, htons(IPPORT_TFTP)); return recvfrom(s, buf, len, flags, from, fromlen); } diff --git a/tftpd/recvfrom.h b/tftpd/recvfrom.h index fda65c0..e3c4055 100644 --- a/tftpd/recvfrom.h +++ b/tftpd/recvfrom.h @@ -19,5 +19,5 @@ int myrecvfrom(int s, void *buf, int len, unsigned int flags, - struct sockaddr *from, socklen_t * fromlen, - struct sockaddr_in *myaddr); + struct sockaddr *from, socklen_t *fromlen, + union sock_addr *myaddr); diff --git a/tftpd/tftpd.8.in b/tftpd/tftpd.8.in index 49cf359..b4d1ac3 100644 --- a/tftpd/tftpd.8.in +++ b/tftpd/tftpd.8.in @@ -40,7 +40,7 @@ .I directory... .SH DESCRIPTION .B tftpd -is a server for the IPv4 Trivial File Transfer Protocol. The TFTP +is a server for the Trivial File Transfer Protocol. The TFTP protocol is extensively used to support remote booting of diskless devices. The server is normally started by .BR inetd , @@ -48,6 +48,12 @@ but can also run standalone. .PP .SH OPTIONS .TP +.B \-4 +Connect with IPv4 only, if IPv6 support was compiled in. +.TP +.B \-6 +Connect with IPv6 only, if IPv6 support was compiled in. +.TP .B \-l Run the server in standalone (listen) mode, rather than run from .BR inetd . diff --git a/tftpd/tftpd.c b/tftpd/tftpd.c index b5798a6..38f5e7b 100644 --- a/tftpd/tftpd.c +++ b/tftpd/tftpd.c @@ -42,7 +42,6 @@ #include #include -#include #include #include #include @@ -65,6 +64,12 @@ int allow_severity = -1; /* Don't log at all */ struct request_info wrap_request; #endif +#ifdef HAVE_IPV6 +int ai_fam = AF_UNSPEC; +#else +int ai_fam = AF_INET; +#endif + #define TIMEOUT 1000000 /* Default timeout (us) */ #define TRIES 6 /* Number of attempts to send each packet */ #define TIMEOUT_LIMIT ((1 << TRIES)-1) @@ -82,7 +87,9 @@ char buf[PKTSIZE]; char ackbuf[PKTSIZE]; unsigned int max_blksize = MAX_SEGSIZE; -struct sockaddr_in from; +char tmpbuf[INET6_ADDRSTRLEN], *tmp_p; + +union sock_addr from; socklen_t fromlen; off_t tsize; int tsize_ok; @@ -254,10 +261,16 @@ int main(int argc, char **argv) struct tftphdr *tp; struct passwd *pw; struct options *opt; - struct sockaddr_in myaddr; - struct sockaddr_in bindaddr; + union sock_addr myaddr; + struct sockaddr_in bindaddr4; +#ifdef HAVE_IPV6 + struct sockaddr_in6 bindaddr6; +#endif int n; - int fd = 0; + int fd = -1; + int fd4 = -1; + int fd6 = -1; + int fdmax = 0; int standalone = 0; /* Standalone (listen) mode */ int nodaemon = 0; /* Do not detach process */ char *address = NULL; /* Address to listen to */ @@ -283,8 +296,16 @@ int main(int argc, char **argv) srand(time(NULL) ^ getpid()); - while ((c = getopt(argc, argv, "cspvVlLa:B:u:U:r:t:T:R:m:")) != -1) + while ((c = getopt(argc, argv, "46cspvVlLa:B:u:U:r:t:T:R:m:")) != -1) switch (c) { +#ifdef HAVE_IPV6 + case '4': + ai_fam = AF_INET; + break; + case '6': + ai_fam = AF_INET6; + break; +#endif case 'c': cancreate = 1; break; @@ -417,12 +438,6 @@ int main(int argc, char **argv) if (spec_umask || !unixperms) umask(my_umask); - /* Note: on Cygwin, select() on a nonblocking socket becomes - a nonblocking select. */ -#ifndef __CYGWIN__ - set_socket_nonblock(fd, 1); -#endif - #ifdef WITH_REGEX if (rewrite_file) rewrite_rules = read_remap_rules(rewrite_file); @@ -430,16 +445,45 @@ int main(int argc, char **argv) /* If we're running standalone, set up the input port */ if (standalone) { - fd = socket(PF_INET, SOCK_DGRAM, 0); - - memset(&bindaddr, 0, sizeof bindaddr); - bindaddr.sin_family = AF_INET; - bindaddr.sin_addr.s_addr = INADDR_ANY; - bindaddr.sin_port = htons(IPPORT_TFTP); - +#ifdef HAVE_IPV6 + if (ai_fam != AF_INET6) { +#endif + fd4 = socket(AF_INET, SOCK_DGRAM, 0); + if (fd4 < 0) { + syslog(LOG_ERR, "cannot open IPv4 socket: %m"); + exit(EX_OSERR); + } +#ifndef __CYGWIN__ + set_socket_nonblock(fd4, 1); +#endif + memset(&bindaddr4, 0, sizeof bindaddr4); + bindaddr4.sin_family = AF_INET; + bindaddr4.sin_addr.s_addr = INADDR_ANY; + bindaddr4.sin_port = htons(IPPORT_TFTP); +#ifdef HAVE_IPV6 + } + if (ai_fam != AF_INET) { + fd6 = socket(AF_INET6, SOCK_DGRAM, 0); + if (fd6 < 0) { + if (fd4 < 0) { + syslog(LOG_ERR, "cannot open IPv6 socket: %m"); + exit(EX_OSERR); + } else { + syslog(LOG_ERR, + "cannot open IPv6 socket, disable IPv6: %m"); + } + } +#ifndef __CYGWIN__ + set_socket_nonblock(fd6, 1); +#endif + memset(&bindaddr6, 0, sizeof bindaddr6); + bindaddr6.sin6_family = AF_INET6; + bindaddr6.sin6_port = htons(IPPORT_TFTP); + } +#endif if (address) { char *portptr, *eportptr; - struct hostent *hostent; + int err; struct servent *servent; unsigned long port; @@ -447,17 +491,40 @@ int main(int argc, char **argv) portptr = strrchr(address, ':'); if (portptr) *portptr++ = '\0'; - + else + portptr = (char *)"tftp"; if (*address) { - hostent = gethostbyname(address); - if (!hostent || hostent->h_addrtype != AF_INET) { - syslog(LOG_ERR, - "cannot resolve local bind address: %s", - address); - exit(EX_NOINPUT); + if (fd4 >= 0) { + bindaddr4.sin_family = AF_INET; + err = set_sock_addr(address, + (union sock_addr *)&bindaddr4, NULL); + if (err) { + syslog(LOG_ERR, + "cannot resolve local IPv4 bind address: %s", + address); + exit(EX_NOINPUT); + } } - memcpy(&bindaddr.sin_addr, hostent->h_addr, - hostent->h_length); +#ifdef HAVE_IPV6 + if (fd6 >= 0) { + err = set_sock_addr(address, + (union sock_addr *)&bindaddr6, NULL); + if (err) { + if (fd4 >= 0) { + syslog(LOG_ERR, + "cannot resolve local IPv6 bind address: %s" + "; using IPv4 only", address); + close(fd6); + fd6 = -1; + } else { + syslog(LOG_ERR, + "cannot resolve local IPv6 bind address: %s", + address); + exit(EX_NOINPUT); + } + } + } +#endif } else { /* Default to using INADDR_ANY */ } @@ -465,10 +532,20 @@ int main(int argc, char **argv) if (portptr && *portptr) { servent = getservbyname(portptr, "udp"); if (servent) { - bindaddr.sin_port = servent->s_port; + if (fd4 >= 0) + bindaddr4.sin_port = servent->s_port; +#ifdef HAVE_IPV6 + if (fd6 >= 0) + bindaddr6.sin6_port = servent->s_port; +#endif } else if ((port = strtoul(portptr, &eportptr, 0)) && !*eportptr) { - bindaddr.sin_port = htons(port); + if (fd4 >= 0) + bindaddr4.sin_port = htons(port); +#ifdef HAVE_IPV6 + if (fd6 >= 0) + bindaddr6.sin6_port = htons(port); +#endif } else if (!strcmp(portptr, "tftp")) { /* It's TFTP, we're OK */ } else { @@ -479,11 +556,37 @@ int main(int argc, char **argv) } } - if (bind(fd, (struct sockaddr *)&bindaddr, sizeof bindaddr) < 0) { - syslog(LOG_ERR, "cannot bind to local socket: %m"); - exit(EX_OSERR); + if (fd4 >= 0) { + if (bind(fd4, (struct sockaddr *)&bindaddr4, + sizeof(bindaddr4)) < 0) { + syslog(LOG_ERR, "cannot bind to local IPv4 socket: %m"); + exit(EX_OSERR); + } } - +#ifdef HAVE_IPV6 + if (fd6 >= 0) { + int on = 1; +#if defined(IPV6_V6ONLY) + if (setsockopt(fd6, IPPROTO_IPV6, IPV6_V6ONLY, (char*)&on, + sizeof(on))) { + syslog(LOG_ERR, "cannot setsockopt IPV6_V6ONLY %m"); + } +#endif + if (bind(fd6, (struct sockaddr *)&bindaddr6, + sizeof(bindaddr6)) < 0) { + if (fd4 >= 0) { + syslog(LOG_ERR, + "cannot bind to local IPv6 socket," + "IPv6 disabled: %m"); + close(fd6); + fd6 = -1; + } else { + syslog(LOG_ERR, "cannot bind to local IPv6 socket: %m"); + exit(EX_OSERR); + } + } + } +#endif /* Daemonize this process */ /* Note: when running in secure mode (-s), we must not chroot, since we are already in the proper directory. */ @@ -491,10 +594,21 @@ int main(int argc, char **argv) syslog(LOG_ERR, "cannot daemonize: %m"); exit(EX_OSERR); } + if (fd6 > fd4) + fdmax = fd6; + else + fdmax = fd4; } else { /* 0 is our socket descriptor */ close(1); close(2); + fd = 0; + fdmax = 0; + /* Note: on Cygwin, select() on a nonblocking socket becomes + a nonblocking select. */ +#ifndef __CYGWIN__ + set_socket_nonblock(fd, 1); +#endif } /* Disable path MTU discovery */ @@ -533,29 +647,61 @@ int main(int argc, char **argv) } FD_ZERO(&readset); - FD_SET(fd, &readset); + if (standalone) { + if (fd4 >= 0) { + FD_SET(fd4, &readset); +#ifdef __CYGWIN__ + /* On Cygwin, select() on a nonblocking socket returns + immediately, with a rv of 0! */ + set_socket_nonblock(fd4, 0); +#endif + } + if (fd6 >= 0) { + FD_SET(fd6, &readset); +#ifdef __CYGWIN__ + /* On Cygwin, select() on a nonblocking socket returns + immediately, with a rv of 0! */ + set_socket_nonblock(fd6, 0); +#endif + } + } else { /* fd always 0 */ + fd = 0; +#ifdef __CYGWIN__ + /* On Cygwin, select() on a nonblocking socket returns + immediately, with a rv of 0! */ + set_socket_nonblock(fd, 0); +#endif + FD_SET(fd, &readset); + } tv_waittime.tv_sec = waittime; tv_waittime.tv_usec = 0; -#ifdef __CYGWIN__ - /* On Cygwin, select() on a nonblocking socket returns immediately, - with a rv of 0! */ - set_socket_nonblock(fd, 0); -#endif /* Never time out if we're in standalone mode */ - rv = select(fd + 1, &readset, NULL, NULL, + rv = select(fdmax + 1, &readset, NULL, NULL, standalone ? NULL : &tv_waittime); if (rv == -1 && errno == EINTR) continue; /* Signal caught, reloop */ + if (rv == -1) { syslog(LOG_ERR, "select loop: %m"); exit(EX_IOERR); } else if (rv == 0) { exit(0); /* Timeout, return to inetd */ } + + if (standalone) { + if ((fd4 >= 0) && FD_ISSET(fd4, &readset)) + fd = fd4; + else if ((fd6 >= 0) && FD_ISSET(fd6, &readset)) + fd = fd6; + else /* not in set ??? */ + continue; + } #ifdef __CYGWIN__ - set_socket_nonblock(fd, 1); + /* On Cygwin, select() on a nonblocking socket returns + immediately, with a rv of 0! */ + set_socket_nonblock(fd, 0); #endif fromlen = sizeof(from); @@ -570,18 +716,32 @@ int main(int argc, char **argv) exit(EX_IOERR); } } - - if (from.sin_family != AF_INET) { - syslog(LOG_ERR, - "received address was not AF_INET, please check your inetd config"); +#ifdef HAVE_IPV6 + if ((from.sa.sa_family != AF_INET) && (from.sa.sa_family != AF_INET6)) { + syslog(LOG_ERR, "received address was not AF_INET/AF_INET6," + " please check your inetd config"); +#else + if (from.sa.sa_family != AF_INET) { + syslog(LOG_ERR, "received address was not AF_INET," + " please check your inetd config"); +#endif exit(EX_PROTOCOL); } - if (standalone && myaddr.sin_addr.s_addr == INADDR_ANY) { - /* myrecvfrom() didn't capture the source address; but we might - have bound to a specific address, if so we should use it */ - memcpy(&myaddr.sin_addr, &bindaddr.sin_addr, - sizeof bindaddr.sin_addr); + if (standalone) { + if ((from.sa.sa_family == AF_INET) && + (myaddr.si.sin_addr.s_addr == INADDR_ANY)) { + /* myrecvfrom() didn't capture the source address; but we might + have bound to a specific address, if so we should use it */ + memcpy(SOCKADDR_P(&myaddr), &bindaddr4.sin_addr, + sizeof(bindaddr4.sin_addr)); +#ifdef HAVE_IPV6 + } else if ((from.sa.sa_family == AF_INET6) && + IN6_IS_ADDR_UNSPECIFIED(SOCKADDR_P(&myaddr))) { + memcpy(SOCKADDR_P(&myaddr), &bindaddr6.sin6_addr, + sizeof(bindaddr6.sin6_addr)); +#endif + } } /* @@ -609,14 +769,19 @@ int main(int argc, char **argv) RQ_FILE, fd, RQ_CLIENT_SIN, &from, RQ_SERVER_SIN, &myaddr, 0); sock_methods(&wrap_request); + + tmp_p = (char *)inet_ntop(myaddr.sa.sa_family, SOCKADDR_P(&myaddr), + tmpbuf, INET6_ADDRSTRLEN); + if (!tmp_p) { + tmp_p = tmpbuf; + strcpy(tmpbuf, "???"); + } if (hosts_access(&wrap_request) == 0) { if (deny_severity != -1) - syslog(deny_severity, "connection refused from %s", - inet_ntoa(from.sin_addr)); + syslog(deny_severity, "connection refused from %s", tmp_p); exit(EX_NOPERM); /* Access denied */ } else if (allow_severity != -1) { - syslog(allow_severity, "connect from %s", - inet_ntoa(from.sin_addr)); + syslog(allow_severity, "connect from %s", tmp_p); } #endif @@ -626,7 +791,7 @@ int main(int argc, char **argv) /* Get a socket. This has to be done before the chroot(), since some systems require access to /dev to create a socket. */ - peer = socket(AF_INET, SOCK_DGRAM, 0); + peer = socket(myaddr.sa.sa_family, SOCK_DGRAM, 0); if (peer < 0) { syslog(LOG_ERR, "socket: %m"); exit(EX_IOERR); @@ -677,16 +842,13 @@ int main(int argc, char **argv) exit(EX_OSERR); } - /* Other basic setup */ - from.sin_family = AF_INET; - /* Process the request... */ if (pick_port_bind(peer, &myaddr, portrange_from, portrange_to) < 0) { syslog(LOG_ERR, "bind: %m"); exit(EX_IOERR); } - if (connect(peer, (struct sockaddr *)&from, sizeof from) < 0) { + if (connect(peer, &from.sa, SOCKLEN(&from)) < 0) { syslog(LOG_ERR, "connect: %m"); exit(EX_IOERR); } @@ -776,16 +938,22 @@ int tftp(struct tftphdr *tp, int size) exit(0); } if (verbosity >= 1) { + tmp_p = (char *)inet_ntop(from.sa.sa_family, SOCKADDR_P(&from), + tmpbuf, INET6_ADDRSTRLEN); + if (!tmp_p) { + tmp_p = tmpbuf; + strcpy(tmpbuf, "???"); + } if (filename == origfilename || !strcmp(filename, origfilename)) syslog(LOG_NOTICE, "%s from %s filename %s\n", tp_opcode == WRQ ? "WRQ" : "RRQ", - inet_ntoa(from.sin_addr), filename); + tmp_p, filename); else syslog(LOG_NOTICE, "%s from %s filename %s remapped to %s\n", tp_opcode == WRQ ? "WRQ" : "RRQ", - inet_ntoa(from.sin_addr), origfilename, + tmp_p, origfilename, filename); } ecode = @@ -998,20 +1166,41 @@ int rewrite_macros(char macro, char *output); int rewrite_macros(char macro, char *output) { - char *p; + char *p, tb[INET6_ADDRSTRLEN]; + int l=0; switch (macro) { case 'i': - p = inet_ntoa(from.sin_addr); - if (output) + p = (char *)inet_ntop(from.sa.sa_family, SOCKADDR_P(&from), + tb, INET6_ADDRSTRLEN); + if (output && p) strcpy(output, p); - return strlen(p); + if (!p) + return 0; + else + return strlen(p); case 'x': - if (output) - sprintf(output, "%08lX", - (unsigned long)ntohl(from.sin_addr.s_addr)); - return 8; + if (output) { + if (from.sa.sa_family == AF_INET) { + sprintf(output, "%08lX", + (unsigned long)ntohl(from.si.sin_addr.s_addr)); + l = 8; +#ifdef HAVE_IPV6 + } else { + unsigned char *c = (unsigned char *)SOCKADDR_P(&from); + p = tb; + for (l = 0; l < 16; l++) { + sprintf(p, "%02X", *c); + c++; + p += 2; + } + strcpy(output, tb); + l = strlen(tb); +#endif + } + } + return l; default: return -1; @@ -1398,8 +1587,14 @@ static void nak(int error, const char *msg) length += 4; /* Add space for header */ if (verbosity >= 2) { + tmp_p = (char *)inet_ntop(from.sa.sa_family, SOCKADDR_P(&from), + tmpbuf, INET6_ADDRSTRLEN); + if (!tmp_p) { + tmp_p = tmpbuf; + strcpy(tmpbuf, "???"); + } syslog(LOG_INFO, "sending NAK (%d, %s) to %s", - error, tp->th_msg, inet_ntoa(from.sin_addr)); + error, tp->th_msg, tmp_p); } if (send(peer, buf, length, 0) != length) From 4bdac0b53698253d85c60c5a3e1da74be0a06cb3 Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Wed, 23 Jul 2008 14:31:48 -0400 Subject: [PATCH 105/184] Document changes for 0.49 --- CHANGES | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGES b/CHANGES index c5daef1..f7ecfe8 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,7 @@ +Changes in 0.49: + Add IPv6 support. Patch by Karsten Keil. + + Changes in 0.48: Unbreak -l -s in the server, which was broken in 0.47. From c3a5c712e2999a5610479fda84a58847bb5543b2 Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Wed, 23 Jul 2008 14:36:18 -0400 Subject: [PATCH 106/184] Allow the -4 option even in an IPv4-only configuration Allow the -4 option even if IPv6 isn't compiled in. --- tftp/main.c | 2 +- tftp/tftp.1.in | 5 +++-- tftpd/tftpd.8.in | 6 +++--- tftpd/tftpd.c | 2 +- 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/tftp/main.c b/tftp/main.c index 6176b37..be11957 100644 --- a/tftp/main.c +++ b/tftp/main.c @@ -221,10 +221,10 @@ int main(int argc, char *argv[]) if (argv[arg][0] == '-') { for (optx = &argv[arg][1]; *optx; optx++) { switch (*optx) { -#ifdef HAVE_IPV6 case '4': ai_fam = AF_INET; break; +#ifdef HAVE_IPV6 case '6': ai_fam = AF_INET6; break; diff --git a/tftp/tftp.1.in b/tftp/tftp.1.in index 8bbb796..b41f7b5 100644 --- a/tftp/tftp.1.in +++ b/tftp/tftp.1.in @@ -30,7 +30,7 @@ .\" SUCH DAMAGE. .\" .\"----------------------------------------------------------------------- */ -.TH TFTP 1 "12 December 2006" "tftp-hpa @@VERSION@@" "User's Manual" +.TH TFTP 1 "23 July 2008" "tftp-hpa @@VERSION@@" "User's Manual" .SH NAME .B tftp \- IPv4 Trivial File Transfer Protocol client @@ -56,9 +56,10 @@ command below.) .SH OPTIONS .TP .B \-4 -Connect with IPv4 only, if IPv6 support was compiled in. +Connect with IPv4 only, even if IPv6 support was compiled in. .TP .B \-6 +Connect with IPv6 only, if compiled in. .TP \fB\-c\fP \fIcommand\fP Execute \fIcommand\fP as if it had been entered on the tftp prompt. diff --git a/tftpd/tftpd.8.in b/tftpd/tftpd.8.in index b4d1ac3..01007ea 100644 --- a/tftpd/tftpd.8.in +++ b/tftpd/tftpd.8.in @@ -30,7 +30,7 @@ .\" SUCH DAMAGE. .\" .\"----------------------------------------------------------------------- */ -.TH TFTPD 8 "16 February 2006" "tftp-hpa @@VERSION@@" "System Manager's Manual" +.TH TFTPD 8 "23 July 2008" "tftp-hpa @@VERSION@@" "System Manager's Manual" .SH NAME .B tftpd \- IPv4 Trivial File Transfer Protocol server @@ -49,10 +49,10 @@ but can also run standalone. .SH OPTIONS .TP .B \-4 -Connect with IPv4 only, if IPv6 support was compiled in. +Connect with IPv4 only, even if IPv6 support was compiled in. .TP .B \-6 -Connect with IPv6 only, if IPv6 support was compiled in. +Connect with IPv6 only, if compiled in. .TP .B \-l Run the server in standalone (listen) mode, rather than run from diff --git a/tftpd/tftpd.c b/tftpd/tftpd.c index 38f5e7b..8321771 100644 --- a/tftpd/tftpd.c +++ b/tftpd/tftpd.c @@ -298,10 +298,10 @@ int main(int argc, char **argv) while ((c = getopt(argc, argv, "46cspvVlLa:B:u:U:r:t:T:R:m:")) != -1) switch (c) { -#ifdef HAVE_IPV6 case '4': ai_fam = AF_INET; break; +#ifdef HAVE_IPV6 case '6': ai_fam = AF_INET6; break; From cfb85d4dec59f433ef9efee0d18b188bb2cb44fa Mon Sep 17 00:00:00 2001 From: Karsten Keil Date: Wed, 30 Jul 2008 16:15:10 +0200 Subject: [PATCH 107/184] Fix numeric IPv6 address handling This patch fix a issue with numeric IPv6 addresses in the tftpd -a address[:port] option. Since IPv6 addresses use colon ':' in differnt counts itself, we cannot detect, if the last colon is a seperator, so it is needed to put the IPv6 address into square brackets, e.g. [2001:db8::1], so a optional port assignment is unambiguous. The patch also allows to specify numeric IPv6 addresses in other places enclosed in [], but in these cases it accept these also without []. Signed-off-by: Karsten Keil Signed-off-by: H. Peter Anvin --- common/tftpsubs.c | 56 ++++++++++++++++++++++++++++++++++++++++++++++- common/tftpsubs.h | 12 ++++++++++ tftpd/tftpd.8.in | 4 ++++ tftpd/tftpd.c | 36 +++++++++++++++++++++++++----- 4 files changed, 102 insertions(+), 6 deletions(-) diff --git a/common/tftpsubs.c b/common/tftpsubs.c index 45f4907..1c91e89 100644 --- a/common/tftpsubs.c +++ b/common/tftpsubs.c @@ -309,7 +309,7 @@ set_sock_addr(char *host,union sock_addr *s, char **name) memset(&hints, 0, sizeof(hints)); hints.ai_family = s->sa.sa_family; hints.ai_flags = AI_CANONNAME; - err = getaddrinfo(host, NULL, &hints, &addrResult); + err = getaddrinfo(strip_address(host), NULL, &hints, &addrResult); if (err) { printf("Error : %s\n", gai_strerror(err)); printf("%s: unknown host\n", host); @@ -329,3 +329,57 @@ set_sock_addr(char *host,union sock_addr *s, char **name) freeaddrinfo(addrResult); return 0; } + +#ifdef HAVE_IPV6 +int is_numeric_ipv6(char *addr) +{ + /* A numeric IPv6 address consist at least of 2 ':' and + * it may have sequences of hex-digits and maybe contain + * a '.' from a IPv4 mapped address and maybe is enclosed in [] + * we do not check here, if it is a valid IPv6 address + * only if is something like a numeric IPv6 address or something else + */ + size_t l; + char *p, s = 0; + + if (!addr) + return 0; + p = strrchr(addr, ']'); + if (p) { + s = *p; + *p = 0; + } + l = strlen(addr); + if (p) + *p = s; + if (l<2) + return 0; + if (l != strspn(addr, "0123456789ABCDEFabcdef:.[")) + return 0; + p = strchr(addr, ':'); + if (p) { + p++; + p = strchr(addr, ':'); + if (p) + return 1; + } + return 0; +} + +/* strip [] from numeric IPv6 addreses */ + +char *strip_address(char *addr) +{ + char *p; + + if (is_numeric_ipv6(addr) && (*addr == '[')) { + p = addr + strlen(addr); + p--; + if (*p == ']') { + *p = 0; + addr++; + } + } + return addr; +} +#endif diff --git a/common/tftpsubs.h b/common/tftpsubs.h index 20cf47e..887a4ab 100644 --- a/common/tftpsubs.h +++ b/common/tftpsubs.h @@ -73,6 +73,18 @@ union sock_addr { (void *)&((union sock_addr*)sock)->si.sin_addr #endif +#ifdef HAVE_IPV6 +int is_numeric_ipv6(char *); +char *strip_address(char *); +#else +#define is_numeric_ipv6(a) 0 + +static inline char *strip_address(char *addr) +{ + return addr; +} +#endif + static inline int sa_set_port(union sock_addr *s, u_short port) { switch (s->sa.sa_family) { diff --git a/tftpd/tftpd.8.in b/tftpd/tftpd.8.in index 01007ea..049b0a6 100644 --- a/tftpd/tftpd.8.in +++ b/tftpd/tftpd.8.in @@ -83,6 +83,10 @@ option. The default is to listen to the port specified in .I /etc/services on all local addresses. + +.B Please note: +Numeric IPv6 adresses must be enclosed in square brackets +to avoid ambiguity with the optional port information. .TP .B \-c Allow new files to be created. By default, diff --git a/tftpd/tftpd.c b/tftpd/tftpd.c index 8321771..e663239 100644 --- a/tftpd/tftpd.c +++ b/tftpd/tftpd.c @@ -256,6 +256,28 @@ static int recv_time(int s, void *rbuf, int len, unsigned int flags, } } +static int split_port(char **ap, char **pp) +{ + char *a, *p; + + a = *ap; + if (is_numeric_ipv6(a)) { + if (*a++ != '[') + return 1; + *ap = a; + p = strrchr(a, ']'); + if (!p) + return 1; + *p++ = 0; + a = p; + } + p = strrchr(a, ':'); + if (p) + *p++ = 0; + *pp = p; + return 0; +} + int main(int argc, char **argv) { struct tftphdr *tp; @@ -482,16 +504,19 @@ int main(int argc, char **argv) } #endif if (address) { - char *portptr, *eportptr; + char *portptr = NULL, *eportptr; int err; struct servent *servent; unsigned long port; address = tfstrdup(address); - portptr = strrchr(address, ':'); - if (portptr) - *portptr++ = '\0'; - else + err = split_port(&address, &portptr); + if (err) { + syslog(LOG_ERR, + "Numeric IPv6 addresses need to be enclosed in []"); + exit(EX_USAGE); + } + if (!portptr) portptr = (char *)"tftp"; if (*address) { if (fd4 >= 0) { @@ -507,6 +532,7 @@ int main(int argc, char **argv) } #ifdef HAVE_IPV6 if (fd6 >= 0) { + bindaddr6.sin6_family = AF_INET6; err = set_sock_addr(address, (union sock_addr *)&bindaddr6, NULL); if (err) { From 7670c83e5ac68ceb986b53368c84c571136ceabd Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Wed, 30 Jul 2008 15:36:46 -0700 Subject: [PATCH 108/184] release.sh: remove Cogito Remove Cogito use in favour of plain git; make sure the index is clean. Signed-off-by: H. Peter Anvin --- release.sh | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/release.sh b/release.sh index 4472469..1f72922 100755 --- a/release.sh +++ b/release.sh @@ -17,20 +17,25 @@ releasedir=$PACKAGE-$release GIT_DIR=`cd "${GIT_DIR-.git}" && pwd` export GIT_DIR +if [ `git diff --cached | wc -l` -ne 0 ]; then + echo "$0: index not clean" 1>&2 + exit 1 +fi + if [ x"$release" = x'test' ]; then release=`cat version` releasetag=HEAD releasedir=$PACKAGE-$release else - echo $release > version.new - if ! cmp -s version version.new ; then - mv -f version.new version - cg-commit -m 'Update version for release' version + echo $release > version + if [ `git diff version | wc -l` -ne 0 ]; then + git add version + git commit -m "Update version for release $release" version else - rm -f version.new + git checkout version fi rm -f "$GIT_DIR"/refs/tags/$releasetag - cg-tag $releasetag + git tag -a -m "$releasetag" -f "$releasetag" fi here=`pwd` @@ -39,7 +44,8 @@ tmpdir=/var/tmp/release.$$ rm -rf $tmpdir mkdir -p $tmpdir cd $tmpdir -cg-export -r $releasetag $releasedir +mkdir -p $releasedir +git archive --format=tar $releasetag | tar -xf - -C $releasedir cd $releasedir make release rm -f release.sh From 0c6f7f86d38c66d9d855f5f9cf3441912783c5e7 Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Wed, 30 Jul 2008 16:17:02 -0700 Subject: [PATCH 109/184] tftpd: mark symbols static Mark symbols not accessed from other files static. Signed-off-by: H. Peter Anvin --- tftpd/tftpd.c | 101 ++++++++++++++++++++++---------------------------- 1 file changed, 45 insertions(+), 56 deletions(-) diff --git a/tftpd/tftpd.c b/tftpd/tftpd.c index e663239..cb78ccb 100644 --- a/tftpd/tftpd.c +++ b/tftpd/tftpd.c @@ -61,13 +61,13 @@ int deny_severity = LOG_WARNING; int allow_severity = -1; /* Don't log at all */ -struct request_info wrap_request; +static struct request_info wrap_request; #endif #ifdef HAVE_IPV6 -int ai_fam = AF_UNSPEC; +static int ai_fam = AF_UNSPEC; #else -int ai_fam = AF_INET; +static int ai_fam = AF_INET; #endif #define TIMEOUT 1000000 /* Default timeout (us) */ @@ -75,29 +75,29 @@ int ai_fam = AF_INET; #define TIMEOUT_LIMIT ((1 << TRIES)-1) const char *__progname; -int peer; -unsigned long timeout = TIMEOUT; /* Current timeout value */ -unsigned long rexmtval = TIMEOUT; /* Basic timeout value */ -unsigned long maxtimeout = TIMEOUT_LIMIT * TIMEOUT; -int timeout_quit = 0; -sigjmp_buf timeoutbuf; +static int peer; +static unsigned long timeout = TIMEOUT; /* Current timeout value */ +static unsigned long rexmtval = TIMEOUT; /* Basic timeout value */ +static unsigned long maxtimeout = TIMEOUT_LIMIT * TIMEOUT; +static int timeout_quit = 0; +static sigjmp_buf timeoutbuf; #define PKTSIZE MAX_SEGSIZE+4 -char buf[PKTSIZE]; -char ackbuf[PKTSIZE]; -unsigned int max_blksize = MAX_SEGSIZE; +static char buf[PKTSIZE]; +static char ackbuf[PKTSIZE]; +static unsigned int max_blksize = MAX_SEGSIZE; -char tmpbuf[INET6_ADDRSTRLEN], *tmp_p; +static char tmpbuf[INET6_ADDRSTRLEN], *tmp_p; -union sock_addr from; -socklen_t fromlen; -off_t tsize; -int tsize_ok; +static union sock_addr from; +static socklen_t fromlen; +static off_t tsize; +static int tsize_ok; -int ndirs; -const char **dirs; +static int ndirs; +static const char **dirs; -int secure = 0; +static int secure = 0; int cancreate = 0; int unixperms = 0; int portrange = 0; @@ -111,15 +111,14 @@ static struct rule *rewrite_rules = NULL; int tftp(struct tftphdr *, int); static void nak(int, const char *); -void timer(int); -void justquit(int); -void do_opt(char *, char *, char **); +static void timer(int); +static void do_opt(char *, char *, char **); -int set_blksize(char *, char **); -int set_blksize2(char *, char **); -int set_tsize(char *, char **); -int set_timeout(char *, char **); -int set_utimeout(char *, char **); +static int set_blksize(char *, char **); +static int set_blksize2(char *, char **); +static int set_tsize(char *, char **); +static int set_timeout(char *, char **); +static int set_utimeout(char *, char **); struct options { const char *o_opt; @@ -889,10 +888,10 @@ int main(int argc, char **argv) exit(0); } -char *rewrite_access(char *, int, const char **); -int validate_access(char *, int, struct formats *, const char **); -void tftp_sendfile(struct formats *, struct tftphdr *, int); -void tftp_recvfile(struct formats *, struct tftphdr *, int); +static char *rewrite_access(char *, int, const char **); +static int validate_access(char *, int, struct formats *, const char **); +static void tftp_sendfile(struct formats *, struct tftphdr *, int); +static void tftp_recvfile(struct formats *, struct tftphdr *, int); struct formats { const char *f_mode; @@ -1021,7 +1020,7 @@ static int blksize_set; /* * Set a non-standard block size (c.f. RFC2348) */ -int set_blksize(char *val, char **ret) +static int set_blksize(char *val, char **ret) { static char b_ret[6]; unsigned int sz; @@ -1048,7 +1047,7 @@ int set_blksize(char *val, char **ret) /* * Set a power-of-two block size (nonstandard) */ -int set_blksize2(char *val, char **ret) +static int set_blksize2(char *val, char **ret) { static char b_ret[6]; unsigned int sz; @@ -1086,7 +1085,7 @@ int set_blksize2(char *val, char **ret) * For netascii mode, we don't know the size ahead of time; * so reject the option. */ -int set_tsize(char *val, char **ret) +static int set_tsize(char *val, char **ret) { static char b_ret[sizeof(uintmax_t) * CHAR_BIT / 3 + 2]; uintmax_t sz; @@ -1109,7 +1108,7 @@ int set_tsize(char *val, char **ret) * to be the (default) retransmission timeout, but being an * integer in seconds it seems a bit limited. */ -int set_timeout(char *val, char **ret) +static int set_timeout(char *val, char **ret) { static char b_ret[4]; unsigned long to; @@ -1128,7 +1127,7 @@ int set_timeout(char *val, char **ret) } /* Similar, but in microseconds. We allow down to 10 ms. */ -int set_utimeout(char *val, char **ret) +static int set_utimeout(char *val, char **ret) { static char b_ret[4]; unsigned long to; @@ -1149,7 +1148,7 @@ int set_utimeout(char *val, char **ret) /* * Parse RFC2347 style options */ -void do_opt(char *opt, char *val, char **ap) +static void do_opt(char *opt, char *val, char **ap) { struct options *po; char *ret; @@ -1188,9 +1187,7 @@ void do_opt(char *opt, char *val, char **ap) * * Return -1 on failure. */ -int rewrite_macros(char macro, char *output); - -int rewrite_macros(char macro, char *output) +static int rewrite_macros(char macro, char *output) { char *p, tb[INET6_ADDRSTRLEN]; int l=0; @@ -1236,7 +1233,7 @@ int rewrite_macros(char macro, char *output) /* * Modify the filename, if applicable. If it returns NULL, deny the access. */ -char *rewrite_access(char *filename, int mode, const char **msg) +static char *rewrite_access(char *filename, int mode, const char **msg) { if (rewrite_rules) { char *newname = @@ -1248,7 +1245,7 @@ char *rewrite_access(char *filename, int mode, const char **msg) } #else -char *rewrite_access(char *filename, int mode, const char **msg) +static char *rewrite_access(char *filename, int mode, const char **msg) { (void)mode; /* Avoid warning */ (void)msg; @@ -1256,7 +1253,7 @@ char *rewrite_access(char *filename, int mode, const char **msg) } #endif -FILE *file; +static FILE *file; /* * Validate file access. Since we * have no uid or gid, for now require @@ -1268,9 +1265,8 @@ FILE *file; * Note also, full path name must be * given as we have no login directory. */ -int -validate_access(char *filename, int mode, - struct formats *pf, const char **errmsg) +static int validate_access(char *filename, int mode, + struct formats *pf, const char **errmsg) { struct stat stbuf; int i, len; @@ -1378,7 +1374,7 @@ validate_access(char *filename, int mode, /* * Send the requested file. */ -void tftp_sendfile(struct formats *pf, struct tftphdr *oap, int oacklen) +static void tftp_sendfile(struct formats *pf, struct tftphdr *oap, int oacklen) { struct tftphdr *dp; struct tftphdr *ap; /* ack packet */ @@ -1472,17 +1468,10 @@ void tftp_sendfile(struct formats *pf, struct tftphdr *oap, int oacklen) (void)fclose(file); } -/* Bail out signal handler */ -void justquit(int sig) -{ - (void)sig; /* Suppress unused warning */ - exit(0); -} - /* * Receive a file. */ -void tftp_recvfile(struct formats *pf, struct tftphdr *oap, int oacklen) +static void tftp_recvfile(struct formats *pf, struct tftphdr *oap, int oacklen) { struct tftphdr *dp; int n, size; From 77fbfeebee4d9d4d176f416e2c516fdd74a36b3f Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Wed, 30 Jul 2008 16:34:44 -0700 Subject: [PATCH 110/184] Implement is_numeric_ipv6() as a state machine Implement is_numeric_ipv6() as a state machine, so we can avoid in-place modification. Signed-off-by: H. Peter Anvin --- common/tftpsubs.c | 68 ++++++++++++++++++++++++++++++++--------------- common/tftpsubs.h | 8 ++---- 2 files changed, 48 insertions(+), 28 deletions(-) diff --git a/common/tftpsubs.c b/common/tftpsubs.c index 1c91e89..9958184 100644 --- a/common/tftpsubs.c +++ b/common/tftpsubs.c @@ -331,7 +331,7 @@ set_sock_addr(char *host,union sock_addr *s, char **name) } #ifdef HAVE_IPV6 -int is_numeric_ipv6(char *addr) +int is_numeric_ipv6(const char *p) { /* A numeric IPv6 address consist at least of 2 ':' and * it may have sequences of hex-digits and maybe contain @@ -339,31 +339,55 @@ int is_numeric_ipv6(char *addr) * we do not check here, if it is a valid IPv6 address * only if is something like a numeric IPv6 address or something else */ - size_t l; - char *p, s = 0; + int colon = 0; + int dot = 0; + int bracket = 0; + char c; - if (!addr) + if (!p) return 0; - p = strrchr(addr, ']'); - if (p) { - s = *p; - *p = 0; + + if (*p == '[') { + bracket = 1; + p++; } - l = strlen(addr); - if (p) - *p = s; - if (l<2) - return 0; - if (l != strspn(addr, "0123456789ABCDEFabcdef:.[")) - return 0; - p = strchr(addr, ':'); - if (p) { - p++; - p = strchr(addr, ':'); - if (p) - return 1; + + while ((c = *p++) && c != ']') { + switch (c) { + case ':': + colon++; + break; + case '.': + dot++; + break; + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': + case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': + break; + default: + return 0; /* Invalid character */ + } } - return 0; + + if (colon < 2 || colon > 7) + return 0; + + if (dot) { + /* An IPv4-mapped address in dot-quad form will have 3 dots */ + if (dot != 3) + return 0; + /* The IPv4-mapped address takes the space of one colon */ + if (colon > 6) + return 0; + } + + /* If bracketed, must be closed, and vice versa */ + if (bracket ^ (c == ']')) + return 0; + + /* Otherwise, assume we're okay */ + return 1; } /* strip [] from numeric IPv6 addreses */ diff --git a/common/tftpsubs.h b/common/tftpsubs.h index 887a4ab..b3a3bf3 100644 --- a/common/tftpsubs.h +++ b/common/tftpsubs.h @@ -74,15 +74,11 @@ union sock_addr { #endif #ifdef HAVE_IPV6 -int is_numeric_ipv6(char *); +int is_numeric_ipv6(const char *); char *strip_address(char *); #else #define is_numeric_ipv6(a) 0 - -static inline char *strip_address(char *addr) -{ - return addr; -} +#define strip_address(a) (a) #endif static inline int sa_set_port(union sock_addr *s, u_short port) From e4d3083006da530ddae4614d2cbb2bf9a431d633 Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Wed, 30 Jul 2008 17:16:00 -0700 Subject: [PATCH 111/184] tftpd: switch to getopt_long() Switch to using getopt_long(); include a version in case the platform lacks it. Signed-off-by: H. Peter Anvin --- config.h | 5 +- configure.in | 2 +- lib/getopt.h | 23 +++++++ lib/getopt_long.c | 150 ++++++++++++++++++++++++++++++++++++++++++++++ tftpd/tftpd.8.in | 84 +++++++++++++------------- tftpd/tftpd.c | 34 ++++++++++- 6 files changed, 253 insertions(+), 45 deletions(-) create mode 100644 lib/getopt.h create mode 100644 lib/getopt_long.c diff --git a/config.h b/config.h index 38be517..bd1dac1 100644 --- a/config.h +++ b/config.h @@ -110,11 +110,10 @@ #include #endif -#ifdef HAVE_GETOPT_H +#ifdef HAVE_GETOPT_LONG #include #else -extern char *optarg; -extern int optind, opterr, optopt; +#include "lib/getopt.h" #endif /* Test for EAGAIN/EWOULDBLOCK */ diff --git a/configure.in b/configure.in index 91fd889..7f8a898 100644 --- a/configure.in +++ b/configure.in @@ -35,7 +35,6 @@ AC_CHECK_HEADERS(inttypes.h) AC_CHECK_HEADERS(stdint.h) PA_CHECK_INTTYPES_H_SANE AC_CHECK_HEADERS(fcntl.h) -AC_CHECK_HEADERS(getopt.h) AC_CHECK_HEADERS(grp.h) AC_CHECK_HEADERS(libgen.h) AC_CHECK_HEADERS(memory.h) @@ -155,6 +154,7 @@ XTRA=false PA_SEARCH_LIBS_AND_ADD(xmalloc, iberty) PA_SEARCH_LIBS_AND_ADD(xstrdup, iberty) PA_SEARCH_LIBS_AND_ADD(bsd_signal, bsd, bsdsignal) +PA_SEARCH_LIBS_AND_ADD(getopt_long, getopt, getopt_long) PA_SEARCH_LIBS_AND_ADD(getaddrinfo, [nsl resolv]) if $pa_add_getaddrinfo then diff --git a/lib/getopt.h b/lib/getopt.h new file mode 100644 index 0000000..c1ad561 --- /dev/null +++ b/lib/getopt.h @@ -0,0 +1,23 @@ +#ifndef LIB_GETOPT_H +#define LIB_GETOPT_H + +extern char *optarg; +extern int optind, opterr, optopt; + +struct option { + const char *name; + int has_arg; + int *flag; + int val; +}; + +enum { + no_argument = 0, + required_argument = 1, + optional_argument = 2, +}; + +int getopt_long(int, char *const *, const char *, + const struct option *, int *); + +#endif /* LIB_GETOPT_H */ diff --git a/lib/getopt_long.c b/lib/getopt_long.c new file mode 100644 index 0000000..49d1274 --- /dev/null +++ b/lib/getopt_long.c @@ -0,0 +1,150 @@ +/* + * getopt_long.c + * + * getopt_long(), or at least a common subset thereof: + * + * - Option reordering is not supported + * - -W foo is not supported + * - First optstring character "-" not supported. + */ + +#include "config.h" + +char *optarg; +int optind, opterr, optopt; + +static struct getopt_private_state { + const char *optptr; + const char *last_optstring; + char *const *last_argv; +} pvt; + +static inline const char *option_matches(const char *arg_str, + const char *opt_name) +{ + while (*arg_str != '\0' && *arg_str != '=') { + if (*arg_str++ != *opt_name++) + return NULL; + } + + if (*opt_name) + return NULL; + + return arg_str; +} + +int getopt_long(int argc, char *const *argv, const char *optstring, + const struct option *longopts, int *longindex) +{ + const char *carg; + const char *osptr; + int opt; + + /* getopt() relies on a number of different global state + variables, which can make this really confusing if there is + more than one use of getopt() in the same program. This + attempts to detect that situation by detecting if the + "optstring" or "argv" argument have changed since last time + we were called; if so, reinitialize the query state. */ + + if (optstring != pvt.last_optstring || argv != pvt.last_argv || + optind < 1 || optind > argc) { + /* optind doesn't match the current query */ + pvt.last_optstring = optstring; + pvt.last_argv = argv; + optind = 1; + pvt.optptr = NULL; + } + + carg = argv[optind]; + + /* First, eliminate all non-option cases */ + + if (!carg || carg[0] != '-' || !carg[1]) + return -1; + + if (carg[1] == '-') { + const struct option *lo; + const char *opt_end = NULL; + + optind++; + + /* Either it's a long option, or it's -- */ + if (!carg[2]) { + /* It's -- */ + return -1; + } + + for (lo = longopts; lo->name; lo++) { + if ((opt_end = option_matches(carg+2, lo->name))) + break; + } + if (!opt_end) + return '?'; + + if (longindex) + *longindex = lo-longopts; + + if (*opt_end == '=') { + if (lo->has_arg) + optarg = (char *)opt_end+1; + else + return '?'; + } else if (lo->has_arg == 1) { + if (!(optarg = argv[optind])) + return '?'; + optind++; + } + + if (lo->flag) { + *lo->flag = lo->val; + return 0; + } else { + return lo->val; + } + } + + if ((uintptr_t) (pvt.optptr - carg) > (uintptr_t) strlen(carg)) { + /* Someone frobbed optind, change to new opt. */ + pvt.optptr = carg + 1; + } + + opt = *pvt.optptr++; + + if (opt != ':' && (osptr = strchr(optstring, opt))) { + if (osptr[1] == ':') { + if (*pvt.optptr) { + /* Argument-taking option with attached + argument */ + optarg = (char *)pvt.optptr; + optind++; + } else { + /* Argument-taking option with non-attached + argument */ + if (argv[optind + 1]) { + optarg = (char *)argv[optind+1]; + optind += 2; + } else { + /* Missing argument */ + optind++; + return (optstring[0] == ':') + ? ':' : '?'; + } + } + return opt; + } else { + /* Non-argument-taking option */ + /* pvt.optptr will remember the exact position to + resume at */ + if (!*pvt.optptr) + optind++; + return opt; + } + } else { + /* Unknown option */ + optopt = opt; + if (!*pvt.optptr) + optind++; + return '?'; + } +} diff --git a/tftpd/tftpd.8.in b/tftpd/tftpd.8.in index 049b0a6..a469ac8 100644 --- a/tftpd/tftpd.8.in +++ b/tftpd/tftpd.8.in @@ -3,7 +3,7 @@ .\" Copyright (c) 1990, 1993, 1994 .\" The Regents of the University of California. All rights reserved. .\" -.\" Copyright 2001 H. Peter Anvin - All Rights Reserved +.\" Copyright 2001-2008 H. Peter Anvin - All Rights Reserved .\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions @@ -30,7 +30,7 @@ .\" SUCH DAMAGE. .\" .\"----------------------------------------------------------------------- */ -.TH TFTPD 8 "23 July 2008" "tftp-hpa @@VERSION@@" "System Manager's Manual" +.TH TFTPD 8 "30 July 2008" "tftp-hpa @@VERSION@@" "System Manager's Manual" .SH NAME .B tftpd \- IPv4 Trivial File Transfer Protocol server @@ -48,36 +48,37 @@ but can also run standalone. .PP .SH OPTIONS .TP -.B \-4 +\fB\-\-ipv4\fP, \fB\-4\fP Connect with IPv4 only, even if IPv6 support was compiled in. .TP -.B \-6 +\fB\-\-ipv6\fP, \fB\-6\fP Connect with IPv6 only, if compiled in. .TP -.B \-l +\fB\-l\fP, \fB\-\-listen\fP Run the server in standalone (listen) mode, rather than run from .BR inetd . In listen mode, the -.B \-t +.B \-\-timeout option is ignored, and the -.B \-a +.B \-\-address option can be used to specify a specific local address or port to listen to. .TP -.B \-L +\fB\-\-foreground\fP, \fB\-L\fP Similar to -.B \-l -but do not detach from the foreground process. +.B \-\-listen +but do not detach from the foreground process. Implies +.BR \-\-listen . .TP -\fB\-a\fP \fI[address][:port]\fP +\fB\-\-address\fP \fI[address][:port]\fP, \fB\-a\fP \fI[address][:port]\fP Specify a specific .I address and .I port to listen to when called with the -.B \-l +.B \-\-listen or -.B \-L +.B \-\-foreground option. The default is to listen to the .I tftp port specified in @@ -88,29 +89,29 @@ on all local addresses. Numeric IPv6 adresses must be enclosed in square brackets to avoid ambiguity with the optional port information. .TP -.B \-c +\fB\-\-create\fP, \fB\-c\fP Allow new files to be created. By default, .B tftpd will only allow upload of files that already exist. Files are created with default permissions allowing anyone to read or write them, unless the -.B \-p +.B \-\-permissive or -.B \-U +.B \-\-umask options are specified. .TP -.B \-s +\fB\-\-secure\fP, \fB\-s\fP Change root directory on startup. This means the remote host does not need to pass along the directory as part of the transfer, and may add security. When -.B \-s +.B \-\-secure is specified, exactly one .I directory should be specified on the command line. The use of this option is recommended for security as well as compatibility with some boot ROMs which cannot be easily made to include a directory name in its request. .TP -\fB\-u\fP \fIusername\fP +\fB\-\-user\fP \fIusername\fP, \fB\-u\fP \fIusername\fP Specify the username which .B tftpd will run as; the default is "nobody". The user ID, group ID, and (if @@ -118,21 +119,21 @@ possible on the platform) the supplementary group IDs will be set to the ones specified in the system permission database for this username. .TP -\fB\-U\fP \fIumask\fP +\fB\-\-umask\fP \fIumask\fP, \fB\-U\fP \fIumask\fP Sets the \fIumask\fP for newly created files to the specified value. The default is zero (anyone can read or write) if the -.B \-p +.B \-\-permissive option is not specified, or inherited from the invoking process if -.B \-p +.B \-\-permissive is specified. .TP -.B \-p +\fB\-\-permissive\fP, \fB\-p\fP Perform no additional permissions checks above the normal system-provided access controls for the user specified via the -.B \-u +.B \-\-user option. .TP -\fB\-t\fP \fItimeout\fP +\fB\-\-timeout\fP \fItimeout\fP, \fB\-t\fP \fItimeout\fP When run from .B inetd this specifies how long, in seconds, to wait for a second connection @@ -141,7 +142,7 @@ before terminating the server. will then respawn the server when another request comes in. The default is 900 (15 minutes.) .TP -\fB\-T\fP \fItimeout\fP +\fB\-\-retransmit\fP \fItimeout, \fP\fB\-T\fP \fItimeout\fP Determine the default timeout, in microseconds, before the first packet is retransmitted. This can be modified by the client if the .B timeout @@ -149,7 +150,7 @@ or .B utimeout option is negotiated. The default is 1000000 (1 second.) .TP -\fB\-m\fP \fIremap-file\fP +\fB\-\-mapfile\fP \fIremap-file\fP, \fB\-m\fP \fIremap-file\fP Specify the use of filename remapping. The .I remap-file is a file containing the remapping rules. See the section on filename @@ -157,16 +158,19 @@ remapping below. This option may not be compiled in, see the output of .B "in.tftpd \-V" to verify whether or not it is available. .TP -.B \-v +\fB\-\-verbose\fP, \fB\-v\fP Increase the logging verbosity of .BR tftpd . This flag can be specified multiple times for even higher verbosity. .TP -\fB\-r\fP \fItftp-option\fP +\fB\-\-verbosity\fP \fIvalue\fP +Set the verbosity value to \fIvalue\fP. +.TP +\fB\-\-refuse\fP \fItftp-option\fP, \fB\-r\fP \fItftp-option\fP Indicate that a specific RFC 2347 TFTP option should never be accepted. .TP -\fB\-B\fP \fImax-block-size\fP +\fB\-\-blocksize\fP \fImax-block-size\fP, \fB\-B\fP \fImax-block-size\fP Specifies the maximum permitted block size. The permitted range for this parameter is from 512 to 65464. Some embedded clients request large block sizes and yet do not handle fragmented packets correctly; @@ -175,11 +179,11 @@ MTU on your network minus 32 bytes (20 bytes for IP, 8 for UDP, and 4 for TFTP; less if you use IP options on your network.) For example, on a standard Ethernet (MTU 1500) a value of 1468 is reasonable. .TP -\fB\-R\fP \fIport:port\fP +\fB\-\-port-range\fP \fIport:port\fP, \fB\-R\fP \fIport:port\fP Force the server port number (the Transaction ID) to be in the specified range of port numbers. .TP -.B \-V +\fB\-\-version\fP, \fB\-V\fP Print the version number and configuration to standard output, then exit gracefully. .SH "RFC 2347 OPTION NEGOTIATION" @@ -216,7 +220,7 @@ Set the time before the server retransmits a packet, in seconds. Set the time before the server retransmits a packet, in microseconds. .PP The -.B \-r +.B \-\-refuse option can be used to disable specific options; this may be necessary to work around bugs in specific TFTP client implementations. For example, some TFTP clients have been found to request the @@ -225,7 +229,7 @@ option, but crash with an error if they actually get the option accepted by the server. .SH "FILENAME REMAPPING" The -.B \-m +.B \-\-mapfile option specifies a file which contains filename remapping rules. Each non-comment line (comments begin with hash marks, .BR # ) @@ -340,17 +344,17 @@ The use of TFTP services does not require an account or password on the server system. Due to the lack of authentication information, .B tftpd will allow only publicly readable files (o+r) to be accessed, unless the -.B \-p +.B \-\-permissive option is specified. Files may be written only if they already exist and are publicly writable, unless the -.B \-c +.B \-\-create option is specified. Note that this extends the concept of ``public'' to include all users on all hosts that can be reached through the network; this may not be appropriate on all systems, and its implications should be considered before enabling TFTP service. Typically, some kind of firewall or packet-filter solution should be employed. If appropriately compiled (see the output of -.BR "in.tftpd \-V" ) +.BR "in.tftpd \-\-version" ) .B tftpd will query the .BR hosts_access (5) @@ -360,7 +364,7 @@ and rely on firewalling or kernel-based packet filters instead. .PP The server should be set to run as the user with the lowest possible privilege; please see the -.B \-u +.B \-\-user flag. It is probably a good idea to set up a specific user account for .BR tftpd , rather than letting it run as "nobody", to guard against privilege @@ -372,12 +376,12 @@ with a list of directories by including pathnames as server program arguments on the command line. In this case access is restricted to files whole names are prefixed by one of the given directories. If possible, it is recommended that the -.B \-s +.B \-\-secure flag is used to set up a chroot() environment for the server to run in once a connection has been set up. .PP Finally, the filename remapping -.RB ( \-m +.RB ( \-\-mapfile flag) support can be used to provide a limited amount of additional access control. .SH "CONFORMING TO" diff --git a/tftpd/tftpd.c b/tftpd/tftpd.c index cb78ccb..2266148 100644 --- a/tftpd/tftpd.c +++ b/tftpd/tftpd.c @@ -277,6 +277,34 @@ static int split_port(char **ap, char **pp) return 0; } +enum long_only_options { + OPT_VERBOSITY = 256, +}; + +static struct option long_options[] = { + { "ipv4", 0, NULL, '4' }, + { "ipv6", 0, NULL, '6' }, + { "create", 0, NULL, 'c' }, + { "secure", 0, NULL, 's' }, + { "permissive", 0, NULL, 'p' }, + { "verbose", 0, NULL, 'v' }, + { "verbosity", 1, NULL, OPT_VERBOSITY }, + { "version", 0, NULL, 'V' }, + { "listen", 0, NULL, 'l' }, + { "foreground", 0, NULL, 'L' }, + { "address", 1, NULL, 'a' }, + { "blocksize", 1, NULL, 'B' }, + { "user", 1, NULL, 'u' }, + { "umask", 1, NULL, 'U' }, + { "refuse", 1, NULL, 'r' }, + { "timeout", 1, NULL, 't' }, + { "retransmit", 1, NULL, 'T' }, + { "port-range", 1, NULL, 'R' }, + { "map-file", 1, NULL, 'm' }, + { NULL, 0, NULL, 0 } +}; +static const char short_options[] = "46cspvVlLa:B:u:U:r:t:T:R:m:"; + int main(int argc, char **argv) { struct tftphdr *tp; @@ -317,7 +345,8 @@ int main(int argc, char **argv) srand(time(NULL) ^ getpid()); - while ((c = getopt(argc, argv, "46cspvVlLa:B:u:U:r:t:T:R:m:")) != -1) + while ((c = getopt_long(argc, argv, short_options, long_options, NULL)) + != -1) switch (c) { case '4': ai_fam = AF_INET; @@ -419,6 +448,9 @@ int main(int argc, char **argv) case 'v': verbosity++; break; + case OPT_VERBOSITY: + verbosity = atoi(optarg); + break; case 'V': /* Print configuration to stdout and exit */ printf("%s\n", TFTPD_CONFIG_STR); From bdb90cf17664d0305049d2425f839d7952e5d0a9 Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Wed, 30 Jul 2008 17:18:17 -0700 Subject: [PATCH 112/184] Add .gitignore file Add .gitignore file so "git status" is actually useful. Signed-off-by: H. Peter Anvin --- .gitignore | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7a98dc8 --- /dev/null +++ b/.gitignore @@ -0,0 +1,16 @@ +/MCONFIG +/aconfig.h +/aconfig.h.in +/autom4te.cache +/config.log +/config.status +/configure +/version.h +/tftp/tftp +/tftpd/tftpd +*.1 +*.8 +*.a +*.o +*~ +\#* From 18fd18bd5c101b787acfb764b44415651ab5eba4 Mon Sep 17 00:00:00 2001 From: Karsten Keil Date: Thu, 31 Jul 2008 13:18:11 +0200 Subject: [PATCH 113/184] Improve address type error handling This patch detects numeric address types to avoid unnecessary warnings/errors. It also cleans up error printing to not print error messages on stderr in the deamon case. Signed-off-by: Karsten Keil Signed-off-by: H. Peter Anvin --- common/tftpsubs.c | 9 ++------ configure.in | 1 + tftp/main.c | 9 +++++++- tftpd/tftpd.c | 57 +++++++++++++++++++++++++++++++++++++---------- 4 files changed, 56 insertions(+), 20 deletions(-) diff --git a/common/tftpsubs.c b/common/tftpsubs.c index 9958184..a796bec 100644 --- a/common/tftpsubs.c +++ b/common/tftpsubs.c @@ -310,15 +310,10 @@ set_sock_addr(char *host,union sock_addr *s, char **name) hints.ai_family = s->sa.sa_family; hints.ai_flags = AI_CANONNAME; err = getaddrinfo(strip_address(host), NULL, &hints, &addrResult); - if (err) { - printf("Error : %s\n", gai_strerror(err)); - printf("%s: unknown host\n", host); + if (err) return err; - } - if (addrResult == NULL) { - printf("%s: unknown host\n", host); + if (addrResult == NULL) return EAI_NONAME; - } memcpy(s, addrResult->ai_addr, addrResult->ai_addrlen); if (name) { if (addrResult->ai_canonname) diff --git a/configure.in b/configure.in index 7f8a898..209b838 100644 --- a/configure.in +++ b/configure.in @@ -175,6 +175,7 @@ then AC_SEARCH_LIBS(inet_ntoa, [nsl resolv], , [AC_MSG_ERROR(inet_ntoa not found)]) fi +AC_SEARCH_LIBS(inet_aton, [nsl resolv], ,[AC_MSG_ERROR(inet_aton not found)]) AC_CHECK_FUNCS(daemon, , [XTRA=true; AC_LIBOBJ(daemon)]) AC_CHECK_FUNCS(dup2, , [XTRA=true; AC_LIBOBJ(dup2)]) diff --git a/tftp/main.c b/tftp/main.c index be11957..1b8a881 100644 --- a/tftp/main.c +++ b/tftp/main.c @@ -416,6 +416,8 @@ void setpeer(int argc, char *argv[]) peeraddr.sa.sa_family = ai_fam; err = set_sock_addr(argv[1], &peeraddr, &hostname); if (err) { + printf("Error: %s\n", gai_strerror(err)); + printf("%s: unknown host\n", argv[1]); connected = 0; return; } @@ -557,6 +559,8 @@ void put(int argc, char *argv[]) peeraddr.sa.sa_family = ai_fam; err = set_sock_addr(cp, &peeraddr,&hostname); if (err) { + printf("Error: %s\n", gai_strerror(err)); + printf("%s: unknown host\n", argv[1]); connected = 0; return; } @@ -645,8 +649,11 @@ void get(int argc, char *argv[]) *src++ = 0; peeraddr.sa.sa_family = ai_fam; err = set_sock_addr(argv[n], &peeraddr, &hostname); - if (err) + if (err) { + printf("Warning: %s\n", gai_strerror(err)); + printf("%s: unknown host\n", argv[1]); continue; + } ai_fam = peeraddr.sa.sa_family; connected = 1; } diff --git a/tftpd/tftpd.c b/tftpd/tftpd.c index 2266148..00c511b 100644 --- a/tftpd/tftpd.c +++ b/tftpd/tftpd.c @@ -258,23 +258,36 @@ static int recv_time(int s, void *rbuf, int len, unsigned int flags, static int split_port(char **ap, char **pp) { char *a, *p; + int ret = AF_UNSPEC; a = *ap; +#ifdef HAVE_IPV6 if (is_numeric_ipv6(a)) { if (*a++ != '[') - return 1; + return -1; *ap = a; p = strrchr(a, ']'); if (!p) - return 1; + return -1; *p++ = 0; a = p; + ret = AF_INET6; + p = strrchr(a, ':'); + if (p) + *p++ = 0; + } else +#endif + { + struct in_addr in; + + p = strrchr(a, ':'); + if (p) + *p++ = 0; + if (inet_aton(a, &in)) + ret = AF_INET; } - p = strrchr(a, ':'); - if (p) - *p++ = 0; *pp = p; - return 0; + return ret; } enum long_only_options { @@ -542,7 +555,26 @@ int main(int argc, char **argv) address = tfstrdup(address); err = split_port(&address, &portptr); - if (err) { + switch (err) { + case AF_INET: +#ifdef HAVE_IPV6 + if (fd6 >= 0) { + close(fd6); + fd6 = -1; + ai_fam = AF_INET; + } + break; + case AF_INET6: + if (fd4 >= 0) { + close(fd4); + fd4 = -1; + ai_fam = AF_INET6; + } + break; +#endif + case AF_UNSPEC: + break; + default: syslog(LOG_ERR, "Numeric IPv6 addresses need to be enclosed in []"); exit(EX_USAGE); @@ -556,8 +588,8 @@ int main(int argc, char **argv) (union sock_addr *)&bindaddr4, NULL); if (err) { syslog(LOG_ERR, - "cannot resolve local IPv4 bind address: %s", - address); + "cannot resolve local IPv4 bind address: %s, %s", + address, gai_strerror(err)); exit(EX_NOINPUT); } } @@ -570,13 +602,14 @@ int main(int argc, char **argv) if (fd4 >= 0) { syslog(LOG_ERR, "cannot resolve local IPv6 bind address: %s" - "; using IPv4 only", address); + "(%s); using IPv4 only", + address, gai_strerror(err)); close(fd6); fd6 = -1; } else { syslog(LOG_ERR, - "cannot resolve local IPv6 bind address: %s", - address); + "cannot resolve local IPv6 bind address: %s" + "(%s)", address, gai_strerror(err)); exit(EX_NOINPUT); } } From 544abd789eebfafa0bb3f250f6a824e808e32122 Mon Sep 17 00:00:00 2001 From: Karsten Keil Date: Fri, 1 Aug 2008 16:31:06 +0200 Subject: [PATCH 114/184] Add error messages if address types mismatch If a user does supply a IPv4 or IPv6 address but force the other type with -4 or -6, give an error. The patch also fix the special [::ffff:127.0.1] address handling, it work now if you bind to this address but only if you not force IPv6 only, it seems that the kernel does not signal connections to a IPv6 socket listen on [::ffff:127.0.0.1], if it was bound IPv6 only. I think we can live with it and do not need a special test for this address. Signed-off-by: Karsten Keil Signed-off-by: H. Peter Anvin --- tftpd/tftpd.c | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/tftpd/tftpd.c b/tftpd/tftpd.c index 00c511b..b29c49a 100644 --- a/tftpd/tftpd.c +++ b/tftpd/tftpd.c @@ -327,6 +327,7 @@ int main(int argc, char **argv) struct sockaddr_in bindaddr4; #ifdef HAVE_IPV6 struct sockaddr_in6 bindaddr6; + int force_ipv6 = 0; #endif int n; int fd = -1; @@ -367,6 +368,7 @@ int main(int argc, char **argv) #ifdef HAVE_IPV6 case '6': ai_fam = AF_INET6; + force_ipv6 = 1; break; #endif case 'c': @@ -561,6 +563,12 @@ int main(int argc, char **argv) if (fd6 >= 0) { close(fd6); fd6 = -1; + if (ai_fam == AF_INET6) { + syslog(LOG_ERR, + "Address %s is not in address family AF_INET6", + address); + exit(EX_USAGE); + } ai_fam = AF_INET; } break; @@ -568,6 +576,12 @@ int main(int argc, char **argv) if (fd4 >= 0) { close(fd4); fd4 = -1; + if (ai_fam == AF_INET) { + syslog(LOG_ERR, + "Address %s is not in address family AF_INET", + address); + exit(EX_USAGE); + } ai_fam = AF_INET6; } break; @@ -655,12 +669,12 @@ int main(int argc, char **argv) } #ifdef HAVE_IPV6 if (fd6 >= 0) { - int on = 1; #if defined(IPV6_V6ONLY) - if (setsockopt(fd6, IPPROTO_IPV6, IPV6_V6ONLY, (char*)&on, - sizeof(on))) { - syslog(LOG_ERR, "cannot setsockopt IPV6_V6ONLY %m"); - } + int on = 1; + if (fd4 >= 0 || force_ipv6) + if (setsockopt(fd6, IPPROTO_IPV6, IPV6_V6ONLY, (char*)&on, + sizeof(on))) + syslog(LOG_ERR, "cannot setsockopt IPV6_V6ONLY %m"); #endif if (bind(fd6, (struct sockaddr *)&bindaddr6, sizeof(bindaddr6)) < 0) { From 740871b0f56f371f2c4566999da98f0372f21a1d Mon Sep 17 00:00:00 2001 From: Sridhar Samudrala Date: Fri, 1 Aug 2008 17:01:28 -0700 Subject: [PATCH 115/184] Fix numeric IPv6 address handling The following patch sets additional hints to restrict the addresses returned by getaddrinfo() to specify preferred socket type, protocol and a flag to return only v4/v6 addresses based on the configured addresses. Signed-off-by: Sridhar Samudrala Signed-off-by: H. Peter Anvin --- common/tftpsubs.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/common/tftpsubs.c b/common/tftpsubs.c index a796bec..8c999f6 100644 --- a/common/tftpsubs.c +++ b/common/tftpsubs.c @@ -308,7 +308,9 @@ set_sock_addr(char *host,union sock_addr *s, char **name) memset(&hints, 0, sizeof(hints)); hints.ai_family = s->sa.sa_family; - hints.ai_flags = AI_CANONNAME; + hints.ai_flags = AI_CANONNAME | AI_ADDRCONFIG; + hints.ai_socktype = SOCK_DGRAM; + hints.ai_protocol = IPPROTO_UDP; err = getaddrinfo(strip_address(host), NULL, &hints, &addrResult); if (err) return err; From a1dfd6baf8d7c561c0cbb4000cf82358be8ee920 Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Sat, 9 Aug 2008 21:14:45 -0700 Subject: [PATCH 116/184] Support editline instead of readline Support editline as an alternative to readline. Signed-off-by: H. Peter Anvin --- configure.in | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/configure.in b/configure.in index 209b838..ca21af7 100644 --- a/configure.in +++ b/configure.in @@ -224,7 +224,7 @@ dnl These libraries apply to the client only dnl AH_TEMPLATE([WITH_READLINE], -[Define if we are compiling with readline command-line editing.]) +[Define if we are compiling with readline/editline command-line editing.]) PA_WITH_BOOL(readline, 1, [ --without-readline disable the use of readline command-line editing], @@ -235,13 +235,17 @@ PA_WITH_BOOL(readline, 1, AC_SEARCH_LIBS(tputs, [termcap terminfo]) AC_SEARCH_LIBS(readline, [readline history], - [ - AC_DEFINE(WITH_READLINE) - ], - [], - [-ltermcap]) + [AC_DEFINE(WITH_READLINE)]) AC_CHECK_HEADERS(readline/history.h) - ]) + ], + [AC_CHECK_HEADER(editline/readline.h, + [ + dnl editline may need libtermcap or somesuch... + AC_SEARCH_LIBS(tputs, [termcap terminfo]) + + AC_SEARCH_LIBS(editline, [edit], + [AC_DEFINE(WITH_READLINE)]) + ])]) ],:) TFTP_LIBS="$LIBS $XTRALIBS" From ac5f0ab996b673cbe9476d2f5f00fed5d636b039 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A0=D0=BE=D0=BC=D0=B0=D0=BD=20=D0=94=D0=BE=D0=BD=D1=87?= =?UTF-8?q?=D0=B5=D0=BD=D0=BA=D0=BE?= Date: Tue, 21 Oct 2008 01:02:00 +0400 Subject: [PATCH 117/184] Compilation of tftp-hpa's Git HEAD with no IPv6, and misc This patch does two things: a) makes tftp-hpa to compile on systems with no IPv6 support (there were some IPv6 macros used unconditionally); b) removes a stray binary character, which was annoying. --- config.h | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/config.h b/config.h index bd1dac1..76c7514 100644 --- a/config.h +++ b/config.h @@ -325,6 +325,11 @@ const char *gai_strerror(int); #define AI_CANONNAME 0x0002 /* Request for canonical name. */ #endif +#ifndef AI_ADDRCONFIG +#define AI_ADDRCONFIG 0x0020 /* Use configuration of this host to choose + returned address type.. */ +#endif + #ifndef EAI_NONAME #define EAI_NONAME -2 /* NAME or SERVICE is unknown. */ #endif @@ -335,15 +340,16 @@ const char *gai_strerror(int); #define EAI_MEMORY -10 /* Memory allocation failure. */ #endif #ifndef EAI_SYSTEM -#define EAI_SYSTEM -11 /* System error returned in rrno'. */ +#define EAI_SYSTEM -11 /* System error returned in `errno'. */ #endif #endif +#ifndef INET6_ADDRSTRLEN +#define INET6_ADDRSTRLEN 46 +#endif + #ifndef HAVE_INET_NTOP const char *inet_ntop(int, const void *, char *, socklen_t); -#ifndef INET6_ADDRSTRLEN -#define INET6_ADDRSTRLEN 48 -#endif #endif /* tftp-hpa version and configuration strings */ From 4f715da2946c342c6dae0ee2d08b356b59b86f9d Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Mon, 20 Oct 2008 15:07:59 -0700 Subject: [PATCH 118/184] Update CHANGES --- CHANGES | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGES b/CHANGES index f7ecfe8..e183bed 100644 --- a/CHANGES +++ b/CHANGES @@ -1,6 +1,10 @@ Changes in 0.49: Add IPv6 support. Patch by Karsten Keil. + Support systems with editline instead of readline. + + Support long options in the server. + Changes in 0.48: Unbreak -l -s in the server, which was broken in 0.47. From 920b99dfeeefcfd5d7f2e81cb50b59d42ee9ed00 Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Mon, 20 Oct 2008 15:08:31 -0700 Subject: [PATCH 119/184] Update version for release 0.49 --- version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version b/version index 8ae5e61..a2ff373 100644 --- a/version +++ b/version @@ -1 +1 @@ -0.48 +0.49 From dd50e8b75cd8b57669a75ee1477e88d94acbbc44 Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Fri, 14 Nov 2008 08:31:03 -0800 Subject: [PATCH 120/184] If AI_CANONNAME or AI_ADDRCONFIG don't exist, set them to zero If the AI_CANONNAME or AI_ADDRCONFIG flags are missing, just set them to zero. Signed-off-by: H. Peter Anvin --- config.h | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/config.h b/config.h index 76c7514..0e35438 100644 --- a/config.h +++ b/config.h @@ -321,14 +321,6 @@ int getaddrinfo(const char *, const char *, const struct addrinfo *, void freeaddrinfo(struct addrinfo *); const char *gai_strerror(int); -#ifndef AI_CANONNAME -#define AI_CANONNAME 0x0002 /* Request for canonical name. */ -#endif - -#ifndef AI_ADDRCONFIG -#define AI_ADDRCONFIG 0x0020 /* Use configuration of this host to choose - returned address type.. */ -#endif #ifndef EAI_NONAME #define EAI_NONAME -2 /* NAME or SERVICE is unknown. */ @@ -344,6 +336,14 @@ const char *gai_strerror(int); #endif #endif +#ifndef AI_CANONNAME +#define AI_CANONNAME 0 +#endif + +#ifndef AI_ADDRCONFIG +#define AI_ADDRCONFIG 0 +#endif + #ifndef INET6_ADDRSTRLEN #define INET6_ADDRSTRLEN 46 #endif From 12996491c2f09be58e667146515af828cafca489 Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Fri, 14 Nov 2008 08:32:29 -0800 Subject: [PATCH 121/184] Document getaddrinfo() workaround. --- CHANGES | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGES b/CHANGES index e183bed..9909cd3 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,8 @@ +Changes in 5.0: + Try to on platforms with getaddrinfo() without AI_ADDRCONFIG or + AI_CANONNAME. + + Changes in 0.49: Add IPv6 support. Patch by Karsten Keil. From b0a2a17864a589adbda6f7e30c57a57a175f622e Mon Sep 17 00:00:00 2001 From: Georg Schwarz Date: Sat, 1 Nov 2008 08:21:56 +0100 Subject: [PATCH 122/184] Cast IPv6 address from SOCKADDR_P() to struct in6_addr * We need to cast IPv6 addresses from SOCKADDR_P() to struct in6_addr * on some platforms, including at least MacOS X. --- tftpd/tftpd.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/tftpd/tftpd.c b/tftpd/tftpd.c index b29c49a..3c96844 100644 --- a/tftpd/tftpd.c +++ b/tftpd/tftpd.c @@ -841,9 +841,10 @@ int main(int argc, char **argv) sizeof(bindaddr4.sin_addr)); #ifdef HAVE_IPV6 } else if ((from.sa.sa_family == AF_INET6) && - IN6_IS_ADDR_UNSPECIFIED(SOCKADDR_P(&myaddr))) { - memcpy(SOCKADDR_P(&myaddr), &bindaddr6.sin6_addr, - sizeof(bindaddr6.sin6_addr)); + IN6_IS_ADDR_UNSPECIFIED((struct in6_addr *) + SOCKADDR_P(&myaddr))) { + memcpy(SOCKADDR_P(&myaddr), &bindaddr6.sin6_addr, + sizeof(bindaddr6.sin6_addr)); #endif } } From fcdd859a7513319f68a5ba1261c0f66ef3a28723 Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Mon, 2 Feb 2009 15:14:05 -0800 Subject: [PATCH 123/184] autogen.sh: script to run relevant autotools --- autogen.sh | 3 +++ 1 file changed, 3 insertions(+) create mode 100755 autogen.sh diff --git a/autogen.sh b/autogen.sh new file mode 100755 index 0000000..defd3bd --- /dev/null +++ b/autogen.sh @@ -0,0 +1,3 @@ +#!/bin/sh +autoheader +autoconf From 932277c9a5f90d906296a319c0c3e5f36bf2e8b1 Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Mon, 2 Feb 2009 15:14:27 -0800 Subject: [PATCH 124/184] tftpd: implement the "rollover" option Implement the "rollover" option, to set the rollover block number to anything other than zero. Apparently some idiots have gotten the idea that block numbers should roll over to one, rather than zero. --- tftpd/tftpd.8.in | 4 ++++ tftpd/tftpd.c | 26 ++++++++++++++++++++++++-- 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/tftpd/tftpd.8.in b/tftpd/tftpd.8.in index a469ac8..f42d874 100644 --- a/tftpd/tftpd.8.in +++ b/tftpd/tftpd.8.in @@ -218,6 +218,10 @@ Set the time before the server retransmits a packet, in seconds. .TP \fButimeout\fP (nonstandard) Set the time before the server retransmits a packet, in microseconds. +.TP +\fBrollover\fP (nonstandard) +Set the block number to resume at after a block number rollover. The +default and recommended value is zero. .PP The .B \-\-refuse diff --git a/tftpd/tftpd.c b/tftpd/tftpd.c index 3c96844..f98d52b 100644 --- a/tftpd/tftpd.c +++ b/tftpd/tftpd.c @@ -81,6 +81,7 @@ static unsigned long rexmtval = TIMEOUT; /* Basic timeout value */ static unsigned long maxtimeout = TIMEOUT_LIMIT * TIMEOUT; static int timeout_quit = 0; static sigjmp_buf timeoutbuf; +static uint16_t rollover_val = 0; #define PKTSIZE MAX_SEGSIZE+4 static char buf[PKTSIZE]; @@ -119,6 +120,7 @@ static int set_blksize2(char *, char **); static int set_tsize(char *, char **); static int set_timeout(char *, char **); static int set_utimeout(char *, char **); +static int set_rollover(char *, char **); struct options { const char *o_opt; @@ -129,6 +131,7 @@ struct options { {"tsize", set_tsize}, {"timeout", set_timeout}, {"utimeout", set_utimeout}, + {"rollover", set_rollover}, {NULL, NULL} }; @@ -1160,6 +1163,23 @@ static int set_blksize2(char *val, char **ret) return (1); } +/* + * Set the block number rollover value + */ +static int set_rollover(char *val, char **ret) +{ + uintmax_t ro; + char *vend; + + ro = strtoumax(val, &vend, 10); + if (ro > 65535 || *vend) + return 0; + + rollover_val = (uint16_t)ro; + *ret = val; + return 1; +} + /* * Return a file size (c.f. RFC2349) * For netascii mode, we don't know the size ahead of time; @@ -1542,7 +1562,8 @@ static void tftp_sendfile(struct formats *pf, struct tftphdr *oap, int oacklen) } } - block++; + if (!++block) + block = rollover_val; } while (size == segsize); abort: (void)fclose(file); @@ -1575,7 +1596,8 @@ static void tftp_recvfile(struct formats *pf, struct tftphdr *oap, int oacklen) ap->th_block = htons((u_short) block); acksize = 4; } - block++; + if (!++block) + block = rollover_val; (void)sigsetjmp(timeoutbuf, 1); send_ack: r_timeout = timeout; From acf818880cbbbeb3709050cfcaa0e5503063df20 Mon Sep 17 00:00:00 2001 From: Florian Lohoff Date: Mon, 16 Feb 2009 14:38:48 -0800 Subject: [PATCH 125/184] tftpd: correctly disable PMTU discovery in standalone mode Use the correct file descriptors so we correctly turn off PMTU. --- tftpd/tftpd.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tftpd/tftpd.c b/tftpd/tftpd.c index f98d52b..b3b49ee 100644 --- a/tftpd/tftpd.c +++ b/tftpd/tftpd.c @@ -719,7 +719,7 @@ int main(int argc, char **argv) } /* Disable path MTU discovery */ - pmtu_discovery_off(0); + pmtu_discovery_off(fd); /* This means we don't want to wait() for children */ #ifdef SA_NOCLDWAIT @@ -962,7 +962,7 @@ int main(int argc, char **argv) } /* Disable path MTU discovery */ - pmtu_discovery_off(0); + pmtu_discovery_off(peer); tp = (struct tftphdr *)buf; tp_opcode = ntohs(tp->th_opcode); From 878b024bcd1de09b092fb9106f1b8a271cdb0ff1 Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Mon, 16 Feb 2009 14:49:43 -0800 Subject: [PATCH 126/184] Update version for release 5.0 --- version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version b/version index a2ff373..819e07a 100644 --- a/version +++ b/version @@ -1 +1 @@ -0.49 +5.0 From e7a5fc2d681119f663af4e420ae898f115771712 Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Mon, 16 Feb 2009 14:51:22 -0800 Subject: [PATCH 127/184] Update CHANGES for 5.0 release --- CHANGES | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGES b/CHANGES index 9909cd3..c74fd95 100644 --- a/CHANGES +++ b/CHANGES @@ -2,6 +2,12 @@ Changes in 5.0: Try to on platforms with getaddrinfo() without AI_ADDRCONFIG or AI_CANONNAME. + Implement the "rollover" option, for clients which want block + number to rollover to anything other than zero. + + Correctly disable PMTU in standalone mode. Patch by Florian + Lohoff. + Changes in 0.49: Add IPv6 support. Patch by Karsten Keil. From 2f3a775f8585f510116f341dfd256c1a494743e8 Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Mon, 16 Feb 2009 14:53:50 -0800 Subject: [PATCH 128/184] autogen.sh: just do "make autoconf" We require GNU make, so we can have autoconf built from inside the Makefile. Just make autogen.sh do "make autoconf" for convenience. --- autogen.sh | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/autogen.sh b/autogen.sh index defd3bd..728a381 100755 --- a/autogen.sh +++ b/autogen.sh @@ -1,3 +1,2 @@ #!/bin/sh -autoheader -autoconf +make autoconf From 9ba4e118d2f6806bf5b01b498812eae319fabc87 Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Tue, 24 Feb 2009 16:26:16 -0800 Subject: [PATCH 129/184] tftpd.c: update copyright notice --- tftpd/tftpd.c | 1 + 1 file changed, 1 insertion(+) diff --git a/tftpd/tftpd.c b/tftpd/tftpd.c index b3b49ee..72d3e5d 100644 --- a/tftpd/tftpd.c +++ b/tftpd/tftpd.c @@ -1,5 +1,6 @@ /* * Copyright (c) 1983 Regents of the University of California. + * Copyright (c) 1999-2009 H. Peter Anvin * All rights reserved. * * Redistribution and use in source and binary forms, with or without From 338648870e57ff91699b8d65633213762ce1bc58 Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Tue, 24 Feb 2009 16:27:27 -0800 Subject: [PATCH 130/184] tftpd.8: we're no longer limited to IPv4 --- tftpd/tftpd.8.in | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tftpd/tftpd.8.in b/tftpd/tftpd.8.in index f42d874..367f7ab 100644 --- a/tftpd/tftpd.8.in +++ b/tftpd/tftpd.8.in @@ -3,7 +3,7 @@ .\" Copyright (c) 1990, 1993, 1994 .\" The Regents of the University of California. All rights reserved. .\" -.\" Copyright 2001-2008 H. Peter Anvin - All Rights Reserved +.\" Copyright 2001-2009 H. Peter Anvin - All Rights Reserved .\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions @@ -30,10 +30,10 @@ .\" SUCH DAMAGE. .\" .\"----------------------------------------------------------------------- */ -.TH TFTPD 8 "30 July 2008" "tftp-hpa @@VERSION@@" "System Manager's Manual" +.TH TFTPD 8 "24 February 2009" "tftp-hpa @@VERSION@@" "System Manager's Manual" .SH NAME .B tftpd -\- IPv4 Trivial File Transfer Protocol server +\- Trivial File Transfer Protocol server .SH SYNOPSIS .B in.tftpd .RI [ options... ] From 44c98cf8b63a644ee07ac12a1ed3503875bb2b4d Mon Sep 17 00:00:00 2001 From: Ferenc Wagner Date: Sat, 5 Sep 2009 18:41:53 +0200 Subject: [PATCH 131/184] Downcase datarootdir, so mandir et al. find their defaults mandir et all uses $(datarootdir), not $(DATAROOTDIR) Signed-off-by: Ferenc Wagner Signed-off-by: H. Peter Anvin --- MCONFIG.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MCONFIG.in b/MCONFIG.in index ecff393..2660f7e 100644 --- a/MCONFIG.in +++ b/MCONFIG.in @@ -32,7 +32,7 @@ MANDIR = @mandir@ SBINDIR = @sbindir@ # Data root directory -DATAROOTDIR = @datarootdir@ +datarootdir = @datarootdir@ # Binary suffixes O = @OBJEXT@ From 3f2bc9833d8b4642aac2866f7718c80d5c0b52be Mon Sep 17 00:00:00 2001 From: Ferenc Wagner Date: Sat, 5 Sep 2009 18:41:59 +0200 Subject: [PATCH 132/184] Ensure that the log socket is available for the child Just in case syslog has been restarted, bounce the log socket before the chroot. Signed-off-by: Ferenc Wagner Signed-off-by: H. Peter Anvin --- tftpd/tftpd.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/tftpd/tftpd.c b/tftpd/tftpd.c index 72d3e5d..721ebd8 100644 --- a/tftpd/tftpd.c +++ b/tftpd/tftpd.c @@ -870,6 +870,15 @@ int main(int argc, char **argv) /* Ignore SIGHUP */ set_signal(SIGHUP, SIG_IGN, 0); + /* Make sure the log socket is still connected. This has to be + done before the chroot, while /dev/log is still accessible. + When not running standalone, there is little chance that the + syslog daemon gets restarted by the time we get here. */ + if (secure && standalone) { + closelog(); + openlog(__progname, LOG_PID | LOG_NDELAY, LOG_DAEMON); + } + #ifdef HAVE_TCPWRAPPERS /* Verify if this was a legal request for us. This has to be done before the chroot, while /etc is still accessible. */ From 85029077c86769bc53e17c1cfaf393a1fe6ba59d Mon Sep 17 00:00:00 2001 From: Ferenc Wagner Date: Sat, 5 Sep 2009 18:42:06 +0200 Subject: [PATCH 133/184] Fix comment typo Signed-off-by: Ferenc Wagner Signed-off-by: H. Peter Anvin --- tftpd/tftpd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tftpd/tftpd.c b/tftpd/tftpd.c index 721ebd8..1f5d9d6 100644 --- a/tftpd/tftpd.c +++ b/tftpd/tftpd.c @@ -696,7 +696,7 @@ int main(int argc, char **argv) } #endif /* Daemonize this process */ - /* Note: when running in secure mode (-s), we must not chroot, since + /* Note: when running in secure mode (-s), we must not chdir, since we are already in the proper directory. */ if (!nodaemon && daemon(secure, 0) < 0) { syslog(LOG_ERR, "cannot daemonize: %m"); From 5a27e30ec25b6ff49de9e5bc65d0f358dd69275b Mon Sep 17 00:00:00 2001 From: Ferenc Wagner Date: Sat, 5 Sep 2009 18:42:12 +0200 Subject: [PATCH 134/184] Untabify tftpd.c Signed-off-by: Ferenc Wagner Signed-off-by: H. Peter Anvin --- tftpd/tftpd.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tftpd/tftpd.c b/tftpd/tftpd.c index 1f5d9d6..ff39c85 100644 --- a/tftpd/tftpd.c +++ b/tftpd/tftpd.c @@ -364,7 +364,7 @@ int main(int argc, char **argv) srand(time(NULL) ^ getpid()); while ((c = getopt_long(argc, argv, short_options, long_options, NULL)) - != -1) + != -1) switch (c) { case '4': ai_fam = AF_INET; @@ -467,9 +467,9 @@ int main(int argc, char **argv) case 'v': verbosity++; break; - case OPT_VERBOSITY: - verbosity = atoi(optarg); - break; + case OPT_VERBOSITY: + verbosity = atoi(optarg); + break; case 'V': /* Print configuration to stdout and exit */ printf("%s\n", TFTPD_CONFIG_STR); From c86f82532ec439678e4dd329b7d9c59d3f9ad6bb Mon Sep 17 00:00:00 2001 From: Ferenc Wagner Date: Sat, 5 Sep 2009 18:42:18 +0200 Subject: [PATCH 135/184] Implement the --pidfile option Setting the umask moved later, right before entering the select loop, so that it does not affect the permissions of the pid file. Signed-off-by: Ferenc Wagner Signed-off-by: H. Peter Anvin --- tftpd/tftpd.8.in | 5 +++++ tftpd/tftpd.c | 49 ++++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 50 insertions(+), 4 deletions(-) diff --git a/tftpd/tftpd.8.in b/tftpd/tftpd.8.in index 367f7ab..8486934 100644 --- a/tftpd/tftpd.8.in +++ b/tftpd/tftpd.8.in @@ -133,6 +133,11 @@ system-provided access controls for the user specified via the .B \-\-user option. .TP +\fB\-\-pidfile\fP \fIpidfile\fP, \fB\-P\fP \fIpidfile\fP When run in +standalone mode, write the process ID of the listening server into +\fIpidfile\fP. On normal termination (SIGTERM or SIGINT) the pid file +is automatically removed. +.TP \fB\-\-timeout\fP \fItimeout\fP, \fB\-t\fP \fItimeout\fP When run from .B inetd diff --git a/tftpd/tftpd.c b/tftpd/tftpd.c index ff39c85..69ff121 100644 --- a/tftpd/tftpd.c +++ b/tftpd/tftpd.c @@ -144,6 +144,13 @@ static void handle_sighup(int sig) caught_sighup = 1; } +/* Handle exit requests by SIGTERM and SIGINT */ +static volatile sig_atomic_t exit_signal = 0; +static void handle_exit(int sig) +{ + exit_signal = sig; +} + /* Handle timeout signal or timeout event */ void timer(int sig) { @@ -318,9 +325,10 @@ static struct option long_options[] = { { "retransmit", 1, NULL, 'T' }, { "port-range", 1, NULL, 'R' }, { "map-file", 1, NULL, 'm' }, + { "pidfile", 1, NULL, 'P' }, { NULL, 0, NULL, 0 } }; -static const char short_options[] = "46cspvVlLa:B:u:U:r:t:T:R:m:"; +static const char short_options[] = "46cspvVlLa:B:u:U:r:t:T:R:m:P:"; int main(int argc, char **argv) { @@ -352,6 +360,7 @@ int main(int argc, char **argv) #ifdef WITH_REGEX char *rewrite_file = NULL; #endif + const char *pidfile = NULL; u_short tp_opcode; /* basename() is way too much of a pain from a portability standpoint */ @@ -475,6 +484,9 @@ int main(int argc, char **argv) printf("%s\n", TFTPD_CONFIG_STR); exit(0); break; + case 'P': + pidfile = optarg; + break; default: syslog(LOG_ERR, "Unknown option: '%c'", optopt); break; @@ -507,16 +519,19 @@ int main(int argc, char **argv) exit(EX_NOUSER); } - if (spec_umask || !unixperms) - umask(my_umask); - #ifdef WITH_REGEX if (rewrite_file) rewrite_rules = read_remap_rules(rewrite_file); #endif + if (pidfile && !standalone) { + syslog(LOG_WARNING, "not in standalone mode, ignoring pid file"); + pidfile = NULL; + } + /* If we're running standalone, set up the input port */ if (standalone) { + FILE *pf; #ifdef HAVE_IPV6 if (ai_fam != AF_INET6) { #endif @@ -702,6 +717,20 @@ int main(int argc, char **argv) syslog(LOG_ERR, "cannot daemonize: %m"); exit(EX_OSERR); } + set_signal(SIGTERM, handle_exit, 0); + set_signal(SIGINT, handle_exit, 0); + if (pidfile) { + pf = fopen (pidfile, "w"); + if (!pf) { + syslog(LOG_ERR, "cannot open pid file '%s' for writing: %m", pidfile); + pidfile = NULL; + } else { + if (fprintf(pf, "%d\n", getpid()) < 0) + syslog(LOG_ERR, "error writing pid file '%s': %m", pidfile); + if (fclose(pf)) + syslog(LOG_ERR, "error closing pid file '%s': %m", pidfile); + } + } if (fd6 > fd4) fdmax = fd6; else @@ -734,11 +763,23 @@ int main(int argc, char **argv) lose packets as a result. */ set_signal(SIGHUP, handle_sighup, 0); + if (spec_umask || !unixperms) + umask(my_umask); + while (1) { fd_set readset; struct timeval tv_waittime; int rv; + if (exit_signal) { /* happens in standalone mode only */ + if (pidfile && unlink(pidfile)) { + syslog(LOG_WARNING, "error removing pid file '%s': %m", pidfile); + exit(EX_OSERR); + } else { + exit(0); + } + } + if (caught_sighup) { caught_sighup = 0; if (standalone) { From ab382980ae555fd6024c4ec24a1d5bddb527829f Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Mon, 14 Sep 2009 14:44:54 -0700 Subject: [PATCH 136/184] Fix man page formatting --- tftpd/tftpd.8.in | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tftpd/tftpd.8.in b/tftpd/tftpd.8.in index 8486934..78b4cfb 100644 --- a/tftpd/tftpd.8.in +++ b/tftpd/tftpd.8.in @@ -30,7 +30,7 @@ .\" SUCH DAMAGE. .\" .\"----------------------------------------------------------------------- */ -.TH TFTPD 8 "24 February 2009" "tftp-hpa @@VERSION@@" "System Manager's Manual" +.TH TFTPD 8 "14 September 2009" "tftp-hpa @@VERSION@@" "System Manager's Manual" .SH NAME .B tftpd \- Trivial File Transfer Protocol server @@ -133,10 +133,10 @@ system-provided access controls for the user specified via the .B \-\-user option. .TP -\fB\-\-pidfile\fP \fIpidfile\fP, \fB\-P\fP \fIpidfile\fP When run in -standalone mode, write the process ID of the listening server into -\fIpidfile\fP. On normal termination (SIGTERM or SIGINT) the pid file -is automatically removed. +\fB\-\-pidfile\fP \fIpidfile\fP, \fB\-P\fP \fIpidfile\fP +When run in standalone mode, write the process ID of the listening +server into \fIpidfile\fP. On normal termination (SIGTERM or SIGINT) +the pid file is automatically removed. .TP \fB\-\-timeout\fP \fItimeout\fP, \fB\-t\fP \fItimeout\fP When run from From e7a7b194833a5283ac5c4cf24db370e70cf2674f Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Mon, 14 Sep 2009 14:46:34 -0700 Subject: [PATCH 137/184] Update CHANGES for future 5.1 release Signed-off-by: H. Peter Anvin --- CHANGES | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/CHANGES b/CHANGES index c74fd95..cdb9a2e 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,12 @@ +Changes in 5.1: + Add -P option to write a PID file. Patch by Ferenc Wagner. + + Bounce the syslog socket in standalone mode, in case the + syslog daemon has been restarted. Patch by Ferenc Wagner. + + Build fixes. + + Changes in 5.0: Try to on platforms with getaddrinfo() without AI_ADDRCONFIG or AI_CANONNAME. From a63534e6e6fa351d1c51b1d7cd14f916af3f835a Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Fri, 11 Dec 2009 16:05:48 -0800 Subject: [PATCH 138/184] recvfrom: fix the type of the fallthrough case If we can't figure out the source address, we have the "fall on our face" version of myrecvfrom(); make sure its prototype matches. This handles building on machines where sockaddr_t != int and yet there is no way to get the source address. This apparently affects at least one version of Solaris. Reported-by: Georg Schwarz Signed-off-by: H. Peter Anvin --- tftpd/recvfrom.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tftpd/recvfrom.c b/tftpd/recvfrom.c index 389ba82..3ee5642 100644 --- a/tftpd/recvfrom.c +++ b/tftpd/recvfrom.c @@ -245,7 +245,8 @@ myrecvfrom(int s, void *buf, int len, unsigned int flags, int myrecvfrom(int s, void *buf, int len, unsigned int flags, - struct sockaddr *from, int *fromlen, union sock_addr *myaddr) + struct sockaddr *from, socklen_t * fromlen, + union sock_addr *myaddr) { /* There is no way we can get the local address, fudge it */ From aeb1c31bae431ad3b9269befa221229458adb854 Mon Sep 17 00:00:00 2001 From: Tim Newsome Date: Wed, 8 Sep 2010 14:11:42 -0700 Subject: [PATCH 139/184] tftpd: Don't resend the OACK packet on block number wrap When uploading a file that is larger than 32MB (with standard block size), the block number will roll over. If it rolls over to 0, the code mistakenly resends the option ack frame instead of acknowledging the 0 data block. This change fixes that behavior. --- tftpd/tftpd.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tftpd/tftpd.c b/tftpd/tftpd.c index 69ff121..0a8da72 100644 --- a/tftpd/tftpd.c +++ b/tftpd/tftpd.c @@ -1646,6 +1646,10 @@ static void tftp_recvfile(struct formats *pf, struct tftphdr *oap, int oacklen) ap->th_opcode = htons((u_short) ACK); ap->th_block = htons((u_short) block); acksize = 4; + /* If we're sending a regular ACK, that means we have successfully + * sent the OACK. Clear oap so that we won't try to send another + * OACK when the block number wraps back to 0. */ + oap = NULL; } if (!++block) block = rollover_val; From 915f62f5c76d7bd953b758d477b542acf6a3bf65 Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Mon, 20 Sep 2010 17:49:10 -0700 Subject: [PATCH 140/184] CHANGES: document wraparound fix --- CHANGES | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES b/CHANGES index cdb9a2e..5238089 100644 --- a/CHANGES +++ b/CHANGES @@ -6,6 +6,9 @@ Changes in 5.1: Build fixes. + Fix handling of block number wraparound after a successful + options negotiation. + Changes in 5.0: Try to on platforms with getaddrinfo() without AI_ADDRCONFIG or From 0b5732e263fa439906b298cdcab8f2dba3f93ce4 Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Thu, 5 May 2011 10:08:53 -0700 Subject: [PATCH 141/184] remap: change the mode argument from a boolean to a character Instead of taking a boolean value for get/put, pass a character; this allows us to extend the number of possibilities in the future. Signed-off-by: H. Peter Anvin --- tftpd/remap.c | 21 ++++++++------------- tftpd/remap.h | 2 +- tftpd/tftpd.c | 3 ++- 3 files changed, 11 insertions(+), 15 deletions(-) diff --git a/tftpd/remap.c b/tftpd/remap.c index cdb9062..1e7abe7 100644 --- a/tftpd/remap.c +++ b/tftpd/remap.c @@ -30,14 +30,13 @@ #define RULE_EXIT 0x04 /* Exit after matching this rule */ #define RULE_RESTART 0x08 /* Restart at the top after matching this rule */ #define RULE_ABORT 0x10 /* Terminate processing with an error */ -#define RULE_GETONLY 0x20 /* Applicable to GET only */ -#define RULE_PUTONLY 0x40 /* Applicable to PUT only */ -#define RULE_INVERSE 0x80 /* Execute if regex *doesn't* match */ +#define RULE_INVERSE 0x20 /* Execute if regex *doesn't* match */ struct rule { struct rule *next; int nrule; int rule_flags; + char rule_mode; regex_t rx; const char *pattern; }; @@ -221,15 +220,13 @@ static int parseline(char *line, struct rule *r, int lineno) case 'i': rxflags |= REG_ICASE; break; - case 'G': - r->rule_flags |= RULE_GETONLY; - break; - case 'P': - r->rule_flags |= RULE_PUTONLY; - break; case '~': r->rule_flags |= RULE_INVERSE; break; + case 'G': + case 'P': + r->rule_mode = *p; + break; default: syslog(LOG_ERR, "Remap command \"%s\" on line %d contains invalid char \"%c\"", @@ -329,7 +326,7 @@ void freerules(struct rule *r) /* Execute a rule set on a string; returns a malloc'd new string. */ char *rewrite_string(const char *input, const struct rule *rules, - int is_put, match_pattern_callback macrosub, + char mode, match_pattern_callback macrosub, const char **errmsg) { char *current = tfstrdup(input); @@ -348,10 +345,8 @@ char *rewrite_string(const char *input, const struct rule *rules, } for (ruleptr = rules; ruleptr; ruleptr = ruleptr->next) { - if (((ruleptr->rule_flags & RULE_GETONLY) && is_put) || - ((ruleptr->rule_flags & RULE_PUTONLY) && !is_put)) { + if (ruleptr->rule_mode && ruleptr->rule_mode != mode) continue; /* Rule not applicable, try next */ - } if (!deadman--) { syslog(LOG_WARNING, diff --git a/tftpd/remap.h b/tftpd/remap.h index 3830b5c..69ca08d 100644 --- a/tftpd/remap.h +++ b/tftpd/remap.h @@ -35,7 +35,7 @@ struct rule *parserulefile(FILE *); void freerules(struct rule *); /* Execute a rule set on a string; returns a malloc'd new string. */ -char *rewrite_string(const char *, const struct rule *, int, +char *rewrite_string(const char *, const struct rule *, char, match_pattern_callback, const char **); #endif /* WITH_REGEX */ diff --git a/tftpd/tftpd.c b/tftpd/tftpd.c index 69ff121..bdbea6d 100644 --- a/tftpd/tftpd.c +++ b/tftpd/tftpd.c @@ -1388,7 +1388,8 @@ static char *rewrite_access(char *filename, int mode, const char **msg) { if (rewrite_rules) { char *newname = - rewrite_string(filename, rewrite_rules, mode != RRQ, + rewrite_string(filename, rewrite_rules, + mode != RRQ ? 'P' : 'G', rewrite_macros, msg); filename = newname; } From ad5aab9281b6d67b77537b4aabfe5bc070e7fe99 Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Thu, 5 May 2011 10:11:59 -0700 Subject: [PATCH 142/184] tftpd: constipate struct formats struct formats should be static const; make it so and mark all users const. Signed-off-by: H. Peter Anvin --- tftpd/tftpd.c | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/tftpd/tftpd.c b/tftpd/tftpd.c index bdbea6d..1f946f6 100644 --- a/tftpd/tftpd.c +++ b/tftpd/tftpd.c @@ -1023,18 +1023,19 @@ int main(int argc, char **argv) } static char *rewrite_access(char *, int, const char **); -static int validate_access(char *, int, struct formats *, const char **); -static void tftp_sendfile(struct formats *, struct tftphdr *, int); -static void tftp_recvfile(struct formats *, struct tftphdr *, int); +static int validate_access(char *, int, const struct formats *, const char **); +static void tftp_sendfile(const struct formats *, struct tftphdr *, int); +static void tftp_recvfile(const struct formats *, struct tftphdr *, int); struct formats { const char *f_mode; char *(*f_rewrite) (char *, int, const char **); - int (*f_validate) (char *, int, struct formats *, const char **); - void (*f_send) (struct formats *, struct tftphdr *, int); - void (*f_recv) (struct formats *, struct tftphdr *, int); + int (*f_validate) (char *, int, const struct formats *, const char **); + void (*f_send) (const struct formats *, struct tftphdr *, int); + void (*f_recv) (const struct formats *, struct tftphdr *, int); int f_convert; -} formats[] = { +}; +static const struct formats formats[] = { { "netascii", rewrite_access, validate_access, tftp_sendfile, tftp_recvfile, 1}, { @@ -1050,7 +1051,7 @@ int tftp(struct tftphdr *tp, int size) { char *cp, *end; int argn, ecode; - struct formats *pf = NULL; + const struct formats *pf = NULL; char *origfilename; char *filename, *mode = NULL; const char *errmsgptr; @@ -1418,7 +1419,7 @@ static FILE *file; * given as we have no login directory. */ static int validate_access(char *filename, int mode, - struct formats *pf, const char **errmsg) + const struct formats *pf, const char **errmsg) { struct stat stbuf; int i, len; @@ -1526,7 +1527,7 @@ static int validate_access(char *filename, int mode, /* * Send the requested file. */ -static void tftp_sendfile(struct formats *pf, struct tftphdr *oap, int oacklen) +static void tftp_sendfile(const struct formats *pf, struct tftphdr *oap, int oacklen) { struct tftphdr *dp; struct tftphdr *ap; /* ack packet */ @@ -1624,7 +1625,7 @@ static void tftp_sendfile(struct formats *pf, struct tftphdr *oap, int oacklen) /* * Receive a file. */ -static void tftp_recvfile(struct formats *pf, struct tftphdr *oap, int oacklen) +static void tftp_recvfile(const struct formats *pf, struct tftphdr *oap, int oacklen) { struct tftphdr *dp; int n, size; From 2864d83feaac090865acb9c147c17d9793ef53dc Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Thu, 12 May 2011 19:16:17 -0700 Subject: [PATCH 143/184] tftpd: try to handle duplicate WRQ packets Duplicate WRQ packets can really hurt, since they end up accessing the same file. This attempts to lock the file, which should work for the case where a correctly implemented TFTP stack uses the same session ID (port number) for each retry; in any other case they look like multiple sessions to the same file and it is a crapshoot if we end up with the correct one. Signed-off-by: H. Peter Anvin --- configure.in | 6 ++++++ tftpd/tftpd.c | 48 +++++++++++++++++++++++++++++++++++++----------- 2 files changed, 43 insertions(+), 11 deletions(-) diff --git a/configure.in b/configure.in index ca21af7..7ab7c5a 100644 --- a/configure.in +++ b/configure.in @@ -46,6 +46,7 @@ AC_CHECK_HEADERS(strings.h) AC_CHECK_HEADERS(sysexits.h) AC_CHECK_HEADERS(time.h) AC_CHECK_HEADERS(unistd.h) +AC_CHECK_HEADERS(sys/file.h) AC_CHECK_HEADERS(sys/filio.h) AC_CHECK_HEADERS(sys/stat.h) AC_CHECK_HEADERS(sys/time.h) @@ -140,6 +141,11 @@ PA_HEADER_DEFINES(fcntl.h, int, O_NONBLOCK) PA_HEADER_DEFINES(fcntl.h, int, O_BINARY) PA_HEADER_DEFINES(fcntl.h, int, O_TEXT) +PA_HEADER_DEFINES(fcntl.h, int, F_SETLK) + +PA_HEADER_DEFINES(sys/file.h, int, LOCK_SH) +PA_HEADER_DEFINES(sys/file.h, int, LOCK_EX) + AH_TEMPLATE([HAVE_SIGSETJMP], [Define if we have sigsetjmp, siglongjmp and sigjmp_buf.]) PA_SIGSETJMP([AC_DEFINE(HAVE_SIGSETJMP)]) diff --git a/tftpd/tftpd.c b/tftpd/tftpd.c index c122d61..4c4d4ed 100644 --- a/tftpd/tftpd.c +++ b/tftpd/tftpd.c @@ -179,6 +179,26 @@ static struct rule *read_remap_rules(const char *file) } #endif +/* + * Rules for locking files; return 0 on success, -1 on failure + */ +static int lock_file(int fd, int lock_write) +{ +#if defined(HAVE_FCNTL) && defined(HAVE_F_SETLK_DEFINITION) + struct flock fl; + + fl.l_type = lock_write ? F_WRLCK : F_RDLCK; + fl.l_whence = SEEK_SET; + fl.l_start = 0; + fl.l_len = 0; /* Whole file */ + return fcntl(fd, F_SETLK, &fl); +#elif defined(HAVE_LOCK_SH_DEFINITION) + return flock(fd, lock_write ? LOCK_EX|LOCK_NB : LOCK_SH|LOCK_NB); +#else + return 0; /* Hope & pray... */ +#endif +} + static void set_socket_nonblock(int fd, int flag) { int err; @@ -1463,11 +1483,13 @@ static int validate_access(char *filename, int mode, * We use different a different permissions scheme if `cancreate' is * set. */ - wmode = O_WRONLY | - (cancreate ? O_CREAT : 0) | - (unixperms ? O_TRUNC : 0) | (pf->f_convert ? O_TEXT : O_BINARY); + wmode = O_WRONLY | (cancreate ? O_CREAT : 0) | (pf->f_convert ? O_TEXT : O_BINARY); rmode = O_RDONLY | (pf->f_convert ? O_TEXT : O_BINARY); +#ifndef HAVE_FTRUNCATE + wmode |= O_TRUNC; /* This really sucks on a dupe */ +#endif + fd = open(filename, mode == RRQ ? rmode : wmode, 0666); if (fd < 0) { switch (errno) { @@ -1486,6 +1508,10 @@ static int validate_access(char *filename, int mode, if (fstat(fd, &stbuf) < 0) exit(EX_OSERR); /* This shouldn't happen */ + /* A duplicate RRQ or (worse!) WRQ packet could really cause havoc... */ + if (lock_file(fd, mode != RRQ)) + exit(0); + if (mode == RRQ) { if (!unixperms && (stbuf.st_mode & (S_IREAD >> 6)) == 0) { *errmsg = "File must have global read permissions"; @@ -1500,15 +1526,15 @@ static int validate_access(char *filename, int mode, *errmsg = "File must have global write permissions"; return (EACCESS); } - - /* We didn't get to truncate the file at open() time */ -#ifdef HAVE_FTRUNCATE - if (ftruncate(fd, (off_t) 0)) { - *errmsg = "Cannot reset file size"; - return (EACCESS); - } -#endif } + +#ifdef HAVE_FTRUNCATE + /* We didn't get to truncate the file at open() time */ + if (ftruncate(fd, (off_t) 0)) { + *errmsg = "Cannot reset file size"; + return (EACCESS); + } +#endif tsize = 0; tsize_ok = 1; } From f3035c45bc50bb5cac87ca01e7ef6a12485184f8 Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Fri, 10 Jun 2011 11:47:02 -0700 Subject: [PATCH 144/184] tftpd: simplify option parsing Simplify the option parsing to make use of the fact that all the options we support are integer options. This fixes a buffer overflow in the utimeout option. Reported-by: Timo Warns Signed-off-by: H. Peter Anvin --- tftpd/tftpd.c | 154 ++++++++++++++++++++++++-------------------------- 1 file changed, 73 insertions(+), 81 deletions(-) diff --git a/tftpd/tftpd.c b/tftpd/tftpd.c index 4c4d4ed..dd3c982 100644 --- a/tftpd/tftpd.c +++ b/tftpd/tftpd.c @@ -114,18 +114,18 @@ static struct rule *rewrite_rules = NULL; int tftp(struct tftphdr *, int); static void nak(int, const char *); static void timer(int); -static void do_opt(char *, char *, char **); +static void do_opt(const char *, const char *, char **); -static int set_blksize(char *, char **); -static int set_blksize2(char *, char **); -static int set_tsize(char *, char **); -static int set_timeout(char *, char **); -static int set_utimeout(char *, char **); -static int set_rollover(char *, char **); +static int set_blksize(uintmax_t *); +static int set_blksize2(uintmax_t *); +static int set_tsize(uintmax_t *); +static int set_timeout(uintmax_t *); +static int set_utimeout(uintmax_t *); +static int set_rollover(uintmax_t *); struct options { const char *o_opt; - int (*o_fnc) (char *, char **); + int (*o_fnc)(uintmax_t *); } options[] = { {"blksize", set_blksize}, {"blksize2", set_blksize2}, @@ -1175,48 +1175,38 @@ static int blksize_set; /* * Set a non-standard block size (c.f. RFC2348) */ -static int set_blksize(char *val, char **ret) +static int set_blksize(uintmax_t *vp) { - static char b_ret[6]; - unsigned int sz; - char *vend; + uintmax_t sz = *vp; - sz = (unsigned int)strtoul(val, &vend, 10); - - if (blksize_set || *vend) + if (blksize_set) return 0; if (sz < 8) - return (0); + return 0; else if (sz > max_blksize) sz = max_blksize; - segsize = sz; - sprintf(*ret = b_ret, "%u", sz); - + *vp = segsize = sz; blksize_set = 1; - - return (1); + return 1; } /* * Set a power-of-two block size (nonstandard) */ -static int set_blksize2(char *val, char **ret) +static int set_blksize2(uintmax_t *vp) { - static char b_ret[6]; - unsigned int sz; - char *vend; + uintmax_t sz = *vp; - sz = (unsigned int)strtoul(val, &vend, 10); - - if (blksize_set || *vend) + if (blksize_set) return 0; if (sz < 8) return (0); else if (sz > max_blksize) sz = max_blksize; + else /* Convert to a power of two */ if (sz & (sz - 1)) { @@ -1227,29 +1217,23 @@ static int set_blksize2(char *val, char **ret) sz = sz1; } - segsize = sz; - sprintf(*ret = b_ret, "%u", sz); - + *vp = segsize = sz; blksize_set = 1; - - return (1); + return 1; } /* * Set the block number rollover value */ -static int set_rollover(char *val, char **ret) +static int set_rollover(uintmax_t *vp) { - uintmax_t ro; - char *vend; + uintmax_t ro = *vp; + + if (ro > 65535) + return 0; - ro = strtoumax(val, &vend, 10); - if (ro > 65535 || *vend) - return 0; - - rollover_val = (uint16_t)ro; - *ret = val; - return 1; + rollover_val = (uint16_t)ro; + return 1; } /* @@ -1257,22 +1241,18 @@ static int set_rollover(char *val, char **ret) * For netascii mode, we don't know the size ahead of time; * so reject the option. */ -static int set_tsize(char *val, char **ret) +static int set_tsize(uintmax_t *vp) { - static char b_ret[sizeof(uintmax_t) * CHAR_BIT / 3 + 2]; - uintmax_t sz; - char *vend; + uintmax_t sz = *vp; - sz = strtoumax(val, &vend, 10); - - if (!tsize_ok || *vend) + if (!tsize_ok) return 0; if (sz == 0) - sz = (uintmax_t) tsize; + sz = tsize; - sprintf(*ret = b_ret, "%" PRIuMAX, sz); - return (1); + *vp = sz; + return 1; } /* @@ -1280,74 +1260,86 @@ static int set_tsize(char *val, char **ret) * to be the (default) retransmission timeout, but being an * integer in seconds it seems a bit limited. */ -static int set_timeout(char *val, char **ret) +static int set_timeout(uintmax_t *vp) { - static char b_ret[4]; - unsigned long to; - char *vend; + uintmax_t to = *vp; - to = strtoul(val, &vend, 10); - - if (to < 1 || to > 255 || *vend) + if (to < 1 || to > 255) return 0; rexmtval = timeout = to * 1000000UL; maxtimeout = rexmtval * TIMEOUT_LIMIT; - sprintf(*ret = b_ret, "%lu", to); - return (1); + return 1; } /* Similar, but in microseconds. We allow down to 10 ms. */ -static int set_utimeout(char *val, char **ret) +static int set_utimeout(uintmax_t *vp) { - static char b_ret[4]; - unsigned long to; - char *vend; + uintmax_t to = *vp; - to = strtoul(val, &vend, 10); - - if (to < 10000UL || to > 255000000UL || *vend) + if (to < 10000UL || to > 255000000UL) return 0; rexmtval = timeout = to; maxtimeout = rexmtval * TIMEOUT_LIMIT; - sprintf(*ret = b_ret, "%lu", to); - return (1); + return 1; } /* - * Parse RFC2347 style options + * Conservative calculation for the size of a buffer which can hold an + * arbitrary integer */ -static void do_opt(char *opt, char *val, char **ap) +#define OPTBUFSIZE (sizeof(uintmax_t) * CHAR_BIT / 3 + 3) + +/* + * Parse RFC2347 style options; we limit the arguments to positive + * integers which matches all our current options. + */ +static void do_opt(const char *opt, const char *val, char **ap) { struct options *po; - char *ret; + char retbuf[OPTBUFSIZE]; + char *p = *ap; + size_t optlen, retlen; + char *vend; + uintmax_t v; /* Global option-parsing variables initialization */ blksize_set = 0; - if (!*opt) + if (!*opt || !*val) return; + errno = 0; + v = strtoumax(val, &vend, 10); + if (*vend || errno == ERANGE) + return; + for (po = options; po->o_opt; po++) if (!strcasecmp(po->o_opt, opt)) { - if (po->o_fnc(val, &ret)) { - if (*ap + strlen(opt) + strlen(ret) + 2 >= - ackbuf + sizeof(ackbuf)) { + if (po->o_fnc(&v)) { + optlen = strlen(opt); + retlen = sprintf(retbuf, "%"PRIuMAX, v); + + if (p + optlen + retlen + 2 >= ackbuf + sizeof(ackbuf)) { nak(EOPTNEG, "Insufficient space for options"); exit(0); } - *ap = strrchr(strcpy(strrchr(strcpy(*ap, opt), '\0') + 1, - ret), '\0') + 1; + + memcpy(p, opt, optlen+1); + p += optlen+1; + memcpy(p, retbuf, retlen+1); + p += retlen+1; } else { nak(EOPTNEG, "Unsupported option(s) requested"); exit(0); } break; } - return; + + *ap = p; } #ifdef WITH_REGEX From bd250a597f74408458bc7c8af02a52712d658965 Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Wed, 22 Jun 2011 16:27:48 -0700 Subject: [PATCH 145/184] CHANGES: Document bug fix Signed-off-by: H. Peter Anvin --- CHANGES | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGES b/CHANGES index 5238089..6df0d97 100644 --- a/CHANGES +++ b/CHANGES @@ -9,6 +9,8 @@ Changes in 5.1: Fix handling of block number wraparound after a successful options negotiation. + Fix a buffer overflow in option parsing. + Changes in 5.0: Try to on platforms with getaddrinfo() without AI_ADDRCONFIG or From 464be3090b6ddfc9afb879887f76373dc61f6e24 Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Wed, 22 Jun 2011 16:28:38 -0700 Subject: [PATCH 146/184] tftpd: add Intel copyright header Part of my Intel job now... Signed-off-by: H. Peter Anvin --- tftpd/tftpd.c | 1 + 1 file changed, 1 insertion(+) diff --git a/tftpd/tftpd.c b/tftpd/tftpd.c index dd3c982..1873e70 100644 --- a/tftpd/tftpd.c +++ b/tftpd/tftpd.c @@ -1,6 +1,7 @@ /* * Copyright (c) 1983 Regents of the University of California. * Copyright (c) 1999-2009 H. Peter Anvin + * Copyright (c) 2011 Intel Corporation; author: H. Peter Anvin * All rights reserved. * * Redistribution and use in source and binary forms, with or without From f6a1282fecc1dbd4b71497ff335ceefe5b2e2541 Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Wed, 22 Jun 2011 16:29:59 -0700 Subject: [PATCH 147/184] Update version for release 5.1 --- version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version b/version index 819e07a..a75b92f 100644 --- a/version +++ b/version @@ -1 +1 @@ -5.0 +5.1 From badf05140d3c2408715a73a52c0f35887e337c04 Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Wed, 22 Jun 2011 16:32:56 -0700 Subject: [PATCH 148/184] spec: BuildPreReq -> BuildRequires; need -devel package Signed-off-by: H. Peter Anvin --- tftp.spec.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tftp.spec.in b/tftp.spec.in index 7a8c7b7..cd5e4cc 100644 --- a/tftp.spec.in +++ b/tftp.spec.in @@ -5,7 +5,7 @@ Release: 1 License: BSD Group: Applications/Internet Source0: http://www.kernel.org/pub/software/network/tftp/tftp-hpa-%{version}.tar.gz -BuildPreReq: tcp_wrappers +BuildRequires: tcp_wrappers-devel BuildRoot: %{_tmppath}/%{name}-root %description From c6d2c36b1a2b1986cab7aebb72d120fa118bdfeb Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Thu, 8 Dec 2011 21:48:06 -0800 Subject: [PATCH 149/184] tftpd: the "is this address local" algorithm no longer works on Linux Linux no longer tries to match the local address with the remote one, so address_is_local() fails. Try instead to simply see if we can bind to the explicit address. Signed-off-by: H. Peter Anvin --- tftpd/recvfrom.c | 39 +++++++++++++++++++++++---------------- 1 file changed, 23 insertions(+), 16 deletions(-) diff --git a/tftpd/recvfrom.c b/tftpd/recvfrom.c index 3ee5642..24abce3 100644 --- a/tftpd/recvfrom.c +++ b/tftpd/recvfrom.c @@ -51,52 +51,59 @@ struct in_pktinfo { #endif /* - * Check to see if this is a valid local address. If so, we should - * end up having the same local and remote address when trying to - * bind to it. + * Check to see if this is a valid local address, meaning that we can + * legally bind to it. */ static int address_is_local(const union sock_addr *addr) { - union sock_addr sa; + union sock_addr sa1, sa2; int sockfd = -1; int e; int rv = 0; socklen_t addrlen; + memcpy(&sa1, addr, sizeof sa1); + /* Multicast or universal broadcast address? */ - if (addr->sa.sa_family == AF_INET) { - if (ntohl(addr->si.sin_addr.s_addr) >= (224UL << 24)) + if (sa1.sa.sa_family == AF_INET) { + if (ntohl(sa1.si.sin_addr.s_addr) >= (224UL << 24)) return 0; + sa1.si.sin_port = 0; /* Any port */ } #ifdef HAVE_IPV6 - else if (addr->sa.sa_family == AF_INET6) { - if (IN6_IS_ADDR_MULTICAST(&addr->s6.sin6_addr)) + else if (sa1.sa.sa_family == AF_INET6) { + if (IN6_IS_ADDR_MULTICAST(&sa1.s6.sin6_addr)) return 0; + sa1.s6.sin6_port = 0; /* Any port */ } #endif else return 0; - sockfd = socket(addr->sa.sa_family, SOCK_DGRAM, 0); + sockfd = socket(sa1.sa.sa_family, SOCK_DGRAM, 0); if (sockfd < 0) goto err; - if (connect(sockfd, &addr->sa, SOCKLEN(addr))) + if (bind(sockfd, &sa1.sa, SOCKLEN(&sa1))) goto err; addrlen = SOCKLEN(addr); - if (getsockname(sockfd, (struct sockaddr *)&sa, &addrlen)) + if (getsockname(sockfd, (struct sockaddr *)&sa2, &addrlen)) goto err; - if (addr->sa.sa_family == AF_INET) - rv = sa.si.sin_addr.s_addr == addr->si.sin_addr.s_addr; + if (sa1.sa.sa_family != sa2.sa.sa_family) + goto err; + + if (sa2.sa.sa_family == AF_INET) + rv = sa1.si.sin_addr.s_addr == sa2.si.sin_addr.s_addr; #ifdef HAVE_IPV6 - else if (addr->sa.sa_family == AF_INET6) - rv = IN6_ARE_ADDR_EQUAL(&sa.s6.sin6_addr, &addr->s6.sin6_addr); + else if (sa2.sa.sa_family == AF_INET6) + rv = IN6_ARE_ADDR_EQUAL(&sa1.s6.sin6_addr, &sa2.s6.sin6_addr); #endif else rv = 0; - err: + +err: e = errno; if (sockfd >= 0) From f08a34ede181274efe845212737e352efbc6ff6d Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Sun, 11 Dec 2011 14:13:13 -0800 Subject: [PATCH 150/184] CHANGES: document is address local fix Signed-off-by: H. Peter Anvin --- CHANGES | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGES b/CHANGES index 6df0d97..6b7b53c 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,8 @@ +Changes in 5.2: + Fix breakage on newer Linux when a single interface has + multiple IP addresses. + + Changes in 5.1: Add -P option to write a PID file. Patch by Ferenc Wagner. From b9708e22014e03c9fa35ff7ced20e46194c39dc6 Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Sun, 11 Dec 2011 14:13:52 -0800 Subject: [PATCH 151/184] Update version for release 5.2 --- version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version b/version index a75b92f..ef425ca 100644 --- a/version +++ b/version @@ -1 +1 @@ -5.1 +5.2 From 128e6a3905fe12c546ff1eb7cb7d71334407872d Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Thu, 1 Mar 2012 22:44:32 -0800 Subject: [PATCH 152/184] Support IPv6 on MacOS X systems Add feature test macros for MacOS X, and don't require IPV6_RECVPKTINFO to exist. Reported-by: YJZ Signed-off-by: H. Peter Anvin --- config.h | 7 +++++++ tftpd/recvfrom.c | 13 ++++++++----- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/config.h b/config.h index 0e35438..b22d540 100644 --- a/config.h +++ b/config.h @@ -17,6 +17,13 @@ #ifndef CONFIG_H #define CONFIG_H 1 +/* Feature enables for specific environments */ +#ifdef __APPLE__ +#if __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ >= 1070 +#define __APPLE_USE_RFC_3542 1 +#endif +#endif + /* Must be included before we include any system headers! */ #include "aconfig.h" /* autogenerated configuration header */ diff --git a/tftpd/recvfrom.c b/tftpd/recvfrom.c index 24abce3..d050b80 100644 --- a/tftpd/recvfrom.c +++ b/tftpd/recvfrom.c @@ -222,12 +222,15 @@ myrecvfrom(int s, void *buf, int len, unsigned int flags, #ifdef HAVE_STRUCT_IN6_PKTINFO if (cmptr->cmsg_level == IPPROTO_IPV6 && - (cmptr->cmsg_type == IPV6_RECVPKTINFO || + ( +#ifdef IPV6_RECVPKTINFO + cmptr->cmsg_type == IPV6_RECVPKTINFO || +#endif cmptr->cmsg_type == IPV6_PKTINFO)) { - memcpy(&pktinfo6, CMSG_DATA(cmptr), - sizeof(struct in6_pktinfo)); - memcpy(&myaddr->s6.sin6_addr, &pktinfo6.ipi6_addr, - sizeof(struct in6_addr)); + memcpy(&pktinfo6, CMSG_DATA(cmptr), + sizeof(struct in6_pktinfo)); + memcpy(&myaddr->s6.sin6_addr, &pktinfo6.ipi6_addr, + sizeof(struct in6_addr)); } #endif } From c89a63a441cdd176c52740bf386b78548f46d492 Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Sat, 7 Jun 2014 12:37:33 -0700 Subject: [PATCH 153/184] tftp: convert IPv6-mapped IPv4 addresses to IPv4 If we receive IPv4 addresses mapped to IPv6, convert them back to IPv4 so that mapping scripts which use \i behave sanely. Signed-off-by: H. Peter Anvin --- tftpd/recvfrom.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/tftpd/recvfrom.c b/tftpd/recvfrom.c index d050b80..b613823 100644 --- a/tftpd/recvfrom.c +++ b/tftpd/recvfrom.c @@ -113,6 +113,25 @@ err: return rv; } +#ifdef HAVE_IPV6 +static void normalize_ip6_compat(union sock_addr *myaddr) +{ + static const uint8_t ip6_compat_prefix[12] = + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff }; + struct sockaddr_in in; + + if (!memcmp(&myaddr->s6.sin6_addr, ip6_compat_prefix, + sizeof ip6_compat_prefix)) { + bzero(&in, sizeof in); + in.sin_family = AF_INET; + in.sin_port = myaddr->s6.sin6_port; + memcpy(&in.sin_addr, (const char *)&myaddr->s6.sin6_addr + + sizeof ip6_compat_prefix, sizeof in.sin_addr); + memcpy(&myaddr->si, &in, sizeof in); + } +} +#endif + int myrecvfrom(int s, void *buf, int len, unsigned int flags, struct sockaddr *from, socklen_t * fromlen, @@ -233,6 +252,7 @@ myrecvfrom(int s, void *buf, int len, unsigned int flags, sizeof(struct in6_addr)); } #endif + normalize_ip6_compat(myaddr); } #endif } From 18ee96a03f8656031b67dd13ad2fcadb5af7ffaf Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Sat, 7 Jun 2014 12:52:57 -0700 Subject: [PATCH 154/184] tftpd: allow IPv4/6-specific remapping rules Allow remapping rules to be conditional on IPv4 vs IPv6. Signed-off-by: H. Peter Anvin --- tftpd/remap.c | 18 ++++++++++++++++-- tftpd/remap.h | 2 +- tftpd/tftpd.c | 21 +++++++++++---------- 3 files changed, 28 insertions(+), 13 deletions(-) diff --git a/tftpd/remap.c b/tftpd/remap.c index 1e7abe7..6f5b409 100644 --- a/tftpd/remap.c +++ b/tftpd/remap.c @@ -1,6 +1,6 @@ /* ----------------------------------------------------------------------- * * - * Copyright 2001-2007 H. Peter Anvin - All Rights Reserved + * Copyright 2001-2014 H. Peter Anvin - All Rights Reserved * * This program is free software available under the same license * as the "OpenBSD" operating system, distributed at @@ -31,6 +31,8 @@ #define RULE_RESTART 0x08 /* Restart at the top after matching this rule */ #define RULE_ABORT 0x10 /* Terminate processing with an error */ #define RULE_INVERSE 0x20 /* Execute if regex *doesn't* match */ +#define RULE_IPV4 0x40 /* IPv4 only */ +#define RULE_IPV6 0x80 /* IPv6 only */ struct rule { struct rule *next; @@ -223,6 +225,12 @@ static int parseline(char *line, struct rule *r, int lineno) case '~': r->rule_flags |= RULE_INVERSE; break; + case '4': + r->rule_flags |= RULE_IPV4; + break; + case '6': + r->rule_flags |= RULE_IPV6; + break; case 'G': case 'P': r->rule_mode = *p; @@ -326,7 +334,7 @@ void freerules(struct rule *r) /* Execute a rule set on a string; returns a malloc'd new string. */ char *rewrite_string(const char *input, const struct rule *rules, - char mode, match_pattern_callback macrosub, + char mode, int af, match_pattern_callback macrosub, const char **errmsg) { char *current = tfstrdup(input); @@ -348,6 +356,12 @@ char *rewrite_string(const char *input, const struct rule *rules, if (ruleptr->rule_mode && ruleptr->rule_mode != mode) continue; /* Rule not applicable, try next */ + if ((ruleptr->rule_flags & RULE_IPV4) && (af != AF_INET)) + continue; /* Rule not applicable, try next */ + + if ((ruleptr->rule_flags & RULE_IPV6) && (af != AF_INET6)) + continue; /* Rule not applicable, try next */ + if (!deadman--) { syslog(LOG_WARNING, "remap: Breaking loop, input = %s, last = %s", input, diff --git a/tftpd/remap.h b/tftpd/remap.h index 69ca08d..7efcf6a 100644 --- a/tftpd/remap.h +++ b/tftpd/remap.h @@ -35,7 +35,7 @@ struct rule *parserulefile(FILE *); void freerules(struct rule *); /* Execute a rule set on a string; returns a malloc'd new string. */ -char *rewrite_string(const char *, const struct rule *, char, +char *rewrite_string(const char *, const struct rule *, char, int, match_pattern_callback, const char **); #endif /* WITH_REGEX */ diff --git a/tftpd/tftpd.c b/tftpd/tftpd.c index 1873e70..88d2812 100644 --- a/tftpd/tftpd.c +++ b/tftpd/tftpd.c @@ -1,7 +1,7 @@ /* * Copyright (c) 1983 Regents of the University of California. * Copyright (c) 1999-2009 H. Peter Anvin - * Copyright (c) 2011 Intel Corporation; author: H. Peter Anvin + * Copyright (c) 2011-2014 Intel Corporation; author: H. Peter Anvin * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -1039,18 +1039,18 @@ int main(int argc, char **argv) tp = (struct tftphdr *)buf; tp_opcode = ntohs(tp->th_opcode); if (tp_opcode == RRQ || tp_opcode == WRQ) - tftp(tp, n); + tftp(tp, n); exit(0); } -static char *rewrite_access(char *, int, const char **); +static char *rewrite_access(char *, int, int, const char **); static int validate_access(char *, int, const struct formats *, const char **); static void tftp_sendfile(const struct formats *, struct tftphdr *, int); static void tftp_recvfile(const struct formats *, struct tftphdr *, int); struct formats { const char *f_mode; - char *(*f_rewrite) (char *, int, const char **); + char *(*f_rewrite) (char *, int, int, const char **); int (*f_validate) (char *, int, const struct formats *, const char **); void (*f_send) (const struct formats *, struct tftphdr *, int); void (*f_recv) (const struct formats *, struct tftphdr *, int); @@ -1112,9 +1112,8 @@ int tftp(struct tftphdr *tp, int size) nak(EBADOP, "Unknown mode"); exit(0); } - if (!(filename = - (*pf->f_rewrite) (origfilename, tp_opcode, - &errmsgptr))) { + if (!(filename = (*pf->f_rewrite) + (origfilename, tp_opcode, from.sa.sa_family, &errmsgptr))) { nak(EACCESS, errmsgptr); /* File denied by mapping rule */ exit(0); } @@ -1398,12 +1397,13 @@ static int rewrite_macros(char macro, char *output) /* * Modify the filename, if applicable. If it returns NULL, deny the access. */ -static char *rewrite_access(char *filename, int mode, const char **msg) +static char *rewrite_access(char *filename, int mode, int af, + const char **msg) { if (rewrite_rules) { char *newname = rewrite_string(filename, rewrite_rules, - mode != RRQ ? 'P' : 'G', + mode != RRQ ? 'P' : 'G', af, rewrite_macros, msg); filename = newname; } @@ -1411,10 +1411,11 @@ static char *rewrite_access(char *filename, int mode, const char **msg) } #else -static char *rewrite_access(char *filename, int mode, const char **msg) +static char *rewrite_access(char *filename, int mode, int af, const char **msg) { (void)mode; /* Avoid warning */ (void)msg; + (void)af; return filename; } #endif From 2ac12abbc919678bc4ebeed20b1e714d08d2ddec Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Sat, 7 Jun 2014 12:55:26 -0700 Subject: [PATCH 155/184] tftpd.8: document IPv6 handling in remapping rules Document the "4" and "6" conditionals as well as how \i and \x handle IPv6 addresses. Signed-off-by: H. Peter Anvin --- tftpd/tftpd.8.in | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/tftpd/tftpd.8.in b/tftpd/tftpd.8.in index 78b4cfb..b500d27 100644 --- a/tftpd/tftpd.8.in +++ b/tftpd/tftpd.8.in @@ -30,7 +30,7 @@ .\" SUCH DAMAGE. .\" .\"----------------------------------------------------------------------- */ -.TH TFTPD 8 "14 September 2009" "tftp-hpa @@VERSION@@" "System Manager's Manual" +.TH TFTPD 8 "7 June 2014" "tftp-hpa @@VERSION@@" "System Manager's Manual" .SH NAME .B tftpd \- Trivial File Transfer Protocol server @@ -295,6 +295,12 @@ This rule applies to GET (RRQ) requests only. .B P This rule applies to PUT (WRQ) requests only. .TP +.B 4 +This rule applies to IPv4 sessions only. +.TP +.B 6 +This rule applies to IPv6 sessions only. +.TP .B ~ Inverse the sense of this rule, i.e. execute the .I operation @@ -318,12 +324,14 @@ subexpressions, \\( ... \\), of the pattern. .TP \fB\\i\fP -The IP address of the requesting host, in dotted-quad notation -(e.g. 192.0.2.169). +The IP address of the requesting host, in dotted-quad notation for +IPv4 (e.g. 192.0.2.169) or conventional colon form for IPv6 +(e.g. 2001:db8::1). .TP \fB\\x\fP -The IP address of the requesting host, in hexadecimal notation -(e.g. C00002A9). +The IP address of the requesting host, in expanded hexadecimal +notation (e.g. C00002A9 for IPv4, or 20010DB8000000000000000000000001 +for IPv6). .TP \fB\\\\\fP Literal backslash. From 8ddf0d87d7463c21e28dd2bea6f3f42d4c92cb1d Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Sat, 7 Jun 2014 13:00:46 -0700 Subject: [PATCH 156/184] tftp: drop "inline" from definition of usage() It is pointless and newer gcc say it is a lose. Signed-off-by: H. Peter Anvin --- tftp/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tftp/main.c b/tftp/main.c index 1b8a881..b2f9059 100644 --- a/tftp/main.c +++ b/tftp/main.c @@ -188,7 +188,7 @@ char *xstrdup(const char *); const char *program; -static inline void usage(int errcode) +static void usage(int errcode) { fprintf(stderr, #ifdef HAVE_IPV6 From 18ac1e26f756dd47fef33f5f706b0ec8fa696216 Mon Sep 17 00:00:00 2001 From: Thorsten Glaser Date: Thu, 31 Jul 2014 16:29:41 +0930 Subject: [PATCH 157/184] __progname[] is provided by libc Rename local variable to tftpd_progname to avoid a clash with glibc global symbols and work around Debian bug #519006 (Closes: #564052). [ hpa: specifically, double-underscore symbols in C are reserved for the implementation, i.e. compiler/libc. ] Signed-off-by: Ron Lee Signed-off-by: H. Peter Anvin --- tftpd/tftpd.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tftpd/tftpd.c b/tftpd/tftpd.c index 88d2812..91f5ae1 100644 --- a/tftpd/tftpd.c +++ b/tftpd/tftpd.c @@ -76,7 +76,7 @@ static int ai_fam = AF_INET; #define TRIES 6 /* Number of attempts to send each packet */ #define TIMEOUT_LIMIT ((1 << TRIES)-1) -const char *__progname; +const char *tftpd_progname; static int peer; static unsigned long timeout = TIMEOUT; /* Current timeout value */ static unsigned long rexmtval = TIMEOUT; /* Basic timeout value */ @@ -387,9 +387,9 @@ int main(int argc, char **argv) /* basename() is way too much of a pain from a portability standpoint */ p = strrchr(argv[0], '/'); - __progname = (p && p[1]) ? p + 1 : argv[0]; + tftpd_progname = (p && p[1]) ? p + 1 : argv[0]; - openlog(__progname, LOG_PID | LOG_NDELAY, LOG_DAEMON); + openlog(tftpd_progname, LOG_PID | LOG_NDELAY, LOG_DAEMON); srand(time(NULL) ^ getpid()); @@ -938,14 +938,14 @@ int main(int argc, char **argv) syslog daemon gets restarted by the time we get here. */ if (secure && standalone) { closelog(); - openlog(__progname, LOG_PID | LOG_NDELAY, LOG_DAEMON); + openlog(tftpd_progname, LOG_PID | LOG_NDELAY, LOG_DAEMON); } #ifdef HAVE_TCPWRAPPERS /* Verify if this was a legal request for us. This has to be done before the chroot, while /etc is still accessible. */ request_init(&wrap_request, - RQ_DAEMON, __progname, + RQ_DAEMON, tftpd_progname, RQ_FILE, fd, RQ_CLIENT_SIN, &from, RQ_SERVER_SIN, &myaddr, 0); sock_methods(&wrap_request); From 3ee2b278091ac0c60e9077096afa2135708de5e4 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Thu, 31 Jul 2014 16:29:42 +0930 Subject: [PATCH 158/184] Update manpage to match source code for --map-file The manpage had --mapfile but the code had --map-file. Closes: #606267 in the Debian BTS Reported-By: Jim Paris Signed-off-by: Ron Lee Signed-off-by: H. Peter Anvin --- tftpd/tftpd.8.in | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tftpd/tftpd.8.in b/tftpd/tftpd.8.in index b500d27..71a712d 100644 --- a/tftpd/tftpd.8.in +++ b/tftpd/tftpd.8.in @@ -155,7 +155,7 @@ or .B utimeout option is negotiated. The default is 1000000 (1 second.) .TP -\fB\-\-mapfile\fP \fIremap-file\fP, \fB\-m\fP \fIremap-file\fP +\fB\-\-map-file\fP \fIremap-file\fP, \fB\-m\fP \fIremap-file\fP Specify the use of filename remapping. The .I remap-file is a file containing the remapping rules. See the section on filename @@ -238,7 +238,7 @@ option, but crash with an error if they actually get the option accepted by the server. .SH "FILENAME REMAPPING" The -.B \-\-mapfile +.B \-\-map-file option specifies a file which contains filename remapping rules. Each non-comment line (comments begin with hash marks, .BR # ) @@ -398,7 +398,7 @@ flag is used to set up a chroot() environment for the server to run in once a connection has been set up. .PP Finally, the filename remapping -.RB ( \-\-mapfile +.RB ( \-\-map-file flag) support can be used to provide a limited amount of additional access control. .SH "CONFORMING TO" From ff819b108a14f40612ebfb15937ae4a1b632bab9 Mon Sep 17 00:00:00 2001 From: Ron Lee Date: Thu, 31 Jul 2014 16:29:43 +0930 Subject: [PATCH 159/184] Make configure an order-only prerequisite of aconfig.h.in MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On filesystems with subsecond resolution, like ext4, we can't trust the timestamp of aconfig.h.in since autoheader leaves it truncated to second resolution (apparently touch -r and cp -p can do this at the very least) while configure has full subsecond resolution, so it can look newer even when it was cleanly created first, leading to the build system looping trying to recreate all of that again ... So in the initial stage of a clean build we can get something like this: $ make spotless $ make autoconf rm -rf MCONFIG configure config.log aconfig.h *.cache autoconf rm -f aconfig.h.in aconfig.h autoheader $ stat configure aconfig.h.in File: ‘configure’ Device: 807h/2055d Inode: 9443466 Links: 1 Access: 2014-07-31 03:27:27.599293442 +0930 Modify: 2014-07-31 03:27:27.711290270 +0930 Change: 2014-07-31 03:27:27.711290270 +0930 File: ‘aconfig.h.in’ Device: 807h/2055d Inode: 9443467 Links: 1 Access: 2014-07-31 03:27:27.000000000 +0930 Modify: 2014-07-31 03:27:27.000000000 +0930 Change: 2014-07-31 03:27:27.903284841 +0930 And with a parallel build, that can then leave 'make all' racing to remove and recreate aconfig.h (and possibly more things), while it begins to build the first targets. Which then fail horribly like we see here: https://buildd.debian.org/status/fetch.php?pkg=tftp-hpa&arch=i386&ver=5.2%2B20140608-1&stamp=1406736363 Possibly we also need to move the actual build job into the rule for the 'all' target, so that the build system update prerequisites are guaranteed to be completed before it runs (as opposed to running in parallel with them), but this change might be enough for now. Signed-off-by: Ron Lee Signed-off-by: H. Peter Anvin --- Makefile | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 9ff12d8..75a2042 100644 --- a/Makefile +++ b/Makefile @@ -58,8 +58,12 @@ aconfig.h: MCONFIG # Adding "configure" to the dependencies serializes this with running # autoconf, because there are apparently race conditions between -# autoconf and autoheader. -aconfig.h.in: configure.in configure aclocal.m4 +# autoconf and autoheader. And worse than that, even when autoconf +# cleanly returns first, autoheader will truncate the timestamp of +# aconfig.h.in to second resolution, so on a filesystem with subsecond +# resolution it can appear older than configure (which isn't truncated). +# So make it an order-only prerequisite to avoid looping regenerating it. +aconfig.h.in: configure.in aclocal.m4 | configure rm -f aconfig.h.in aconfig.h autoheader From 7678ae7f14e5aa6792c4486d4eedcb2db887872c Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Fri, 7 Aug 2015 11:49:13 -0700 Subject: [PATCH 160/184] tftpd: Canonicalize all the addresses We cannot canonicalize myaddr and not the from address. We need to canonicalize both of them, or else we'll try to create an IPv4 socket and bind an IPv6-mapped IPv4 address to it, which is going to fail. Signed-off-by: H. Peter Anvin --- tftpd/recvfrom.c | 43 +++++++++++++++++++++++-------------------- tftpd/recvfrom.h | 3 +-- tftpd/tftpd.c | 5 +---- 3 files changed, 25 insertions(+), 26 deletions(-) diff --git a/tftpd/recvfrom.c b/tftpd/recvfrom.c index b613823..83ef29a 100644 --- a/tftpd/recvfrom.c +++ b/tftpd/recvfrom.c @@ -113,14 +113,15 @@ err: return rv; } -#ifdef HAVE_IPV6 static void normalize_ip6_compat(union sock_addr *myaddr) { +#ifdef HAVE_IPV6 static const uint8_t ip6_compat_prefix[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff }; struct sockaddr_in in; - if (!memcmp(&myaddr->s6.sin6_addr, ip6_compat_prefix, + if (myaddr->sa.sa_family == AF_INET6 && + !memcmp(&myaddr->s6.sin6_addr, ip6_compat_prefix, sizeof ip6_compat_prefix)) { bzero(&in, sizeof in); in.sin_family = AF_INET; @@ -129,13 +130,14 @@ static void normalize_ip6_compat(union sock_addr *myaddr) sizeof ip6_compat_prefix, sizeof in.sin_addr); memcpy(&myaddr->si, &in, sizeof in); } -} +#else + (void)myaddr; #endif +} int myrecvfrom(int s, void *buf, int len, unsigned int flags, - struct sockaddr *from, socklen_t * fromlen, - union sock_addr *myaddr) + union sock_addr *from, union sock_addr *myaddr) { struct msghdr msg; struct iovec iov; @@ -168,16 +170,16 @@ myrecvfrom(int s, void *buf, int len, unsigned int flags, /* Try to enable getting the return address */ #ifdef IP_RECVDSTADDR - if (from->sa_family == AF_INET) + if (from->sa.sa_family == AF_INET) setsockopt(s, IPPROTO_IP, IP_RECVDSTADDR, &on, sizeof(on)); #endif #ifdef IP_PKTINFO - if (from->sa_family == AF_INET) + if (from->sa.sa_family == AF_INET) setsockopt(s, IPPROTO_IP, IP_PKTINFO, &on, sizeof(on)); #endif #ifdef HAVE_IPV6 #ifdef IPV6_RECVPKTINFO - if (from->sa_family == AF_INET6) + if (from->sa.sa_family == AF_INET6) setsockopt(s, IPPROTO_IPV6, IPV6_RECVPKTINFO, &on, sizeof(on)); #endif #endif @@ -186,8 +188,8 @@ myrecvfrom(int s, void *buf, int len, unsigned int flags, msg.msg_controllen = sizeof(control_un); msg.msg_flags = 0; - msg.msg_name = from; - msg.msg_namelen = *fromlen; + msg.msg_name = &from->sa; + msg.msg_namelen = sizeof(*from); iov.iov_base = buf; iov.iov_len = len; msg.msg_iov = &iov; @@ -196,11 +198,9 @@ myrecvfrom(int s, void *buf, int len, unsigned int flags, if ((n = recvmsg(s, &msg, flags)) < 0) return n; /* Error */ - *fromlen = msg.msg_namelen; - if (myaddr) { bzero(myaddr, sizeof(*myaddr)); - myaddr->sa.sa_family = from->sa_family; + myaddr->sa.sa_family = from->sa.sa_family; if (msg.msg_controllen < sizeof(struct cmsghdr) || (msg.msg_flags & MSG_CTRUNC)) @@ -209,7 +209,7 @@ myrecvfrom(int s, void *buf, int len, unsigned int flags, for (cmptr = CMSG_FIRSTHDR(&msg); cmptr != NULL; cmptr = CMSG_NXTHDR(&msg, cmptr)) { - if (from->sa_family == AF_INET) { + if (from->sa.sa_family == AF_INET) { myaddr->sa.sa_family = AF_INET; #ifdef IP_RECVDSTADDR if (cmptr->cmsg_level == IPPROTO_IP && @@ -230,7 +230,7 @@ myrecvfrom(int s, void *buf, int len, unsigned int flags, #endif } #ifdef HAVE_IPV6 - else if (from->sa_family == AF_INET6) { + else if (from->sa.sa_family == AF_INET6) { myaddr->sa.sa_family = AF_INET6; #ifdef IP6_RECVDSTADDR if (cmptr->cmsg_level == IPPROTO_IPV6 && @@ -252,7 +252,6 @@ myrecvfrom(int s, void *buf, int len, unsigned int flags, sizeof(struct in6_addr)); } #endif - normalize_ip6_compat(myaddr); } #endif } @@ -268,6 +267,10 @@ myrecvfrom(int s, void *buf, int len, unsigned int flags, #endif } } + + normalize_ip6_compat(myaddr); + normalize_ip6_compat(from); + return n; } @@ -275,16 +278,16 @@ myrecvfrom(int s, void *buf, int len, unsigned int flags, int myrecvfrom(int s, void *buf, int len, unsigned int flags, - struct sockaddr *from, socklen_t * fromlen, - union sock_addr *myaddr) + union sock_addr *from, union sock_addr *myaddr) { /* There is no way we can get the local address, fudge it */ + socklen_t fromlen = sizeof(*from); bzero(myaddr, sizeof(*myaddr)); - myaddr->sa.sa_family = from->sa_family; + myaddr->sa.sa_family = from->sa.sa_family; sa_set_port(myaddr, htons(IPPORT_TFTP)); - return recvfrom(s, buf, len, flags, from, fromlen); + return recvfrom(s, buf, len, flags, from, &fromlen); } #endif diff --git a/tftpd/recvfrom.h b/tftpd/recvfrom.h index e3c4055..7773a0d 100644 --- a/tftpd/recvfrom.h +++ b/tftpd/recvfrom.h @@ -19,5 +19,4 @@ int myrecvfrom(int s, void *buf, int len, unsigned int flags, - struct sockaddr *from, socklen_t *fromlen, - union sock_addr *myaddr); + union sock_addr *from, union sock_addr *myaddr); diff --git a/tftpd/tftpd.c b/tftpd/tftpd.c index 91f5ae1..364e7d2 100644 --- a/tftpd/tftpd.c +++ b/tftpd/tftpd.c @@ -93,7 +93,6 @@ static unsigned int max_blksize = MAX_SEGSIZE; static char tmpbuf[INET6_ADDRSTRLEN], *tmp_p; static union sock_addr from; -static socklen_t fromlen; static off_t tsize; static int tsize_ok; @@ -874,9 +873,7 @@ int main(int argc, char **argv) set_socket_nonblock(fd, 0); #endif - fromlen = sizeof(from); - n = myrecvfrom(fd, buf, sizeof(buf), 0, - (struct sockaddr *)&from, &fromlen, &myaddr); + n = myrecvfrom(fd, buf, sizeof(buf), 0, &from, &myaddr); if (n < 0) { if (E_WOULD_BLOCK(errno) || errno == EINTR) { From b2b34cecc8cbc18ff6f1fc00bda6ae6e9011e6c7 Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Fri, 7 Aug 2015 11:55:08 -0700 Subject: [PATCH 161/184] tftpd: Canonicalize myaddr before address_is_local() The comparisons for forbidden addresses in address_is_local() only work on canonicalized addresses. Also, work in the case myaddr is NULL (if we ever call it that way...) Signed-off-by: H. Peter Anvin --- tftpd/recvfrom.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tftpd/recvfrom.c b/tftpd/recvfrom.c index 83ef29a..d7ef500 100644 --- a/tftpd/recvfrom.c +++ b/tftpd/recvfrom.c @@ -255,6 +255,9 @@ myrecvfrom(int s, void *buf, int len, unsigned int flags, } #endif } + + normalize_ip6_compat(myaddr); + /* If the address is not a valid local address, * then bind to any address... */ @@ -268,7 +271,6 @@ myrecvfrom(int s, void *buf, int len, unsigned int flags, } } - normalize_ip6_compat(myaddr); normalize_ip6_compat(from); return n; From e83d71d394901e6d08605bcd7f35b2f112fe73ea Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Wed, 29 May 2024 15:03:40 -0700 Subject: [PATCH 162/184] autoconf: rename configure.in to configure.ac Rename configure.in to configure.ac in accordance with current practice. Signed-off-by: H. Peter Anvin --- Makefile | 5 ++--- configure.in => configure.ac | 0 2 files changed, 2 insertions(+), 3 deletions(-) rename configure.in => configure.ac (100%) diff --git a/Makefile b/Makefile index 75a2042..8c779d3 100644 --- a/Makefile +++ b/Makefile @@ -63,11 +63,11 @@ aconfig.h: MCONFIG # aconfig.h.in to second resolution, so on a filesystem with subsecond # resolution it can appear older than configure (which isn't truncated). # So make it an order-only prerequisite to avoid looping regenerating it. -aconfig.h.in: configure.in aclocal.m4 | configure +aconfig.h.in: configure.ac aclocal.m4 | configure rm -f aconfig.h.in aconfig.h autoheader -configure: configure.in aclocal.m4 +configure: configure.ac aclocal.m4 rm -rf MCONFIG configure config.log aconfig.h *.cache autoconf @@ -76,4 +76,3 @@ version.h: version tftp.spec: tftp.spec.in version sed -e "s/@@VERSION@@/`cat version`/g" < $< > $@ || rm -f $@ - diff --git a/configure.in b/configure.ac similarity index 100% rename from configure.in rename to configure.ac From 9c0908a778d0c57226c8a4e8ac6582d35b83739c Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Wed, 29 May 2024 15:09:38 -0700 Subject: [PATCH 163/184] autoconf: remove workarounds for some very old systems Remove workarounds for some ridiculously old systems; these workarounds are basically untestable. Signed-off-by: H. Peter Anvin --- config.h | 31 +++++------------------------- configure.ac | 54 ++++++++-------------------------------------------- 2 files changed, 13 insertions(+), 72 deletions(-) diff --git a/config.h b/config.h index b22d540..8c9af73 100644 --- a/config.h +++ b/config.h @@ -30,6 +30,10 @@ /* Standard includes */ #include +#include +#include +#include +#include #ifdef HAVE_SYS_TYPES_H #include @@ -39,21 +43,6 @@ #include #endif -#ifdef STDC_HEADERS -#include -#include -#else -#ifdef HAVE_STDLIB_H -#include -#endif -#endif - -#ifdef HAVE_MEMORY_H -#ifndef STDC_HEADERS -#include -#endif -#endif - #ifdef HAVE_STRING_H #include #endif @@ -80,15 +69,8 @@ #include #endif -#ifdef TIME_WITH_SYS_TIME +#ifdef HAVE_SYS_TIME_H #include -#include -#else -#if HAVE_SYS_TIME_H -#include -#else -#include -#endif #endif #ifdef HAVE_GRP_H @@ -99,9 +81,6 @@ #include #endif -#include -#include - #ifdef HAVE_SYS_SOCKET_H #include #else diff --git a/configure.ac b/configure.ac index 7ab7c5a..9a467b3 100644 --- a/configure.ac +++ b/configure.ac @@ -2,12 +2,13 @@ dnl dnl autoconf input file to generate MCONFIG dnl -AC_PREREQ(2.61) -AC_INIT(MCONFIG.in) +AC_PREREQ([2.71]) +AC_INIT +AC_CONFIG_SRCDIR([MCONFIG.in]) AC_PREFIX_DEFAULT(/usr) AC_USE_SYSTEM_EXTENSIONS -AC_ISC_POSIX +AC_SEARCH_LIBS([strerror],[cposix]) AC_PROG_CC AC_C_CONST @@ -30,21 +31,15 @@ PA_ADD_CFLAGS(-Wsign-compare) PA_ADD_CFLAGS(-pipe) PA_ADD_CFLAGS(-fno-strict-aliasing) -AC_HEADER_STDC AC_CHECK_HEADERS(inttypes.h) AC_CHECK_HEADERS(stdint.h) PA_CHECK_INTTYPES_H_SANE AC_CHECK_HEADERS(fcntl.h) AC_CHECK_HEADERS(grp.h) AC_CHECK_HEADERS(libgen.h) -AC_CHECK_HEADERS(memory.h) AC_CHECK_HEADERS(setjmp.h) -AC_CHECK_HEADERS(stddef.h) -AC_CHECK_HEADERS(stdlib.h) -AC_CHECK_HEADERS(string.h) AC_CHECK_HEADERS(strings.h) AC_CHECK_HEADERS(sysexits.h) -AC_CHECK_HEADERS(time.h) AC_CHECK_HEADERS(unistd.h) AC_CHECK_HEADERS(sys/file.h) AC_CHECK_HEADERS(sys/filio.h) @@ -53,7 +48,7 @@ AC_CHECK_HEADERS(sys/time.h) AC_CHECK_HEADERS(sys/types.h) AC_CHECK_HEADERS(arpa/inet.h) AC_CHECK_HEADERS(netdb.h) -AC_HEADER_TIME + dnl This is needed on some versions of FreeBSD... AC_CHECK_HEADERS(machine/param.h) AC_CHECK_HEADERS(sys/socket.h) @@ -78,44 +73,10 @@ dnl isn't among the list of standard headers that autoconf checks dnl but POSIX requires for socklen_t to be defined. dnl AC_CHECK_TYPES(socklen_t,,, -[ -#include +[AC_INCLUDES_DEFAULT #if HAVE_SYS_TYPES_H # include #endif -#if HAVE_SYS_STAT_H -# include -#endif -#if STDC_HEADERS -# include -# include -#else -# if HAVE_STDLIB_H -# include -# endif -#endif -#if HAVE_STRING_H -# if !STDC_HEADERS && HAVE_MEMORY_H -# include -# endif -# include -#endif -#if HAVE_STRINGS_H -# include -#endif -#if HAVE_INTTYPES_H -# include -#else -# if HAVE_STDINT_H -# include -# endif -#endif -#if HAVE_UNISTD_H -# include -#endif -#ifdef HAVE_SYS_SOCKET_H -# include -#endif ]) AC_SEARCH_LIBS(socket, [socket ws2_32 wsock32], , [AC_MSG_ERROR(socket library not found)]) @@ -300,4 +261,5 @@ AC_PROG_INSTALL fi] AC_CONFIG_HEADERS(aconfig.h) -AC_OUTPUT(MCONFIG) +AC_CONFIG_FILES([MCONFIG]) +AC_OUTPUT From 1f4b33a1f726d832742c370dc0c6d7f242424a5a Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Wed, 29 May 2024 15:34:57 -0700 Subject: [PATCH 164/184] Modernize: use sigaction() whenever possible. Remove uses of common. bsd_signal() may not be defined, and there is really no reason to even try to use it if sigaction() is avaiable; using sigaction() guarantees the semantics we really want. Replace uses of common variables with explicit instantiation and extern declarations in a header file. Signed-off-by: H. Peter Anvin --- common/Makefile | 2 +- common/signal.c | 42 ++++++++++++++++++++++++++++++++++++++++++ config.h | 11 ++++++++--- configure.ac | 4 +++- lib/Makefile | 2 -- lib/bsdsignal.c | 27 --------------------------- tftp/extern.h | 7 +++++-- tftp/main.c | 4 ++-- tftp/tftp.c | 7 +++---- 9 files changed, 64 insertions(+), 42 deletions(-) create mode 100644 common/signal.c delete mode 100644 lib/bsdsignal.c diff --git a/common/Makefile b/common/Makefile index a825213..2aaffb7 100644 --- a/common/Makefile +++ b/common/Makefile @@ -4,7 +4,7 @@ VERSION = $(shell cat ../version) -include ../MCONFIG include ../MRULES -OBJS = tftpsubs.$(O) +OBJS = tftpsubs.$(O) signal.$(O) LIB = libcommon.a all: $(LIB) diff --git a/common/signal.c b/common/signal.c new file mode 100644 index 0000000..9119d57 --- /dev/null +++ b/common/signal.c @@ -0,0 +1,42 @@ +/* + * signal.c + * + * Use sigaction() to simulate BSD signal() + */ + +#include "config.h" + +#ifdef HAVE_SIGACTION + +sighandler_t tftp_signal(int signum, sighandler_t handler) +{ + struct sigaction action, oldaction; + + memset(&action, 0, sizeof action); + action.sa_handler = handler; + sigemptyset(&action.sa_mask); + sigaddset(&action.sa_mask, signum); + action.sa_flags = SA_RESTART; + + if (sigaction(signum, &action, &oldaction)) + return SIG_ERR; + + return oldaction.sa_handler; +} + +#elif defined(HAVE_BSD_SIGNAL) + +sighandler_t tftp_signal(int signum, sighandler_t handler) +{ + return bsd_signal(signum, handler); +} + +#else + +/* This is dangerous at best. Let's hope it works. */ +sighandler_t tftp_signal(int signum, sighandler_t handler) +{ + return signal(signum, handler); +} + +#endif diff --git a/config.h b/config.h index 8c9af73..a6a402b 100644 --- a/config.h +++ b/config.h @@ -1,6 +1,6 @@ /* -*- c -*- ------------------------------------------------------------- * * - * Copyright 2001-2006 H. Peter Anvin - All Rights Reserved + * Copyright 2001-2024 H. Peter Anvin - All Rights Reserved * * This program is free software available under the same license * as the "OpenBSD" operating system, distributed at @@ -279,9 +279,14 @@ typedef int socklen_t; void *xmalloc(size_t); char *xstrdup(const char *); -#ifndef HAVE_BSD_SIGNAL -void (*bsd_signal(int, void (*)(int))) (int); +#ifndef HAVE_SIGHANDLER_T +typedef void (*sighandler_t)(int); #endif +#ifndef SIG_ERR +#define SIG_ERR NULL +#endif +sighandler_t tftp_signal(int, sighandler_t); + #ifndef HAVE_DUP2 int dup2(int, int); #endif diff --git a/configure.ac b/configure.ac index 9a467b3..04b6f98 100644 --- a/configure.ac +++ b/configure.ac @@ -89,6 +89,9 @@ AC_CHECK_FUNCS(setreuid) AC_CHECK_FUNCS(setregid) AC_CHECK_FUNCS(initgroups) AC_CHECK_FUNCS(setgroups) +AC_CHECK_FUNCS(sigaction) +AC_CHECK_FUNCS(bsd_signal) +AC_CHECK_TYPES(sighandler_t) dnl Solaris 8 has [u]intmax_t but not strtoumax(). How utterly braindamaged. AC_CHECK_FUNCS(strtoumax) @@ -120,7 +123,6 @@ OBJROOT=`pwd` XTRA=false PA_SEARCH_LIBS_AND_ADD(xmalloc, iberty) PA_SEARCH_LIBS_AND_ADD(xstrdup, iberty) -PA_SEARCH_LIBS_AND_ADD(bsd_signal, bsd, bsdsignal) PA_SEARCH_LIBS_AND_ADD(getopt_long, getopt, getopt_long) PA_SEARCH_LIBS_AND_ADD(getaddrinfo, [nsl resolv]) if $pa_add_getaddrinfo diff --git a/lib/Makefile b/lib/Makefile index a43ce19..5dd83d0 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -25,5 +25,3 @@ libxtra.a: $(LIBOBJS) -rm -f libxtra.a $(AR) libxtra.a $(LIBOBJS) $(RANLIB) libxtra.a - - diff --git a/lib/bsdsignal.c b/lib/bsdsignal.c deleted file mode 100644 index 0aae136..0000000 --- a/lib/bsdsignal.c +++ /dev/null @@ -1,27 +0,0 @@ -/* - * bsdsignal.c - * - * Use sigaction() to simulate BSD signal() - */ - -#include "config.h" - -void (*bsd_signal(int signum, void (*handler) (int))) (int) { - struct sigaction action, oldaction; - - memset(&action, 0, sizeof action); - action.sa_handler = handler; - sigemptyset(&action.sa_mask); - sigaddset(&action.sa_mask, signum); - action.sa_flags = SA_RESTART; - - if (sigaction(signum, &action, &oldaction) == -1) { -#ifdef SIG_ERR - return SIG_ERR; -#else - return NULL; -#endif - } - - return oldaction.sa_handler; -} diff --git a/tftp/extern.h b/tftp/extern.h index 78474fc..9c578e6 100644 --- a/tftp/extern.h +++ b/tftp/extern.h @@ -31,10 +31,13 @@ * SUCH DAMAGE. */ -#ifndef RECVFILE_H -#define RECVFILE_H +#ifndef EXTERN_H +#define EXTERN_H + +#include "config.h" void tftp_recvfile(int, const char *, const char *); void tftp_sendfile(int, const char *, const char *); +extern sigjmp_buf toplevel; #endif diff --git a/tftp/main.c b/tftp/main.c index b2f9059..438f48a 100644 --- a/tftp/main.c +++ b/tftp/main.c @@ -305,7 +305,7 @@ int main(int argc, char *argv[]) sp->s_proto = (char *)"udp"; } - bsd_signal(SIGINT, intr); + tftp_signal(SIGINT, intr); if (peerargc) { /* Set peer */ @@ -768,7 +768,7 @@ void intr(int sig) { (void)sig; /* Quiet unused warning */ - bsd_signal(SIGALRM, SIG_IGN); + tftp_signal(SIGALRM, SIG_IGN); alarm(0); siglongjmp(toplevel, -1); } diff --git a/tftp/tftp.c b/tftp/tftp.c index d15da22..aecdeb9 100644 --- a/tftp/tftp.c +++ b/tftp/tftp.c @@ -48,8 +48,7 @@ extern int maxtimeout; #define PKTSIZE SEGSIZE+4 char ackbuf[PKTSIZE]; int timeout; -sigjmp_buf toplevel; -sigjmp_buf timeoutbuf; +static sigjmp_buf timeoutbuf; static void nak(int, const char *); static int makerequest(int, const char *, struct tftphdr *, const char *); @@ -85,7 +84,7 @@ void tftp_sendfile(int fd, const char *name, const char *mode) is_request = 1; /* First packet is the actual WRQ */ amount = 0; - bsd_signal(SIGALRM, timer); + tftp_signal(SIGALRM, timer); do { if (is_request) { size = makerequest(WRQ, name, dp, mode) - 4; @@ -191,7 +190,7 @@ void tftp_recvfile(int fd, const char *name, const char *mode) firsttrip = 1; amount = 0; - bsd_signal(SIGALRM, timer); + tftp_signal(SIGALRM, timer); do { if (firsttrip) { size = makerequest(RRQ, name, ap, mode); From aaaa76e8e7a8a81e71b5ee1df1d9d31c37f69a4d Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Wed, 29 May 2024 15:37:44 -0700 Subject: [PATCH 165/184] tftpd: explicitly declare timer() static Declare the function timer() as static, which is what it ought to be. Signed-off-by: H. Peter Anvin --- tftpd/tftpd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tftpd/tftpd.c b/tftpd/tftpd.c index 364e7d2..f6d0565 100644 --- a/tftpd/tftpd.c +++ b/tftpd/tftpd.c @@ -152,7 +152,7 @@ static void handle_exit(int sig) } /* Handle timeout signal or timeout event */ -void timer(int sig) +static void timer(int sig) { (void)sig; /* Suppress unused warning */ timeout <<= 1; From 15c4f369ee741e9205dc28ce631aaf6799193b04 Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Wed, 29 May 2024 15:40:13 -0700 Subject: [PATCH 166/184] Update version for release 5.3 --- version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version b/version index ef425ca..d346e2a 100644 --- a/version +++ b/version @@ -1 +1 @@ -5.2 +5.3 From 1dc6d558115746f50e78ba8d84915a56ae66ec5f Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Wed, 29 May 2024 16:01:54 -0700 Subject: [PATCH 167/184] tftpd: handle restricted user namespaces on Linux If the tftpd process lacks the privilege to set its uid, gid, or groups, then assume it is already restricted in the way the administrator requested, if and only if EPERM is returned. Signed-off-by: H. Peter Anvin --- tftpd/tftpd.c | 33 +++++++++++++++++++++++++-------- 1 file changed, 25 insertions(+), 8 deletions(-) diff --git a/tftpd/tftpd.c b/tftpd/tftpd.c index f6d0565..9abf009 100644 --- a/tftpd/tftpd.c +++ b/tftpd/tftpd.c @@ -374,6 +374,7 @@ int main(int argc, char **argv) int spec_umask = 0; int c; int setrv; + int die; int waittime = 900; /* Default time to wait for a connect */ const char *user = "nobody"; /* Default user */ char *p, *ep; @@ -974,21 +975,30 @@ int main(int argc, char **argv) exit(EX_IOERR); } - /* Set up the supplementary group access list if possible */ - /* /etc/group still need to be accessible at this point */ + /* Set up the supplementary group access list if possible + /etc/group still need to be accessible at this point. + If we get EPERM, this is already a restricted process, e.g. + using user namespaces on Linux. */ + setrv = -1; + die = 0; #ifdef HAVE_INITGROUPS setrv = initgroups(user, pw->pw_gid); - if (setrv) { + if (setrv && errno != EPERM) { syslog(LOG_ERR, "cannot set groups for user %s", user); - exit(EX_OSERR); + die = EX_OSERR; } -#else +#endif #ifdef HAVE_SETGROUPS - if (setgroups(0, NULL)) { - syslog(LOG_ERR, "cannot clear group list"); + if (setrv) { + setrv = setgroups(0, NULL); + if (setrv && errno != EPERM) { + syslog(LOG_ERR, "cannot clear group list"); + die = EX_OSERR; + } } #endif -#endif + if (die) + exit(die); /* Chroot and drop privileges */ if (secure) { @@ -1000,11 +1010,15 @@ int main(int argc, char **argv) chdir("/"); /* Cygwin chroot() bug workaround */ #endif } + #ifdef HAVE_SETREGID setrv = setregid(pw->pw_gid, pw->pw_gid); #else setrv = setegid(pw->pw_gid) || setgid(pw->pw_gid); #endif + if (setrv && errno == EPERM) { + setrv = 0; /* Already restricted */ + } #ifdef HAVE_SETREUID setrv = setrv || setreuid(pw->pw_uid, pw->pw_uid); @@ -1013,6 +1027,9 @@ int main(int argc, char **argv) setrv = setrv || setuid(pw->pw_uid) || (geteuid() != pw->pw_uid && seteuid(pw->pw_uid)); #endif + if (setrv && errno == EPERM) { + setrv = 0; /* Already restricted */ + } if (setrv) { syslog(LOG_ERR, "cannot drop privileges: %m"); From 6f96fcd1b669dbf9c0a3d96d26d370117fddcadd Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Wed, 29 May 2024 17:16:06 -0700 Subject: [PATCH 168/184] tftpd: allow a rewrite rule to probe for the existence of a file This adds an "E" flag to the rewrite rules, which exits rule processing if and only if the result is a valid, accessible filename. If combined with "r", the rewrite is cancelled if the rule is not applied. Signed-off-by: H. Peter Anvin --- tftpd/remap.c | 72 +++++++++++++++++++++++++++++++++++------------- tftpd/remap.h | 4 ++- tftpd/tftpd.8.in | 13 +++++++-- tftpd/tftpd.c | 52 +++++++++++++++++----------------- tftpd/tftpd.h | 9 ++++++ 5 files changed, 103 insertions(+), 47 deletions(-) diff --git a/tftpd/remap.c b/tftpd/remap.c index 6f5b409..c7c8f85 100644 --- a/tftpd/remap.c +++ b/tftpd/remap.c @@ -1,6 +1,6 @@ /* ----------------------------------------------------------------------- * * - * Copyright 2001-2014 H. Peter Anvin - All Rights Reserved + * Copyright 2001-2024 H. Peter Anvin - All Rights Reserved * * This program is free software available under the same license * as the "OpenBSD" operating system, distributed at @@ -34,6 +34,8 @@ #define RULE_IPV4 0x40 /* IPv4 only */ #define RULE_IPV6 0x80 /* IPv6 only */ +#define RULE_HASFILE 0x100 /* Valid if rule results in a valid filename */ + struct rule { struct rule *next; int nrule; @@ -213,6 +215,9 @@ static int parseline(char *line, struct rule *r, int lineno) case 'e': r->rule_flags |= RULE_EXIT; break; + case 'E': + r->rule_flags |= RULE_HASFILE; + break; case 's': r->rule_flags |= RULE_RESTART; break; @@ -244,15 +249,21 @@ static int parseline(char *line, struct rule *r, int lineno) } } - /* RULE_GLOBAL only applies when RULE_REWRITE specified */ - if (!(r->rule_flags & RULE_REWRITE)) - r->rule_flags &= ~RULE_GLOBAL; - - if ((r->rule_flags & (RULE_INVERSE | RULE_REWRITE)) == - (RULE_INVERSE | RULE_REWRITE)) { - syslog(LOG_ERR, "r rules cannot be inverted, line %d: %s\n", - lineno, line); - return -1; /* Error */ + if (r->rule_flags & RULE_REWRITE) { + if (r->rule_flags & RULE_INVERSE) { + syslog(LOG_ERR, "r rules cannot be inverted, line %d: %s\n", + lineno, line); + return -1; /* Error */ + } + if ((r->rule_flags & (RULE_GLOBAL|RULE_HASFILE)) + == (RULE_GLOBAL|RULE_HASFILE)) { + syslog(LOG_ERR, "E rules cannot be combined with g, line %d: %s\n", + lineno, line); + return -1; /* Error */ + } + } else { + /* RULE_GLOBAL is meaningless without RULE_REWRITE */ + r->rule_flags &= ~RULE_GLOBAL; } /* Read and compile the regex */ @@ -333,12 +344,14 @@ void freerules(struct rule *r) } /* Execute a rule set on a string; returns a malloc'd new string. */ -char *rewrite_string(const char *input, const struct rule *rules, +char *rewrite_string(const struct formats *pf, + const char *input, const struct rule *rules, char mode, int af, match_pattern_callback macrosub, const char **errmsg) { char *current = tfstrdup(input); char *newstr; + const char *accerr; const struct rule *ruleptr = rules; regmatch_t pmatch[10]; int len; @@ -410,13 +423,34 @@ char *rewrite_string(const char *input, const struct rule *rules, newstr = tfmalloc(len + 1); genmatchstring(newstr, ruleptr->pattern, current, pmatch, macrosub); - free(current); - current = newstr; - if (verbosity >= 3) { - syslog(LOG_INFO, "remap: rule %d: rewrite: %s", - ruleptr->nrule, current); - } - } + if ((ruleptr->rule_flags & RULE_HASFILE) && + pf->f_validate(newstr, mode == 'G' ? RRQ : WRQ, + pf, &accerr)) { + if (verbosity >= 3) { + syslog(LOG_INFO, "remap: rule %d: ignored rewrite (%s): %s", + ruleptr->nrule, accerr, newstr); + } + free(newstr); + was_match = 0; + break; + } + free(current); + current = newstr; + if (verbosity >= 3) { + syslog(LOG_INFO, "remap: rule %d: rewrite: %s", + ruleptr->nrule, current); + } + } else if (ruleptr->rule_flags & RULE_HASFILE) { + if (pf->f_validate(current, mode == 'G' ? RRQ : WRQ, + pf, &accerr)) { + if (verbosity >= 3) { + syslog(LOG_INFO, "remap: rule %d: not exiting (%s)\n", + ruleptr->nrule, accerr); + } + was_match = 0; + break; + } + } } else { break; /* No match, terminate unconditionally */ } @@ -426,7 +460,7 @@ char *rewrite_string(const char *input, const struct rule *rules, if (was_match) { was_match = 0; - if (ruleptr->rule_flags & RULE_EXIT) { + if (ruleptr->rule_flags & (RULE_EXIT|RULE_HASFILE)) { if (verbosity >= 3) { syslog(LOG_INFO, "remap: rule %d: exit", ruleptr->nrule); diff --git a/tftpd/remap.h b/tftpd/remap.h index 7efcf6a..2290aad 100644 --- a/tftpd/remap.h +++ b/tftpd/remap.h @@ -35,7 +35,9 @@ struct rule *parserulefile(FILE *); void freerules(struct rule *); /* Execute a rule set on a string; returns a malloc'd new string. */ -char *rewrite_string(const char *, const struct rule *, char, int, +struct formats; +char *rewrite_string(const struct formats *, const char *, + const struct rule *, char, int, match_pattern_callback, const char **); #endif /* WITH_REGEX */ diff --git a/tftpd/tftpd.8.in b/tftpd/tftpd.8.in index 71a712d..c9722cf 100644 --- a/tftpd/tftpd.8.in +++ b/tftpd/tftpd.8.in @@ -281,6 +281,15 @@ case-insensitively. By default it is case sensitive. .B e If this rule matches, end rule processing after executing the rule. .TP +.B E +If this rule matches, +\fIand the result matches a filename that can be transferred\fP, +end rule processing after executing the rule. If this is combined with +.BR r , +then if the substitution does \fInot\fP result in a valid filename, +the substitution is undone. This cannot be combined with +.BR g . +.TP .B s If this rule matches, start rule processing over from the very first rule after executing this rule. @@ -307,10 +316,10 @@ Inverse the sense of this rule, i.e. execute the only if the .I regex .I doesn't -match. Cannot used together with +match. Cannot used together with .BR r . .PP -The following escape sequences are recognized as part of the +The following escape sequences are recognized as part of a .IR "replacement pattern" : .TP \fB\\0\fP diff --git a/tftpd/tftpd.c b/tftpd/tftpd.c index 9abf009..3349894 100644 --- a/tftpd/tftpd.c +++ b/tftpd/tftpd.c @@ -106,11 +106,12 @@ int portrange = 0; unsigned int portrange_from, portrange_to; int verbosity = 0; -struct formats; #ifdef WITH_REGEX static struct rule *rewrite_rules = NULL; #endif +static FILE *file; + int tftp(struct tftphdr *, int); static void nak(int, const char *); static void timer(int); @@ -162,14 +163,14 @@ static void timer(int sig) } #ifdef WITH_REGEX -static struct rule *read_remap_rules(const char *file) +static struct rule *read_remap_rules(const char *rulefile) { FILE *f; struct rule *rulep; - f = fopen(file, "rt"); + f = fopen(rulefile, "rt"); if (!f) { - syslog(LOG_ERR, "Cannot open map file: %s: %m", file); + syslog(LOG_ERR, "Cannot open map file: %s: %m", rulefile); exit(EX_NOINPUT); } rulep = parserulefile(f); @@ -1057,19 +1058,12 @@ int main(int argc, char **argv) exit(0); } -static char *rewrite_access(char *, int, int, const char **); +static char *rewrite_access(const struct formats *, + char *, int, int, const char **); static int validate_access(char *, int, const struct formats *, const char **); static void tftp_sendfile(const struct formats *, struct tftphdr *, int); static void tftp_recvfile(const struct formats *, struct tftphdr *, int); -struct formats { - const char *f_mode; - char *(*f_rewrite) (char *, int, int, const char **); - int (*f_validate) (char *, int, const struct formats *, const char **); - void (*f_send) (const struct formats *, struct tftphdr *, int); - void (*f_recv) (const struct formats *, struct tftphdr *, int); - int f_convert; -}; static const struct formats formats[] = { { "netascii", rewrite_access, validate_access, tftp_sendfile, @@ -1126,8 +1120,9 @@ int tftp(struct tftphdr *tp, int size) nak(EBADOP, "Unknown mode"); exit(0); } + file = NULL; if (!(filename = (*pf->f_rewrite) - (origfilename, tp_opcode, from.sa.sa_family, &errmsgptr))) { + (pf, origfilename, tp_opcode, from.sa.sa_family, &errmsgptr))) { nak(EACCESS, errmsgptr); /* File denied by mapping rule */ exit(0); } @@ -1150,12 +1145,18 @@ int tftp(struct tftphdr *tp, int size) tmp_p, origfilename, filename); } - ecode = - (*pf->f_validate) (filename, tp_opcode, pf, &errmsgptr); - if (ecode) { - nak(ecode, errmsgptr); - exit(0); - } + /* + * If "file" is already set, then a file was already validated + * and opened during remap processing. + */ + if (!file) { + ecode = + (*pf->f_validate) (filename, tp_opcode, pf, &errmsgptr); + if (ecode) { + nak(ecode, errmsgptr); + exit(0); + } + } opt = ++cp; } else if (argn & 1) { val = ++cp; @@ -1411,12 +1412,12 @@ static int rewrite_macros(char macro, char *output) /* * Modify the filename, if applicable. If it returns NULL, deny the access. */ -static char *rewrite_access(char *filename, int mode, int af, - const char **msg) +static char *rewrite_access(const struct formats *pf, char *filename, + int mode, int af, const char **msg) { if (rewrite_rules) { char *newname = - rewrite_string(filename, rewrite_rules, + rewrite_string(pf, filename, rewrite_rules, mode != RRQ ? 'P' : 'G', af, rewrite_macros, msg); filename = newname; @@ -1425,8 +1426,10 @@ static char *rewrite_access(char *filename, int mode, int af, } #else -static char *rewrite_access(char *filename, int mode, int af, const char **msg) +static char *rewrite_access(const struct formats *pf, char *filename, + int mode, int af, const char **msg) { + (void)pf; (void)mode; /* Avoid warning */ (void)msg; (void)af; @@ -1434,7 +1437,6 @@ static char *rewrite_access(char *filename, int mode, int af, const char **msg) } #endif -static FILE *file; /* * Validate file access. Since we * have no uid or gid, for now require diff --git a/tftpd/tftpd.h b/tftpd/tftpd.h index e1d8bf0..277e5d2 100644 --- a/tftpd/tftpd.h +++ b/tftpd/tftpd.h @@ -23,4 +23,13 @@ char *tfstrdup(const char *); extern int verbosity; +struct formats { + const char *f_mode; + char *(*f_rewrite) (const struct formats *, char *, int, int, const char **); + int (*f_validate) (char *, int, const struct formats *, const char **); + void (*f_send) (const struct formats *, struct tftphdr *, int); + void (*f_recv) (const struct formats *, struct tftphdr *, int); + int f_convert; +}; + #endif From 351907e3f0021876abeba14ac47081cf2c21b50a Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Wed, 29 May 2024 17:28:53 -0700 Subject: [PATCH 169/184] tftpd: handle rule filter flags more cleanly Instead of a bunch of ad hoc tests, keep a bitmask of flags that would keep this rule from being executed. This also removes the ugly hack of converting the request mode between opcode and character encodings for really no good reason. Signed-off-by: H. Peter Anvin --- tftpd/remap.c | 38 ++++++++++++++++++++------------------ tftpd/remap.h | 2 +- tftpd/tftpd.c | 3 +-- 3 files changed, 22 insertions(+), 21 deletions(-) diff --git a/tftpd/remap.c b/tftpd/remap.c index c7c8f85..16801c1 100644 --- a/tftpd/remap.c +++ b/tftpd/remap.c @@ -35,12 +35,13 @@ #define RULE_IPV6 0x80 /* IPv6 only */ #define RULE_HASFILE 0x100 /* Valid if rule results in a valid filename */ +#define RULE_RRQ 0x200 /* Get (read) only */ +#define RULE_WRQ 0x400 /* Put (write) only */ struct rule { struct rule *next; int nrule; - int rule_flags; - char rule_mode; + unsigned int rule_flags; regex_t rx; const char *pattern; }; @@ -164,7 +165,7 @@ static int readescstring(char *buf, char **str) int wasbs = 0, len = 0; while (*p && isspace(*p)) - p++; + p++; if (!*p) { *buf = '\0'; @@ -237,9 +238,11 @@ static int parseline(char *line, struct rule *r, int lineno) r->rule_flags |= RULE_IPV6; break; case 'G': + r->rule_flags |= RULE_RRQ; + break; case 'P': - r->rule_mode = *p; - break; + r->rule_flags |= RULE_WRQ; + break; default: syslog(LOG_ERR, "Remap command \"%s\" on line %d contains invalid char \"%c\"", @@ -346,7 +349,7 @@ void freerules(struct rule *r) /* Execute a rule set on a string; returns a malloc'd new string. */ char *rewrite_string(const struct formats *pf, const char *input, const struct rule *rules, - char mode, int af, match_pattern_callback macrosub, + int mode, int af, match_pattern_callback macrosub, const char **errmsg) { char *current = tfstrdup(input); @@ -357,6 +360,7 @@ char *rewrite_string(const struct formats *pf, int len; int was_match = 0; int deadman = DEADMAN_MAX_STEPS; + unsigned int bad_flags; /* Default error */ *errmsg = "Remap table failure"; @@ -365,15 +369,15 @@ char *rewrite_string(const struct formats *pf, syslog(LOG_INFO, "remap: input: %s", current); } + bad_flags = 0; + if (mode != RRQ) bad_flags |= RULE_RRQ; + if (mode != WRQ) bad_flags |= RULE_WRQ; + if (af != AF_INET) bad_flags |= RULE_IPV4; + if (af != AF_INET6) bad_flags |= RULE_IPV6; + for (ruleptr = rules; ruleptr; ruleptr = ruleptr->next) { - if (ruleptr->rule_mode && ruleptr->rule_mode != mode) - continue; /* Rule not applicable, try next */ - - if ((ruleptr->rule_flags & RULE_IPV4) && (af != AF_INET)) - continue; /* Rule not applicable, try next */ - - if ((ruleptr->rule_flags & RULE_IPV6) && (af != AF_INET6)) - continue; /* Rule not applicable, try next */ + if (ruleptr->rule_flags & bad_flags) + continue; /* This rule is excluded by flags */ if (!deadman--) { syslog(LOG_WARNING, @@ -424,8 +428,7 @@ char *rewrite_string(const struct formats *pf, genmatchstring(newstr, ruleptr->pattern, current, pmatch, macrosub); if ((ruleptr->rule_flags & RULE_HASFILE) && - pf->f_validate(newstr, mode == 'G' ? RRQ : WRQ, - pf, &accerr)) { + pf->f_validate(newstr, mode, pf, &accerr)) { if (verbosity >= 3) { syslog(LOG_INFO, "remap: rule %d: ignored rewrite (%s): %s", ruleptr->nrule, accerr, newstr); @@ -441,8 +444,7 @@ char *rewrite_string(const struct formats *pf, ruleptr->nrule, current); } } else if (ruleptr->rule_flags & RULE_HASFILE) { - if (pf->f_validate(current, mode == 'G' ? RRQ : WRQ, - pf, &accerr)) { + if (pf->f_validate(current, mode, pf, &accerr)) { if (verbosity >= 3) { syslog(LOG_INFO, "remap: rule %d: not exiting (%s)\n", ruleptr->nrule, accerr); diff --git a/tftpd/remap.h b/tftpd/remap.h index 2290aad..a9954ab 100644 --- a/tftpd/remap.h +++ b/tftpd/remap.h @@ -37,7 +37,7 @@ void freerules(struct rule *); /* Execute a rule set on a string; returns a malloc'd new string. */ struct formats; char *rewrite_string(const struct formats *, const char *, - const struct rule *, char, int, + const struct rule *, int, int, match_pattern_callback, const char **); #endif /* WITH_REGEX */ diff --git a/tftpd/tftpd.c b/tftpd/tftpd.c index 3349894..14182f3 100644 --- a/tftpd/tftpd.c +++ b/tftpd/tftpd.c @@ -1417,8 +1417,7 @@ static char *rewrite_access(const struct formats *pf, char *filename, { if (rewrite_rules) { char *newname = - rewrite_string(pf, filename, rewrite_rules, - mode != RRQ ? 'P' : 'G', af, + rewrite_string(pf, filename, rewrite_rules, mode, af, rewrite_macros, msg); filename = newname; } From b9f2335e88dcb3939015843c7143f1533c755a46 Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Wed, 29 May 2024 17:35:14 -0700 Subject: [PATCH 170/184] tftpd: shut up one more setjmp complaint One more place where the compiler complains about setjmp. Work around it by creating yet another static variable. Ugly, but it works. Signed-off-by: H. Peter Anvin --- tftpd/tftpd.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tftpd/tftpd.c b/tftpd/tftpd.c index 14182f3..d9b3f46 100644 --- a/tftpd/tftpd.c +++ b/tftpd/tftpd.c @@ -1660,17 +1660,21 @@ static void tftp_sendfile(const struct formats *pf, struct tftphdr *oap, int oac /* * Receive a file. */ -static void tftp_recvfile(const struct formats *pf, struct tftphdr *oap, int oacklen) +static void tftp_recvfile(const struct formats *pf, + struct tftphdr *oack, int oacklen) { struct tftphdr *dp; int n, size; /* These are "static" to avoid longjmp funnies */ + static struct tftphdr *oap; static struct tftphdr *ap; /* ack buffer */ static u_short block = 0; static int acksize; u_short dp_opcode, dp_block; unsigned long r_timeout; + oap = oack; + dp = w_init(); do { timeout = rexmtval; From 416046e2ad67508869ce472d8b8b0dd5578db0d7 Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Wed, 29 May 2024 17:49:21 -0700 Subject: [PATCH 171/184] tftpd: call setgroups() before initgroups() Unconditionally call setgroups() before calling initgroups(). That way if initgroups() fails for some reason (e.g. it is unable to access /etc/groups or the equivalent) then at least the group list will be empty. Signed-off-by: H. Peter Anvin --- tftpd/tftpd.c | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/tftpd/tftpd.c b/tftpd/tftpd.c index d9b3f46..2db7977 100644 --- a/tftpd/tftpd.c +++ b/tftpd/tftpd.c @@ -980,23 +980,20 @@ int main(int argc, char **argv) /etc/group still need to be accessible at this point. If we get EPERM, this is already a restricted process, e.g. using user namespaces on Linux. */ - setrv = -1; die = 0; +#ifdef HAVE_SETGROUPS + setrv = setgroups(0, NULL); + if (setrv && errno != EPERM) { + syslog(LOG_ERR, "cannot clear group list"); + die = EX_OSERR; + } +#endif #ifdef HAVE_INITGROUPS setrv = initgroups(user, pw->pw_gid); if (setrv && errno != EPERM) { syslog(LOG_ERR, "cannot set groups for user %s", user); die = EX_OSERR; } -#endif -#ifdef HAVE_SETGROUPS - if (setrv) { - setrv = setgroups(0, NULL); - if (setrv && errno != EPERM) { - syslog(LOG_ERR, "cannot clear group list"); - die = EX_OSERR; - } - } #endif if (die) exit(die); From 99112f02064359f5070c82298aa9a6f4dd58e535 Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Wed, 29 May 2024 17:52:42 -0700 Subject: [PATCH 172/184] tftpd: allow initgroups() to rescue setgroups() failure If setgroups() fails, but initgroups() succeeds (somehow) then allow the process to continue, as initgroups() ought to have set the group list properly. Signed-off-by: H. Peter Anvin --- tftpd/tftpd.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tftpd/tftpd.c b/tftpd/tftpd.c index 2db7977..e410d42 100644 --- a/tftpd/tftpd.c +++ b/tftpd/tftpd.c @@ -990,7 +990,9 @@ int main(int argc, char **argv) #endif #ifdef HAVE_INITGROUPS setrv = initgroups(user, pw->pw_gid); - if (setrv && errno != EPERM) { + if (!setrv) { + die = 0; + } else if (errno != EPERM) { syslog(LOG_ERR, "cannot set groups for user %s", user); die = EX_OSERR; } From 5e8d5c24b260b2e9e25a248a93eddc11bc9260ac Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Wed, 29 May 2024 18:10:41 -0700 Subject: [PATCH 173/184] tftpd: Use setres*id() if available POSIX apparently doesn't clearly specify the behavior of the saved ID when calling setre*id(). If the system has setres*id() then use it to make absolutely sure that the ID changes cannot be undone. Signed-off-by: H. Peter Anvin --- configure.ac | 2 ++ tftpd/tftpd.c | 12 ++++++++---- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/configure.ac b/configure.ac index 04b6f98..8770431 100644 --- a/configure.ac +++ b/configure.ac @@ -86,7 +86,9 @@ AC_CHECK_FUNCS(setsid) AC_CHECK_FUNCS(recvmsg) AC_CHECK_FUNCS(ftruncate) AC_CHECK_FUNCS(setreuid) +AC_CHECK_FUNCS(setresuid) AC_CHECK_FUNCS(setregid) +AC_CHECK_FUNCS(setresgid) AC_CHECK_FUNCS(initgroups) AC_CHECK_FUNCS(setgroups) AC_CHECK_FUNCS(sigaction) diff --git a/tftpd/tftpd.c b/tftpd/tftpd.c index e410d42..fcb29aa 100644 --- a/tftpd/tftpd.c +++ b/tftpd/tftpd.c @@ -1011,16 +1011,20 @@ int main(int argc, char **argv) #endif } -#ifdef HAVE_SETREGID +#ifdef HAVE_SETRESGID + setrv = setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid); +#elif defined(HAVE_SETREGID) setrv = setregid(pw->pw_gid, pw->pw_gid); #else setrv = setegid(pw->pw_gid) || setgid(pw->pw_gid); #endif if (setrv && errno == EPERM) { - setrv = 0; /* Already restricted */ + setrv = 0; /* Assume already restricted by system policy */ } -#ifdef HAVE_SETREUID +#ifdef HAVE_SETRESUID + setrv = setrv || setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid); +#elif defined(HAVE_SETREUID) setrv = setrv || setreuid(pw->pw_uid, pw->pw_uid); #else /* Important: setuid() must come first */ @@ -1028,7 +1032,7 @@ int main(int argc, char **argv) (geteuid() != pw->pw_uid && seteuid(pw->pw_uid)); #endif if (setrv && errno == EPERM) { - setrv = 0; /* Already restricted */ + setrv = 0; /* Assume already restricted by system policy */ } if (setrv) { From e52af4207a6e09a1ba21f61d6f85d8b2b1e6efc0 Mon Sep 17 00:00:00 2001 From: Yegor Yefremov Date: Fri, 31 May 2024 09:03:28 +0200 Subject: [PATCH 174/184] Add EditorConfig support Provide common indentation configuration for the source files. For more information, visit https://editorconfig.org/. Signed-off-by: Yegor Yefremov --- .editorconfig | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 .editorconfig diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..0143c3c --- /dev/null +++ b/.editorconfig @@ -0,0 +1,11 @@ +root = true + +[*] +end_of_line = lf +trim_trailing_whitespace = true +insert_final_newline = true + +[{*.c,*.h}] +indent_style = space +indent_size = 4 +tab_width = 4 From 33051a296c4890270d75c6ed22dc074b2ab9520a Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Mon, 10 Jun 2024 14:36:28 -0700 Subject: [PATCH 175/184] signals: require and always use sigaction() tftpd already requires sigaction() to compile, so there is no reason to use anything else. It also allows for nicer combination of flags. Signed-off-by: H. Peter Anvin --- common/signal.c | 39 ++++++++------------------------------- config.h | 5 +---- configure.ac | 11 ++++++----- tftp/main.c | 4 ++-- tftp/tftp.c | 4 ++-- tftpd/misc.c | 14 +++----------- 6 files changed, 22 insertions(+), 55 deletions(-) diff --git a/common/signal.c b/common/signal.c index 9119d57..ccd4af8 100644 --- a/common/signal.c +++ b/common/signal.c @@ -1,42 +1,19 @@ /* * signal.c * - * Use sigaction() to simulate BSD signal() + * User-friendly wrapper around sigaction(). */ #include "config.h" -#ifdef HAVE_SIGACTION - -sighandler_t tftp_signal(int signum, sighandler_t handler) +int tftp_signal(int signum, sighandler_t handler, int flags) { - struct sigaction action, oldaction; + struct sigaction sa; - memset(&action, 0, sizeof action); - action.sa_handler = handler; - sigemptyset(&action.sa_mask); - sigaddset(&action.sa_mask, signum); - action.sa_flags = SA_RESTART; + memset(&sa, 0, sizeof sa); + sa.sa_handler = handler; + sigemptyset(&sa.sa_mask); + sa.sa_flags = flags; - if (sigaction(signum, &action, &oldaction)) - return SIG_ERR; - - return oldaction.sa_handler; + return sigaction(signum, &sa, NULL); } - -#elif defined(HAVE_BSD_SIGNAL) - -sighandler_t tftp_signal(int signum, sighandler_t handler) -{ - return bsd_signal(signum, handler); -} - -#else - -/* This is dangerous at best. Let's hope it works. */ -sighandler_t tftp_signal(int signum, sighandler_t handler) -{ - return signal(signum, handler); -} - -#endif diff --git a/config.h b/config.h index a6a402b..1983058 100644 --- a/config.h +++ b/config.h @@ -282,10 +282,7 @@ char *xstrdup(const char *); #ifndef HAVE_SIGHANDLER_T typedef void (*sighandler_t)(int); #endif -#ifndef SIG_ERR -#define SIG_ERR NULL -#endif -sighandler_t tftp_signal(int, sighandler_t); +int tftp_signal(int, sighandler_t, int); #ifndef HAVE_DUP2 int dup2(int, int); diff --git a/configure.ac b/configure.ac index 8770431..2d4b93d 100644 --- a/configure.ac +++ b/configure.ac @@ -74,9 +74,12 @@ dnl but POSIX requires for socklen_t to be defined. dnl AC_CHECK_TYPES(socklen_t,,, [AC_INCLUDES_DEFAULT -#if HAVE_SYS_TYPES_H +#ifdef HAVE_SYS_TYPES_H # include #endif +#ifdef HAVE_SYS_SOCKET_H +# include +#endif ]) AC_SEARCH_LIBS(socket, [socket ws2_32 wsock32], , [AC_MSG_ERROR(socket library not found)]) @@ -85,14 +88,12 @@ AC_CHECK_FUNCS(fcntl) AC_CHECK_FUNCS(setsid) AC_CHECK_FUNCS(recvmsg) AC_CHECK_FUNCS(ftruncate) -AC_CHECK_FUNCS(setreuid) AC_CHECK_FUNCS(setresuid) -AC_CHECK_FUNCS(setregid) +AC_CHECK_FUNCS(setreuid) AC_CHECK_FUNCS(setresgid) +AC_CHECK_FUNCS(setregid) AC_CHECK_FUNCS(initgroups) AC_CHECK_FUNCS(setgroups) -AC_CHECK_FUNCS(sigaction) -AC_CHECK_FUNCS(bsd_signal) AC_CHECK_TYPES(sighandler_t) dnl Solaris 8 has [u]intmax_t but not strtoumax(). How utterly braindamaged. diff --git a/tftp/main.c b/tftp/main.c index 438f48a..ac06330 100644 --- a/tftp/main.c +++ b/tftp/main.c @@ -305,7 +305,7 @@ int main(int argc, char *argv[]) sp->s_proto = (char *)"udp"; } - tftp_signal(SIGINT, intr); + tftp_signal(SIGINT, intr, 0); if (peerargc) { /* Set peer */ @@ -768,8 +768,8 @@ void intr(int sig) { (void)sig; /* Quiet unused warning */ - tftp_signal(SIGALRM, SIG_IGN); alarm(0); + tftp_signal(SIGALRM, SIG_DFL, 0); siglongjmp(toplevel, -1); } diff --git a/tftp/tftp.c b/tftp/tftp.c index aecdeb9..33a4175 100644 --- a/tftp/tftp.c +++ b/tftp/tftp.c @@ -84,7 +84,7 @@ void tftp_sendfile(int fd, const char *name, const char *mode) is_request = 1; /* First packet is the actual WRQ */ amount = 0; - tftp_signal(SIGALRM, timer); + tftp_signal(SIGALRM, timer, 0); do { if (is_request) { size = makerequest(WRQ, name, dp, mode) - 4; @@ -190,7 +190,7 @@ void tftp_recvfile(int fd, const char *name, const char *mode) firsttrip = 1; amount = 0; - tftp_signal(SIGALRM, timer); + tftp_signal(SIGALRM, timer, 0); do { if (firsttrip) { size = makerequest(RRQ, name, ap, mode); diff --git a/tftpd/misc.c b/tftpd/misc.c index 07684dd..9070f96 100644 --- a/tftpd/misc.c +++ b/tftpd/misc.c @@ -19,19 +19,11 @@ #include "tftpd.h" /* - * Set the signal handler and flags. Basically a user-friendly - * wrapper around sigaction(). + * Set the signal handler and flags, and error out on failure. */ -void set_signal(int signum, void (*handler) (int), int flags) +void set_signal(int signum, sighandler_t handler, int flags) { - struct sigaction sa; - - memset(&sa, 0, sizeof sa); - sa.sa_handler = handler; - sigemptyset(&sa.sa_mask); - sa.sa_flags = flags; - - if (sigaction(signum, &sa, NULL)) { + if (tftp_signal(signum, handler, flags)) { syslog(LOG_ERR, "sigaction: %m"); exit(EX_OSERR); } From fefaa2cc5c2ae1537fccbb552496787130feeeaa Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Mon, 10 Jun 2024 18:24:53 -0700 Subject: [PATCH 176/184] autoconf: modernize and modularize Use my modular m4 library used for other things as well; update autoconf macros to 2.71 standard. Signed-off-by: H. Peter Anvin --- .gitignore | 11 +- Makefile | 37 ++-- aclocal.m4 | 280 ------------------------ autoconf/m4/pa_add_cflags.m4 | 9 + autoconf/m4/pa_add_flags.m4 | 39 ++++ autoconf/m4/pa_add_headers.m4 | 13 ++ autoconf/m4/pa_add_langflags.m4 | 27 +++ autoconf/m4/pa_arg_bool.m4 | 19 ++ autoconf/m4/pa_arg_disabled.m4 | 4 + autoconf/m4/pa_arg_enabled.m4 | 4 + autoconf/m4/pa_build_ifelse.m4 | 16 ++ autoconf/m4/pa_c_typeof.m4 | 32 +++ autoconf/m4/pa_check_bad_stdc_inline.m4 | 26 +++ autoconf/m4/pa_check_inttypes_h_sane.m4 | 17 ++ autoconf/m4/pa_cross_compile.m4 | 41 ++++ autoconf/m4/pa_flags_langlist.m4 | 19 ++ autoconf/m4/pa_have_tcpwrappers.m4 | 26 +++ autoconf/m4/pa_lang_foreach.m4 | 15 ++ autoconf/m4/pa_lang_seen_list.m4 | 20 ++ autoconf/m4/pa_option_debug.m4 | 13 ++ autoconf/m4/pa_option_profiling.m4 | 8 + autoconf/m4/pa_prog_cc.m4 | 13 ++ autoconf/m4/pa_search_libs_and_add.m4 | 22 ++ autoconf/m4/pa_sigsetjmp.m4 | 25 +++ autoconf/m4/pa_sym.m4 | 11 + autogen.sh | 85 ++++++- common/Makefile | 2 +- config.h | 10 +- MCONFIG.in => config/MCONFIG.in | 0 configure.ac | 278 +++++++++++------------ lib/Makefile | 2 +- tftp/Makefile | 2 +- tftpd/Makefile | 2 +- tftpd/recvfrom.c | 2 +- tftpd/tftpd.c | 11 +- 35 files changed, 680 insertions(+), 461 deletions(-) delete mode 100644 aclocal.m4 create mode 100644 autoconf/m4/pa_add_cflags.m4 create mode 100644 autoconf/m4/pa_add_flags.m4 create mode 100644 autoconf/m4/pa_add_headers.m4 create mode 100644 autoconf/m4/pa_add_langflags.m4 create mode 100644 autoconf/m4/pa_arg_bool.m4 create mode 100644 autoconf/m4/pa_arg_disabled.m4 create mode 100644 autoconf/m4/pa_arg_enabled.m4 create mode 100644 autoconf/m4/pa_build_ifelse.m4 create mode 100644 autoconf/m4/pa_c_typeof.m4 create mode 100644 autoconf/m4/pa_check_bad_stdc_inline.m4 create mode 100644 autoconf/m4/pa_check_inttypes_h_sane.m4 create mode 100644 autoconf/m4/pa_cross_compile.m4 create mode 100644 autoconf/m4/pa_flags_langlist.m4 create mode 100644 autoconf/m4/pa_have_tcpwrappers.m4 create mode 100644 autoconf/m4/pa_lang_foreach.m4 create mode 100644 autoconf/m4/pa_lang_seen_list.m4 create mode 100644 autoconf/m4/pa_option_debug.m4 create mode 100644 autoconf/m4/pa_option_profiling.m4 create mode 100644 autoconf/m4/pa_prog_cc.m4 create mode 100644 autoconf/m4/pa_search_libs_and_add.m4 create mode 100644 autoconf/m4/pa_sigsetjmp.m4 create mode 100644 autoconf/m4/pa_sym.m4 rename MCONFIG.in => config/MCONFIG.in (100%) diff --git a/.gitignore b/.gitignore index 7a98dc8..bb95251 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,9 @@ -/MCONFIG -/aconfig.h -/aconfig.h.in +/config/MCONFIG +/config/config.h +/config/config.h.in +/autoconf/aclocal.m4 +/autoconf/clean.sh +/autoconf/helpers/ /autom4te.cache /config.log /config.status @@ -12,5 +15,7 @@ *.8 *.a *.o +*.i +*.s *~ \#* diff --git a/Makefile b/Makefile index 8c779d3..2e790fd 100644 --- a/Makefile +++ b/Makefile @@ -3,10 +3,10 @@ SUB = lib common tftp tftpd -%.build: MCONFIG aconfig.h version.h +%.build: config/MCONFIG config/config.h version.h $(MAKE) -C $(patsubst %.build, %, $@) -%.install: MCONFIG aconfig.h version.h +%.install: config/MCONFIG config/config.h version.h $(MAKE) -C $(patsubst %.install, %, $@) install %.clean: @@ -15,12 +15,12 @@ SUB = lib common tftp tftpd %.distclean: $(MAKE) -C $(patsubst %.distclean, %, $@) distclean -all: MCONFIG $(patsubst %, %.build, $(SUB)) +all: config/MCONFIG $(patsubst %, %.build, $(SUB)) tftp.build: lib.build common.build tftpd.build: lib.build common.build -install: MCONFIG $(patsubst %, %.install, $(SUB)) +install: config/MCONFIG $(patsubst %, %.install, $(SUB)) clean: localclean $(patsubst %, %.clean, $(SUB)) @@ -30,46 +30,37 @@ localclean: distclean: localdistclean $(patsubst %, %.distclean, $(SUB)) localdistclean: localclean - rm -f MCONFIG config.status config.log aconfig.h *~ \#* + rm -f config/config/MCONFIG config.status config.log config/config.h *~ \#* rm -rf *.cache find . -type f \( -name \*.orig -o -name \*.rej \) | xargs rm -f spotless: distclean - rm -f configure aconfig.h.in tftp.spec + rm -f configure config/config.h.in tftp.spec -autoconf: configure aconfig.h.in +autoconf: configure config/config.h.in -config: MCONFIG aconfig.h +config: config/MCONFIG config/config.h release: $(MAKE) autoconf $(MAKE) tftp.spec $(MAKE) distclean -MCONFIG: configure MCONFIG.in aconfig.h.in +config/MCONFIG: configure config/MCONFIG.in config/config.h.in if test -x config.status; then \ ./config.status --recheck && ./config.status ; \ else \ ./configure ; \ fi -aconfig.h: MCONFIG +config/config.h: config/MCONFIG : Generated by side effect -# Adding "configure" to the dependencies serializes this with running -# autoconf, because there are apparently race conditions between -# autoconf and autoheader. And worse than that, even when autoconf -# cleanly returns first, autoheader will truncate the timestamp of -# aconfig.h.in to second resolution, so on a filesystem with subsecond -# resolution it can appear older than configure (which isn't truncated). -# So make it an order-only prerequisite to avoid looping regenerating it. -aconfig.h.in: configure.ac aclocal.m4 | configure - rm -f aconfig.h.in aconfig.h - autoheader +configure: configure.ac + sh autogen.sh -configure: configure.ac aclocal.m4 - rm -rf MCONFIG configure config.log aconfig.h *.cache - autoconf +config/config.h.in: configure + : Generated by side effect version.h: version echo \#define VERSION \"tftp-hpa `cat version`\" > version.h diff --git a/aclocal.m4 b/aclocal.m4 deleted file mode 100644 index c07702c..0000000 --- a/aclocal.m4 +++ /dev/null @@ -1,280 +0,0 @@ -dnl ----------------------------------------------------------------------- -dnl -dnl Copyright 1999-2008 H. Peter Anvin - All Rights Reserved -dnl -dnl This program is free software; you can redistribute it and/or modify -dnl it under the terms of the GNU General Public License as published by -dnl the Free Software Foundation, Inc., 53 Temple Place Ste 330, -dnl Bostom MA 02111-1307, USA; either version 2 of the License, or -dnl (at your option) any later version; incorporated herein by reference. -dnl -dnl ----------------------------------------------------------------------- - -dnl -------------------------------------------------------------------------- -dnl PA_ADD_CFLAGS() -dnl -dnl Attempt to add the given option to CFLAGS, if it doesn't break compilation -dnl -------------------------------------------------------------------------- -AC_DEFUN(PA_ADD_CFLAGS, -[AC_MSG_CHECKING([if $CC accepts $1]) - pa_add_cflags__old_cflags="$CFLAGS" - CFLAGS="$CFLAGS $1" - AC_TRY_LINK([#include ], - [printf("Hello, World!\n");], - AC_MSG_RESULT([yes]), - AC_MSG_RESULT([no]) - CFLAGS="$pa_add_cflags__old_cflags")]) - -dnl -------------------------------------------------------------------------- -dnl PA_SIGSETJMP -dnl -dnl Do we have sigsetjmp/siglongjmp? (AC_CHECK_FUNCS doesn't seem to work -dnl for these particular functions.) -dnl -------------------------------------------------------------------------- -AC_DEFUN(PA_SIGSETJMP, -[AC_MSG_CHECKING([for sigsetjmp]) - AC_TRY_LINK([ -#ifdef HAVE_SETJMP_H -#include -#endif], - [sigjmp_buf buf; - sigsetjmp(buf,1); - siglongjmp(buf,2);], - AC_MSG_RESULT([yes]) - $1, - AC_MSG_RESULT([no]) - $2)]) - -dnl -------------------------------------------------------------------------- -dnl PA_MSGHDR_MSG_CONTROL -dnl -dnl Does struct msghdr have the msg_control field? -dnl -------------------------------------------------------------------------- -AH_TEMPLATE([HAVE_MSGHDR_MSG_CONTROL], -[Define if struct msghdr has the msg_control field.]) - -AC_DEFUN(PA_MSGHDR_MSG_CONTROL, - [AC_CHECK_MEMBER(struct msghdr.msg_control, - [AC_DEFINE(HAVE_MSGHDR_MSG_CONTROL)], - [], - [ -#include -#include -#include - ])]) - -dnl ------------------------------------------------------------------------ -dnl PA_STRUCT_IN_PKTINFO -dnl -dnl Look for definition of struct in_pktinfo, which at least has an -dnl ipi_addr member. Some versions of glibc lack struct in_pktinfo; -dnl if so we need to include the definition ourselves -- but we only -dnl want to do that if absolutely necessary! -dnl ------------------------------------------------------------------------ -AH_TEMPLATE([HAVE_STRUCT_IN_PKTINFO], -[Define if struct in_pktinfo is defined.]) - -AC_DEFUN(PA_STRUCT_IN_PKTINFO, - [AC_CHECK_MEMBER(struct in_pktinfo.ipi_addr, - [AC_DEFINE(HAVE_STRUCT_IN_PKTINFO)], - [], - [ -#include -#include -#include -#include -#include -#include - ])]) - - -dnl ------------------------------------------------------------------------ -dnl PA_STRUCT_SOCKADDR_IN6 -dnl -dnl Look for definition of struct sockaddr_in6, which at least has an -dnl sin6_addr member -dnl -AH_TEMPLATE([HAVE_STRUCT_SOCKADDR_IN6], -[Define if struct sockaddr_in6 is defined.]) - -AC_DEFUN(PA_STRUCT_SOCKADDR_IN6, - [AC_CHECK_MEMBER(struct sockaddr_in6.sin6_addr, - [ - AC_DEFINE(HAVE_STRUCT_SOCKADDR_IN6) - HAVE_INET6=true; - ], - [ - HAVE_INET6=false; - ], - [ -#include -#include -#include -#include - ])]) - -dnl ------------------------------------------------------------------------ -dnl PA_STRUCT_ADDRINFO -dnl -dnl Look for definition of struct addrinfo, which at least has an -dnl ai_addr member -dnl -AH_TEMPLATE([HAVE_STRUCT_ADDRINFO], -[Define if struct addrinfo is defined.]) - -AC_DEFUN(PA_STRUCT_ADDRINFO, - [AC_CHECK_MEMBER(struct addrinfo.ai_addr, - [AC_DEFINE(HAVE_STRUCT_ADDRINFO)], - [], - [ -#include -#include -#include - ])]) - -dnl ------------------------------------------------------------------------ -dnl PA_STRUCT_IN6_PKTINFO -dnl -dnl Look for definition of struct in6_pktinfo, which at least has an -dnl ipi6_addr member -dnl -AH_TEMPLATE([HAVE_STRUCT_IN6_PKTINFO], -[Define if struct in6_pktinfo is defined.]) - -AC_DEFUN(PA_STRUCT_IN6_PKTINFO, - [AC_CHECK_MEMBER(struct in6_pktinfo.ipi6_addr, - [AC_DEFINE(HAVE_STRUCT_IN6_PKTINFO)], - [], - [ -#include -#include -#include -#include - ])]) - -dnl -------------------------------------------------------------------------- -dnl PA_HAVE_TCPWRAPPERS -dnl -dnl Do we have the tcpwrappers -lwrap? This can't be done using AC_CHECK_LIBS -dnl due to the need to provide "allow_severity" and "deny_severity" variables -dnl -------------------------------------------------------------------------- -AH_TEMPLATE([HAVE_TCPWRAPPERS], -[Define if we have tcpwrappers (-lwrap) and .]) - -AC_DEFUN(PA_HAVE_TCPWRAPPERS, -[AC_CHECK_LIB([wrap], [main]) - AC_MSG_CHECKING([for tcpwrappers]) - AC_TRY_LINK( -[ -#include -int allow_severity = 0; -int deny_severity = 0; -], -[ - hosts_ctl("sample_daemon", STRING_UNKNOWN, STRING_UNKNOWN, STRING_UNKNOWN); -], -[ - AC_DEFINE(HAVE_TCPWRAPPERS) - AC_MSG_RESULT([yes]) -], -[ - AC_MSG_RESULT([no]) -])]) - -dnl ------------------------------------------------------------------------ -dnl PA_CHECK_INTTYPES_H_SANE -dnl -dnl At least some versions of AIX 4 have macros which are -dnl completely broken. Try to detect those. -dnl -------------------------------------------------------------------------- -AH_TEMPLATE([INTTYPES_H_IS_SANE], -[Define if the macros in are usable]) - -AC_DEFUN(PA_CHECK_INTTYPES_H_SANE, -[AC_CHECK_HEADERS(inttypes.h, - [ - AC_MSG_CHECKING([if inttypes.h is sane]) - AC_TRY_LINK( - [ -#include -#include - ], - [uintmax_t max = UINTMAX_C(0); - printf("%"PRIuMAX"\n", max);], - AC_MSG_RESULT([yes]) - AC_DEFINE(INTTYPES_H_IS_SANE), - AC_MSG_RESULT([no (AIX, eh?)])) - ]) -]) - -dnl ------------------------------------------------------------------------ -dnl PA_WITH_BOOL -dnl -dnl PA_WITH_BOOL(option, default, help, enable, disable) -dnl -dnl Provides a more convenient way to specify --with-option and -dnl --without-option, with a default. default should be either 0 or 1. -dnl ------------------------------------------------------------------------ -AC_DEFUN(PA_WITH_BOOL, -[AC_ARG_WITH([$1], [$3], -if test ["$withval"] != no; then -[$4] -else -[$5] -fi, -if test [$2] -ne 0; then -[$4] -else -[$5] -fi)]) - -dnl -------------------------------------------------------------------------- -dnl PA_HEADER_DEFINES -dnl -dnl PA_HEADER_DEFINES(header, type, value) -dnl -------------------------------------------------------------------------- -AC_DEFUN(PA_HEADER_DEFINES, -[AC_MSG_CHECKING([if $1 defines $3]) - AH_TEMPLATE([HAVE_$3_DEFINITION], [Define if $1 defines $3]) - AC_TRY_COMPILE([ -#include <$1> -], -[ -int main() -{ - $2 dummy = $3; - return 0; -} -], -[ - pa_header_define=`echo HAVE_$3_DEFINITION | tr '[a-z]' '[A-Z]'` - AC_DEFINE_UNQUOTED($pa_header_define) - AC_MSG_RESULT(yes) -], -[ - AC_MSG_RESULT(no) -])]) - -dnl -------------------------------------------------------------------------- -dnl PA_SEARCH_LIBS_AND_ADD -dnl -dnl PA_SEARCH_LIBS_AND_ADD(function, libraries [,function to add]) -dnl -------------------------------------------------------------------------- - -AC_DEFUN(PA_SEARCH_LIBS_AND_ADD, - [ - AH_TEMPLATE(AS_TR_CPP(HAVE_$1), [Define if $1 function was found]) - AC_SEARCH_LIBS($1, $2, - [ - AC_DEFINE_UNQUOTED(AS_TR_CPP(HAVE_$1)) - pa_add_$1=false; - ], - [ - XTRA=true; - if test $# -eq 3; then - AC_LIBOBJ($3) - else - AC_LIBOBJ($1) - fi - pa_add_$1=true; - ])]) diff --git a/autoconf/m4/pa_add_cflags.m4 b/autoconf/m4/pa_add_cflags.m4 new file mode 100644 index 0000000..26d55c4 --- /dev/null +++ b/autoconf/m4/pa_add_cflags.m4 @@ -0,0 +1,9 @@ +dnl -------------------------------------------------------------------------- +dnl PA_ADD_CFLAGS(variable, flag [,actual_flag [,success [,failure]]]]) +dnl +dnl Attempt to add the given option to xFLAGS, if it doesn't break +dnl compilation. If the option to be tested is different than the +dnl option that should actually be added, add the option to be +dnl actually added as a second argument. +dnl -------------------------------------------------------------------------- +AC_DEFUN([PA_ADD_CFLAGS], [PA_ADD_FLAGS(CFLAGS, [$1], [$2], [$3], [$4])]) diff --git a/autoconf/m4/pa_add_flags.m4 b/autoconf/m4/pa_add_flags.m4 new file mode 100644 index 0000000..23f96f1 --- /dev/null +++ b/autoconf/m4/pa_add_flags.m4 @@ -0,0 +1,39 @@ +dnl -------------------------------------------------------------------------- +dnl PA_ADD_FLAGS(flagvar, flags) +dnl +dnl Add [flags] to the variable [flagvar] if and only if it is accepted +dnl by all languages affected by [flagvar], if those languages have +dnl been previously seen in the script. +dnl -------------------------------------------------------------------------- +AC_DEFUN([PA_ADD_FLAGS], +[ + AS_VAR_PUSHDEF([old], [_$0_$1_orig]) + AS_VAR_PUSHDEF([ok], [_$0_$1_ok]) + AS_VAR_PUSHDEF([flags], [$1]) + + AS_VAR_COPY([old], [flags]) + AS_VAR_SET([flags], ["$flags $2"]) + AS_VAR_SET([ok], [yes]) + + PA_LANG_FOREACH(PA_FLAGS_LANGLIST($1), + [AS_VAR_IF([ok], [yes], + [AC_MSG_CHECKING([if $]_AC_CC[ accepts $2]) + PA_BUILD_IFELSE([], + [AC_MSG_RESULT([yes])], + [AC_MSG_RESULT([no]) + AS_VAR_SET([ok], [no])])]) + ]) + + AS_VAR_IF([ok], [yes], + [m4_ifnblank([$3],[AS_VAR_SET([flags], ["$old $3"])]) + m4_foreach_w([_pa_add_flags_flag], [m4_ifblank([$3],[$2],[$3])], + [AC_DEFINE(PA_SYM([$1_]_pa_add_flags_flag), 1, + [Define to 1 if compiled with the ]_pa_add_flags_flag[ compiler flag])]) + $4], + [AS_VAR_SET([flags], ["$old"]) + $5]) + + AS_VAR_POPDEF([flags]) + AS_VAR_POPDEF([ok]) + AS_VAR_POPDEF([old]) +]) diff --git a/autoconf/m4/pa_add_headers.m4 b/autoconf/m4/pa_add_headers.m4 new file mode 100644 index 0000000..d3c478a --- /dev/null +++ b/autoconf/m4/pa_add_headers.m4 @@ -0,0 +1,13 @@ +dnl -------------------------------------------------------------------------- +dnl PA_ADD_HEADERS(headers...) +dnl +dnl Call AC_CHECK_HEADERS(), and add to ac_includes_default if found +dnl -------------------------------------------------------------------------- +AC_DEFUN([_PA_ADD_HEADER], +[AC_CHECK_HEADERS([$1],[ac_includes_default="$ac_includes_default +#include <$1>" +]) +]) + +AC_DEFUN([PA_ADD_HEADERS], +[m4_map_args_w([$1],[_PA_ADD_HEADER(],[)])]) diff --git a/autoconf/m4/pa_add_langflags.m4 b/autoconf/m4/pa_add_langflags.m4 new file mode 100644 index 0000000..05c3114 --- /dev/null +++ b/autoconf/m4/pa_add_langflags.m4 @@ -0,0 +1,27 @@ +dnl -------------------------------------------------------------------------- +dnl PA_ADD_LANGFLAGS(flag...) +dnl +dnl Attempt to add the option in the given list to each compiler flags +dnl (CFLAGS, CXXFLAGS, ...), if it doesn't break compilation. +dnl -------------------------------------------------------------------------- +m4_defun([_PA_LANGFLAG_VAR], +[m4_case([$1], + [C], [CFLAGS], + [C++], [CXXFLAGS], + [Fortran 77], [FFLAGS], + [Fortran], [FCFLAGS], + [Erlang], [ERLCFLAGS], + [Objective C], [OBJCFLAGS], + [Objective C++], [OBJCXXFLAGS], + [Go], [GOFLAGS], + [m4_fatal([PA_ADD_LANGFLAGS: Unknown language: $1])])]) + +AC_DEFUN([PA_ADD_LANGFLAGS], +[m4_pushdef([_pa_langflags],m4_dquote($1))dnl +m4_set_foreach(_PA_LANG_SEEN_SET,[_pa_lang],dnl +[_pa_flag_found=no + m4_foreach_w([_pa_flag], _pa_langflags, + [AS_IF([test $_pa_flag_found = no], + [PA_ADD_FLAGS(_PA_LANGFLAG_VAR(_pa_lang),_pa_flag,[],[_pa_flag_found=yes])]) + ])]) +m4_popdef([_pa_langflags])]) diff --git a/autoconf/m4/pa_arg_bool.m4 b/autoconf/m4/pa_arg_bool.m4 new file mode 100644 index 0000000..5289ed4 --- /dev/null +++ b/autoconf/m4/pa_arg_bool.m4 @@ -0,0 +1,19 @@ +dnl -------------------------------------------------------------------------- +dnl PA_ARG_BOOL(option,helptext,default,enabled_action,disabled_action) +dnl +dnl The last three arguments are optional; default can be yes or no. +dnl +dnl Simpler-to-use versions of AC_ARG_ENABLED, that include the +dnl test for $enableval and the AS_HELP_STRING definition. This is only +dnl to be used for boolean options. +dnl -------------------------------------------------------------------------- +AC_DEFUN([PA_ARG_BOOL], +[m4_pushdef([pa_default],m4_default(m4_normalize([$3]),[no])) + m4_pushdef([pa_option],m4_case(pa_default,[yes],[disable],[enable])) + AC_ARG_ENABLE([$1], + [AS_HELP_STRING([--]m4_defn([pa_option])[-$1],[$2])], + [pa_arg_bool_enableval="$enableval"], + [pa_arg_bool_enableval="]m4_defn([pa_default])["]) + m4_popdef([pa_option], [pa_default]) + AS_IF([test x"$pa_arg_bool_enableval" != xno], [$4], [$5]) +]) diff --git a/autoconf/m4/pa_arg_disabled.m4 b/autoconf/m4/pa_arg_disabled.m4 new file mode 100644 index 0000000..42f4ce8 --- /dev/null +++ b/autoconf/m4/pa_arg_disabled.m4 @@ -0,0 +1,4 @@ +dnl -------------------------------------------------------------------------- +dnl PA_ARG_DISABLED(option,helptext,disabled_action,enabled_action) +dnl -------------------------------------------------------------------------- +AC_DEFUN([PA_ARG_DISABLED],[PA_ARG_BOOL([$1],[$2],yes,[$4],[$3])]) diff --git a/autoconf/m4/pa_arg_enabled.m4 b/autoconf/m4/pa_arg_enabled.m4 new file mode 100644 index 0000000..7d66a21 --- /dev/null +++ b/autoconf/m4/pa_arg_enabled.m4 @@ -0,0 +1,4 @@ +dnl -------------------------------------------------------------------------- +dnl PA_ARG_ENABLED(option,helptext,enabled_action,disabled_action) +dnl -------------------------------------------------------------------------- +AC_DEFUN([PA_ARG_ENABLED],[PA_ARG_BOOL([$1],[$2],no,[$3],[$4])]) diff --git a/autoconf/m4/pa_build_ifelse.m4 b/autoconf/m4/pa_build_ifelse.m4 new file mode 100644 index 0000000..1aacfcd --- /dev/null +++ b/autoconf/m4/pa_build_ifelse.m4 @@ -0,0 +1,16 @@ +dnl -------------------------------------------------------------------------- +dnl PA_BUILD_IFELSE(input [,success [,failure]]) +dnl +dnl Same as AC_LINK_IFELSE for languages where linking is applicable, +dnl otherwise AC_COMPILE_IFELSE. +dnl +dnl If the first argument is empty, use _AC_LANG_IO_PROGRAM. +dnl -------------------------------------------------------------------------- +m4_defun([_PA_BUILD_IFELSE], +[m4_case(_AC_LANG, + [Erlang], [AC_COMPILE_IFELSE($@)], + [AC_LINK_IFELSE($@)])]) + +AC_DEFUN([PA_BUILD_IFELSE], +[_PA_BUILD_IFELSE([m4_ifblank([$1],[AC_LANG_SOURCE(_AC_LANG_IO_PROGRAM)], + [$1])],[$2],[$3])]) diff --git a/autoconf/m4/pa_c_typeof.m4 b/autoconf/m4/pa_c_typeof.m4 new file mode 100644 index 0000000..909b171 --- /dev/null +++ b/autoconf/m4/pa_c_typeof.m4 @@ -0,0 +1,32 @@ +dnl -------------------------------------------------------------------------- +dnl PA_C_TYPEOF +dnl +dnl Find if typeof() exists, or an equivalent (__typeof__, decltype, +dnl __decltype__) +dnl -------------------------------------------------------------------------- +AC_DEFUN([PA_C_TYPEOF], +[AC_CACHE_CHECK([if $CC supports typeof], [pa_cv_typeof], + [pa_cv_typeof=no + for pa_typeof_try in typeof __typeof __typeof__ decltype __decltype __decltype__ _Decltype + do + AS_IF([test $pa_cv_typeof = no], + [AC_COMPILE_IFELSE([AC_LANG_SOURCE([ +AC_INCLUDES_DEFAULT +int testme(int x); +int testme(int x) +{ + $pa_typeof_try(x) y = x*x; + return y; +} +])], + [pa_cv_typeof=$pa_typeof_try])]) + done + ]) + AS_IF([test $pa_cv_typeof = no], + [], + [AC_DEFINE([HAVE_TYPEOF], 1, + [Define to 1 if you have some version of the typeof operator.]) + AS_IF([test $pa_cv_typeof = typeof], + [], + [AC_DEFINE_UNQUOTED([typeof], [$pa_cv_typeof], + [Define if your typeof operator is not named `typeof'.])])])]) diff --git a/autoconf/m4/pa_check_bad_stdc_inline.m4 b/autoconf/m4/pa_check_bad_stdc_inline.m4 new file mode 100644 index 0000000..3fbc53a --- /dev/null +++ b/autoconf/m4/pa_check_bad_stdc_inline.m4 @@ -0,0 +1,26 @@ +dnl -------------------------------------------------------------------------- +dnl PA_CHECK_BAD_STDC_INLINE +dnl +dnl Some versions of gcc seem to apply -Wmissing-prototypes to C99 +dnl inline functions, which means we need to use GNU inline syntax +dnl -------------------------------------------------------------------------- +AC_DEFUN([PA_CHECK_BAD_STDC_INLINE], +[AC_MSG_CHECKING([if $CC supports C99 external inlines]) + AC_COMPILE_IFELSE([AC_LANG_SOURCE([ +AC_INCLUDES_DEFAULT + +/* Don't mistake GNU inlines for c99 */ +#if defined(__GNUC__) && !defined(__GNUC_STDC_INLINE__) +# error "Using gnu inline standard" +#endif + +inline int foo(int x) +{ + return x+1; +} + ])], + [AC_MSG_RESULT([yes]) + AC_DEFINE([HAVE_STDC_INLINE], 1, + [Define to 1 if your compiler supports C99 extern inline])], + [AC_MSG_RESULT([no]) + PA_ADD_CFLAGS([-fgnu89-inline])])]) diff --git a/autoconf/m4/pa_check_inttypes_h_sane.m4 b/autoconf/m4/pa_check_inttypes_h_sane.m4 new file mode 100644 index 0000000..589e0a2 --- /dev/null +++ b/autoconf/m4/pa_check_inttypes_h_sane.m4 @@ -0,0 +1,17 @@ +dnl ------------------------------------------------------------------------ +dnl PA_CHECK_INTTYPES_H_SANE +dnl +dnl At least some versions of AIX 4 have macros which are +dnl completely broken. Try to detect those. +dnl -------------------------------------------------------------------------- +AC_DEFUN([PA_CHECK_INTTYPES_H_SANE], +[AC_CHECK_HEADERS_ONCE(inttypes.h) +AS_IF([test "x$ac_cv_header_inttypes_h" = xyes],[ + AC_MSG_CHECKING([if inttypes.h is sane]) + AC_LINK_IFELSE([AC_LANG_PROGRAM([AC_INCLUDES_DEFAULT], + [uintmax_t max = UINTMAX_C(0); + printf("%"PRIuMAX"\n", max);])], + [AC_MSG_RESULT([yes]) + AC_DEFINE(INTTYPES_H_IS_SANE, 1, + [Define if the macros in are usable])], + [AC_MSG_RESULT([no (AIX, eh?)])])])]) diff --git a/autoconf/m4/pa_cross_compile.m4 b/autoconf/m4/pa_cross_compile.m4 new file mode 100644 index 0000000..f082973 --- /dev/null +++ b/autoconf/m4/pa_cross_compile.m4 @@ -0,0 +1,41 @@ +dnl -------------------------------------------------------------------------- +dnl PA_CROSS_COMPILE +dnl +dnl Get the canonical name for the build and host (runtime) systems; +dnl then figure out if this is cross-compilation. Specifically, this +dnl disables invoking WINE on non-Windows systems which are configured +dnl to run WINE automatically. +dnl +dnl Use PA_CROSS_COMPILE_TOOL if the target system (output of a code- +dnl generation tool) is applicable. +dnl +dnl This doesn't explicitly print any messages as that is automatically +dnl done elsewhere. +dnl -------------------------------------------------------------------------- +AC_DEFUN_ONCE([PA_CROSS_COMPILE], +[ + AC_BEFORE([$0], [AC_LANG_COMPILER]) + AC_BEFORE([$0], [AC_LANG]) + AC_BEFORE([$0], [AC_PROG_CC]) + AC_BEFORE([$0], [AC_PROG_CPP]) + AC_BEFORE([$0], [AC_PROG_CXX]) + AC_BEFORE([$0], [AC_PROG_CXXCPP]) + AC_BEFORE([$0], [AC_PROG_OBJC]) + AC_BEFORE([$0], [AC_PROG_OBJCPP]) + AC_BEFORE([$0], [AC_PROG_OBJCXX]) + AC_BEFORE([$0], [AC_PROG_OBJCXXCPP]) + AC_BEFORE([$0], [AC_PROG_F77]) + AC_BEFORE([$0], [AC_PROG_FC]) + AC_BEFORE([$0], [AC_PROG_GO]) + + # Disable WINE + WINELOADER=/dev/null + export WINELOADER + WINESERVER=/dev/null + export WINESERVER + WINEPREFIX=/dev/null + export WINEPREFIX + + AC_CANONICAL_BUILD + AC_CANONICAL_HOST +]) diff --git a/autoconf/m4/pa_flags_langlist.m4 b/autoconf/m4/pa_flags_langlist.m4 new file mode 100644 index 0000000..2ef5ded --- /dev/null +++ b/autoconf/m4/pa_flags_langlist.m4 @@ -0,0 +1,19 @@ +dnl -------------------------------------------------------------------------- +dnl PA_FLAGS_LANGLIST(flagvar) +dnl +dnl Return a list of languages affected by the variable flagvar. +dnl If flagvar is unknown, assume it affects the current language. +dnl -------------------------------------------------------------------------- +AC_DEFUN([PA_FLAGS_LANGLIST], +[m4_dquote(m4_case([$1], + [CPPFLAGS], [[C],[C++],[Objective C],[Objective C++]], + [CFLAGS], [[C]], + [CXXFLAGS], [[C++]], + [FFLAGS], [[Fortran 77]], + [FCFLAGS], [[Fortran]], + [ERLCFLAGS], [[Erlang]], + [OBJCFLAGS], [[Objective C]], + [OBJCXXFLAGS], [[Objective C++]], + [GOFLAGS], [[Go]], + [LDFLAGS], [[C],[C++],[Fortran 77],[Fortran],[Objective C],[Objective C++],[Go]], + m4_dquote(_AC_LANG)))]) diff --git a/autoconf/m4/pa_have_tcpwrappers.m4 b/autoconf/m4/pa_have_tcpwrappers.m4 new file mode 100644 index 0000000..fba87ce --- /dev/null +++ b/autoconf/m4/pa_have_tcpwrappers.m4 @@ -0,0 +1,26 @@ +dnl -------------------------------------------------------------------------- +dnl PA_HAVE_TCPWRAPPERS +dnl +dnl Do we have the tcpwrappers -lwrap? This can't be done using AC_CHECK_LIBS +dnl due to the need to provide "allow_severity" and "deny_severity" variables +dnl -------------------------------------------------------------------------- +AC_DEFUN([PA_HAVE_TCPWRAPPERS], +[AC_CHECK_LIB([wrap], [main]) + AC_MSG_CHECKING([for tcpwrappers]) + AC_LINK_IFELSE([AC_LANG_PROGRAM( + [[ +#include +int allow_severity = 0; +int deny_severity = 0; + ]], + [[ + hosts_ctl("sample_daemon", STRING_UNKNOWN, STRING_UNKNOWN, STRING_UNKNOWN); + ]])], + [ + AC_DEFINE(HAVE_TCPWRAPPERS, 1, + [Define if we have tcpwrappers (-lwrap) and .]) + AC_MSG_RESULT([yes]) + ], + [ + AC_MSG_RESULT([no]) + ])]) diff --git a/autoconf/m4/pa_lang_foreach.m4 b/autoconf/m4/pa_lang_foreach.m4 new file mode 100644 index 0000000..650913d --- /dev/null +++ b/autoconf/m4/pa_lang_foreach.m4 @@ -0,0 +1,15 @@ +dnl -------------------------------------------------------------------------- +dnl PA_LANG_FOREACH(subset, body) +dnl +dnl Expand [body] for each language encountered in the configure script also +dnl present in [subset], or all if [subset] is empty +dnl -------------------------------------------------------------------------- +AC_DEFUN([_PA_LANG_DO],dnl +[AC_LANG([$2])dnl +$1]) + +AC_DEFUN([PA_LANG_FOREACH],dnl +[m4_pushdef([_pa_lang_foreach_current],[_AC_LANG])dnl +m4_map_args([m4_curry([_PA_LANG_DO],[$2])],m4_unquote(PA_LANG_SEEN_LIST($1)))dnl +AC_LANG(_pa_lang_foreach_current)dnl +m4_popdef([_pa_lang_foreach_current])]) diff --git a/autoconf/m4/pa_lang_seen_list.m4 b/autoconf/m4/pa_lang_seen_list.m4 new file mode 100644 index 0000000..d524013 --- /dev/null +++ b/autoconf/m4/pa_lang_seen_list.m4 @@ -0,0 +1,20 @@ +dnl -------------------------------------------------------------------------- +dnl PA_LANG_SEEN_LIST(subset) +dnl +dnl List of the language lang has been used in the configuration +dnl script so far, possibly subset by [subset]. +dnl +dnl This relies on overriding _AC_LANG_SET(from, to), +dnl the internal implementation of _AC_LANG. +dnl -------------------------------------------------------------------------- +m4_ifndef([_PA_LANG_SET], +[m4_rename([_AC_LANG_SET], [_PA_LANG_SET])dnl +m4_defun([_AC_LANG_SET], [m4_set_add([_PA_LANG_SEEN_SET],[$2])dnl +_PA_LANG_SET($@)])]) + +AC_DEFUN([PA_LANG_SEEN_LIST], +[m4_set_delete([_pa_lang_seen_subset])dnl +m4_pushdef([_pa_lang_seen_subset_list],m4_ifnblank([$1],[$1],m4_dquote(m4_set_list([_PA_LANG_SEEN_SET]))))dnl +m4_set_add_all([_pa_lang_seen_subset],_pa_lang_seen_subset_list)dnl +m4_cdr(m4_set_intersection([_pa_lang_seen_subset],[_PA_LANG_SEEN_SET]))dnl +m4_popdef([_pa_lang_seen_subset_list])]) diff --git a/autoconf/m4/pa_option_debug.m4 b/autoconf/m4/pa_option_debug.m4 new file mode 100644 index 0000000..ae7d9db --- /dev/null +++ b/autoconf/m4/pa_option_debug.m4 @@ -0,0 +1,13 @@ +dnl -------------------------------------------------------------------------- +dnl PA_OPTION_DEBUG(with_debug, without_debug) +dnl +dnl Set debug flags and optimization flags depending on if +dnl --enable-debug is set or not. Some flags are set regardless... +dnl -------------------------------------------------------------------------- +AC_DEFUN([PA_OPTION_DEBUG], +[PA_ARG_DISABLED([gdb], [disable gdb debug extensions], + [PA_ADD_LANGFLAGS([-g3])], [PA_ADD_LANGFLAGS([-ggdb3 -g3])]) + PA_ARG_ENABLED([debug], [optimize for debugging], + [PA_ADD_LANGFLAGS([-Og -O0]) + $1], + [$2])]) diff --git a/autoconf/m4/pa_option_profiling.m4 b/autoconf/m4/pa_option_profiling.m4 new file mode 100644 index 0000000..39a3f6c --- /dev/null +++ b/autoconf/m4/pa_option_profiling.m4 @@ -0,0 +1,8 @@ +dnl -------------------------------------------------------------------------- +dnl PA_OPTION_PROFILING(with_profiling, without_profiling) +dnl +dnl Try to enable profiling if --enable-profiling is set. +dnl -------------------------------------------------------------------------- +AC_DEFUN([PA_OPTION_PROFILING], +[PA_ARG_ENABLED([profiling], [compile with profiling (-pg option)], +[PA_ADD_LANGFLAGS([-pg])])]) diff --git a/autoconf/m4/pa_prog_cc.m4 b/autoconf/m4/pa_prog_cc.m4 new file mode 100644 index 0000000..0debe5d --- /dev/null +++ b/autoconf/m4/pa_prog_cc.m4 @@ -0,0 +1,13 @@ +dnl -------------------------------------------------------------------------- +dnl PA_PROG_CC() +dnl +dnl Similar to AC_PROG_CC, but add a prototype for main() to +dnl AC_INCLUDES_DEFAULT to avoid -Werror from breaking compilation. +dnl -------------------------------------------------------------------------- +AC_DEFUN([PA_PROG_CC], +[AC_PROG_CC + AS_IF([test x$ac_cv_prog != xno], + [ac_includes_default="$ac_includes_default +#ifndef __cplusplus +extern int main(void); +#endif"])]) diff --git a/autoconf/m4/pa_search_libs_and_add.m4 b/autoconf/m4/pa_search_libs_and_add.m4 new file mode 100644 index 0000000..b006128 --- /dev/null +++ b/autoconf/m4/pa_search_libs_and_add.m4 @@ -0,0 +1,22 @@ +dnl -------------------------------------------------------------------------- +dnl PA_SEARCH_LIBS_AND_ADD +dnl +dnl PA_SEARCH_LIBS_AND_ADD(function, libraries [,function to add]) +dnl -------------------------------------------------------------------------- +AC_DEFUN([PA_SEARCH_LIBS_AND_ADD], + [ + AH_TEMPLATE(AS_TR_CPP(HAVE_$1), [Define if $1 function was found]) + AC_SEARCH_LIBS($1, $2, + [ + AC_DEFINE_UNQUOTED(AS_TR_CPP(HAVE_$1)) + pa_add_$1=false; + ], + [ + XTRA=true; + if test $# -eq 3; then + AC_LIBOBJ($3) + else + AC_LIBOBJ($1) + fi + pa_add_$1=true; + ])]) diff --git a/autoconf/m4/pa_sigsetjmp.m4 b/autoconf/m4/pa_sigsetjmp.m4 new file mode 100644 index 0000000..898b562 --- /dev/null +++ b/autoconf/m4/pa_sigsetjmp.m4 @@ -0,0 +1,25 @@ +dnl -------------------------------------------------------------------------- +dnl PA_SIGSETJMP +dnl +dnl Do we have sigsetjmp/siglongjmp? (AC_CHECK_FUNCS doesn't seem to work +dnl for these particular functions.) +dnl -------------------------------------------------------------------------- +AC_DEFUN([PA_SIGSETJMP], +[AC_MSG_CHECKING([for sigsetjmp]) + AC_LINK_IFELSE([AC_LANG_SOURCE( +[ +AC_INCLUDES_DEFAULT +#include + +int main(void) { + sigjmp_buf buf; + if (sigsetjmp(buf,1)) + return 0; + siglongjmp(buf,2); + return 1; + } +])], + [AC_MSG_RESULT([yes]) + AC_DEFINE([HAVE_SIGSETJMP], 1, + [Define to 1 if your system has sigsetjmp/siglongjmp])], + [AC_MSG_RESULT([no])])]) diff --git a/autoconf/m4/pa_sym.m4 b/autoconf/m4/pa_sym.m4 new file mode 100644 index 0000000..d3a8965 --- /dev/null +++ b/autoconf/m4/pa_sym.m4 @@ -0,0 +1,11 @@ +dnl -------------------------------------------------------------------------- +dnl PA_SYM(prefix, string) +dnl +dnl Convert a (semi-) arbitrary string to a CPP symbol +dnl Compact underscores and convert non-C characters to underscore, +dnl except + which is converted to X (so C++ -> CXX). +dnl -------------------------------------------------------------------------- +AC_DEFUN([PA_SYM], +[m4_bpatsubsts(m4_quote(m4_toupper([$*])), + [,],[],[\+],[X],[[^ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789]+],[_],dnl +[^._?\(.*\)_.$],[[\1]])]) diff --git a/autogen.sh b/autogen.sh index 728a381..0c892cc 100755 --- a/autogen.sh +++ b/autogen.sh @@ -1,2 +1,83 @@ -#!/bin/sh -make autoconf +#!/bin/sh -x +# +# Run this script to regenerate autoconf files +# +recheck=false +for arg; do + case x"$arg" in + x--recheck) + recheck=true + config=$(sh config.status --config 2>/dev/null) + ;; + x--clearenv) + unset AUTOCONF AUTOMAKE ACLOCAL AUTOHEADER ACLOCAL_PATH + ;; + *) + echo "$0: unknown option: $arg" 1>&2 + ;; + esac +done + +# This allows for overriding the default autoconf programs +AUTOCONF="${AUTOCONF:-${AUTOTOOLS_PREFIX}autoconf}" +AUTOMAKE="${AUTOMAKE:-${AUTOTOOLS_PREFIX}automake}" +ACLOCAL="${ACLOCAL:-${AUTOTOOLS_PREFIX}aclocal}" +AUTOHEADER="${AUTOHEADER:-${AUTOTOOLS_PREFIX}autoheader}" + +mkdir -p autoconf autoconf/helpers config +autolib="`"$AUTOMAKE" --print-libdir`" +if test ! x"$autolib" = x; then + for prg in install-sh compile config.guess config.sub; do + # Update autoconf helpers if and only if newer ones are available + if test -f "$autolib"/"$prg" && \ + ( set -e ; \ + test -f autoconf/helpers/"$prg" && sed -n \ + -e 's/^scriptver=/scriptversion=/' \ + -e 's/^timestamp=/scriptversion=/' \ + -e 's/^scriptversion=['\''"]?\([^'\''"]*\).*$/\1/p' \ + "$autolib"/"$prg" autoconf/helpers/"$prg" | \ + sort -c 2>/dev/null ; \ + test $? -ne 0 ) + then + cp -f "$autolib"/"$prg" autoconf/helpers + fi + done +fi +mv -f autoconf/aclocal.m4 autoconf/aclocal.m4.old +mkdir -p autoconf/m4.old autoconf/m4 +mv -f autoconf/m4/*.m4 autoconf/m4.old/ 2>/dev/null || true +ACLOCAL_PATH="${ACLOCAL_PATH}${ACLOCAL_PATH:+:}`pwd`/autoconf/m4.old" +export ACLOCAL_PATH +"$ACLOCAL" --install --output=autoconf/aclocal.m4 -I autoconf/m4 +if test ! -f autoconf/aclocal.m4; then + # aclocal failed, revert to previous files + mv -f autoconf/m4.old/*.m4 autoconf/m4/ + mv -f autoconf/aclocal.m4.old autoconf/aclocal.m4 + exit 1 +fi +rm -rf autoconf/*m4.old +"$AUTOHEADER" -B autoconf +"$AUTOCONF" -B autoconf +( + echo '#!/bin/sh' + "$AUTOCONF" -B autoconf \ + -t AC_CONFIG_HEADERS:'rm -f $*' \ + -t AC_CONFIG_FILES:'rm -f $*' + echo 'rm -f config.log config.status' + echo 'rm -rf autom4te.cache' +) > autoconf/clean.sh +chmod +x autoconf/clean.sh +sh autoconf/clean.sh + +rm -f configure~ || true + +# Try to regenerate unconfig.h if Perl is available and unconfig.pl +# is present in the autoconf directory. +if [ -n "$(which perl)" -a -f autoconf/unconfig.pl ]; then + perl autoconf/unconfig.pl . config/config.h.in config/unconfig.h +fi + +if $recheck; then + # This bizarre statement has to do with how config.status quotes its output + echo exec sh configure $config | sh - +fi diff --git a/common/Makefile b/common/Makefile index 2aaffb7..89e351d 100644 --- a/common/Makefile +++ b/common/Makefile @@ -1,7 +1,7 @@ SRCROOT = .. VERSION = $(shell cat ../version) --include ../MCONFIG +-include ../config/MCONFIG include ../MRULES OBJS = tftpsubs.$(O) signal.$(O) diff --git a/config.h b/config.h index 1983058..a8c55ac 100644 --- a/config.h +++ b/config.h @@ -25,7 +25,7 @@ #endif /* Must be included before we include any system headers! */ -#include "aconfig.h" /* autogenerated configuration header */ +#include "config/config.h" /* autogenerated configuration header */ /* Standard includes */ @@ -115,10 +115,10 @@ /* Some broken systems care about text versus binary, but real Unix systems don't... */ -#ifndef HAVE_O_TEXT_DEFINITION +#if !HAVE_DECL_O_TEXT #define O_TEXT 0 #endif -#ifndef HAVE_O_BINARY_DEFINITION +#if !HAVE_DECL_O_BINARY #define O_BINARY 0 #endif @@ -253,11 +253,9 @@ typedef int socklen_t; #include -#ifndef HAVE_IPPORT_TFTP_DEFINITION -#ifndef IPPORT_TFTP +#if !HAVE_DECL_IPPORT_TFTP && !defined(IPPORT_TFTP) #define IPPORT_TFTP 69 #endif -#endif /* arpa/{inet,tftp}.h, and possible missing pieces */ diff --git a/MCONFIG.in b/config/MCONFIG.in similarity index 100% rename from MCONFIG.in rename to config/MCONFIG.in diff --git a/configure.ac b/configure.ac index 2d4b93d..be2a500 100644 --- a/configure.ac +++ b/configure.ac @@ -1,61 +1,89 @@ -dnl -dnl autoconf input file to generate MCONFIG -dnl - +dnl Process this file with autoconf 2.71 or later to produce +dnl a configure script. AC_PREREQ([2.71]) AC_INIT -AC_CONFIG_SRCDIR([MCONFIG.in]) -AC_PREFIX_DEFAULT(/usr) +AC_CONFIG_SRCDIR([MRULES]) +AC_PREFIX_DEFAULT([/usr]) +AC_CONFIG_AUX_DIR([autoconf/helpers]) +dnl This prevents us from running Wine and thinking we are not +dnl cross-compiling when in fact we are; running Wine here is at +dnl the best very slow and doesn't buy us a single thing at all. +PA_CROSS_COMPILE + +dnl Enable any available C extensions +PA_PROG_CC AC_USE_SYSTEM_EXTENSIONS -AC_SEARCH_LIBS([strerror],[cposix]) -AC_PROG_CC +dnl Options for debugging and profiling +PA_OPTION_DEBUG +PA_OPTION_PROFILING + +dnl LLVM doesn't error out on invalid -W options unless this option is +dnl specified first. Enable this so this script can actually discover +dnl which -W options are possible for this compiler. +PA_ADD_CFLAGS([-Werror=unknown-warning-option]) + +dnl Force gcc and gcc-compatible compilers treat signed integers +dnl as 2's complement +PA_ADD_CFLAGS([-fwrapv]) + +dnl Force clang to behave in a predictable manner, in order to make bugs +dnl possible to track down. gcc appears to have this behavior by default. +PA_ADD_CFLAGS([-ftrivial-auto-var-init=zero]) + +dnl Some environments abuse __STRICT_ANSI__ to disable some +dnl function declarations +PA_ADD_CFLAGS([-U__STRICT_ANSI__]) + +dnl Don't put things in common if we can avoid it. We don't want to +dnl assume all compilers support common, and this will help find those +dnl problems. This also works around an OSX linker problem. +PA_ADD_CFLAGS([-fno-common]) + +dnl Tests which may trigger warnings on some compilers AC_C_CONST AC_C_INLINE +AC_C_RESTRICT -PA_ADD_CFLAGS(-W) -PA_ADD_CFLAGS(-Wall) -PA_ADD_CFLAGS(-Wpointer-arith) -PA_ADD_CFLAGS(-Wbad-function-cast) -PA_ADD_CFLAGS(-Wcast-equal) -PA_ADD_CFLAGS(-Wstrict-prototypes) -PA_ADD_CFLAGS(-Wmissing-prototypes) -PA_ADD_CFLAGS(-Wmissing-declarations) -PA_ADD_CFLAGS(-Wnested-externs) -PA_ADD_CFLAGS(-Winline) -PA_ADD_CFLAGS(-Wwrite-strings) -PA_ADD_CFLAGS(-Wundef) -PA_ADD_CFLAGS(-Wshadow) -PA_ADD_CFLAGS(-Wsign-compare) -PA_ADD_CFLAGS(-pipe) -PA_ADD_CFLAGS(-fno-strict-aliasing) +dnl Checks for header files. +AC_CHECK_INCLUDES_DEFAULT -AC_CHECK_HEADERS(inttypes.h) -AC_CHECK_HEADERS(stdint.h) +dnl See if we need extra libraries +XTRA=false + +AC_SEARCH_LIBS([strerror],[cposix]) + +AC_CHECK_HEADERS_ONCE(inttypes.h) +AC_CHECK_HEADERS_ONCE(stdint.h) +AC_CHECK_HEADERS_ONCE(grp.h) +AC_CHECK_HEADERS_ONCE(libgen.h) +AC_CHECK_HEADERS_ONCE(setjmp.h) +AC_CHECK_HEADERS_ONCE(strings.h) +AC_CHECK_HEADERS_ONCE(sysexits.h) +AC_CHECK_HEADERS_ONCE(unistd.h) +AC_CHECK_HEADERS_ONCE(sys/filio.h) +AC_CHECK_HEADERS_ONCE(sys/stat.h) +AC_CHECK_HEADERS_ONCE(sys/time.h) PA_CHECK_INTTYPES_H_SANE -AC_CHECK_HEADERS(fcntl.h) -AC_CHECK_HEADERS(grp.h) -AC_CHECK_HEADERS(libgen.h) -AC_CHECK_HEADERS(setjmp.h) -AC_CHECK_HEADERS(strings.h) -AC_CHECK_HEADERS(sysexits.h) -AC_CHECK_HEADERS(unistd.h) -AC_CHECK_HEADERS(sys/file.h) -AC_CHECK_HEADERS(sys/filio.h) -AC_CHECK_HEADERS(sys/stat.h) -AC_CHECK_HEADERS(sys/time.h) -AC_CHECK_HEADERS(sys/types.h) -AC_CHECK_HEADERS(arpa/inet.h) -AC_CHECK_HEADERS(netdb.h) dnl This is needed on some versions of FreeBSD... -AC_CHECK_HEADERS(machine/param.h) -AC_CHECK_HEADERS(sys/socket.h) -AC_CHECK_HEADERS(winsock2.h) -AC_CHECK_HEADERS(winsock.h) +AC_CHECK_HEADERS_ONCE(machine/param.h) -AC_SYS_LARGEFILE +dnl Windows... +PA_ADD_HEADERS(windows.h) +PA_ADD_HEADERS(winsock2.h) +AS_IF([test "x$ac_cv_header_winsock2_h" != xyes], + [PA_ADD_HEADERS(winsock.h)]) + +PA_ADD_HEADERS(fcntl.h) +PA_ADD_HEADERS(sys/types.h) +PA_ADD_HEADERS(arpa/inet.h) +PA_ADD_HEADERS(sys/socket.h) +PA_ADD_HEADERS(sys/file.h) +PA_ADD_HEADERS(netinet/in.h) +PA_ADD_HEADERS(sys/uio.h) +PA_ADD_HEADERS(netdb.h) AC_TYPE_OFF_T AC_TYPE_PID_T @@ -68,23 +96,13 @@ AC_CHECK_TYPES(uint32_t) AC_CHECK_TYPES(u_short) AC_CHECK_TYPES(u_long) -dnl -dnl isn't among the list of standard headers that autoconf checks, -dnl but POSIX requires for socklen_t to be defined. -dnl -AC_CHECK_TYPES(socklen_t,,, -[AC_INCLUDES_DEFAULT -#ifdef HAVE_SYS_TYPES_H -# include -#endif -#ifdef HAVE_SYS_SOCKET_H -# include -#endif -]) +AC_CHECK_TYPES(socklen_t) -AC_SEARCH_LIBS(socket, [socket ws2_32 wsock32], , [AC_MSG_ERROR(socket library not found)]) +AC_SEARCH_LIBS(socket, [socket ws2_32 wsock32], , + [AC_MSG_ERROR(socket library not found)]) AC_CHECK_FUNCS(fcntl) +AC_CHECK_FUNCS(flock) AC_CHECK_FUNCS(setsid) AC_CHECK_FUNCS(recvmsg) AC_CHECK_FUNCS(ftruncate) @@ -100,22 +118,15 @@ dnl Solaris 8 has [u]intmax_t but not strtoumax(). How utterly braindamaged. AC_CHECK_FUNCS(strtoumax) AC_CHECK_FUNCS(strtoull) -PA_MSGHDR_MSG_CONTROL -PA_STRUCT_IN_PKTINFO -PA_STRUCT_ADDRINFO +AC_CHECK_MEMBERS(struct msghdr.msg_control) +AC_CHECK_MEMBERS(struct in_pktinfo.ipi_addr) +AC_CHECK_MEMBERS(struct addrinfo.ai_addr) -PA_HEADER_DEFINES(fcntl.h, int, O_NONBLOCK) -PA_HEADER_DEFINES(fcntl.h, int, O_BINARY) -PA_HEADER_DEFINES(fcntl.h, int, O_TEXT) +AC_CHECK_DECLS([O_NONBLOCK, O_BINARY, O_TEXT]) +AC_CHECK_DECLS([F_SETLK]) +AC_CHECK_DECLS([LOCK_SH, LOCK_EX]) -PA_HEADER_DEFINES(fcntl.h, int, F_SETLK) - -PA_HEADER_DEFINES(sys/file.h, int, LOCK_SH) -PA_HEADER_DEFINES(sys/file.h, int, LOCK_EX) - -AH_TEMPLATE([HAVE_SIGSETJMP], -[Define if we have sigsetjmp, siglongjmp and sigjmp_buf.]) -PA_SIGSETJMP([AC_DEFINE(HAVE_SIGSETJMP)]) +PA_SIGSETJMP dnl dnl Get common paths @@ -123,38 +134,31 @@ dnl SRCROOT=`cd $srcdir && pwd` OBJROOT=`pwd` -XTRA=false PA_SEARCH_LIBS_AND_ADD(xmalloc, iberty) PA_SEARCH_LIBS_AND_ADD(xstrdup, iberty) PA_SEARCH_LIBS_AND_ADD(getopt_long, getopt, getopt_long) PA_SEARCH_LIBS_AND_ADD(getaddrinfo, [nsl resolv]) -if $pa_add_getaddrinfo -then - AC_SEARCH_LIBS(gethostbyname, [nsl resolv], +AS_IF([$pa_add_getaddrinfo], +[AC_SEARCH_LIBS(gethostbyname, [nsl resolv], [AC_SEARCH_LIBS(herror, [nsl resolv], , [AC_MSG_ERROR(herror not found)])], - [AC_MSG_ERROR(gethostbyname not found)]) -else - AC_SEARCH_LIBS(freeaddrinfo, [nsl resolv], , + [AC_MSG_ERROR(gethostbyname not found)])], +[AC_SEARCH_LIBS(freeaddrinfo, [nsl resolv], , [AC_MSG_ERROR(getaddrinfo but not freeaddrinfo found)]) AC_SEARCH_LIBS(gai_strerror, [nsl resolv], , - [AC_MSG_ERROR(getaddrinfo but not gai_strerror found)]) -fi + [AC_MSG_ERROR(getaddrinfo but not gai_strerror found)])]) PA_SEARCH_LIBS_AND_ADD(inet_ntop, [nsl resolv]) -if $pa_add_inet_ntop -then - AC_SEARCH_LIBS(inet_ntoa, [nsl resolv], , - [AC_MSG_ERROR(inet_ntoa not found)]) -fi +AS_IF([$pa_add_inet_ntop], + [AC_SEARCH_LIBS(inet_ntoa, [nsl resolv], , + [AC_MSG_ERROR(inet_ntoa not found)])]) + AC_SEARCH_LIBS(inet_aton, [nsl resolv], ,[AC_MSG_ERROR(inet_aton not found)]) -AC_CHECK_FUNCS(daemon, , [XTRA=true; AC_LIBOBJ(daemon)]) -AC_CHECK_FUNCS(dup2, , [XTRA=true; AC_LIBOBJ(dup2)]) -if $XTRA -then - XTRALIBS="$OBJROOT/lib/libxtra.a $XTRALIBS" -fi +PA_SEARCH_LIBS_AND_ADD(daemon) +PA_SEARCH_LIBS_AND_ADD(dup2) + +AS_IF([$XTRA], [XTRALIBS="$OBJROOT/lib/libxtra.a $XTRALIBS"]) dnl dnl These libraries apply to the server only @@ -162,31 +166,14 @@ dnl common_libs="$LIBS" -PA_HEADER_DEFINES(netinet/in.h, int, IPPORT_TFTP) +AC_CHECK_DECLS(IPPORT_TFTP) -PA_WITH_BOOL(tcpwrappers, 1, -[ --without-tcpwrappers disable tcpwrapper permissions checking], -[ +PA_ARG_DISABLED([tcpwrappers], + [disable tcpwrapper permissions checking], [], + [ AC_SEARCH_LIBS(yp_get_default_domain, [nsl resolv]) PA_HAVE_TCPWRAPPERS -],:) - - -AH_TEMPLATE([WITH_REGEX], -[Define if we are compiling with regex filename remapping.]) - -PA_WITH_BOOL(remap, 1, -[ --without-remap disable regex-based filename remapping], -[ - AC_CHECK_HEADER(regex.h, - [ - AC_SEARCH_LIBS(regcomp, [regex rx], - [ - AC_DEFINE(WITH_REGEX) - TFTPDOBJS="remap.${OBJEXT} $TFTPDOBJS" - ]) - ]) -],:) + ]) TFTPD_LIBS="$LIBS $XTRALIBS" LIBS="$common_libs" @@ -198,10 +185,10 @@ dnl AH_TEMPLATE([WITH_READLINE], [Define if we are compiling with readline/editline command-line editing.]) -PA_WITH_BOOL(readline, 1, -[ --without-readline disable the use of readline command-line editing], -[ - AC_CHECK_HEADER(readline/readline.h, +PA_ARG_DISABLED([readline], + [disable the use of readline command-line editing], [], + [ + AC_CHECK_HEADER(readline/readline.h, [ dnl readline may need libtermcap or somesuch... AC_SEARCH_LIBS(tputs, [termcap terminfo]) @@ -226,23 +213,17 @@ LIBS="$common_libs" dnl dnl Check for IPV6 and disable-ipv6 dnl -PA_STRUCT_SOCKADDR_IN6 -AC_MSG_CHECKING([for IPv6 support]) -PA_WITH_BOOL(ipv6, 1, -[ --without-ipv6 disable the support for IPv6], -[ - if $HAVE_INET6 - then - AC_MSG_RESULT(yes) - AC_DEFINE(HAVE_IPV6, 1, [Define if IPv6 support is enabled.]) - PA_STRUCT_IN6_PKTINFO - else - AC_MSG_RESULT(no) - AC_MSG_WARN([*** we do not have required IPv6 structs - IPv6 will be disabled]) - fi -], -[AC_MSG_RESULT(disabled)]) +AC_CHECK_MEMBERS(struct sockaddr_in6.sin6_addr) +AC_MSG_CHECKING([for IPv6 support]) +PA_ARG_DISABLED([ipv6], + [disable support for IPv6], + [AC_MSG_RESULT(disabled)], + [AS_IF([test x"$ac_cv_member_struct_sockaddr_in6_sin6_addr$ac_cv_member_struct_addrinfo_ai_addr" = xyesyes], + [AC_MSG_RESULT(yes) + AC_DEFINE(HAVE_IPV6, 1, [define if IPv6 support is enabled])], + [AC_MSG_RESULT(no) + AC_MSG_WARN([*** we do not have required IPv6 structs - IPv6 will be disabled])])]) AC_SUBST(SRCROOT) AC_SUBST(OBJROOT) @@ -265,6 +246,29 @@ AC_PROG_INSTALL INSTALL='\${SRCROOT}'/"$INSTALL" fi] -AC_CONFIG_HEADERS(aconfig.h) -AC_CONFIG_FILES([MCONFIG]) +PA_ADD_CFLAGS(-W) +PA_ADD_CFLAGS(-Wall) +PA_ADD_CFLAGS(-Wpointer-arith) +PA_ADD_CFLAGS(-Wbad-function-cast) +PA_ADD_CFLAGS(-Wcast-equal) +PA_ADD_CFLAGS(-Wstrict-prototypes) +PA_ADD_CFLAGS(-Wmissing-prototypes) +PA_ADD_CFLAGS(-Wmissing-declarations) +PA_ADD_CFLAGS(-Wnested-externs) +PA_ADD_CFLAGS(-Winline) +PA_ADD_CFLAGS(-Wwrite-strings) +PA_ADD_CFLAGS(-Wundef) +PA_ADD_CFLAGS(-Wshadow) +PA_ADD_CFLAGS(-Wsign-compare) +PA_ADD_CFLAGS(-fno-strict-aliasing) + +dnl +dnl Test compiler features. On some compilers, this can be affected +dnl by -Werror options, so run this *after* those options are added. +dnl +PA_CHECK_BAD_STDC_INLINE +PA_C_TYPEOF + +AC_CONFIG_HEADERS([config/config.h]) +AC_CONFIG_FILES([config/MCONFIG]) AC_OUTPUT diff --git a/lib/Makefile b/lib/Makefile index 5dd83d0..a7fd057 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -4,7 +4,7 @@ SRCROOT = .. --include ../MCONFIG +-include ../config/MCONFIG include ../MRULES ifeq ($(LIBOBJS),) diff --git a/tftp/Makefile b/tftp/Makefile index 20f4c18..9faa62f 100644 --- a/tftp/Makefile +++ b/tftp/Makefile @@ -1,7 +1,7 @@ SRCROOT = .. VERSION = $(shell cat ../version) --include ../MCONFIG +-include ../config/MCONFIG include ../MRULES OBJS = tftp.$(O) main.$(O) diff --git a/tftpd/Makefile b/tftpd/Makefile index a05335d..5963581 100644 --- a/tftpd/Makefile +++ b/tftpd/Makefile @@ -1,7 +1,7 @@ SRCROOT = .. VERSION = $(shell cat ../version) --include ../MCONFIG +-include ../config/MCONFIG include ../MRULES OBJS = tftpd.$(O) recvfrom.$(O) misc.$(O) $(TFTPDOBJS) diff --git a/tftpd/recvfrom.c b/tftpd/recvfrom.c index d7ef500..50b788b 100644 --- a/tftpd/recvfrom.c +++ b/tftpd/recvfrom.c @@ -289,7 +289,7 @@ myrecvfrom(int s, void *buf, int len, unsigned int flags, myaddr->sa.sa_family = from->sa.sa_family; sa_set_port(myaddr, htons(IPPORT_TFTP)); - return recvfrom(s, buf, len, flags, from, &fromlen); + return recvfrom(s, buf, len, flags, &from->sa, &fromlen); } #endif diff --git a/tftpd/tftpd.c b/tftpd/tftpd.c index fcb29aa..bec0f34 100644 --- a/tftpd/tftpd.c +++ b/tftpd/tftpd.c @@ -185,7 +185,8 @@ static struct rule *read_remap_rules(const char *rulefile) */ static int lock_file(int fd, int lock_write) { -#if defined(HAVE_FCNTL) && defined(HAVE_F_SETLK_DEFINITION) + (void)lock_write; +#if defined(HAVE_FCNTL) && HAVE_DECL_F_SETLK struct flock fl; fl.l_type = lock_write ? F_WRLCK : F_RDLCK; @@ -193,7 +194,7 @@ static int lock_file(int fd, int lock_write) fl.l_start = 0; fl.l_len = 0; /* Whole file */ return fcntl(fd, F_SETLK, &fl); -#elif defined(HAVE_LOCK_SH_DEFINITION) +#elif defined(HAVE_FLOCK) && HAVE_DECL_LOCK_SH && HAVE_DECL_LOCK_EX return flock(fd, lock_write ? LOCK_EX|LOCK_NB : LOCK_SH|LOCK_NB); #else return 0; /* Hope & pray... */ @@ -325,7 +326,7 @@ static int split_port(char **ap, char **pp) enum long_only_options { OPT_VERBOSITY = 256, }; - + static struct option long_options[] = { { "ipv4", 0, NULL, '4' }, { "ipv6", 0, NULL, '6' }, @@ -1246,7 +1247,7 @@ static int set_blksize2(uintmax_t *vp) static int set_rollover(uintmax_t *vp) { uintmax_t ro = *vp; - + if (ro > 65535) return 0; @@ -1345,7 +1346,7 @@ static void do_opt(const char *opt, const char *val, char **ap) nak(EOPTNEG, "Insufficient space for options"); exit(0); } - + memcpy(p, opt, optlen+1); p += optlen+1; memcpy(p, retbuf, retlen+1); From cb619257ed1875fcc95c31c81e43a21c0e00fbf1 Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Mon, 10 Jun 2024 18:38:43 -0700 Subject: [PATCH 177/184] recvfrom: update config.h define for in_pktinfo.ipi_addr configure.ac now explicitly checks for struct in_pktinfo.ipi_addr; update the configure name to match. Signed-off-by: H. Peter Anvin --- tftpd/recvfrom.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tftpd/recvfrom.c b/tftpd/recvfrom.c index 50b788b..320678d 100644 --- a/tftpd/recvfrom.c +++ b/tftpd/recvfrom.c @@ -26,10 +26,12 @@ #if defined(HAVE_RECVMSG) && defined(HAVE_MSGHDR_MSG_CONTROL) -#include +#ifdef HAVE_SYS_UIO_H +# include +#endif #ifdef IP_PKTINFO -# ifndef HAVE_STRUCT_IN_PKTINFO +# ifndef HAVE_STRUCT_IN_PKTINFO_IPI_ADDR # ifdef __linux__ /* Assume this version of glibc simply lacks the definition */ struct in_pktinfo { From 74c5d8a02030a0e598f6dd7db0c122d1b38bd904 Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Mon, 10 Jun 2024 19:31:17 -0700 Subject: [PATCH 178/184] .editorconfig: tabs are 8 The size of a tab is 8, even if the indentation is 4. Signed-off-by: H. Peter Anvin --- .editorconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.editorconfig b/.editorconfig index 0143c3c..544d21f 100644 --- a/.editorconfig +++ b/.editorconfig @@ -8,4 +8,4 @@ insert_final_newline = true [{*.c,*.h}] indent_style = space indent_size = 4 -tab_width = 4 +tab_width = 8 From ac7f98e4d8036d6194b41920e46e37be40b496a4 Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Mon, 10 Jun 2024 20:19:55 -0700 Subject: [PATCH 179/184] remap: fix timeouts for "g", add a "gg" flag to match sed s///g Make sure that when using the global option, we still bump the deadman timer. The "g" option really should only have applied to the right-hand unmatched part of the string, like in sed. Add a "gg" option which does that. Signed-off-by: H. Peter Anvin --- tftpd/remap.c | 287 ++++++++++++++++++++++++++++------------------- tftpd/tftpd.8.in | 21 +++- 2 files changed, 189 insertions(+), 119 deletions(-) diff --git a/tftpd/remap.c b/tftpd/remap.c index 16801c1..d74b2db 100644 --- a/tftpd/remap.c +++ b/tftpd/remap.c @@ -22,7 +22,7 @@ #include "tftpd.h" #include "remap.h" -#define DEADMAN_MAX_STEPS 1024 /* Timeout after this many steps */ +#define DEADMAN_MAX_STEPS 4096 /* Timeout after this many steps */ #define MAXLINE 16384 /* Truncate a line at this many bytes */ #define RULE_REWRITE 0x01 /* This is a rewrite rule */ @@ -37,6 +37,7 @@ #define RULE_HASFILE 0x100 /* Valid if rule results in a valid filename */ #define RULE_RRQ 0x200 /* Get (read) only */ #define RULE_WRQ 0x400 /* Put (write) only */ +#define RULE_SEDG 0x800 /* sed-style global */ struct rule { struct rule *next; @@ -61,22 +62,30 @@ static int xform_tolower(int c) return tolower(c); } -/* Do \-substitution. Call with string == NULL to get length only. */ -static int genmatchstring(char *string, const char *pattern, - const char *input, const regmatch_t * pmatch, - match_pattern_callback macrosub) +/* + * Do \-substitution. Call with string == NULL to get length only. + * "start" indicates an offset into the input buffer where the pattern + * match was started. + */ +static int do_genmatchstring(char *string, const char *pattern, + const char *ibuf, + const regmatch_t * pmatch, + match_pattern_callback macrosub, + int start, int *nextp) { int (*xform) (int) = xform_null; int len = 0; int n, mlen, sublen; int endbytes; + const char *input = ibuf + start; /* Get section before match; note pmatch[0] is the whole match */ endbytes = strlen(input) - pmatch[0].rm_eo; - len = pmatch[0].rm_so + endbytes; + len = start + pmatch[0].rm_so; if (string) { - memcpy(string, input, pmatch[0].rm_so); - string += pmatch[0].rm_so; + /* Copy the prefix before "start" as well! */ + memcpy(string, ibuf, start + pmatch[0].rm_so); + string += start + pmatch[0].rm_so; } /* Transform matched section */ @@ -102,7 +111,7 @@ static int genmatchstring(char *string, const char *pattern, mlen = pmatch[n].rm_eo - pmatch[n].rm_so; len += mlen; if (string) { - const char *p = input + pmatch[n].rm_so; + const char *p = input + start + pmatch[n].rm_so; while (mlen--) *string++ = xform(*p++); } @@ -145,7 +154,12 @@ static int genmatchstring(char *string, const char *pattern, } } + /* Pointer to post-substitution tail */ + if (nextp) + *nextp = len; + /* Copy section after match */ + len += endbytes; if (string) { memcpy(string, input + pmatch[0].rm_eo, endbytes); string[endbytes] = '\0'; @@ -154,6 +168,26 @@ static int genmatchstring(char *string, const char *pattern, return len; } +/* + * Ditto, but allocate the string in a new buffer + */ + +static int genmatchstring(char **string, const char *pattern, + const char *ibuf, + const regmatch_t * pmatch, + match_pattern_callback macrosub, + int start, int *nextp) +{ + int len; + char *buf; + + len = do_genmatchstring(NULL, pattern, ibuf, pmatch, + macrosub, start, NULL); + buf = tf_malloc(len + 1); + return do_genmatchstring(buf, pattern, ibuf, pmatch, + macrosub, start, nextp); +} + /* * Extract a string terminated by non-escaped whitespace; ignoring * leading whitespace. Consider an unescaped # to be a comment marker, @@ -165,7 +199,7 @@ static int readescstring(char *buf, char **str) int wasbs = 0, len = 0; while (*p && isspace(*p)) - p++; + p++; if (!*p) { *buf = '\0'; @@ -211,14 +245,17 @@ static int parseline(char *line, struct rule *r, int lineno) r->rule_flags |= RULE_REWRITE; break; case 'g': - r->rule_flags |= RULE_GLOBAL; + if (r->rule_flags & RULE_GLOBAL) + r->rule_flags |= RULE_SEDG; + else + r->rule_flags |= RULE_GLOBAL; break; case 'e': r->rule_flags |= RULE_EXIT; break; - case 'E': - r->rule_flags |= RULE_HASFILE; - break; + case 'E': + r->rule_flags |= RULE_HASFILE; + break; case 's': r->rule_flags |= RULE_RESTART; break; @@ -231,18 +268,18 @@ static int parseline(char *line, struct rule *r, int lineno) case '~': r->rule_flags |= RULE_INVERSE; break; - case '4': - r->rule_flags |= RULE_IPV4; - break; - case '6': - r->rule_flags |= RULE_IPV6; - break; - case 'G': - r->rule_flags |= RULE_RRQ; - break; - case 'P': - r->rule_flags |= RULE_WRQ; - break; + case '4': + r->rule_flags |= RULE_IPV4; + break; + case '6': + r->rule_flags |= RULE_IPV6; + break; + case 'G': + r->rule_flags |= RULE_RRQ; + break; + case 'P': + r->rule_flags |= RULE_WRQ; + break; default: syslog(LOG_ERR, "Remap command \"%s\" on line %d contains invalid char \"%c\"", @@ -253,20 +290,20 @@ static int parseline(char *line, struct rule *r, int lineno) } if (r->rule_flags & RULE_REWRITE) { - if (r->rule_flags & RULE_INVERSE) { - syslog(LOG_ERR, "r rules cannot be inverted, line %d: %s\n", - lineno, line); - return -1; /* Error */ - } - if ((r->rule_flags & (RULE_GLOBAL|RULE_HASFILE)) - == (RULE_GLOBAL|RULE_HASFILE)) { - syslog(LOG_ERR, "E rules cannot be combined with g, line %d: %s\n", - lineno, line); - return -1; /* Error */ - } + if (r->rule_flags & RULE_INVERSE) { + syslog(LOG_ERR, "r rules cannot be inverted, line %d: %s\n", + lineno, line); + return -1; /* Error */ + } + if ((r->rule_flags & (RULE_GLOBAL|RULE_SEDG|RULE_HASFILE)) + == (RULE_GLOBAL|RULE_HASFILE)) { + syslog(LOG_ERR, "E rules cannot be combined with g (but gg is OK), line %d: %s\n", + lineno, line); + return -1; /* Error */ + } } else { - /* RULE_GLOBAL is meaningless without RULE_REWRITE */ - r->rule_flags &= ~RULE_GLOBAL; + /* RULE_GLOBAL and RULE_SEDG are meaningless without RULE_REWRITE */ + r->rule_flags &= ~(RULE_GLOBAL|RULE_SEDG); } /* Read and compile the regex */ @@ -348,19 +385,23 @@ void freerules(struct rule *r) /* Execute a rule set on a string; returns a malloc'd new string. */ char *rewrite_string(const struct formats *pf, - const char *input, const struct rule *rules, + const char *input, const struct rule *rules, int mode, int af, match_pattern_callback macrosub, const char **errmsg) { char *current = tfstrdup(input); - char *newstr; + char *newstr, *newerstr; const char *accerr; const struct rule *ruleptr = rules; regmatch_t pmatch[10]; - int len; + int i; + int len, newlen; int was_match = 0; int deadman = DEADMAN_MAX_STEPS; + int matchsense; + int pmatches; unsigned int bad_flags; + int ggoffset; /* Default error */ *errmsg = "Remap table failure"; @@ -376,30 +417,25 @@ char *rewrite_string(const struct formats *pf, if (af != AF_INET6) bad_flags |= RULE_IPV6; for (ruleptr = rules; ruleptr; ruleptr = ruleptr->next) { - if (ruleptr->rule_flags & bad_flags) - continue; /* This rule is excluded by flags */ + if (ruleptr->rule_flags & bad_flags) + continue; /* This rule is excluded by flags */ - if (!deadman--) { - syslog(LOG_WARNING, - "remap: Breaking loop, input = %s, last = %s", input, - current); - free(current); - return NULL; /* Did not terminate! */ - } + matchsense = ruleptr->rule_flags & RULE_INVERSE ? REG_NOMATCH : 0; + pmatches = ruleptr->rule_flags & RULE_INVERSE ? 0 : 10; + + /* Clear the pmatch[] array */ + for (i = 0; i < 10; i++) + pmatch[i].rm_so = pmatch[i].rm_eo = -1; do { - if (regexec(&ruleptr->rx, current, 10, pmatch, 0) == - (ruleptr->rule_flags & RULE_INVERSE ? REG_NOMATCH : 0)) { + if (!deadman--) + goto dead; + + if (regexec(&ruleptr->rx, current, pmatches, pmatch, 0) + == matchsense) { /* Match on this rule */ was_match = 1; - if (ruleptr->rule_flags & RULE_INVERSE) { - /* No actual match, so clear out the pmatch array */ - int i; - for (i = 0; i < 10; i++) - pmatch[i].rm_so = pmatch[i].rm_eo = -1; - } - if (ruleptr->rule_flags & RULE_ABORT) { if (verbosity >= 3) { syslog(LOG_INFO, "remap: rule %d: abort: %s", @@ -407,12 +443,8 @@ char *rewrite_string(const struct formats *pf, } if (ruleptr->pattern[0]) { /* Custom error message */ - len = - genmatchstring(NULL, ruleptr->pattern, current, - pmatch, macrosub); - newstr = tfmalloc(len + 1); - genmatchstring(newstr, ruleptr->pattern, current, - pmatch, macrosub); + genmatchstring(&newstr, ruleptr->pattern, current, + pmatch, macrosub, 0, NULL); *errmsg = newstr; } else { *errmsg = NULL; @@ -422,58 +454,76 @@ char *rewrite_string(const struct formats *pf, } if (ruleptr->rule_flags & RULE_REWRITE) { - len = genmatchstring(NULL, ruleptr->pattern, current, - pmatch, macrosub); - newstr = tfmalloc(len + 1); - genmatchstring(newstr, ruleptr->pattern, current, - pmatch, macrosub); - if ((ruleptr->rule_flags & RULE_HASFILE) && - pf->f_validate(newstr, mode, pf, &accerr)) { - if (verbosity >= 3) { - syslog(LOG_INFO, "remap: rule %d: ignored rewrite (%s): %s", - ruleptr->nrule, accerr, newstr); - } - free(newstr); - was_match = 0; - break; - } - free(current); - current = newstr; - if (verbosity >= 3) { - syslog(LOG_INFO, "remap: rule %d: rewrite: %s", - ruleptr->nrule, current); - } - } else if (ruleptr->rule_flags & RULE_HASFILE) { - if (pf->f_validate(current, mode, pf, &accerr)) { - if (verbosity >= 3) { - syslog(LOG_INFO, "remap: rule %d: not exiting (%s)\n", - ruleptr->nrule, accerr); - } - was_match = 0; - break; - } - } - } else { - break; /* No match, terminate unconditionally */ + len = genmatchstring(&newstr, ruleptr->pattern, current, + pmatch, macrosub, 0, &ggoffset); + + if (ruleptr->rule_flags & RULE_SEDG) { + /* sed-style partial-matching global */ + while (ggoffset < len && + regexec(ruleptr->rx, newstr + ggoffset, + pmatches, pmatch, + ggoffset ? REG_NOTBOL : 0) + == matchsense) { + if (!deadman--) { + free(current); + current = newstr; + goto dead; + } + len = genmatchstring(&newerstr, ruleptr->pattern, + newstr, pmatch, macrosub, + ggoffset, &ggoffset); + free(newstr); + newstr = newerstr; + } + } + + if ((ruleptr->rule_flags & RULE_HASFILE) && + pf->f_validate(newstr, mode, pf, &accerr)) { + if (verbosity >= 3) { + syslog(LOG_INFO, "remap: rule %d: ignored rewrite (%s): %s", + ruleptr->nrule, accerr, newstr); + } + free(newstr); + was_match = 0; + break; + } + } + free(current); + current = newstr; + if (verbosity >= 3) { + syslog(LOG_INFO, "remap: rule %d: rewrite: %s", + ruleptr->nrule, current); + } + } else if (ruleptr->rule_flags & RULE_HASFILE) { + if (pf->f_validate(current, mode, pf, &accerr)) { + if (verbosity >= 3) { + syslog(LOG_INFO, "remap: rule %d: not exiting (%s)\n", + ruleptr->nrule, accerr); + } + was_match = 0; + break; + } } - /* If the rule is global, keep going until no match */ - } while (ruleptr->rule_flags & RULE_GLOBAL); + } else { + break; /* No match, terminate unconditionally */ + } + /* If the rule is (old-style) global, keep going until no match */ + } while ((ruleptr->rule_flags & (RULE_GLOBAL|RULE_SEDG)) == RULE_GLOBAL); - if (was_match) { - was_match = 0; + if (was_match) { + was_match = 0; - if (ruleptr->rule_flags & (RULE_EXIT|RULE_HASFILE)) { - if (verbosity >= 3) { - syslog(LOG_INFO, "remap: rule %d: exit", - ruleptr->nrule); - } - return current; /* Exit here, we're done */ - } else if (ruleptr->rule_flags & RULE_RESTART) { - ruleptr = rules; /* Start from the top */ - if (verbosity >= 3) { - syslog(LOG_INFO, "remap: rule %d: restart", - ruleptr->nrule); - } + if (ruleptr->rule_flags & (RULE_EXIT|RULE_HASFILE)) { + if (verbosity >= 3) { + syslog(LOG_INFO, "remap: rule %d: exit", + ruleptr->nrule); + } + return current; /* Exit here, we're done */ + } else if (ruleptr->rule_flags & RULE_RESTART) { + ruleptr = rules; /* Start from the top */ + if (verbosity >= 3) { + syslog(LOG_INFO, "remap: rule %d: restart", + ruleptr->nrule); } } } @@ -482,4 +532,11 @@ char *rewrite_string(const struct formats *pf, syslog(LOG_INFO, "remap: done"); } return current; + +dead: /* Deadman expired */ + syslog(LOG_WARNING, + "remap: Breaking loop, input = %s, last = %s", input, + current); + free(current); + return NULL; /* Did not terminate! */ } diff --git a/tftpd/tftpd.8.in b/tftpd/tftpd.8.in index c9722cf..9baf7fd 100644 --- a/tftpd/tftpd.8.in +++ b/tftpd/tftpd.8.in @@ -1,5 +1,5 @@ .\" -*- nroff -*- --------------------------------------------------------- * -.\" +.\" .\" Copyright (c) 1990, 1993, 1994 .\" The Regents of the University of California. All rights reserved. .\" @@ -270,7 +270,18 @@ by the The replacement pattern may contain escape sequences; see below. .TP .B g -Repeat this rule until it no longer matches. This is always used with +Repeat this rule until it no longer matches. This is always used with +.BR r . +.TP +.B gg +Repeat this rule until it no longer matches, but only on the portion +of the string that has not yet been matched, similar to how the +.B s +command with the +.B g +option works in +.BR sed (1). +This is always used with .BR r . .TP .B i @@ -288,7 +299,9 @@ end rule processing after executing the rule. If this is combined with .BR r , then if the substitution does \fInot\fP result in a valid filename, the substitution is undone. This cannot be combined with -.BR g . +.BR g , +but \fIcan\fP be combined with +.BR gg . .TP .B s If this rule matches, start rule processing over from the very first @@ -400,7 +413,7 @@ Access to files can, and should, be restricted by invoking .B tftpd with a list of directories by including pathnames as server program arguments on the command line. In this case access is restricted to -files whole names are prefixed by one of the given directories. If +files whose names are prefixed by one of the given directories. If possible, it is recommended that the .B \-\-secure flag is used to set up a chroot() environment for the server to run in From 9a92dec1dcc618051500908a1f24761ab29abdcb Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Mon, 10 Jun 2024 20:36:22 -0700 Subject: [PATCH 180/184] tftpd: make it possible to adjust the remap deadman Allow the user to tweak the remap deadman counter if it is necessary for whatever reason. Signed-off-by: H. Peter Anvin --- tftpd/remap.c | 10 ++++++---- tftpd/remap.h | 3 +++ tftpd/tftpd.8.in | 4 ++++ tftpd/tftpd.c | 14 ++++++++++++++ 4 files changed, 27 insertions(+), 4 deletions(-) diff --git a/tftpd/remap.c b/tftpd/remap.c index d74b2db..6497cad 100644 --- a/tftpd/remap.c +++ b/tftpd/remap.c @@ -39,6 +39,8 @@ #define RULE_WRQ 0x400 /* Put (write) only */ #define RULE_SEDG 0x800 /* sed-style global */ +int deadman_max_steps = DEADMAN_MAX_STEPS; + struct rule { struct rule *next; int nrule; @@ -397,7 +399,7 @@ char *rewrite_string(const struct formats *pf, int i; int len, newlen; int was_match = 0; - int deadman = DEADMAN_MAX_STEPS; + int deadman = deadman_max_steps; int matchsense; int pmatches; unsigned int bad_flags; @@ -534,9 +536,9 @@ char *rewrite_string(const struct formats *pf, return current; dead: /* Deadman expired */ - syslog(LOG_WARNING, - "remap: Breaking loop, input = %s, last = %s", input, - current); + syslog(LOG_ERR, + "remap: Breaking loop after %d steps, input = %s, last = %s", + deadman_max_steps, input, current); free(current); return NULL; /* Did not terminate! */ } diff --git a/tftpd/remap.h b/tftpd/remap.h index a9954ab..c8d76ff 100644 --- a/tftpd/remap.h +++ b/tftpd/remap.h @@ -40,5 +40,8 @@ char *rewrite_string(const struct formats *, const char *, const struct rule *, int, int, match_pattern_callback, const char **); +/* Remapping deadman counter */ +extern int deadman_max_steps; + #endif /* WITH_REGEX */ #endif /* TFTPD_REMAP_H */ diff --git a/tftpd/tftpd.8.in b/tftpd/tftpd.8.in index 9baf7fd..3d43325 100644 --- a/tftpd/tftpd.8.in +++ b/tftpd/tftpd.8.in @@ -163,6 +163,10 @@ remapping below. This option may not be compiled in, see the output of .B "in.tftpd \-V" to verify whether or not it is available. .TP +.\fB\-\-map-steps\fP \fIsteps\fP +Specify the number of remapping rules that may be executed before the +filename mapping fails. The default is 4096. +.TP \fB\-\-verbose\fP, \fB\-v\fP Increase the logging verbosity of .BR tftpd . diff --git a/tftpd/tftpd.c b/tftpd/tftpd.c index bec0f34..fc7e54a 100644 --- a/tftpd/tftpd.c +++ b/tftpd/tftpd.c @@ -325,6 +325,7 @@ static int split_port(char **ap, char **pp) enum long_only_options { OPT_VERBOSITY = 256, + OPT_MAP_STEPS }; static struct option long_options[] = { @@ -347,6 +348,7 @@ static struct option long_options[] = { { "retransmit", 1, NULL, 'T' }, { "port-range", 1, NULL, 'R' }, { "map-file", 1, NULL, 'm' }, + { "map-steps", 1, NULL, OPT_MAP_STEPS }, { "pidfile", 1, NULL, 'P' }, { NULL, 0, NULL, 0 } }; @@ -495,6 +497,18 @@ int main(int argc, char **argv) } rewrite_file = optarg; break; + case OPT_MAP_STEPS: + { + char *ep; + unsigned long steps = strtoul(optarg, &ep, 0); + if (*optarg && *ep && steps > 0 && steps <= INT_MAX) { + deadman_max_steps = steps; + } else { + syslog(LOG_ERR, "Bad --map-steps option: %s", optarg); + exit(EX_USAGE); + } + break; + } #endif case 'v': verbosity++; From 33ec23c0dccbee8d8af207d2f8f59ee52ecfb6fa Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Mon, 10 Jun 2024 20:56:27 -0700 Subject: [PATCH 181/184] remap: re-enable in autoconf; fix missing pointer assignment Need to actually output something, too. The whole section for remap had gotten dropped from autoconf, with the obvious results... Signed-off-by: H. Peter Anvin --- configure.ac | 12 ++++++++++-- tftpd/remap.c | 2 +- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/configure.ac b/configure.ac index be2a500..49b23f9 100644 --- a/configure.ac +++ b/configure.ac @@ -175,6 +175,14 @@ PA_ARG_DISABLED([tcpwrappers], PA_HAVE_TCPWRAPPERS ]) +AC_CHECK_HEADERS_ONCE([regex.h]) +PA_ARG_DISABLED([remap], + [disable regex-based filename remapping], [], + [AS_IF([test x"$ac_cv_header_regex_h" = xyes], + [AC_SEARCH_LIBS(regcomp, [regex rx], + [AC_DEFINE([WITH_REGEX], 1, + [Define if we are compiling with regex filename remapping.])])])]) + TFTPD_LIBS="$LIBS $XTRALIBS" LIBS="$common_libs" @@ -188,7 +196,7 @@ AH_TEMPLATE([WITH_READLINE], PA_ARG_DISABLED([readline], [disable the use of readline command-line editing], [], [ - AC_CHECK_HEADER(readline/readline.h, + AC_CHECK_HEADER([readline/readline.h], [ dnl readline may need libtermcap or somesuch... AC_SEARCH_LIBS(tputs, [termcap terminfo]) @@ -197,7 +205,7 @@ PA_ARG_DISABLED([readline], [AC_DEFINE(WITH_READLINE)]) AC_CHECK_HEADERS(readline/history.h) ], - [AC_CHECK_HEADER(editline/readline.h, + [AC_CHECK_HEADER([editline/readline.h], [ dnl editline may need libtermcap or somesuch... AC_SEARCH_LIBS(tputs, [termcap terminfo]) diff --git a/tftpd/remap.c b/tftpd/remap.c index 6497cad..3fea915 100644 --- a/tftpd/remap.c +++ b/tftpd/remap.c @@ -185,7 +185,7 @@ static int genmatchstring(char **string, const char *pattern, len = do_genmatchstring(NULL, pattern, ibuf, pmatch, macrosub, start, NULL); - buf = tf_malloc(len + 1); + *string = buf = tf_malloc(len + 1); return do_genmatchstring(buf, pattern, ibuf, pmatch, macrosub, start, nextp); } From 2c86ff58dcc003107b47f2d35aa0fdc4a3fd95e1 Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Mon, 10 Jun 2024 21:06:50 -0700 Subject: [PATCH 182/184] remap: *actually* build, and fix masked logic errors Well, now it is actually being compiled, and should hopefully work again... Signed-off-by: H. Peter Anvin --- configure.ac | 3 +- tftpd/remap.c | 115 +++++++++++++++++++++++++------------------------- tftpd/tftpd.c | 1 - 3 files changed, 60 insertions(+), 59 deletions(-) diff --git a/configure.ac b/configure.ac index 49b23f9..deaa4fc 100644 --- a/configure.ac +++ b/configure.ac @@ -181,7 +181,8 @@ PA_ARG_DISABLED([remap], [AS_IF([test x"$ac_cv_header_regex_h" = xyes], [AC_SEARCH_LIBS(regcomp, [regex rx], [AC_DEFINE([WITH_REGEX], 1, - [Define if we are compiling with regex filename remapping.])])])]) + [Define if we are compiling with regex filename remapping.]) + TFTPDOBJS="remap.\$(O) $TFTPOBJS"])])]) TFTPD_LIBS="$LIBS $XTRALIBS" LIBS="$common_libs" diff --git a/tftpd/remap.c b/tftpd/remap.c index 3fea915..413d117 100644 --- a/tftpd/remap.c +++ b/tftpd/remap.c @@ -185,7 +185,7 @@ static int genmatchstring(char **string, const char *pattern, len = do_genmatchstring(NULL, pattern, ibuf, pmatch, macrosub, start, NULL); - *string = buf = tf_malloc(len + 1); + *string = buf = tfmalloc(len + 1); return do_genmatchstring(buf, pattern, ibuf, pmatch, macrosub, start, nextp); } @@ -397,7 +397,7 @@ char *rewrite_string(const struct formats *pf, const struct rule *ruleptr = rules; regmatch_t pmatch[10]; int i; - int len, newlen; + int len; int was_match = 0; int deadman = deadman_max_steps; int matchsense; @@ -429,67 +429,71 @@ char *rewrite_string(const struct formats *pf, for (i = 0; i < 10; i++) pmatch[i].rm_so = pmatch[i].rm_eo = -1; + was_match = 0; + do { if (!deadman--) goto dead; if (regexec(&ruleptr->rx, current, pmatches, pmatch, 0) - == matchsense) { - /* Match on this rule */ - was_match = 1; + != matchsense) + break; /* No match, break out of do loop */ - if (ruleptr->rule_flags & RULE_ABORT) { - if (verbosity >= 3) { - syslog(LOG_INFO, "remap: rule %d: abort: %s", - ruleptr->nrule, current); - } - if (ruleptr->pattern[0]) { - /* Custom error message */ - genmatchstring(&newstr, ruleptr->pattern, current, - pmatch, macrosub, 0, NULL); - *errmsg = newstr; - } else { - *errmsg = NULL; - } - free(current); - return (NULL); + /* Match on this rule */ + was_match = 1; + + if (ruleptr->rule_flags & RULE_ABORT) { + if (verbosity >= 3) { + syslog(LOG_INFO, "remap: rule %d: abort: %s", + ruleptr->nrule, current); } + if (ruleptr->pattern[0]) { + /* Custom error message */ + genmatchstring(&newstr, ruleptr->pattern, current, + pmatch, macrosub, 0, NULL); + *errmsg = newstr; + } else { + *errmsg = NULL; + } + free(current); + return (NULL); + } - if (ruleptr->rule_flags & RULE_REWRITE) { - len = genmatchstring(&newstr, ruleptr->pattern, current, - pmatch, macrosub, 0, &ggoffset); + if (ruleptr->rule_flags & RULE_REWRITE) { + len = genmatchstring(&newstr, ruleptr->pattern, current, + pmatch, macrosub, 0, &ggoffset); - if (ruleptr->rule_flags & RULE_SEDG) { - /* sed-style partial-matching global */ - while (ggoffset < len && - regexec(ruleptr->rx, newstr + ggoffset, - pmatches, pmatch, - ggoffset ? REG_NOTBOL : 0) - == matchsense) { - if (!deadman--) { - free(current); - current = newstr; - goto dead; - } - len = genmatchstring(&newerstr, ruleptr->pattern, - newstr, pmatch, macrosub, - ggoffset, &ggoffset); - free(newstr); - newstr = newerstr; - } - } - - if ((ruleptr->rule_flags & RULE_HASFILE) && - pf->f_validate(newstr, mode, pf, &accerr)) { - if (verbosity >= 3) { - syslog(LOG_INFO, "remap: rule %d: ignored rewrite (%s): %s", - ruleptr->nrule, accerr, newstr); + if (ruleptr->rule_flags & RULE_SEDG) { + /* sed-style partial-matching global */ + while (ggoffset < len && + regexec(&ruleptr->rx, newstr + ggoffset, + pmatches, pmatch, + ggoffset ? REG_NOTBOL : 0) + == matchsense) { + if (!deadman--) { + free(current); + current = newstr; + goto dead; } + len = genmatchstring(&newerstr, ruleptr->pattern, + newstr, pmatch, macrosub, + ggoffset, &ggoffset); free(newstr); - was_match = 0; - break; + newstr = newerstr; } } + + if ((ruleptr->rule_flags & RULE_HASFILE) && + pf->f_validate(newstr, mode, pf, &accerr)) { + if (verbosity >= 3) { + syslog(LOG_INFO, "remap: rule %d: ignored rewrite (%s): %s", + ruleptr->nrule, accerr, newstr); + } + free(newstr); + was_match = 0; + break; + } + free(current); current = newstr; if (verbosity >= 3) { @@ -506,14 +510,11 @@ char *rewrite_string(const struct formats *pf, break; } } - } else { - break; /* No match, terminate unconditionally */ - } - /* If the rule is (old-style) global, keep going until no match */ - } while ((ruleptr->rule_flags & (RULE_GLOBAL|RULE_SEDG)) == RULE_GLOBAL); + /* If the rule is (old-style) global, keep going until no match */ + } while ((ruleptr->rule_flags & (RULE_GLOBAL|RULE_SEDG)) == RULE_GLOBAL); - if (was_match) { - was_match = 0; + if (!was_match) + continue; /* Next rule */ if (ruleptr->rule_flags & (RULE_EXIT|RULE_HASFILE)) { if (verbosity >= 3) { diff --git a/tftpd/tftpd.c b/tftpd/tftpd.c index fc7e54a..e0041dc 100644 --- a/tftpd/tftpd.c +++ b/tftpd/tftpd.c @@ -499,7 +499,6 @@ int main(int argc, char **argv) break; case OPT_MAP_STEPS: { - char *ep; unsigned long steps = strtoul(optarg, &ep, 0); if (*optarg && *ep && steps > 0 && steps <= INT_MAX) { deadman_max_steps = steps; From 37ac880897b0d53bfe2bfbfc1e03078039ec9846 Mon Sep 17 00:00:00 2001 From: Alexander Zhirov Date: Fri, 25 Apr 2025 01:21:57 +0300 Subject: [PATCH 183/184] Applying Arch Linux Patch https://gitlab.archlinux.org/archlinux/packaging/packages/tftp-hpa/-/blob/a70146278f2d494448a831b29cd6fa955e352b2a/tftp-hpa-0.49-fortify-strcpy-crash.patch --- tftp/tftp.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/tftp/tftp.c b/tftp/tftp.c index 33a4175..9c9c5af 100644 --- a/tftp/tftp.c +++ b/tftp/tftp.c @@ -278,15 +278,16 @@ makerequest(int request, const char *name, struct tftphdr *tp, const char *mode) { char *cp; + size_t len; tp->th_opcode = htons((u_short) request); cp = (char *)&(tp->th_stuff); - strcpy(cp, name); - cp += strlen(name); - *cp++ = '\0'; - strcpy(cp, mode); - cp += strlen(mode); - *cp++ = '\0'; + len = strlen(name) + 1; + memcpy(cp, name, len); + cp += len; + len = strlen(mode) + 1; + memcpy(cp, mode, len); + cp += len; return (cp - (char *)tp); } From 1dd16bc0df85f556db194168cd6e37f2d892b927 Mon Sep 17 00:00:00 2001 From: Alexander Zhirov Date: Fri, 25 Apr 2025 01:23:54 +0300 Subject: [PATCH 184/184] doesn't flush by default, so the prompt is never printed https://gitlab.alpinelinux.org/alpine/aports/-/blob/3.21-stable/main/tftp-hpa/fix-prompt-flush.patch --- tftp/main.c | 1 + 1 file changed, 1 insertion(+) diff --git a/tftp/main.c b/tftp/main.c index ac06330..ecdc0e0 100644 --- a/tftp/main.c +++ b/tftp/main.c @@ -806,6 +806,7 @@ static void command(void) exit(0); /* EOF */ #else fputs(prompt, stdout); + fflush(stdout); if (fgets(line, LBUFLEN, stdin) == 0) { if (feof(stdin)) { exit(0);