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 + Added multicast option (rfc2090) and timeout option (rfc2349) to tftp. + Tim Riker 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 */ /* */ /* 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< 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("write"); + 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 + +static void +printtime(void) +{ + struct timeval tv; + gettimeofday(&tv, NULL); + printf("%11lu.%06lu ", tv.tv_sec, tv.tv_usec); +} + +#endif