diff -Naur busybox.orig/include/applets.h busybox/include/applets.h --- busybox.orig/include/applets.h 2008-02-27 17:46:30 +0000 +++ busybox/include/applets.h 2008-03-10 22:46:09 +0000 @@ -356,6 +356,7 @@ #if ENABLE_FEATURE_TFTP_GET || ENABLE_FEATURE_TFTP_PUT USE_TFTP(APPLET(tftp, _BB_DIR_USR_BIN, _BB_SUID_NEVER)) #endif +USE_TFTPD(APPLET(tftpd, _BB_DIR_USR_SBIN, _BB_SUID_NEVER)) USE_TIME(APPLET(time, _BB_DIR_USR_BIN, _BB_SUID_NEVER)) USE_TOP(APPLET(top, _BB_DIR_USR_BIN, _BB_SUID_NEVER)) USE_TOUCH(APPLET_NOFORK(touch, touch, _BB_DIR_BIN, _BB_SUID_NEVER, touch)) diff -Naur busybox.orig/include/usage.h busybox/include/usage.h --- busybox.orig/include/usage.h 2008-02-27 17:46:30 +0000 +++ busybox/include/usage.h 2008-03-11 00:15:10 +0000 @@ -3916,6 +3916,16 @@ USE_FEATURE_TFTP_BLOCKSIZE( \ "\n -b SIZE Transfer blocks of SIZE octets" \ ) + USE_DEBUG_TFTP( \ + "\n -v Be verbose" \ + ) + +#define tftpd_trivial_usage +#define tftpd_full_usage \ + "Trivial File Transfer Protocol Daemon" \ + "\n\nExample:" \ + "\n udpsvd -E 0 69 softlimit -m 99999 tftpd" + #define time_trivial_usage \ "[OPTION]... COMMAND [ARGS...]" #define time_full_usage \ diff -Naur busybox.orig/networking/Config.in busybox/networking/Config.in --- busybox.orig/networking/Config.in 2008-02-27 13:19:56 +0000 +++ busybox/networking/Config.in 2008-03-04 23:43:37 +0000 @@ -806,6 +806,12 @@ into problems with tftp as the protocol doesn't help you much when you run into problems. +config TFTPD + bool "tftpd" + default n + help + Bare bones TFTP Daemon. + config TRACEROUTE bool "traceroute" default n diff -Naur busybox.orig/networking/Kbuild busybox/networking/Kbuild --- busybox.orig/networking/Kbuild 2008-02-26 23:33:24 +0000 +++ busybox/networking/Kbuild 2008-03-10 22:43:10 +0000 @@ -35,6 +35,7 @@ lib-$(CONFIG_TELNET) += telnet.o lib-$(CONFIG_TELNETD) += telnetd.o lib-$(CONFIG_TFTP) += tftp.o +lib-$(CONFIG_TFTPD) += tftp.o lib-$(CONFIG_TRACEROUTE) += traceroute.o lib-$(CONFIG_VCONFIG) += vconfig.o lib-$(CONFIG_WGET) += wget.o diff -Naur busybox.orig/networking/tftp.c busybox/networking/tftp.c --- busybox.orig/networking/tftp.c 2008-02-17 06:50:48 +0000 +++ busybox/networking/tftp.c 2008-03-11 00:05:28 +0000 @@ -21,39 +21,41 @@ #include "libbb.h" -#if ENABLE_FEATURE_TFTP_GET || ENABLE_FEATURE_TFTP_PUT - #define TFTP_BLOCKSIZE_DEFAULT 512 /* according to RFC 1350, don't change */ #define TFTP_TIMEOUT_MS 50 #define TFTP_MAXTIMEOUT_MS 2000 #define TFTP_NUM_RETRIES 12 /* number of backed-off retries */ -/* opcodes we support */ -#define TFTP_RRQ 1 -#define TFTP_WRQ 2 -#define TFTP_DATA 3 -#define TFTP_ACK 4 -#define TFTP_ERROR 5 -#define TFTP_OACK 6 - -#if ENABLE_FEATURE_TFTP_GET && !ENABLE_FEATURE_TFTP_PUT -#define USE_GETPUT(...) -#define CMD_GET(cmd) 1 -#define CMD_PUT(cmd) 0 -#elif !ENABLE_FEATURE_TFTP_GET && ENABLE_FEATURE_TFTP_PUT -#define USE_GETPUT(...) -#define CMD_GET(cmd) 0 -#define CMD_PUT(cmd) 1 -#else -#define USE_GETPUT(...) __VA_ARGS__ -/* masks coming from getpot32 */ -#define CMD_GET(cmd) ((cmd) & 1) -#define CMD_PUT(cmd) ((cmd) & 2) -#endif -/* NB: in the code below - * CMD_GET(cmd) and CMD_PUT(cmd) are mutually exclusive - */ +enum { + OPT_g = 1 << 0, + OPT_p = 1 << 1, + OPT_l = 1 << 2, + OPT_r = 1 << 3, + OPT_b = 1 << 4, + OPT_v = 1 << 5, +}; +/* opcodes we support */ +enum { + TFTP_RRQ = 1, + TFTP_WRQ, + TFTP_DATA, + TFTP_ACK, + TFTP_ERROR, + TFTP_OACK, +}; + +/* errcodes we support */ +enum { + ERR_NOT_FOUND = 1, + ERR_ACCESS, + ERR_NO_ROOM, + ERR_OPCODE, + ERR_TID, + ERR_EXISTS, + ERR_USER, + ERR_OPTION, +}; #if ENABLE_FEATURE_TFTP_BLOCKSIZE @@ -109,16 +111,16 @@ #endif -static int tftp( USE_GETPUT(const int cmd,) +static int tftp(const int cmd, len_and_sockaddr *peer_lsa, const char *remotefile, const int localfd, unsigned port, int tftp_bufsize) { - struct pollfd pfd[1]; -#define socketfd (pfd[0].fd) + struct pollfd pfd; +#define socketfd (pfd.fd) int len; int send_len; - USE_FEATURE_TFTP_BLOCKSIZE(smallint want_option_ack = 0;) + USE_FEATURE_TFTP_BLOCKSIZE(bool want_option_ack = 0;) smallint finished = 0; uint16_t opcode; uint16_t block_nr = 1; @@ -131,7 +133,7 @@ /* Can't use RESERVE_CONFIG_BUFFER here since the allocation * size varies meaning BUFFERS_GO_ON_STACK would fail */ - /* We must keep the transmit and receive buffers seperate */ + /* We must keep the transmit and receive buffers separate */ /* In case we rcv a garbage pkt and we need to rexmit the last pkt */ char *xbuf = xmalloc(tftp_bufsize += 4); char *rbuf = xmalloc(tftp_bufsize); @@ -141,10 +143,7 @@ socketfd = xsocket(peer_lsa->u.sa.sa_family, SOCK_DGRAM, 0); /* build opcode */ - opcode = TFTP_WRQ; - if (CMD_GET(cmd)) { - opcode = TFTP_RRQ; - } + opcode = (cmd & OPT_g) ? TFTP_RRQ : TFTP_WRQ; cp = xbuf + 2; /* add filename and mode */ /* fill in packet if the filename fits into xbuf */ @@ -169,8 +168,8 @@ } /* add "blksize", , blocksize */ strcpy(cp, "blksize"); - cp += sizeof("blksize"); - cp += snprintf(cp, 6, "%d", len) + 1; + cp = utoa_to_buf(len, cp + sizeof("blksize"), 6); + *cp++ = '\0'; want_option_ack = 1; } #endif @@ -187,7 +186,7 @@ cp += 2; block_nr++; opcode = TFTP_ACK; - if (CMD_PUT(cmd)) { + if (cmd & OPT_p) { opcode = TFTP_DATA; len = full_read(localfd, cp, tftp_bufsize - 4); if (len < 0) { @@ -210,12 +209,12 @@ waittime_ms = TFTP_TIMEOUT_MS; send_again: -#if ENABLE_DEBUG_TFTP - fprintf(stderr, "sending %u bytes\n", send_len); - for (cp = xbuf; cp < &xbuf[send_len]; cp++) - fprintf(stderr, "%02x ", (unsigned char) *cp); - fprintf(stderr, "\n"); -#endif + if (cmd & OPT_v) { + bb_error_msg("sending %u bytes", send_len); + for (cp = xbuf; cp < &xbuf[send_len]; cp++) + fprintf(stderr, "%02x ", (unsigned char) *cp); + bb_error_msg(""); + } xsendto(socketfd, xbuf, send_len, &peer_lsa->u.sa, peer_lsa->len); /* Was it final ACK? then exit */ if (finished && (opcode == TFTP_ACK)) @@ -223,9 +222,9 @@ recv_again: /* Receive packet */ - /*pfd[0].fd = socketfd;*/ - pfd[0].events = POLLIN; - switch (safe_poll(pfd, 1, waittime_ms)) { + //pfd.fd = socketfd; + pfd.events = POLLIN; + switch (safe_poll(&pfd, 1, waittime_ms)) { unsigned from_port; case 1: from->len = peer_lsa->len; @@ -270,9 +269,9 @@ opcode = ntohs( ((uint16_t*)rbuf)[0] ); recv_blk = ntohs( ((uint16_t*)rbuf)[1] ); -#if ENABLE_DEBUG_TFTP - fprintf(stderr, "received %d bytes: %04x %04x\n", len, opcode, recv_blk); -#endif + if (cmd & OPT_v) { + bb_error_msg("received %d bytes: %04x %04x", len, opcode, recv_blk); + } if (opcode == TFTP_ERROR) { static const char *const errcode_str[] = { @@ -320,10 +319,9 @@ bb_error_msg("server proposes bad blksize %d, exiting", blksize); goto ret; } -#if ENABLE_DEBUG_TFTP - fprintf(stderr, "using blksize %u\n", - blksize); -#endif + if (cmd & OPT_v) { + bb_error_msg("using blksize %u", blksize); + } tftp_bufsize = blksize + 4; /* Send ACK for OACK ("block" no: 0) */ block_nr = 0; @@ -343,7 +341,7 @@ /* block_nr is already advanced to next block# we expect * to get / block# we are about to send next time */ - if (CMD_GET(cmd) && (opcode == TFTP_DATA)) { + if ((cmd & OPT_g) && (opcode == TFTP_DATA)) { if (recv_blk == block_nr) { len = full_write(localfd, &rbuf[4], len - 4); if (len < 0) { @@ -362,7 +360,7 @@ } } - if (CMD_PUT(cmd) && (opcode == TFTP_ACK)) { + if ((cmd & OPT_p) && (opcode == TFTP_ACK)) { /* did server ACK our last DATA pkt? */ if (recv_blk == (uint16_t) (block_nr - 1)) { if (finished) @@ -377,7 +375,7 @@ * must never resend the current DATA packet on receipt * of a duplicate ACK". * DATA pkts are resent ONLY on timeout. - * Thus "goto send_again" will ba a bad mistake above. + * Thus "goto send_again" will be a bad mistake above. * See: * http://en.wikipedia.org/wiki/Sorcerer's_Apprentice_Syndrome */ @@ -397,49 +395,37 @@ len_and_sockaddr *peer_lsa; const char *localfile = NULL; const char *remotefile = NULL; -#if ENABLE_FEATURE_TFTP_BLOCKSIZE - const char *sblocksize = NULL; -#endif int port; - USE_GETPUT(int cmd;) + int cmd; int fd = -1; int flags = 0; int result; int blocksize = TFTP_BLOCKSIZE_DEFAULT; /* -p or -g is mandatory, and they are mutually exclusive */ - opt_complementary = "" USE_FEATURE_TFTP_GET("g:") USE_FEATURE_TFTP_PUT("p:") - USE_GETPUT("?g--p:p--g"); + opt_complementary = "-1:g:p:?g--p:p--g:b+"; + cmd = getopt32(argv, + "gpl:r:" USE_FEATURE_TFTP_BLOCKSIZE("b:") USE_DEBUG_TFTP("v"), + &localfile, &remotefile + USE_FEATURE_TFTP_BLOCKSIZE(, &blocksize) + ); + if (!tftp_blocksize_check(blocksize, 0)) { + return EXIT_FAILURE; + } - USE_GETPUT(cmd =) getopt32(argv, - USE_FEATURE_TFTP_GET("g") USE_FEATURE_TFTP_PUT("p") - "l:r:" USE_FEATURE_TFTP_BLOCKSIZE("b:"), - &localfile, &remotefile - USE_FEATURE_TFTP_BLOCKSIZE(, &sblocksize)); argv += optind; - flags = O_RDONLY; - if (CMD_GET(cmd)) - flags = O_WRONLY | O_CREAT | O_TRUNC; - -#if ENABLE_FEATURE_TFTP_BLOCKSIZE - if (sblocksize) { - blocksize = xatoi_u(sblocksize); - if (!tftp_blocksize_check(blocksize, 0)) { - return EXIT_FAILURE; - } - } -#endif + flags = (cmd & OPT_g) ? O_WRONLY | O_CREAT | O_TRUNC : O_RDONLY; if (!localfile) localfile = remotefile; if (!remotefile) remotefile = localfile; - /* Error if filename or host is not known */ - if (!remotefile || !argv[0]) + /* Error if filename is not known */ + if (!remotefile) bb_show_usage(); - fd = CMD_GET(cmd) ? STDOUT_FILENO : STDIN_FILENO; + fd = (cmd & OPT_g) ? STDOUT_FILENO : STDIN_FILENO; if (!LONE_DASH(localfile)) { fd = xopen(localfile, flags); } @@ -447,20 +433,143 @@ port = bb_lookup_port(argv[1], "udp", 69); peer_lsa = xhost2sockaddr(argv[0], port); -#if ENABLE_DEBUG_TFTP - fprintf(stderr, "using server '%s', remotefile '%s', localfile '%s'\n", + if (cmd & OPT_v) { + bb_error_msg("using server '%s', remotefile '%s', localfile '%s'", xmalloc_sockaddr2dotted(&peer_lsa->u.sa), remotefile, localfile); -#endif + } - result = tftp( USE_GETPUT(cmd,) peer_lsa, remotefile, fd, port, blocksize); + result = tftp(cmd, peer_lsa, remotefile, fd, port, blocksize); if (ENABLE_FEATURE_CLEAN_UP) close(fd); - if (result != EXIT_SUCCESS && !LONE_DASH(localfile) && CMD_GET(cmd)) { + if (result != EXIT_SUCCESS && !LONE_DASH(localfile) && (cmd & OPT_g)) { unlink(localfile); } return result; } -#endif /* ENABLE_FEATURE_TFTP_GET || ENABLE_FEATURE_TFTP_PUT */ +/* vi: set sw=4 ts=4: */ +/* + * bare bones TFTP daemon + * + * Copyright (C) 2008 by Vladimir Dronnikov + * + * Licensed under GPLv2, see file LICENSE in this tarball for details. + */ +int tftpd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int tftpd_main(int argc, char **argv) +{ + int fd = -1; + unsigned seq = 0; + ssize_t len; +#if ENABLE_FEATURE_TFTP_BLOCKSIZE // +212 octets + size_t blksize = TFTP_BLOCKSIZE_DEFAULT; +#else +# define blksize TFTP_BLOCKSIZE_DEFAULT +#endif + char *buf; + + // allocate transfer buffer + // + buf = xmalloc(blksize+4); + + // read packets: stdin, stdout -- network. + // packets of length < 5 indicate terminal condition + while ((len = safe_read(STDIN_FILENO, buf, blksize+4)) >= 4) { + uint8_t cmd = ntohs(*((uint16_t *)buf)); +#define err cmd + // read or write requested? + if (TFTP_RRQ == cmd || TFTP_WRQ == cmd) { +#if ENABLE_FEATURE_TFTP_BLOCKSIZE + char *s; + // buf: 0? filename 0 "octet" 0 ["blksize" 0 #octets 0] + // we support only "octet" binary mode + s = buf+2; + s += strlen(s)+1; + if (0 != strcmp("octet", s)) { + err = ERR_OPCODE; + goto bad; + + } +#endif + cmd -= TFTP_RRQ; + // open or create requested file + fd = open_or_warn(buf+2, (cmd) ? (O_CREAT | O_WRONLY | O_TRUNC | O_EXCL) : O_RDONLY); + if (fd < 0) { + err = cmd ? ERR_EXISTS : ERR_NOT_FOUND; + goto bad; + } +#if ENABLE_FEATURE_TFTP_BLOCKSIZE + // check if blksize option is given + s = tftp_option_get(buf+2, len-2, "blksize"); + if (s) { + // reallocate transfer buffer + blksize = xatoi_u(s); + if (tftp_blocksize_check(blksize, blksize)) { + buf = xrealloc(buf, blksize+4); + // send OACK + buf[1] = TFTP_OACK; + strcpy(buf+2, "blksize"); + s = utoa_to_buf(blksize, buf+2+sizeof("blksize"), 6); + *s = '\0'; + len = (s-buf+1)-4; // send sends len+4 bytes + goto send; + } + // blocksize is bad... -> who cares? + } +#endif + // no blksize option is given or it is not valid -> + // perform vanilla processing + // read requested? + if (!cmd) { + // RRQ should be acknowledged with data packet of seq num 1 + goto send_data; + // write requested? + } else { + // WRQ should be acknowledged with seq num 0 + *((uint16_t *)(buf+2)) = 0; + // ack + goto send_ack; + } + // data packet? + } else if (TFTP_DATA == cmd) { + // ... dump contents to the file being written + if (full_write(fd, buf+4, len-4) < 0) { + err = ERR_NO_ROOM; + goto bad; + } + send_ack: + // send ACK + buf[1] = TFTP_ACK; + len = 0; + goto send; + // ack packet? + } else if (TFTP_ACK == cmd) { + send_data: + // ... send next data chunk + len = full_read(fd, buf+4, blksize); + if (len < 0) { + err = ERR_ACCESS; + goto bad; + } + // send DATA + buf[1] = TFTP_DATA; + *((uint16_t *)(buf+2)) = htons(++seq); + send: + xwrite(STDOUT_FILENO, buf, len+4); + // bad packet! + } else { + err = ERR_OPCODE; + bad: + buf[1] = TFTP_ERROR; + *((uint16_t *)(buf+2)) = htons(err); + buf[4] = 0; // error string = "" + full_write(STDOUT_FILENO, buf, 5); + xfunc_die(); + } + } + close(fd); + + return EXIT_SUCCESS; +}