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", <nul>, 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 <dronnikov@gmail.com>
+ *
+ * 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;
+}
