diff options
author | Rob Landley <rob@landley.net> | 2006-06-16 15:08:59 +0000 |
---|---|---|
committer | Rob Landley <rob@landley.net> | 2006-06-16 15:08:59 +0000 |
commit | 1449a2014a4e715a7a52b27caec528a9c802fa5f (patch) | |
tree | 281bba4feed27caadd90981d70e713caedbf15f2 /patches/tftp_timeout_multicast.diff | |
parent | 747041955ed38fb14be3e5d0c3a946cfa74bc15e (diff) | |
download | busybox-1449a2014a4e715a7a52b27caec528a9c802fa5f.tar.gz |
This directory was fallout from the great feature freeze of 2003, which led
up to the 1.00 release. I just moved what was left of it to
http://busybox.net/~landley/pending because it does _not_ belong in the
tree anymore.
Diffstat (limited to 'patches/tftp_timeout_multicast.diff')
-rw-r--r-- | patches/tftp_timeout_multicast.diff | 1053 |
1 files changed, 0 insertions, 1053 deletions
diff --git a/patches/tftp_timeout_multicast.diff b/patches/tftp_timeout_multicast.diff deleted file mode 100644 index 5147a6714..000000000 --- a/patches/tftp_timeout_multicast.diff +++ /dev/null @@ -1,1053 +0,0 @@ -Index: AUTHORS -=================================================================== -RCS file: /var/cvs/busybox/AUTHORS,v -retrieving revision 1.40 -diff -u -r1.40 AUTHORS ---- a/AUTHORS 9 Oct 2003 21:19:21 -0000 1.40 -+++ b/AUTHORS 5 Mar 2004 15:45:47 -0000 -@@ -92,6 +92,9 @@ - Original author of BusyBox in 1995, 1996. Some of his code can - still be found hiding here and there... - -+John Powers <jpp@ti.com> -+ Added multicast option (rfc2090) and timeout option (rfc2349) to tftp. -+ - Tim Riker <Tim@Rikers.org> - bug fixes, member of fan club - -Index: include/usage.h -=================================================================== -RCS file: /var/cvs/busybox/include/usage.h,v -retrieving revision 1.191 -diff -u -r1.191 usage.h ---- a/include/usage.h 25 Feb 2004 10:35:55 -0000 1.191 -+++ b/include/usage.h 5 Mar 2004 15:45:59 -0000 -@@ -2492,6 +2492,21 @@ - #else - #define USAGE_TFTP_BS(a) - #endif -+#ifdef CONFIG_FEATURE_TFTP_TIMEOUT -+ #define USAGE_TFTP_TIMEOUT(a) a -+#else -+ #define USAGE_TFTP_TIMEOUT(a) -+#endif -+#ifdef CONFIG_FEATURE_TFTP_MULTICAST -+ #define USAGE_TFTP_MULTICAST(a) a -+#else -+ #define USAGE_TFTP_MULTICAST(a) -+#endif -+#ifdef CONFIG_FEATURE_TFTP_DEBUG -+ #define USAGE_TFTP_DEBUG(a) a -+#else -+ #define USAGE_TFTP_DEBUG(a) -+#endif - - #define tftp_trivial_usage \ - "[OPTION]... HOST [PORT]" -@@ -2508,6 +2523,16 @@ - ) \ - USAGE_TFTP_BS( \ - "\t-b SIZE\tTransfer blocks of SIZE octets.\n" \ -+ ) \ -+ USAGE_TFTP_TIMEOUT( \ -+ "\t-T SEC\tClient timeout SEC seconds (default: 5).\n" \ -+ "\t-t SEC\tServer timeout SEC seconds\n" \ -+ ) \ -+ USAGE_TFTP_MULTICAST( \ -+ "\t-m\tMulticast get file.\n" \ -+ ) \ -+ USAGE_TFTP_DEBUG( \ -+ "\t-D\tPrint debug messages.\n" \ - ) - #define time_trivial_usage \ - "[OPTION]... COMMAND [ARGS...]" -Index: networking/Config.in -=================================================================== -RCS file: /var/cvs/busybox/networking/Config.in,v -retrieving revision 1.27 -diff -u -r1.27 Config.in ---- a/networking/Config.in 22 Feb 2004 12:25:47 -0000 1.27 -+++ b/networking/Config.in 5 Mar 2004 15:45:59 -0000 -@@ -522,6 +522,13 @@ - Add support for the GET command within the TFTP client. This allows - a client to retrieve a file from a TFTP server. - -+config CONFIG_FEATURE_TFTP_MULTICAST -+ bool " Enable \"multicast\" option" -+ default n -+ depends on CONFIG_FEATURE_TFTP_GET -+ help -+ Allow the client to receive multicast file transfers. -+ - config CONFIG_FEATURE_TFTP_PUT - bool " Enable \"put\" command" - default y -@@ -531,12 +538,19 @@ - a client to transfer a file to a TFTP server. - - config CONFIG_FEATURE_TFTP_BLOCKSIZE -- bool " Enable \"blocksize\" command" -+ bool " Enable \"blksize\" option" - default n - depends on CONFIG_TFTP - help - Allow the client to specify the desired block size for transfers. - -+config CONFIG_FEATURE_TFTP_TIMEOUT -+ bool " Enable \"timeout\" option" -+ default n -+ depends on CONFIG_TFTP -+ help -+ Allow the client to negotiate timeout option with server. -+ - config CONFIG_FEATURE_TFTP_DEBUG - bool " Enable debug" - default n -Index: networking/tftp.c -=================================================================== -RCS file: /var/cvs/busybox/networking/tftp.c,v -retrieving revision 1.25 -diff -u -r1.25 tftp.c ---- a/networking/tftp.c 5 Mar 2004 13:04:39 -0000 1.25 -+++ b/networking/tftp.c 5 Mar 2004 15:46:00 -0000 -@@ -1,11 +1,26 @@ -+/* vi: set sw=4 ts=4: */ - /* ------------------------------------------------------------------------- */ - /* tftp.c */ -+/* Copyright (c) 2003, 2004 Texas Instruments */ -+/* */ -+/* This package is free software; you can redistribute it and/or */ -+/* modify it under the terms of the license found in the file */ -+/* named COPYING that should have accompanied this file. */ -+/* */ -+/* THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR */ -+/* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED */ -+/* WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. */ - /* */ - /* A simple tftp client for busybox. */ - /* Tries to follow RFC1350. */ - /* Only "octet" mode supported. */ - /* Optional blocksize negotiation (RFC2347 + RFC2348) */ - /* */ -+/* New features added at Texas Instruments, October 2003 */ -+/* Author: John Powers */ -+/* Multicast option: rfc2090 */ -+/* Timeout option: rfc2349 */ -+/* */ - /* Copyright (C) 2001 Magnus Damm <damm@opensource.se> */ - /* */ - /* Parts of the code based on: */ -@@ -46,8 +61,20 @@ - - #include "busybox.h" - -+#if defined(CONFIG_FEATURE_TFTP_BLOCKSIZE) || defined(CONFIG_FEATURE_TFTP_MULTICAST) || defined(CONFIG_FEATURE_TFTP_TIMEOUT) -+ #define TFTP_OPTIONS -+#endif -+ - //#define CONFIG_FEATURE_TFTP_DEBUG - -+#ifdef CONFIG_FEATURE_TFTP_DEBUG -+ static void printtime(void); -+ #define dprintf(fmt...) if (debug) {printtime(); printf(fmt);} -+ int debug = 0; -+#else -+ #define dprintf(fmt...) -+#endif -+ - #define TFTP_BLOCKSIZE_DEFAULT 512 /* according to RFC 1350, don't change */ - #define TFTP_TIMEOUT 5 /* seconds */ - -@@ -68,12 +95,24 @@ - "Illegal TFTP operation", - "Unknown transfer ID", - "File already exists", -- "No such user" -+ "No such user", -+#ifdef TFTP_OPTIONS -+ "Unsupported option", -+#endif - }; - - const int tftp_cmd_get = 1; - const int tftp_cmd_put = 2; - -+ -+struct tftp_option { -+ int multicast; -+ int blksize; -+ int client_timeout; -+ int server_timeout; -+}; -+ -+ - #ifdef CONFIG_FEATURE_TFTP_BLOCKSIZE - - static int tftp_blocksize_check(int blocksize, int bufsize) -@@ -93,16 +132,158 @@ - return blocksize; - } - -+#endif -+ -+#ifdef CONFIG_FEATURE_TFTP_TIMEOUT -+ -+static int -+tftp_timeout_check(int timeout) -+{ -+ /* Check if timeout seconds is valid: -+ * RFC2349 says between 1 and 255. -+ */ -+ -+ if (timeout < 1 || timeout > 255) { -+ bb_error_msg("bad timeout value"); -+ return 0; -+ } -+ return timeout; -+} -+ -+#endif -+ -+#ifdef CONFIG_FEATURE_TFTP_MULTICAST -+static int -+tftp_multicast_check(const char *opt, char **phost, unsigned short *pport, int *pactive) -+{ -+ /* Option string contains comma delimited addr,port,active. -+ * addr = multicast IP address -+ * port = port number -+ * active = 1 if active client -+ * 0 if passive client -+ * -+ * Addr and port will be empty fields when the server notifies a -+ * passive client that it is now the active client. -+ * -+ * The host address string must be freed by the caller. Neither host -+ * nor port will be set/changed if the input fields are empty. -+ * -+ * If any tokenization errors occur in the opt string, the host -+ * address string is automatically freed. -+ * -+ * Return 0 if any tokenization error, 1 if all parameters are good. -+ */ -+ -+ char *token = NULL; -+ char *parse_buf = NULL; -+ char *tokenv = NULL; -+ char *host = NULL; -+ int port; -+ int active; -+ -+ parse_buf = bb_xstrdup(opt); -+ -+ dprintf("multicast option=%s\n", opt); -+ -+ /* IP address */ -+ if ((token = strtok_r(parse_buf, ",", &tokenv)) == NULL) { -+ dprintf("tftp_multicast_check: cannot parse IP address from %s\n", parse_buf); -+ free(parse_buf); -+ return 0; -+ } -+ if (strlen(token) > 0) -+ *phost = host = bb_xstrdup(token); -+ -+ /* Port */ -+ if ((token = strtok_r(NULL, ",", &tokenv)) == NULL) { -+ dprintf("tftp_multicast_check: cannot parse port number from %s\n", tokenv); -+ goto token_error; -+ } -+ if (strlen(token) > 0) { -+ port = atoi(token); -+ if (port < 0 || port > 0xFFFF) { -+ dprintf("tftp_multicast_check: bad port number (%d)\n", port); -+ goto token_error; -+ } -+ *pport = htons(port); -+ } -+ -+ /* Active/passive */ -+ if ((token = strtok_r(NULL, ",", &tokenv)) == NULL) { -+ dprintf("tftp_multicast_check: cannot parse active/passive from %s\n", tokenv); -+ goto token_error; -+ } -+ active = atoi(token); -+ if (active != 0 && active != 1) { -+ dprintf("tftp_multicast_check: bad active/passive flag (%d)\n", active); -+ goto token_error; -+ } -+ *pactive = active; -+ -+ free(parse_buf); -+ return 1; -+ -+token_error: -+ free(parse_buf); -+ if (host != NULL) -+ free(host); -+ *phost = NULL; -+ return 0; -+ -+} -+ -+#define VECTOR_QUANTUM_WIDTH 8 -+#define VECTOR_QUANTUM_ALL_ONES ((1<<VECTOR_QUANTUM_WIDTH)-1) -+ -+static void inline -+bit_set(int bit, unsigned char *vector) -+{ -+ int offset = bit / VECTOR_QUANTUM_WIDTH; -+ int mask = 1 << (bit % VECTOR_QUANTUM_WIDTH); -+ vector[offset] |= mask; -+} -+ -+static int inline -+bit_isset(int bit, const unsigned char *vector) -+{ -+ int offset = bit / VECTOR_QUANTUM_WIDTH; -+ int mask = 1 << (bit % VECTOR_QUANTUM_WIDTH); -+ return vector[offset] & mask ? 1 : 0; -+} -+ -+static int inline -+bit_lmz(const unsigned char *vector) -+{ -+ /* Return number of left-most zero in bit vector */ -+ const unsigned char *vp = vector; -+ int i; -+ unsigned char velem; -+ -+ while (*vp == VECTOR_QUANTUM_ALL_ONES) -+ vp++; -+ velem = *vp; -+ for (i = 0; i < VECTOR_QUANTUM_WIDTH; i++) { -+ if ((velem & (1 << i)) == 0) -+ break; -+ } -+ dprintf("bit_lmz: block=%d\n", (vp - vector)*VECTOR_QUANTUM_WIDTH + i); -+ return (vp - vector)*VECTOR_QUANTUM_WIDTH + i; -+} -+ -+#endif -+ -+ -+ -+#ifdef TFTP_OPTIONS -+ - static char *tftp_option_get(char *buf, int len, char *option) - { -- int opt_val = 0; -+ int opt_val = 0; - int opt_found = 0; - int k; -- -- while (len > 0) { - -+ while (len > 0) { - /* Make sure the options are terminated correctly */ -- - for (k = 0; k < len; k++) { - if (buf[k] == '\0') { - break; -@@ -117,9 +298,8 @@ - if (strcasecmp(buf, option) == 0) { - opt_found = 1; - } -- } -- else { -- if (opt_found) { -+ } else { -+ if (opt_found) { - return buf; - } - } -@@ -138,7 +318,8 @@ - #endif - - static inline int tftp(const int cmd, const struct hostent *host, -- const char *remotefile, int localfd, const unsigned short port, int tftp_bufsize) -+ const char *remotefile, int localfd, const unsigned short port, -+ struct tftp_option *option) - { - const int cmd_get = cmd & tftp_cmd_get; - const int cmd_put = cmd & tftp_cmd_put; -@@ -155,18 +336,29 @@ - int len; - int opcode = 0; - int finished = 0; -- int timeout = bb_tftp_num_retries; -+ int retry = bb_tftp_num_retries; - unsigned short block_nr = 1; - --#ifdef CONFIG_FEATURE_TFTP_BLOCKSIZE -- int want_option_ack = 0; -+#ifdef CONFIG_FEATURE_TFTP_MULTICAST -+ struct hostent *mchost; -+ struct sockaddr_in mcsa; -+ char *mchostname; -+ unsigned short mcport; -+ unsigned char *mcblockmap = NULL; -+ int master_client = 1; -+ int mcfd = -1; -+ int mcmaxblock = 0x10000; -+ int ack_oack = 0; -+#else -+ #define master_client 1 -+ #define ack_oack 0 - #endif - - /* Can't use RESERVE_CONFIG_BUFFER here since the allocation - * size varies meaning BUFFERS_GO_ON_STACK would fail */ -- char *buf=xmalloc(tftp_bufsize + 4); -+ char *buf=xmalloc(option->blksize + 4); - -- tftp_bufsize += 4; -+ int tftp_bufsize = option->blksize + 4; - - if ((socketfd = socket(PF_INET, SOCK_DGRAM, 0)) < 0) { - bb_perror_msg("socket"); -@@ -183,15 +375,21 @@ - memcpy(&sa.sin_addr, (struct in_addr *) host->h_addr, - sizeof(sa.sin_addr)); - -- /* build opcode */ -- -- if (cmd_get) { -- opcode = TFTP_RRQ; -+#ifdef CONFIG_FEATURE_TFTP_MULTICAST -+ if (option->multicast) { -+ const int bmsize = 0x10000 / VECTOR_QUANTUM_WIDTH; -+ if ((mcfd = socket(PF_INET, SOCK_DGRAM, 0)) < 0) { -+ bb_perror_msg("multicast socket"); -+ return EXIT_FAILURE; -+ } -+ mcblockmap = xmalloc(bmsize+1); -+ memset(mcblockmap, 0, bmsize+1); - } -+#endif - -- if (cmd_put) { -- opcode = TFTP_WRQ; -- } -+ /* build opcode */ -+ -+ opcode = cmd_get ? TFTP_RRQ : TFTP_WRQ; - - while (1) { - -@@ -203,7 +401,7 @@ - - cp += 2; - -- /* add filename and mode */ -+ /* First packet of file transfer includes file name, mode, and options */ - - if ((cmd_get && (opcode == TFTP_RRQ)) || - (cmd_put && (opcode == TFTP_WRQ))) { -@@ -223,7 +421,7 @@ - } - - if (too_long || ((&buf[tftp_bufsize - 1] - cp) < 6)) { -- bb_error_msg("too long remote-filename"); -+ bb_error_msg("too long: remote filename"); - break; - } - -@@ -238,8 +436,8 @@ - - if (len != TFTP_BLOCKSIZE_DEFAULT) { - -- if ((&buf[tftp_bufsize - 1] - cp) < 15) { -- bb_error_msg("too long remote-filename"); -+ if ((&buf[tftp_bufsize - 1] - cp) < 15) { -+ bb_error_msg("buffer too small for blksize option"); - break; - } - -@@ -249,16 +447,65 @@ - cp += 8; - - cp += snprintf(cp, 6, "%d", len) + 1; -+ } -+#endif -+ -+ -+ -+#ifdef CONFIG_FEATURE_TFTP_MULTICAST -+ -+ if (option->multicast) { -+ if ((&buf[tftp_bufsize - 1] - cp) < 12) { -+ bb_error_msg("buffer too small for multicast option"); -+ break; -+ } -+ -+ /* add "multicast" option */ - -- want_option_ack = 1; -+ memcpy(cp, "multicast\0", 11); -+ cp += 11; -+ -+ option->multicast = 0; /* turn back on when server accepts option */ -+ ack_oack = 1; /* acknowledge OACK */ - } -+ - #endif -+ -+#ifdef CONFIG_FEATURE_TFTP_TIMEOUT -+ -+ if (option->server_timeout != TFTP_TIMEOUT) { -+ if ((&buf[tftp_bufsize - 1] - cp) < 12) { -+ bb_error_msg("buffer too small for timeout option"); -+ break; -+ } -+ -+ /* add "timeout" option */ -+ -+ memcpy(cp, "timeout", 8); -+ cp += 8; -+ -+ cp += snprintf(cp, 4, "%d", option->server_timeout) + 1; -+ } -+#endif -+ - } - - /* add ack and data */ - -- if ((cmd_get && (opcode == TFTP_ACK)) || -- (cmd_put && (opcode == TFTP_DATA))) { -+#ifdef CONFIG_FEATURE_TFTP_MULTICAST -+ else if (option->multicast && opcode == TFTP_ACK) { -+ if (master_client || ack_oack) { -+ int blocknum = bit_lmz(mcblockmap); -+ *((unsigned short *) cp) = htons(blocknum); -+ cp += 2; -+ if (blocknum >= mcmaxblock) -+ finished = 1; -+ dprintf("ack block %d/%d %s\n", blocknum, mcmaxblock, finished? "finished": ""); -+ } -+ } -+#endif -+ else if ((cmd_get && opcode == TFTP_ACK) || -+ (cmd_put && opcode == TFTP_DATA)) { - - *((unsigned short *) cp) = htons(block_nr); - -@@ -275,7 +522,7 @@ - } - - if (len != (tftp_bufsize - 4)) { -- finished++; -+ finished = 1; - } - - cp += len; -@@ -283,82 +530,119 @@ - } - - -- /* send packet */ -+ /* send packet and receive reply */ - - -- timeout = bb_tftp_num_retries; /* re-initialize */ -+ retry = bb_tftp_num_retries; /* re-initialize */ - do { -- -+ int selectrc; - len = cp - buf; - --#ifdef CONFIG_FEATURE_TFTP_DEBUG -- fprintf(stderr, "sending %u bytes\n", len); -- for (cp = buf; cp < &buf[len]; cp++) -- fprintf(stderr, "%02x ", (unsigned char)*cp); -- fprintf(stderr, "\n"); --#endif -- if (sendto(socketfd, buf, len, 0, -- (struct sockaddr *) &sa, sizeof(sa)) < 0) { -- bb_perror_msg("send"); -- len = -1; -- break; -- } -- -+ /* send packet */ -+ if ((len > 2) && (! option->multicast || master_client || ack_oack)) { - -- if (finished && (opcode == TFTP_ACK)) { -- break; -+#ifdef CONFIG_FEATURE_TFTP_DEBUG -+ dprintf("sending %u bytes\n", len); -+ for (cp = buf; cp < &buf[len]; cp++) -+ if (debug) -+ printf("%02x ", *(unsigned char *)cp); -+ if (debug) -+ printf("\n"); -+#endif -+#ifdef CONFIG_FEATURE_TFTP_MULTICAST -+ ack_oack = 0; -+#endif -+ if (sendto(socketfd, buf, len, 0, (struct sockaddr *) &sa, sizeof(sa)) < 0) { -+ bb_perror_msg("send"); -+ len = -1; -+ break; -+ } -+ if (finished && opcode == TFTP_ACK) { -+ break; -+ } - } - -- /* receive packet */ -+ /* receive reply packet */ - - memset(&from, 0, sizeof(from)); - fromlen = sizeof(from); - -- tv.tv_sec = TFTP_TIMEOUT; -+ tv.tv_sec = option->client_timeout; - tv.tv_usec = 0; - - FD_ZERO(&rfds); - FD_SET(socketfd, &rfds); -+ dprintf("set to receive from socketfd (%d)\n", socketfd); -+#ifdef CONFIG_FEATURE_TFTP_MULTICAST -+ if (option->multicast) { -+ FD_SET(mcfd, &rfds); -+ dprintf("set to receive from mcfd (%d)\n", mcfd); -+ } -+#endif - -- switch (select(FD_SETSIZE, &rfds, NULL, NULL, &tv)) { -- case 1: -- len = recvfrom(socketfd, buf, tftp_bufsize, 0, -- (struct sockaddr *) &from, &fromlen); -- -- if (len < 0) { -- bb_perror_msg("recvfrom"); -- break; -+ dprintf("select\n"); -+ selectrc = select(FD_SETSIZE, &rfds, NULL, NULL, &tv); -+ if (selectrc > 0) { -+ /* A packet was received */ -+ if (FD_ISSET(socketfd, &rfds)) { /* Unicast packet */ -+ dprintf("from socketfd\n"); -+ len = recvfrom(socketfd, buf, tftp_bufsize, 0, (struct sockaddr *) &from, &fromlen); -+ -+ if (len < 0) { -+ bb_perror_msg("recvfrom"); -+ } else { -+ if (sa.sin_port == port) { -+ sa.sin_port = from.sin_port; -+ } -+ if (sa.sin_port == from.sin_port) { -+ retry = 0; -+ } else { -+ /* bad packet */ -+ /* discard the packet - treat as timeout */ -+ retry = bb_tftp_num_retries; -+ bb_error_msg("timeout"); -+ } -+ } - } - -- timeout = 0; -- -- if (sa.sin_port == port) { -- sa.sin_port = from.sin_port; -+#ifdef CONFIG_FEATURE_TFTP_MULTICAST -+ else if (option->multicast && FD_ISSET(mcfd, &rfds)) { /* Multicast packet */ -+ dprintf("from mcfd\n"); -+ len = recvfrom(mcfd, buf, tftp_bufsize, 0, (struct sockaddr *) &from, &fromlen); -+ if (len < 0) { -+ bb_perror_msg("multicast recvfrom"); -+ } else { -+ if (mcsa.sin_port == mcport) { -+ mcsa.sin_port = from.sin_port; -+ } -+ if (mcsa.sin_port == from.sin_port) { -+ retry = 0; -+ } else { -+ retry = bb_tftp_num_retries; -+ bb_error_msg("multicast timeout"); -+ } -+ } - } -- if (sa.sin_port == from.sin_port) { -- break; -- } -- -- /* fall-through for bad packets! */ -- /* discard the packet - treat as timeout */ -- timeout = bb_tftp_num_retries; -+#endif - -- case 0: -+ } else if (selectrc == 0) { -+ /* Time out */ -+ dprintf("timeout\n"); - bb_error_msg("timeout"); - -- timeout--; -- if (timeout == 0) { -+ retry--; -+ if (retry == 0) { - len = -1; - bb_error_msg("last timeout"); - } -- break; -- -- default: -+ } else { -+ /* Error condition */ -+ dprintf("error\n"); - bb_perror_msg("select"); - len = -1; - } - -- } while (timeout && (len >= 0)); -+ } while (retry && len >= 0); - - if ((finished) || (len < 0)) { - break; -@@ -370,9 +654,8 @@ - opcode = ntohs(*((unsigned short *) buf)); - tmp = ntohs(*((unsigned short *) &buf[2])); - --#ifdef CONFIG_FEATURE_TFTP_DEBUG -- fprintf(stderr, "received %d bytes: %04x %04x\n", len, opcode, tmp); --#endif -+ dprintf("received %d bytes: %04x %04x\n", len, opcode, tmp); -+ dprintf("master_client=%d\n", master_client); - - if (opcode == TFTP_ERROR) { - char *msg = NULL; -@@ -393,55 +676,116 @@ - break; - } - --#ifdef CONFIG_FEATURE_TFTP_BLOCKSIZE -- if (want_option_ack) { -+#ifdef TFTP_OPTIONS - -- want_option_ack = 0; -+ if (opcode == TFTP_OACK) { - -- if (opcode == TFTP_OACK) { -+ /* server seems to support options */ - -- /* server seems to support options */ -+ char *res; -+ -+ block_nr = 0; /* acknowledge option packet with block number 0 */ -+ opcode = cmd_put ? TFTP_DATA : TFTP_ACK; - -- char *res; - -- res = tftp_option_get(&buf[2], len-2, -- "blksize"); -+#ifdef CONFIG_FEATURE_TFTP_BLOCKSIZE -+ res = tftp_option_get(&buf[2], len-2, "blksize"); - -- if (res) { -- int blksize = atoi(res); -- -- if (tftp_blocksize_check(blksize, -- tftp_bufsize - 4)) { -+ if (res) { -+ int blksize = atoi(res); - -- if (cmd_put) { -- opcode = TFTP_DATA; -- } -- else { -- opcode = TFTP_ACK; -- } --#ifdef CONFIG_FEATURE_TFTP_DEBUG -- fprintf(stderr, "using blksize %u\n", blksize); -+ if (tftp_blocksize_check(blksize, tftp_bufsize - 4)) { -+ dprintf("using blksize %d\n", blksize); -+ tftp_bufsize = blksize + 4; -+ free(buf); -+ buf = xmalloc(tftp_bufsize); -+ } else { -+ bb_error_msg("bad blksize %d", blksize); -+ break; -+ } -+ } - #endif -- tftp_bufsize = blksize + 4; -- block_nr = 0; -- continue; -- } -- } -- /* FIXME: -- * we should send ERROR 8 */ -- bb_error_msg("bad server option"); -- break; -- } - -- bb_error_msg("warning: blksize not supported by server" -- " - reverting to 512"); - -- tftp_bufsize = TFTP_BLOCKSIZE_DEFAULT + 4; -+#ifdef CONFIG_FEATURE_TFTP_MULTICAST -+ res = tftp_option_get(&buf[2], len-2, "multicast"); -+ -+ if (res) { -+ ack_oack = 1; -+ if (tftp_multicast_check(res, &mchostname, &mcport, &master_client)) { -+ struct ip_mreq mreq; -+ struct in_addr mcaddr; -+ -+ dprintf("using multicast\n"); -+ -+ mchost = xgethostbyname(mchostname); -+ if (mchost) { -+ memcpy(&mcaddr, mchost->h_addr, mchost->h_length); -+ if (! IN_MULTICAST(ntohl(mcaddr.s_addr))) { -+ bb_error_msg("bad multicast address: %s", mchostname); -+ break; -+ } -+ } else { -+ bb_error_msg("bad multicast address: %s", mchostname); -+ break; -+ } -+ -+ memset(&mcsa, 0, sizeof(mcsa)); -+ mcsa.sin_family = AF_INET; -+ mcsa.sin_addr.s_addr = htonl(INADDR_ANY); -+ mcsa.sin_port = mcport; -+ -+ bind(mcfd, (struct sockaddr *)&mcsa, sizeof(mcsa)); -+ -+ mreq.imr_multiaddr.s_addr = mcaddr.s_addr; -+ mreq.imr_interface.s_addr = htonl(INADDR_ANY); -+ -+ if (setsockopt(mcfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0) -+ { -+ bb_error_msg("setsockopt"); -+ break; -+ } -+ -+ option->multicast = 1; -+ } else { -+ bb_error_msg("bad multicast option value: %s", res); -+ break; -+ } -+ } -+#endif -+ - } -+ else - #endif - - if (cmd_get && (opcode == TFTP_DATA)) { - -+#ifdef CONFIG_FEATURE_TFTP_MULTICAST -+ if (option->multicast) { -+ int bn = tmp - 1; -+ /* Do I need this block? */ -+ if (! bit_isset(bn, mcblockmap)) { -+ lseek(localfd, bn*(tftp_bufsize-4), SEEK_SET); -+ len = write(localfd, &buf[4], len-4); -+ if (len < 0) { -+ bb_perror_msg(bb_msg_write_error); -+ break; -+ } -+ bit_set(bn, mcblockmap); -+ if (len != (tftp_bufsize-4)) { -+ mcmaxblock = tmp; -+ dprintf("mcmaxblock=%d, (len(%d) != tftp_bufsize-4(%d))\n", mcmaxblock, len, tftp_bufsize-4); -+ } -+ opcode = TFTP_ACK; -+ } -+ /* Do not acknowledge block if I already have a copy of the block. A situation can arise when the server -+ * and client timeout nearly simultaneously. The server retransmits the block at the same time the client -+ * re-requests the block. From then on out, each block is transmitted twice--not a good use of bandwidth. -+ */ -+ } -+ else -+#endif -+ - if (tmp == block_nr) { - - len = write(localfd, &buf[4], len - 4); -@@ -452,15 +796,14 @@ - } - - if (len != (tftp_bufsize - 4)) { -- finished++; -+ finished = 1; - } - - opcode = TFTP_ACK; -- continue; - } - } - -- if (cmd_put && (opcode == TFTP_ACK)) { -+ else if (cmd_put && opcode == TFTP_ACK) { - - if (tmp == (unsigned short)(block_nr - 1)) { - if (finished) { -@@ -468,15 +811,19 @@ - } - - opcode = TFTP_DATA; -- continue; - } - } - } - - #ifdef CONFIG_FEATURE_CLEAN_UP - close(socketfd); -+ free(buf); -+ -+#ifdef CONFIG_FEATURE_TFTP_MULTICAST -+ if (mcblockmap != NULL) -+ free(mcblockmap); -+#endif - -- free(buf); - #endif - - return finished ? EXIT_SUCCESS : EXIT_FAILURE; -@@ -487,13 +834,18 @@ - struct hostent *host = NULL; - char *localfile = NULL; - char *remotefile = NULL; -- int port; -+ unsigned short port; - int cmd = 0; - int fd = -1; - int flags = 0; - int opt; - int result; -- int blocksize = TFTP_BLOCKSIZE_DEFAULT; -+ struct tftp_option option = { -+ .multicast = 0, -+ .blksize = TFTP_BLOCKSIZE_DEFAULT, -+ .client_timeout = TFTP_TIMEOUT, -+ .server_timeout = TFTP_TIMEOUT, -+ }; - - /* figure out what to pass to getopt */ - -@@ -515,13 +867,45 @@ - #define PUT - #endif - -- while ((opt = getopt(argc, argv, BS GET PUT "l:r:")) != -1) { -+#ifdef CONFIG_FEATURE_TFTP_TIMEOUT -+#define TO "T:t:" -+#else -+#define TO -+#endif -+ -+#ifdef CONFIG_FEATURE_TFTP_MULTICAST -+#define MC "m" -+#else -+#define MC -+#endif -+ -+#ifdef CONFIG_FEATURE_TFTP_DEBUG -+#define DB "D" -+#else -+#define DB -+#endif -+ -+ while ((opt = getopt(argc, argv, BS GET PUT TO MC DB "l:r:")) != -1) { - switch (opt) { - #ifdef CONFIG_FEATURE_TFTP_BLOCKSIZE - case 'b': -- blocksize = atoi(optarg); -- if (!tftp_blocksize_check(blocksize, 0)) { -- return EXIT_FAILURE; -+ option.blksize = atoi(optarg); -+ if (!tftp_blocksize_check(option.blksize, 0)) { -+ return EXIT_FAILURE; -+ } -+ break; -+#endif -+#ifdef CONFIG_FEATURE_TFTP_TIMEOUT -+ case 'T': -+ option.client_timeout = atoi(optarg); -+ if (!tftp_timeout_check(option.client_timeout)) { -+ return EXIT_FAILURE; -+ } -+ break; -+ case 't': -+ option.server_timeout = atoi(optarg); -+ if (!tftp_timeout_check(option.server_timeout)) { -+ return EXIT_FAILURE; - } - break; - #endif -@@ -537,18 +921,34 @@ - flags = O_RDONLY; - break; - #endif -+#ifdef CONFIG_FEATURE_TFTP_MULTICAST -+ case 'm': -+ option.multicast = 1; /* receive multicast file */ -+ break; -+#endif -+#ifdef CONFIG_FEATURE_TFTP_DEBUG -+ case 'D': -+ debug = 1; -+ break; -+#endif - case 'l': - localfile = bb_xstrdup(optarg); - break; - case 'r': - remotefile = bb_xstrdup(optarg); - break; -+ default: -+ bb_show_usage(); - } - } - - if ((cmd == 0) || (optind == argc)) { - bb_show_usage(); - } -+ if (cmd == tftp_cmd_put && option.multicast) { -+ fprintf(stderr, "Multicast (-m) invalid option with put (-p) command\n"); -+ exit(EXIT_FAILURE); -+ } - if(localfile && strcmp(localfile, "-") == 0) { - fd = fileno((cmd==tftp_cmd_get)? stdout : stdin); - } -@@ -566,14 +966,12 @@ - host = xgethostbyname(argv[optind]); - port = bb_lookup_port(argv[optind + 1], "udp", 69); - --#ifdef CONFIG_FEATURE_TFTP_DEBUG -- fprintf(stderr, "using server \"%s\", remotefile \"%s\", " -+ dprintf("using server \"%s\", remotefile \"%s\", " - "localfile \"%s\".\n", - inet_ntoa(*((struct in_addr *) host->h_addr)), - remotefile, localfile); --#endif - -- result = tftp(cmd, host, remotefile, fd, port, blocksize); -+ result = tftp(cmd, host, remotefile, fd, port, &option); - - #ifdef CONFIG_FEATURE_CLEAN_UP - if (!(fd == STDOUT_FILENO || fd == STDIN_FILENO)) { -@@ -582,3 +980,18 @@ - #endif - return(result); - } -+ -+ -+#ifdef CONFIG_FEATURE_TFTP_DEBUG -+ -+#include <sys/time.h> -+ -+static void -+printtime(void) -+{ -+ struct timeval tv; -+ gettimeofday(&tv, NULL); -+ printf("%11lu.%06lu ", tv.tv_sec, tv.tv_usec); -+} -+ -+#endif |