2 ===================================================================
3 RCS file: /var/cvs/busybox/AUTHORS,v
4 retrieving revision 1.40
6 --- a/AUTHORS 9 Oct 2003 21:19:21 -0000 1.40
7 +++ b/AUTHORS 5 Mar 2004 15:45:47 -0000
9 Original author of BusyBox in 1995, 1996. Some of his code can
10 still be found hiding here and there...
12 +John Powers <jpp@ti.com>
13 + Added multicast option (rfc2090) and timeout option (rfc2349) to tftp.
15 Tim Riker <Tim@Rikers.org>
16 bug fixes, member of fan club
18 Index: include/usage.h
19 ===================================================================
20 RCS file: /var/cvs/busybox/include/usage.h,v
21 retrieving revision 1.191
22 diff -u -r1.191 usage.h
23 --- a/include/usage.h 25 Feb 2004 10:35:55 -0000 1.191
24 +++ b/include/usage.h 5 Mar 2004 15:45:59 -0000
25 @@ -2492,6 +2492,21 @@
27 #define USAGE_TFTP_BS(a)
29 +#ifdef CONFIG_FEATURE_TFTP_TIMEOUT
30 + #define USAGE_TFTP_TIMEOUT(a) a
32 + #define USAGE_TFTP_TIMEOUT(a)
34 +#ifdef CONFIG_FEATURE_TFTP_MULTICAST
35 + #define USAGE_TFTP_MULTICAST(a) a
37 + #define USAGE_TFTP_MULTICAST(a)
39 +#ifdef CONFIG_FEATURE_TFTP_DEBUG
40 + #define USAGE_TFTP_DEBUG(a) a
42 + #define USAGE_TFTP_DEBUG(a)
45 #define tftp_trivial_usage \
46 "[OPTION]... HOST [PORT]"
47 @@ -2508,6 +2523,16 @@
50 "\t-b SIZE\tTransfer blocks of SIZE octets.\n" \
52 + USAGE_TFTP_TIMEOUT( \
53 + "\t-T SEC\tClient timeout SEC seconds (default: 5).\n" \
54 + "\t-t SEC\tServer timeout SEC seconds\n" \
56 + USAGE_TFTP_MULTICAST( \
57 + "\t-m\tMulticast get file.\n" \
60 + "\t-D\tPrint debug messages.\n" \
62 #define time_trivial_usage \
63 "[OPTION]... COMMAND [ARGS...]"
64 Index: networking/Config.in
65 ===================================================================
66 RCS file: /var/cvs/busybox/networking/Config.in,v
67 retrieving revision 1.27
68 diff -u -r1.27 Config.in
69 --- a/networking/Config.in 22 Feb 2004 12:25:47 -0000 1.27
70 +++ b/networking/Config.in 5 Mar 2004 15:45:59 -0000
72 Add support for the GET command within the TFTP client. This allows
73 a client to retrieve a file from a TFTP server.
75 +config CONFIG_FEATURE_TFTP_MULTICAST
76 + bool " Enable \"multicast\" option"
78 + depends on CONFIG_FEATURE_TFTP_GET
80 + Allow the client to receive multicast file transfers.
82 config CONFIG_FEATURE_TFTP_PUT
83 bool " Enable \"put\" command"
86 a client to transfer a file to a TFTP server.
88 config CONFIG_FEATURE_TFTP_BLOCKSIZE
89 - bool " Enable \"blocksize\" command"
90 + bool " Enable \"blksize\" option"
92 depends on CONFIG_TFTP
94 Allow the client to specify the desired block size for transfers.
96 +config CONFIG_FEATURE_TFTP_TIMEOUT
97 + bool " Enable \"timeout\" option"
99 + depends on CONFIG_TFTP
101 + Allow the client to negotiate timeout option with server.
103 config CONFIG_FEATURE_TFTP_DEBUG
106 Index: networking/tftp.c
107 ===================================================================
108 RCS file: /var/cvs/busybox/networking/tftp.c,v
109 retrieving revision 1.25
110 diff -u -r1.25 tftp.c
111 --- a/networking/tftp.c 5 Mar 2004 13:04:39 -0000 1.25
112 +++ b/networking/tftp.c 5 Mar 2004 15:46:00 -0000
114 +/* vi: set sw=4 ts=4: */
115 /* ------------------------------------------------------------------------- */
117 +/* Copyright (c) 2003, 2004 Texas Instruments */
119 +/* This package is free software; you can redistribute it and/or */
120 +/* modify it under the terms of the license found in the file */
121 +/* named COPYING that should have accompanied this file. */
123 +/* THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR */
124 +/* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED */
125 +/* WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. */
127 /* A simple tftp client for busybox. */
128 /* Tries to follow RFC1350. */
129 /* Only "octet" mode supported. */
130 /* Optional blocksize negotiation (RFC2347 + RFC2348) */
132 +/* New features added at Texas Instruments, October 2003 */
133 +/* Author: John Powers */
134 +/* Multicast option: rfc2090 */
135 +/* Timeout option: rfc2349 */
137 /* Copyright (C) 2001 Magnus Damm <damm@opensource.se> */
139 /* Parts of the code based on: */
144 +#if defined(CONFIG_FEATURE_TFTP_BLOCKSIZE) || defined(CONFIG_FEATURE_TFTP_MULTICAST) || defined(CONFIG_FEATURE_TFTP_TIMEOUT)
145 + #define TFTP_OPTIONS
148 //#define CONFIG_FEATURE_TFTP_DEBUG
150 +#ifdef CONFIG_FEATURE_TFTP_DEBUG
151 + static void printtime(void);
152 + #define dprintf(fmt...) if (debug) {printtime(); printf(fmt);}
155 + #define dprintf(fmt...)
158 #define TFTP_BLOCKSIZE_DEFAULT 512 /* according to RFC 1350, don't change */
159 #define TFTP_TIMEOUT 5 /* seconds */
162 "Illegal TFTP operation",
163 "Unknown transfer ID",
164 "File already exists",
168 + "Unsupported option",
172 const int tftp_cmd_get = 1;
173 const int tftp_cmd_put = 2;
176 +struct tftp_option {
179 + int client_timeout;
180 + int server_timeout;
184 #ifdef CONFIG_FEATURE_TFTP_BLOCKSIZE
186 static int tftp_blocksize_check(int blocksize, int bufsize)
187 @@ -93,16 +132,158 @@
193 +#ifdef CONFIG_FEATURE_TFTP_TIMEOUT
196 +tftp_timeout_check(int timeout)
198 + /* Check if timeout seconds is valid:
199 + * RFC2349 says between 1 and 255.
202 + if (timeout < 1 || timeout > 255) {
203 + bb_error_msg("bad timeout value");
211 +#ifdef CONFIG_FEATURE_TFTP_MULTICAST
213 +tftp_multicast_check(const char *opt, char **phost, unsigned short *pport, int *pactive)
215 + /* Option string contains comma delimited addr,port,active.
216 + * addr = multicast IP address
217 + * port = port number
218 + * active = 1 if active client
219 + * 0 if passive client
221 + * Addr and port will be empty fields when the server notifies a
222 + * passive client that it is now the active client.
224 + * The host address string must be freed by the caller. Neither host
225 + * nor port will be set/changed if the input fields are empty.
227 + * If any tokenization errors occur in the opt string, the host
228 + * address string is automatically freed.
230 + * Return 0 if any tokenization error, 1 if all parameters are good.
233 + char *token = NULL;
234 + char *parse_buf = NULL;
235 + char *tokenv = NULL;
240 + parse_buf = bb_xstrdup(opt);
242 + dprintf("multicast option=%s\n", opt);
245 + if ((token = strtok_r(parse_buf, ",", &tokenv)) == NULL) {
246 + dprintf("tftp_multicast_check: cannot parse IP address from %s\n", parse_buf);
250 + if (strlen(token) > 0)
251 + *phost = host = bb_xstrdup(token);
254 + if ((token = strtok_r(NULL, ",", &tokenv)) == NULL) {
255 + dprintf("tftp_multicast_check: cannot parse port number from %s\n", tokenv);
258 + if (strlen(token) > 0) {
259 + port = atoi(token);
260 + if (port < 0 || port > 0xFFFF) {
261 + dprintf("tftp_multicast_check: bad port number (%d)\n", port);
264 + *pport = htons(port);
267 + /* Active/passive */
268 + if ((token = strtok_r(NULL, ",", &tokenv)) == NULL) {
269 + dprintf("tftp_multicast_check: cannot parse active/passive from %s\n", tokenv);
272 + active = atoi(token);
273 + if (active != 0 && active != 1) {
274 + dprintf("tftp_multicast_check: bad active/passive flag (%d)\n", active);
291 +#define VECTOR_QUANTUM_WIDTH 8
292 +#define VECTOR_QUANTUM_ALL_ONES ((1<<VECTOR_QUANTUM_WIDTH)-1)
295 +bit_set(int bit, unsigned char *vector)
297 + int offset = bit / VECTOR_QUANTUM_WIDTH;
298 + int mask = 1 << (bit % VECTOR_QUANTUM_WIDTH);
299 + vector[offset] |= mask;
303 +bit_isset(int bit, const unsigned char *vector)
305 + int offset = bit / VECTOR_QUANTUM_WIDTH;
306 + int mask = 1 << (bit % VECTOR_QUANTUM_WIDTH);
307 + return vector[offset] & mask ? 1 : 0;
311 +bit_lmz(const unsigned char *vector)
313 + /* Return number of left-most zero in bit vector */
314 + const unsigned char *vp = vector;
316 + unsigned char velem;
318 + while (*vp == VECTOR_QUANTUM_ALL_ONES)
321 + for (i = 0; i < VECTOR_QUANTUM_WIDTH; i++) {
322 + if ((velem & (1 << i)) == 0)
325 + dprintf("bit_lmz: block=%d\n", (vp - vector)*VECTOR_QUANTUM_WIDTH + i);
326 + return (vp - vector)*VECTOR_QUANTUM_WIDTH + i;
335 static char *tftp_option_get(char *buf, int len, char *option)
345 /* Make sure the options are terminated correctly */
347 for (k = 0; k < len; k++) {
348 if (buf[k] == '\0') {
351 if (strcasecmp(buf, option) == 0) {
365 static inline int tftp(const int cmd, const struct hostent *host,
366 - const char *remotefile, int localfd, const unsigned short port, int tftp_bufsize)
367 + const char *remotefile, int localfd, const unsigned short port,
368 + struct tftp_option *option)
370 const int cmd_get = cmd & tftp_cmd_get;
371 const int cmd_put = cmd & tftp_cmd_put;
372 @@ -155,18 +336,29 @@
376 - int timeout = bb_tftp_num_retries;
377 + int retry = bb_tftp_num_retries;
378 unsigned short block_nr = 1;
380 -#ifdef CONFIG_FEATURE_TFTP_BLOCKSIZE
381 - int want_option_ack = 0;
382 +#ifdef CONFIG_FEATURE_TFTP_MULTICAST
383 + struct hostent *mchost;
384 + struct sockaddr_in mcsa;
386 + unsigned short mcport;
387 + unsigned char *mcblockmap = NULL;
388 + int master_client = 1;
390 + int mcmaxblock = 0x10000;
393 + #define master_client 1
397 /* Can't use RESERVE_CONFIG_BUFFER here since the allocation
398 * size varies meaning BUFFERS_GO_ON_STACK would fail */
399 - char *buf=xmalloc(tftp_bufsize + 4);
400 + char *buf=xmalloc(option->blksize + 4);
403 + int tftp_bufsize = option->blksize + 4;
405 if ((socketfd = socket(PF_INET, SOCK_DGRAM, 0)) < 0) {
406 bb_perror_msg("socket");
407 @@ -183,15 +375,21 @@
408 memcpy(&sa.sin_addr, (struct in_addr *) host->h_addr,
409 sizeof(sa.sin_addr));
415 +#ifdef CONFIG_FEATURE_TFTP_MULTICAST
416 + if (option->multicast) {
417 + const int bmsize = 0x10000 / VECTOR_QUANTUM_WIDTH;
418 + if ((mcfd = socket(PF_INET, SOCK_DGRAM, 0)) < 0) {
419 + bb_perror_msg("multicast socket");
420 + return EXIT_FAILURE;
422 + mcblockmap = xmalloc(bmsize+1);
423 + memset(mcblockmap, 0, bmsize+1);
432 + opcode = cmd_get ? TFTP_RRQ : TFTP_WRQ;
440 - /* add filename and mode */
441 + /* First packet of file transfer includes file name, mode, and options */
443 if ((cmd_get && (opcode == TFTP_RRQ)) ||
444 (cmd_put && (opcode == TFTP_WRQ))) {
448 if (too_long || ((&buf[tftp_bufsize - 1] - cp) < 6)) {
449 - bb_error_msg("too long remote-filename");
450 + bb_error_msg("too long: remote filename");
456 if (len != TFTP_BLOCKSIZE_DEFAULT) {
458 - if ((&buf[tftp_bufsize - 1] - cp) < 15) {
459 - bb_error_msg("too long remote-filename");
460 + if ((&buf[tftp_bufsize - 1] - cp) < 15) {
461 + bb_error_msg("buffer too small for blksize option");
465 @@ -249,16 +447,65 @@
468 cp += snprintf(cp, 6, "%d", len) + 1;
474 +#ifdef CONFIG_FEATURE_TFTP_MULTICAST
476 + if (option->multicast) {
477 + if ((&buf[tftp_bufsize - 1] - cp) < 12) {
478 + bb_error_msg("buffer too small for multicast option");
482 + /* add "multicast" option */
484 - want_option_ack = 1;
485 + memcpy(cp, "multicast\0", 11);
488 + option->multicast = 0; /* turn back on when server accepts option */
489 + ack_oack = 1; /* acknowledge OACK */
494 +#ifdef CONFIG_FEATURE_TFTP_TIMEOUT
496 + if (option->server_timeout != TFTP_TIMEOUT) {
497 + if ((&buf[tftp_bufsize - 1] - cp) < 12) {
498 + bb_error_msg("buffer too small for timeout option");
502 + /* add "timeout" option */
504 + memcpy(cp, "timeout", 8);
507 + cp += snprintf(cp, 4, "%d", option->server_timeout) + 1;
513 /* add ack and data */
515 - if ((cmd_get && (opcode == TFTP_ACK)) ||
516 - (cmd_put && (opcode == TFTP_DATA))) {
517 +#ifdef CONFIG_FEATURE_TFTP_MULTICAST
518 + else if (option->multicast && opcode == TFTP_ACK) {
519 + if (master_client || ack_oack) {
520 + int blocknum = bit_lmz(mcblockmap);
521 + *((unsigned short *) cp) = htons(blocknum);
523 + if (blocknum >= mcmaxblock)
525 + dprintf("ack block %d/%d %s\n", blocknum, mcmaxblock, finished? "finished": "");
529 + else if ((cmd_get && opcode == TFTP_ACK) ||
530 + (cmd_put && opcode == TFTP_DATA)) {
532 *((unsigned short *) cp) = htons(block_nr);
537 if (len != (tftp_bufsize - 4)) {
543 @@ -283,82 +530,119 @@
548 + /* send packet and receive reply */
551 - timeout = bb_tftp_num_retries; /* re-initialize */
552 + retry = bb_tftp_num_retries; /* re-initialize */
558 -#ifdef CONFIG_FEATURE_TFTP_DEBUG
559 - fprintf(stderr, "sending %u bytes\n", len);
560 - for (cp = buf; cp < &buf[len]; cp++)
561 - fprintf(stderr, "%02x ", (unsigned char)*cp);
562 - fprintf(stderr, "\n");
564 - if (sendto(socketfd, buf, len, 0,
565 - (struct sockaddr *) &sa, sizeof(sa)) < 0) {
566 - bb_perror_msg("send");
572 + if ((len > 2) && (! option->multicast || master_client || ack_oack)) {
574 - if (finished && (opcode == TFTP_ACK)) {
576 +#ifdef CONFIG_FEATURE_TFTP_DEBUG
577 + dprintf("sending %u bytes\n", len);
578 + for (cp = buf; cp < &buf[len]; cp++)
580 + printf("%02x ", *(unsigned char *)cp);
584 +#ifdef CONFIG_FEATURE_TFTP_MULTICAST
587 + if (sendto(socketfd, buf, len, 0, (struct sockaddr *) &sa, sizeof(sa)) < 0) {
588 + bb_perror_msg("send");
592 + if (finished && opcode == TFTP_ACK) {
597 - /* receive packet */
598 + /* receive reply packet */
600 memset(&from, 0, sizeof(from));
601 fromlen = sizeof(from);
603 - tv.tv_sec = TFTP_TIMEOUT;
604 + tv.tv_sec = option->client_timeout;
608 FD_SET(socketfd, &rfds);
609 + dprintf("set to receive from socketfd (%d)\n", socketfd);
610 +#ifdef CONFIG_FEATURE_TFTP_MULTICAST
611 + if (option->multicast) {
612 + FD_SET(mcfd, &rfds);
613 + dprintf("set to receive from mcfd (%d)\n", mcfd);
617 - switch (select(FD_SETSIZE, &rfds, NULL, NULL, &tv)) {
619 - len = recvfrom(socketfd, buf, tftp_bufsize, 0,
620 - (struct sockaddr *) &from, &fromlen);
623 - bb_perror_msg("recvfrom");
625 + dprintf("select\n");
626 + selectrc = select(FD_SETSIZE, &rfds, NULL, NULL, &tv);
627 + if (selectrc > 0) {
628 + /* A packet was received */
629 + if (FD_ISSET(socketfd, &rfds)) { /* Unicast packet */
630 + dprintf("from socketfd\n");
631 + len = recvfrom(socketfd, buf, tftp_bufsize, 0, (struct sockaddr *) &from, &fromlen);
634 + bb_perror_msg("recvfrom");
636 + if (sa.sin_port == port) {
637 + sa.sin_port = from.sin_port;
639 + if (sa.sin_port == from.sin_port) {
643 + /* discard the packet - treat as timeout */
644 + retry = bb_tftp_num_retries;
645 + bb_error_msg("timeout");
652 - if (sa.sin_port == port) {
653 - sa.sin_port = from.sin_port;
654 +#ifdef CONFIG_FEATURE_TFTP_MULTICAST
655 + else if (option->multicast && FD_ISSET(mcfd, &rfds)) { /* Multicast packet */
656 + dprintf("from mcfd\n");
657 + len = recvfrom(mcfd, buf, tftp_bufsize, 0, (struct sockaddr *) &from, &fromlen);
659 + bb_perror_msg("multicast recvfrom");
661 + if (mcsa.sin_port == mcport) {
662 + mcsa.sin_port = from.sin_port;
664 + if (mcsa.sin_port == from.sin_port) {
667 + retry = bb_tftp_num_retries;
668 + bb_error_msg("multicast timeout");
672 - if (sa.sin_port == from.sin_port) {
676 - /* fall-through for bad packets! */
677 - /* discard the packet - treat as timeout */
678 - timeout = bb_tftp_num_retries;
682 + } else if (selectrc == 0) {
684 + dprintf("timeout\n");
685 bb_error_msg("timeout");
688 - if (timeout == 0) {
692 bb_error_msg("last timeout");
698 + /* Error condition */
699 + dprintf("error\n");
700 bb_perror_msg("select");
704 - } while (timeout && (len >= 0));
705 + } while (retry && len >= 0);
707 if ((finished) || (len < 0)) {
710 opcode = ntohs(*((unsigned short *) buf));
711 tmp = ntohs(*((unsigned short *) &buf[2]));
713 -#ifdef CONFIG_FEATURE_TFTP_DEBUG
714 - fprintf(stderr, "received %d bytes: %04x %04x\n", len, opcode, tmp);
716 + dprintf("received %d bytes: %04x %04x\n", len, opcode, tmp);
717 + dprintf("master_client=%d\n", master_client);
719 if (opcode == TFTP_ERROR) {
721 @@ -393,55 +676,116 @@
725 -#ifdef CONFIG_FEATURE_TFTP_BLOCKSIZE
726 - if (want_option_ack) {
729 - want_option_ack = 0;
730 + if (opcode == TFTP_OACK) {
732 - if (opcode == TFTP_OACK) {
733 + /* server seems to support options */
735 - /* server seems to support options */
738 + block_nr = 0; /* acknowledge option packet with block number 0 */
739 + opcode = cmd_put ? TFTP_DATA : TFTP_ACK;
743 - res = tftp_option_get(&buf[2], len-2,
745 +#ifdef CONFIG_FEATURE_TFTP_BLOCKSIZE
746 + res = tftp_option_get(&buf[2], len-2, "blksize");
749 - int blksize = atoi(res);
751 - if (tftp_blocksize_check(blksize,
752 - tftp_bufsize - 4)) {
754 + int blksize = atoi(res);
757 - opcode = TFTP_DATA;
762 -#ifdef CONFIG_FEATURE_TFTP_DEBUG
763 - fprintf(stderr, "using blksize %u\n", blksize);
764 + if (tftp_blocksize_check(blksize, tftp_bufsize - 4)) {
765 + dprintf("using blksize %d\n", blksize);
766 + tftp_bufsize = blksize + 4;
768 + buf = xmalloc(tftp_bufsize);
770 + bb_error_msg("bad blksize %d", blksize);
775 - tftp_bufsize = blksize + 4;
781 - * we should send ERROR 8 */
782 - bb_error_msg("bad server option");
786 - bb_error_msg("warning: blksize not supported by server"
787 - " - reverting to 512");
789 - tftp_bufsize = TFTP_BLOCKSIZE_DEFAULT + 4;
790 +#ifdef CONFIG_FEATURE_TFTP_MULTICAST
791 + res = tftp_option_get(&buf[2], len-2, "multicast");
795 + if (tftp_multicast_check(res, &mchostname, &mcport, &master_client)) {
796 + struct ip_mreq mreq;
797 + struct in_addr mcaddr;
799 + dprintf("using multicast\n");
801 + mchost = xgethostbyname(mchostname);
803 + memcpy(&mcaddr, mchost->h_addr, mchost->h_length);
804 + if (! IN_MULTICAST(ntohl(mcaddr.s_addr))) {
805 + bb_error_msg("bad multicast address: %s", mchostname);
809 + bb_error_msg("bad multicast address: %s", mchostname);
813 + memset(&mcsa, 0, sizeof(mcsa));
814 + mcsa.sin_family = AF_INET;
815 + mcsa.sin_addr.s_addr = htonl(INADDR_ANY);
816 + mcsa.sin_port = mcport;
818 + bind(mcfd, (struct sockaddr *)&mcsa, sizeof(mcsa));
820 + mreq.imr_multiaddr.s_addr = mcaddr.s_addr;
821 + mreq.imr_interface.s_addr = htonl(INADDR_ANY);
823 + if (setsockopt(mcfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0)
825 + bb_error_msg("setsockopt");
829 + option->multicast = 1;
831 + bb_error_msg("bad multicast option value: %s", res);
841 if (cmd_get && (opcode == TFTP_DATA)) {
843 +#ifdef CONFIG_FEATURE_TFTP_MULTICAST
844 + if (option->multicast) {
846 + /* Do I need this block? */
847 + if (! bit_isset(bn, mcblockmap)) {
848 + lseek(localfd, bn*(tftp_bufsize-4), SEEK_SET);
849 + len = write(localfd, &buf[4], len-4);
851 + bb_perror_msg(bb_msg_write_error);
854 + bit_set(bn, mcblockmap);
855 + if (len != (tftp_bufsize-4)) {
857 + dprintf("mcmaxblock=%d, (len(%d) != tftp_bufsize-4(%d))\n", mcmaxblock, len, tftp_bufsize-4);
861 + /* Do not acknowledge block if I already have a copy of the block. A situation can arise when the server
862 + * and client timeout nearly simultaneously. The server retransmits the block at the same time the client
863 + * re-requests the block. From then on out, each block is transmitted twice--not a good use of bandwidth.
869 if (tmp == block_nr) {
871 len = write(localfd, &buf[4], len - 4);
872 @@ -452,15 +796,14 @@
875 if (len != (tftp_bufsize - 4)) {
885 - if (cmd_put && (opcode == TFTP_ACK)) {
886 + else if (cmd_put && opcode == TFTP_ACK) {
888 if (tmp == (unsigned short)(block_nr - 1)) {
890 @@ -468,15 +811,19 @@
899 #ifdef CONFIG_FEATURE_CLEAN_UP
903 +#ifdef CONFIG_FEATURE_TFTP_MULTICAST
904 + if (mcblockmap != NULL)
911 return finished ? EXIT_SUCCESS : EXIT_FAILURE;
912 @@ -487,13 +834,18 @@
913 struct hostent *host = NULL;
914 char *localfile = NULL;
915 char *remotefile = NULL;
917 + unsigned short port;
923 - int blocksize = TFTP_BLOCKSIZE_DEFAULT;
924 + struct tftp_option option = {
926 + .blksize = TFTP_BLOCKSIZE_DEFAULT,
927 + .client_timeout = TFTP_TIMEOUT,
928 + .server_timeout = TFTP_TIMEOUT,
931 /* figure out what to pass to getopt */
933 @@ -515,13 +867,45 @@
937 - while ((opt = getopt(argc, argv, BS GET PUT "l:r:")) != -1) {
938 +#ifdef CONFIG_FEATURE_TFTP_TIMEOUT
944 +#ifdef CONFIG_FEATURE_TFTP_MULTICAST
950 +#ifdef CONFIG_FEATURE_TFTP_DEBUG
956 + while ((opt = getopt(argc, argv, BS GET PUT TO MC DB "l:r:")) != -1) {
958 #ifdef CONFIG_FEATURE_TFTP_BLOCKSIZE
960 - blocksize = atoi(optarg);
961 - if (!tftp_blocksize_check(blocksize, 0)) {
962 - return EXIT_FAILURE;
963 + option.blksize = atoi(optarg);
964 + if (!tftp_blocksize_check(option.blksize, 0)) {
965 + return EXIT_FAILURE;
969 +#ifdef CONFIG_FEATURE_TFTP_TIMEOUT
971 + option.client_timeout = atoi(optarg);
972 + if (!tftp_timeout_check(option.client_timeout)) {
973 + return EXIT_FAILURE;
977 + option.server_timeout = atoi(optarg);
978 + if (!tftp_timeout_check(option.server_timeout)) {
979 + return EXIT_FAILURE;
983 @@ -537,18 +921,34 @@
987 +#ifdef CONFIG_FEATURE_TFTP_MULTICAST
989 + option.multicast = 1; /* receive multicast file */
992 +#ifdef CONFIG_FEATURE_TFTP_DEBUG
998 localfile = bb_xstrdup(optarg);
1001 remotefile = bb_xstrdup(optarg);
1008 if ((cmd == 0) || (optind == argc)) {
1011 + if (cmd == tftp_cmd_put && option.multicast) {
1012 + fprintf(stderr, "Multicast (-m) invalid option with put (-p) command\n");
1013 + exit(EXIT_FAILURE);
1015 if(localfile && strcmp(localfile, "-") == 0) {
1016 fd = fileno((cmd==tftp_cmd_get)? stdout : stdin);
1018 @@ -566,14 +966,12 @@
1019 host = xgethostbyname(argv[optind]);
1020 port = bb_lookup_port(argv[optind + 1], "udp", 69);
1022 -#ifdef CONFIG_FEATURE_TFTP_DEBUG
1023 - fprintf(stderr, "using server \"%s\", remotefile \"%s\", "
1024 + dprintf("using server \"%s\", remotefile \"%s\", "
1025 "localfile \"%s\".\n",
1026 inet_ntoa(*((struct in_addr *) host->h_addr)),
1027 remotefile, localfile);
1030 - result = tftp(cmd, host, remotefile, fd, port, blocksize);
1031 + result = tftp(cmd, host, remotefile, fd, port, &option);
1033 #ifdef CONFIG_FEATURE_CLEAN_UP
1034 if (!(fd == STDOUT_FILENO || fd == STDIN_FILENO)) {
1035 @@ -582,3 +980,18 @@
1041 +#ifdef CONFIG_FEATURE_TFTP_DEBUG
1043 +#include <sys/time.h>
1048 + struct timeval tv;
1049 + gettimeofday(&tv, NULL);
1050 + printf("%11lu.%06lu ", tv.tv_sec, tv.tv_usec);