From 3e2b64a4ebbab0c0fd7b1e9df9817e91d768cb10 Mon Sep 17 00:00:00 2001 From: Christos Zoulas Date: Fri, 8 Jan 2010 21:05:14 +0000 Subject: [PATCH] Patrick Welche - add -p pathsep option - make wrap to zero work, but produce a warning While here: - fix gcc warnings, in particular variable clobbered warnings (compiling with fewer warnings does not really fix the problem) --- libexec/tftpd/Makefile | 4 +- libexec/tftpd/tftpd.8 | 22 ++++++--- libexec/tftpd/tftpd.c | 120 ++++++++++++++++++++++++++++++------------------- 3 files changed, 91 insertions(+), 55 deletions(-) diff --git a/libexec/tftpd/Makefile b/libexec/tftpd/Makefile index 8bc8ce665fa..9f8de6ec981 100644 --- a/libexec/tftpd/Makefile +++ b/libexec/tftpd/Makefile @@ -1,7 +1,7 @@ -# $NetBSD: Makefile,v 1.11 2002/09/18 06:24:35 lukem Exp $ +# $NetBSD: Makefile,v 1.12 2009/03/16 02:24:57 lukem Exp $ # from: @(#)Makefile 8.1 (Berkeley) 6/4/93 -WARNS?= 2 # XXX: setjmp clobber warnings +WARNS?= 5 .include diff --git a/libexec/tftpd/tftpd.8 b/libexec/tftpd/tftpd.8 index ddf00d4e4e4..038f6c516df 100644 --- a/libexec/tftpd/tftpd.8 +++ b/libexec/tftpd/tftpd.8 @@ -1,4 +1,4 @@ -.\" $NetBSD: tftpd.8,v 1.20 2003/07/04 12:11:45 wiz Exp $ +.\" $NetBSD: tftpd.8,v 1.21 2003/08/07 09:46:53 agc Exp $ .\" .\" Copyright (c) 1983, 1991, 1993 .\" The Regents of the University of California. All rights reserved. @@ -29,7 +29,7 @@ .\" .\" from: @(#)tftpd.8 8.1 (Berkeley) 6/4/93 .\" -.Dd June 11, 2003 +.Dd January 8, 2010 .Dt TFTPD 8 .Os .Sh NAME @@ -43,6 +43,7 @@ Internet Trivial File Transfer Protocol server .Op Fl g Ar group .Op Fl l .Op Fl n +.Op Fl p Ar path separator .Op Fl s Ar directory .Op Fl u Ar user .Op Ar directory ... @@ -107,6 +108,11 @@ Logs all requests using .It Fl n Suppresses negative acknowledgement of requests for nonexistent relative filenames. +.It Fl p Ar path separator +All occurances of the single character +.Ar path separator +in the requested filename are replaced with +.Sq / . .It Fl s Ar directory .Nm will @@ -193,11 +199,15 @@ TFTP options were implemented by Wasabi Systems, Inc., in 2003, and first appeared in .Nx 2.0 . .Sh BUGS -Files larger than 33488896 octets (65535 blocks) cannot be transferred -without client and server supporting blocksize negotiation (RFCs -2347 and 2348). +Files larger than 33,553,919 octets (65535 blocks, last one <512 +octets) cannot be correctly transferred without client and server +supporting blocksize negotiation (RFCs 2347 and 2348). As a kludge, +.Nm +accepts a sequence of block numbers which wrap to zero after 65535. .Pp -Many tftp clients will not transfer files over 16744448 octets (32767 blocks). +Many tftp clients will not transfer files over 16,776,703 octets +(32767 blocks), as they incorrectly count the block number using +a signed rather than unsigned 16-bit integer. .Sh SECURITY CONSIDERATIONS You are .Em strongly diff --git a/libexec/tftpd/tftpd.c b/libexec/tftpd/tftpd.c index 405f079f619..f98c099b868 100644 --- a/libexec/tftpd/tftpd.c +++ b/libexec/tftpd/tftpd.c @@ -1,4 +1,4 @@ -/* $NetBSD: tftpd.c,v 1.31 2008/07/21 13:25:47 lukem Exp $ */ +/* $NetBSD: tftpd.c,v 1.32 2009/03/16 01:56:21 lukem Exp $ */ /* * Copyright (c) 1983, 1993 @@ -36,7 +36,7 @@ __COPYRIGHT("@(#) Copyright (c) 1983, 1993\ #if 0 static char sccsid[] = "@(#)tftpd.c 8.1 (Berkeley) 6/4/93"; #else -__RCSID("$NetBSD: tftpd.c,v 1.31 2008/07/21 13:25:47 lukem Exp $"); +__RCSID("$NetBSD: tftpd.c,v 1.32 2009/03/16 01:56:21 lukem Exp $"); #endif #endif /* not lint */ @@ -77,20 +77,20 @@ __RCSID("$NetBSD: tftpd.c,v 1.31 2008/07/21 13:25:47 lukem Exp $"); #define TIMEOUT 5 -int peer; -int rexmtval = TIMEOUT; -int maxtimeout = 5*TIMEOUT; +static int peer; +static int rexmtval = TIMEOUT; +static int maxtimeout = 5*TIMEOUT; -char buf[MAXPKTSIZE]; -char ackbuf[PKTSIZE]; -char oackbuf[PKTSIZE]; -struct sockaddr_storage from; -socklen_t fromlen; -int debug; +static char buf[MAXPKTSIZE]; +static char ackbuf[PKTSIZE]; +static char oackbuf[PKTSIZE]; +static struct sockaddr_storage from; +static socklen_t fromlen; +static int debug; -int tftp_opt_tsize = 0; -int tftp_blksize = SEGSIZE; -int tftp_tsize = 0; +static int tftp_opt_tsize = 0; +static int tftp_blksize = SEGSIZE; +static int tftp_tsize = 0; /* * Null-terminated directory prefix list for absolute pathname requests and @@ -107,24 +107,24 @@ static struct dirlist { static int suppress_naks; static int logging; static int secure; +static char pathsep = '\0'; static char *securedir; struct formats; static const char *errtomsg(int); -static void nak(int); -static void tftp(struct tftphdr *, int); -static void usage(void); +static void nak(int); +static void tftp(struct tftphdr *, int); +static void usage(void) __attribute__((__noreturn__)); static char *verifyhost(struct sockaddr *); -void justquit(int); -int main(int, char **); -void recvfile(struct formats *, int, int); -void sendfile(struct formats *, int, int); -void timer(int); +static void justquit(int); +static void recvfile(struct formats *, int, int); +static void sendfile(struct formats *, int, int); +static void timer(int); static const char *opcode(int); -int validate_access(char **, int); +static int validate_access(char **, int); -struct formats { +static struct formats { const char *f_mode; int (*f_validate)(char **, int); void (*f_send)(struct formats *, int, int); @@ -133,7 +133,7 @@ struct formats { } formats[] = { { "netascii", validate_access, sendfile, recvfile, 1 }, { "octet", validate_access, sendfile, recvfile, 0 }, - { 0 } + { .f_mode = NULL } }; static void @@ -141,7 +141,7 @@ usage(void) { syslog(LOG_ERR, - "Usage: %s [-dln] [-u user] [-g group] [-s directory] [directory ...]", + "Usage: %s [-dln] [-u user] [-g group] [-s directory] [-p pathsep] [directory ...]", getprogname()); exit(1); } @@ -153,7 +153,8 @@ main(int argc, char *argv[]) struct passwd *pwent; struct group *grent; struct tftphdr *tp; - char *tgtuser, *tgtgroup, *ep; + const char *tgtuser, *tgtgroup; + char *ep; int n, ch, on, fd; int soopt; socklen_t len; @@ -170,7 +171,7 @@ main(int argc, char *argv[]) curuid = getuid(); curgid = getgid(); - while ((ch = getopt(argc, argv, "dg:lns:u:")) != -1) + while ((ch = getopt(argc, argv, "dg:lnp:s:u:w:")) != -1) switch (ch) { case 'd': debug++; @@ -188,6 +189,12 @@ main(int argc, char *argv[]) suppress_naks = 1; break; + case 'p': + if (optarg[0] == '\0' || optarg[1] != '\0') + usage(); + pathsep = optarg[0]; + break; + case 's': secure = 1; securedir = optarg; @@ -536,15 +543,15 @@ tsize_handler(struct tftphdr *tp, char *opt, char *val, char *ack, return 0; } -struct tftp_options { - char *o_name; +static const struct tftp_options { + const char *o_name; int (*o_handler)(struct tftphdr *, char *, char *, char *, int *, int *); } options[] = { { "blksize", blk_handler }, { "timeout", timeout_handler }, { "tsize", tsize_handler }, - { NULL, NULL } + { .o_name = NULL } }; /* @@ -555,7 +562,7 @@ static int get_options(struct tftphdr *tp, char *cp, int size, char *ackb, int *alen, int *err) { - struct tftp_options *op; + const struct tftp_options *op; char *option, *value, *endp; int r, rv=0, ec=0; @@ -609,7 +616,7 @@ tftp(struct tftphdr *tp, int size) struct formats *pf; char *cp; char *filename, *mode; - int first, ecode, alen, etftp=0, r; + int first, ecode, alen, etftp = 0, r; ecode = 0; /* XXX gcc */ first = 1; @@ -662,6 +669,16 @@ again: exit(1); } } + /* + * Globally replace the path separator given in the -p option + * with / to cope with clients expecting a non-unix path separator. + */ + if (pathsep != '\0') { + for (cp = filename; *cp != '\0'; ++cp) { + if (*cp == pathsep) + *cp = '/'; + } + } ecode = (*pf->f_validate)(&filename, tp->th_opcode); if (logging) { syslog(LOG_INFO, "%s: %s request for %s: %s", @@ -821,10 +838,10 @@ validate_access(char **filep, int mode) return (0); } -int timeout; -jmp_buf timeoutbuf; +static int timeout; +static jmp_buf timeoutbuf; -void +static void timer(int dummy) { @@ -853,7 +870,7 @@ opcode(int code) case OACK: return "OACK"; default: - (void)snprintf(obuf, sizeof(obuf), "*code %d*", code); + (void)snprintf(obuf, sizeof(obuf), "*code 0x%x*", code); return obuf; } } @@ -861,13 +878,14 @@ opcode(int code) /* * Send the requested file. */ -void -sendfile(struct formats *pf, int etftp, int acklength) +static void +sendfile(struct formats *pf, volatile int etftp, int acklength) { volatile unsigned int block; struct tftphdr *dp; struct tftphdr *ap; /* ack packet */ - int size, n; + volatile int size; + int n; signal(SIGALRM, timer); ap = (struct tftphdr *)ackbuf; @@ -918,20 +936,20 @@ send_data: goto abort; case ACK: - if (ap->th_block == 0) { + if (etftp && ap->th_block == 0) { etftp = 0; acklength = 0; dp = r_init(); goto done; } - if (ap->th_block == block) + if (ap->th_block == (u_short)block) goto done; if (debug) syslog(LOG_DEBUG, "Resync ACK %u != %u", (unsigned int)ap->th_block, block); /* Re-synchronize with the other side */ (void) synchnet(peer, tftp_blksize); - if (ap->th_block == (block -1)) + if (ap->th_block == (u_short)(block - 1)) goto send_data; default: syslog(LOG_INFO, "Received %s in sendfile\n", @@ -942,13 +960,16 @@ send_data: done: if (debug) syslog(LOG_DEBUG, "Received ACK for block %u", block); + if (block == UINT16_MAX && size == tftp_blksize) + syslog(LOG_WARNING, + "Block number wrapped (hint: increase block size)"); block++; } while (size == tftp_blksize || block == 1); abort: (void) fclose(file); } -void +static void justquit(int dummy) { @@ -958,13 +979,14 @@ justquit(int dummy) /* * Receive a file. */ -void -recvfile(struct formats *pf, int etftp, int acklength) +static void +recvfile(struct formats *pf, volatile int etftp, volatile int acklength) { volatile unsigned int block; struct tftphdr *dp; struct tftphdr *ap; /* ack buffer */ - int n, size; + volatile int size; + int n; signal(SIGALRM, timer); dp = w_init(); @@ -980,9 +1002,13 @@ recvfile(struct formats *pf, int etftp, int acklength) } if (debug) syslog(LOG_DEBUG, "Sending ACK for block %u\n", block); + if (block == UINT16_MAX) + syslog(LOG_WARNING, + "Block number wrapped (hint: increase block size)"); block++; (void) setjmp(timeoutbuf); send_ack: + ap = (struct tftphdr *) (etftp ? oackbuf : ackbuf); if (send(peer, ap, acklength, 0) != acklength) { syslog(LOG_ERR, "tftpd: write: %m"); goto abort; -- 2.11.4.GIT