move the ifdef to after libbb.h include, so it can do some good.
[people/mcb30/busybox.git] / patches / tftp_timeout_multicast.diff
1 Index: AUTHORS
2 ===================================================================
3 RCS file: /var/cvs/busybox/AUTHORS,v
4 retrieving revision 1.40
5 diff -u -r1.40 AUTHORS
6 --- a/AUTHORS   9 Oct 2003 21:19:21 -0000       1.40
7 +++ b/AUTHORS   5 Mar 2004 15:45:47 -0000
8 @@ -92,6 +92,9 @@
9      Original author of BusyBox in 1995, 1996. Some of his code can
10      still be found hiding here and there...
11
12 +John Powers <jpp@ti.com>
13 +    Added multicast option (rfc2090) and timeout option (rfc2349) to tftp.
14 +
15  Tim Riker <Tim@Rikers.org>
16      bug fixes, member of fan club
17
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 @@
26  #else
27    #define USAGE_TFTP_BS(a)
28  #endif
29 +#ifdef CONFIG_FEATURE_TFTP_TIMEOUT
30 +  #define USAGE_TFTP_TIMEOUT(a) a
31 +#else
32 +  #define USAGE_TFTP_TIMEOUT(a)
33 +#endif
34 +#ifdef CONFIG_FEATURE_TFTP_MULTICAST
35 +  #define USAGE_TFTP_MULTICAST(a) a
36 +#else
37 +  #define USAGE_TFTP_MULTICAST(a)
38 +#endif
39 +#ifdef CONFIG_FEATURE_TFTP_DEBUG
40 +  #define USAGE_TFTP_DEBUG(a) a
41 +#else
42 +  #define USAGE_TFTP_DEBUG(a)
43 +#endif
44
45  #define tftp_trivial_usage \
46         "[OPTION]... HOST [PORT]"
47 @@ -2508,6 +2523,16 @@
48         ) \
49         USAGE_TFTP_BS( \
50         "\t-b SIZE\tTransfer blocks of SIZE octets.\n" \
51 +       ) \
52 +       USAGE_TFTP_TIMEOUT( \
53 +       "\t-T SEC\tClient timeout SEC seconds (default: 5).\n" \
54 +       "\t-t SEC\tServer timeout SEC seconds\n" \
55 +       ) \
56 +       USAGE_TFTP_MULTICAST( \
57 +       "\t-m\tMulticast get file.\n" \
58 +       ) \
59 +       USAGE_TFTP_DEBUG( \
60 +       "\t-D\tPrint debug messages.\n" \
61         )
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
71 @@ -522,6 +522,13 @@
72           Add support for the GET command within the TFTP client.  This allows
73           a client to retrieve a file from a TFTP server.
74
75 +config CONFIG_FEATURE_TFTP_MULTICAST
76 +       bool "  Enable \"multicast\" option"
77 +       default n
78 +       depends on CONFIG_FEATURE_TFTP_GET
79 +       help
80 +         Allow the client to receive multicast file transfers.
81 +
82  config CONFIG_FEATURE_TFTP_PUT
83         bool "  Enable \"put\" command"
84         default y
85 @@ -531,12 +538,19 @@
86           a client to transfer a file to a TFTP server.
87
88  config CONFIG_FEATURE_TFTP_BLOCKSIZE
89 -       bool "  Enable \"blocksize\" command"
90 +       bool "  Enable \"blksize\" option"
91         default n
92         depends on CONFIG_TFTP
93         help
94           Allow the client to specify the desired block size for transfers.
95
96 +config CONFIG_FEATURE_TFTP_TIMEOUT
97 +       bool "  Enable \"timeout\" option"
98 +       default n
99 +       depends on CONFIG_TFTP
100 +       help
101 +         Allow the client to negotiate timeout option with server.
102 +
103  config CONFIG_FEATURE_TFTP_DEBUG
104         bool "  Enable debug"
105         default n
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
113 @@ -1,11 +1,26 @@
114 +/* vi: set sw=4 ts=4: */
115  /* ------------------------------------------------------------------------- */
116  /* tftp.c                                                                    */
117 +/* Copyright (c) 2003, 2004 Texas Instruments                                */
118 +/*                                                                           */
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.                     */
122 +/*                                                                           */
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.       */
126  /*                                                                           */
127  /* A simple tftp client for busybox.                                         */
128  /* Tries to follow RFC1350.                                                  */
129  /* Only "octet" mode supported.                                              */
130  /* Optional blocksize negotiation (RFC2347 + RFC2348)                        */
131  /*                                                                           */
132 +/* New features added at Texas Instruments, October 2003                     */
133 +/* Author: John Powers                                                       */
134 +/* Multicast option: rfc2090                                                 */
135 +/* Timeout option: rfc2349                                                   */
136 +/*                                                                           */
137  /* Copyright (C) 2001 Magnus Damm <damm@opensource.se>                       */
138  /*                                                                           */
139  /* Parts of the code based on:                                               */
140 @@ -46,8 +61,20 @@
141
142  #include "busybox.h"
143
144 +#if defined(CONFIG_FEATURE_TFTP_BLOCKSIZE) || defined(CONFIG_FEATURE_TFTP_MULTICAST) || defined(CONFIG_FEATURE_TFTP_TIMEOUT)
145 +  #define TFTP_OPTIONS
146 +#endif
147 +
148  //#define CONFIG_FEATURE_TFTP_DEBUG
149
150 +#ifdef CONFIG_FEATURE_TFTP_DEBUG
151 +       static void printtime(void);
152 +       #define dprintf(fmt...) if (debug) {printtime(); printf(fmt);}
153 +       int debug = 0;
154 +#else
155 +       #define dprintf(fmt...)
156 +#endif
157 +
158  #define TFTP_BLOCKSIZE_DEFAULT 512 /* according to RFC 1350, don't change */
159  #define TFTP_TIMEOUT 5             /* seconds */
160
161 @@ -68,12 +95,24 @@
162         "Illegal TFTP operation",
163         "Unknown transfer ID",
164         "File already exists",
165 -       "No such user"
166 +       "No such user",
167 +#ifdef TFTP_OPTIONS
168 +       "Unsupported option",
169 +#endif
170  };
171
172  const int tftp_cmd_get = 1;
173  const int tftp_cmd_put = 2;
174
175 +
176 +struct tftp_option {
177 +       int multicast;
178 +       int blksize;
179 +       int client_timeout;
180 +       int server_timeout;
181 +};
182 +
183 +
184  #ifdef CONFIG_FEATURE_TFTP_BLOCKSIZE
185
186  static int tftp_blocksize_check(int blocksize, int bufsize)
187 @@ -93,16 +132,158 @@
188         return blocksize;
189  }
190
191 +#endif
192 +
193 +#ifdef CONFIG_FEATURE_TFTP_TIMEOUT
194 +
195 +static int
196 +tftp_timeout_check(int timeout)
197 +{
198 +       /* Check if timeout seconds is valid:
199 +        * RFC2349 says between 1 and 255.
200 +        */
201 +
202 +       if (timeout < 1 || timeout > 255) {
203 +               bb_error_msg("bad timeout value");
204 +               return 0;
205 +       }
206 +       return timeout;
207 +}
208 +
209 +#endif
210 +
211 +#ifdef CONFIG_FEATURE_TFTP_MULTICAST
212 +static int
213 +tftp_multicast_check(const char *opt, char **phost, unsigned short *pport, int *pactive)
214 +{
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
220 +        *
221 +        * Addr and port will be empty fields when the server notifies a
222 +        * passive client that it is now the active client.
223 +        *
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.
226 +        *
227 +        * If any tokenization errors occur in the opt string, the host
228 +        * address string is automatically freed.
229 +        *
230 +        * Return 0 if any tokenization error, 1 if all parameters are good.
231 +        */
232 +
233 +       char *token = NULL;
234 +       char *parse_buf = NULL;
235 +       char *tokenv = NULL;
236 +       char *host = NULL;
237 +       int port;
238 +       int active;
239 +
240 +       parse_buf = bb_xstrdup(opt);
241 +
242 +       dprintf("multicast option=%s\n", opt);
243 +
244 +       /* IP address */
245 +       if ((token = strtok_r(parse_buf, ",", &tokenv)) == NULL) {
246 +               dprintf("tftp_multicast_check: cannot parse IP address from %s\n", parse_buf);
247 +               free(parse_buf);
248 +               return 0;
249 +       }
250 +       if (strlen(token) > 0)
251 +               *phost = host = bb_xstrdup(token);
252 +
253 +       /* Port */
254 +       if ((token = strtok_r(NULL, ",", &tokenv)) == NULL) {
255 +               dprintf("tftp_multicast_check: cannot parse port number from %s\n", tokenv);
256 +               goto token_error;
257 +       }
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);
262 +                       goto token_error;
263 +               }
264 +               *pport = htons(port);
265 +       }
266 +
267 +       /* Active/passive */
268 +       if ((token = strtok_r(NULL, ",", &tokenv)) == NULL) {
269 +               dprintf("tftp_multicast_check: cannot parse active/passive from %s\n", tokenv);
270 +               goto token_error;
271 +       }
272 +       active = atoi(token);
273 +       if (active != 0 && active != 1) {
274 +               dprintf("tftp_multicast_check: bad active/passive flag (%d)\n", active);
275 +               goto token_error;
276 +       }
277 +       *pactive = active;
278 +
279 +       free(parse_buf);
280 +       return 1;
281 +
282 +token_error:
283 +       free(parse_buf);
284 +       if (host != NULL)
285 +               free(host);
286 +       *phost = NULL;
287 +       return 0;
288 +
289 +}
290 +
291 +#define VECTOR_QUANTUM_WIDTH 8
292 +#define VECTOR_QUANTUM_ALL_ONES ((1<<VECTOR_QUANTUM_WIDTH)-1)
293 +
294 +static void inline
295 +bit_set(int bit, unsigned char *vector)
296 +{
297 +       int offset = bit / VECTOR_QUANTUM_WIDTH;
298 +       int mask = 1 << (bit % VECTOR_QUANTUM_WIDTH);
299 +       vector[offset] |= mask;
300 +}
301 +
302 +static int inline
303 +bit_isset(int bit, const unsigned char *vector)
304 +{
305 +       int offset = bit / VECTOR_QUANTUM_WIDTH;
306 +       int mask = 1 << (bit % VECTOR_QUANTUM_WIDTH);
307 +       return vector[offset] & mask ? 1 : 0;
308 +}
309 +
310 +static int inline
311 +bit_lmz(const unsigned char *vector)
312 +{
313 +       /* Return number of left-most zero in bit vector */
314 +       const unsigned char *vp = vector;
315 +       int i;
316 +       unsigned char velem;
317 +
318 +       while (*vp == VECTOR_QUANTUM_ALL_ONES)
319 +               vp++;
320 +       velem = *vp;
321 +       for (i = 0; i < VECTOR_QUANTUM_WIDTH; i++) {
322 +               if ((velem & (1 << i)) == 0)
323 +                       break;
324 +       }
325 +       dprintf("bit_lmz: block=%d\n", (vp - vector)*VECTOR_QUANTUM_WIDTH + i);
326 +       return (vp - vector)*VECTOR_QUANTUM_WIDTH + i;
327 +}
328 +
329 +#endif
330 +
331 +
332 +
333 +#ifdef TFTP_OPTIONS
334 +
335  static char *tftp_option_get(char *buf, int len, char *option)
336  {
337 -        int opt_val = 0;
338 +       int opt_val = 0;
339         int opt_found = 0;
340         int k;
341 -
342 -       while (len > 0) {
343
344 +       while (len > 0) {
345                 /* Make sure the options are terminated correctly */
346 -
347                 for (k = 0; k < len; k++) {
348                         if (buf[k] == '\0') {
349                                 break;
350 @@ -117,9 +298,8 @@
351                         if (strcasecmp(buf, option) == 0) {
352                                 opt_found = 1;
353                         }
354 -               }
355 -               else {
356 -                       if (opt_found) {
357 +               } else {
358 +                       if (opt_found) {
359                                 return buf;
360                         }
361                 }
362 @@ -138,7 +318,8 @@
363  #endif
364
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)
369  {
370         const int cmd_get = cmd & tftp_cmd_get;
371         const int cmd_put = cmd & tftp_cmd_put;
372 @@ -155,18 +336,29 @@
373         int len;
374         int opcode = 0;
375         int finished = 0;
376 -       int timeout = bb_tftp_num_retries;
377 +       int retry = bb_tftp_num_retries;
378         unsigned short block_nr = 1;
379
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;
385 +       char *mchostname;
386 +       unsigned short mcport;
387 +       unsigned char *mcblockmap = NULL;
388 +       int master_client = 1;
389 +       int mcfd = -1;
390 +       int mcmaxblock = 0x10000;
391 +       int ack_oack = 0;
392 +#else
393 +       #define master_client 1
394 +    #define ack_oack 0
395  #endif
396
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);
401
402 -       tftp_bufsize += 4;
403 +       int tftp_bufsize = option->blksize + 4;
404
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));
410
411 -       /* build opcode */
412 -
413 -       if (cmd_get) {
414 -               opcode = TFTP_RRQ;
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;
421 +               }
422 +               mcblockmap = xmalloc(bmsize+1);
423 +               memset(mcblockmap, 0, bmsize+1);
424         }
425 +#endif
426
427 -       if (cmd_put) {
428 -               opcode = TFTP_WRQ;
429 -       }
430 +       /* build opcode */
431 +
432 +       opcode = cmd_get ? TFTP_RRQ : TFTP_WRQ;
433
434         while (1) {
435
436 @@ -203,7 +401,7 @@
437
438                 cp += 2;
439
440 -               /* add filename and mode */
441 +               /* First packet of file transfer includes file name, mode, and options */
442
443                 if ((cmd_get && (opcode == TFTP_RRQ)) ||
444                         (cmd_put && (opcode == TFTP_WRQ))) {
445 @@ -223,7 +421,7 @@
446                         }
447
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");
451                                 break;
452                         }
453
454 @@ -238,8 +436,8 @@
455
456                         if (len != TFTP_BLOCKSIZE_DEFAULT) {
457
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");
462                                         break;
463                                 }
464
465 @@ -249,16 +447,65 @@
466                                 cp += 8;
467
468                                 cp += snprintf(cp, 6, "%d", len) + 1;
469 +                       }
470 +#endif
471 +
472 +
473 +
474 +#ifdef CONFIG_FEATURE_TFTP_MULTICAST
475 +
476 +                       if (option->multicast) {
477 +                               if ((&buf[tftp_bufsize - 1] - cp) < 12) {
478 +                                       bb_error_msg("buffer too small for multicast option");
479 +                                       break;
480 +                               }
481 +
482 +                               /* add "multicast" option */
483
484 -                               want_option_ack = 1;
485 +                               memcpy(cp, "multicast\0", 11);
486 +                               cp += 11;
487 +
488 +                               option->multicast = 0;  /* turn back on when server accepts option */
489 +                               ack_oack = 1;   /* acknowledge OACK */
490                         }
491 +
492  #endif
493 +
494 +#ifdef CONFIG_FEATURE_TFTP_TIMEOUT
495 +
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");
499 +                                       break;
500 +                               }
501 +
502 +                               /* add "timeout" option */
503 +
504 +                               memcpy(cp, "timeout", 8);
505 +                               cp += 8;
506 +
507 +                               cp += snprintf(cp, 4, "%d", option->server_timeout) + 1;
508 +                       }
509 +#endif
510 +
511                 }
512
513                 /* add ack and data */
514
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);
522 +                               cp += 2;
523 +                               if (blocknum >= mcmaxblock)
524 +                                       finished = 1;
525 +                               dprintf("ack block %d/%d %s\n", blocknum, mcmaxblock, finished? "finished": "");
526 +                       }
527 +               }
528 +#endif
529 +               else if ((cmd_get && opcode == TFTP_ACK) ||
530 +                       (cmd_put && opcode == TFTP_DATA)) {
531
532                         *((unsigned short *) cp) = htons(block_nr);
533
534 @@ -275,7 +522,7 @@
535                                 }
536
537                                 if (len != (tftp_bufsize - 4)) {
538 -                                       finished++;
539 +                                       finished = 1;
540                                 }
541
542                                 cp += len;
543 @@ -283,82 +530,119 @@
544                 }
545
546
547 -               /* send packet */
548 +               /* send packet and receive reply */
549
550
551 -               timeout = bb_tftp_num_retries;  /* re-initialize */
552 +               retry = bb_tftp_num_retries;  /* re-initialize */
553                 do {
554 -
555 +                       int selectrc;
556                         len = cp - buf;
557
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");
563 -#endif
564 -                       if (sendto(socketfd, buf, len, 0,
565 -                                       (struct sockaddr *) &sa, sizeof(sa)) < 0) {
566 -                               bb_perror_msg("send");
567 -                               len = -1;
568 -                               break;
569 -                       }
570 -
571 +                       /* send packet */
572 +                       if ((len > 2) && (! option->multicast || master_client || ack_oack)) {
573
574 -                       if (finished && (opcode == TFTP_ACK)) {
575 -                               break;
576 +#ifdef CONFIG_FEATURE_TFTP_DEBUG
577 +                               dprintf("sending %u bytes\n", len);
578 +                               for (cp = buf; cp < &buf[len]; cp++)
579 +                                       if (debug)
580 +                                               printf("%02x ", *(unsigned char *)cp);
581 +                               if (debug)
582 +                                       printf("\n");
583 +#endif
584 +#ifdef CONFIG_FEATURE_TFTP_MULTICAST
585 +                               ack_oack = 0;
586 +#endif
587 +                               if (sendto(socketfd, buf, len, 0, (struct sockaddr *) &sa, sizeof(sa)) < 0) {
588 +                                       bb_perror_msg("send");
589 +                                       len = -1;
590 +                                       break;
591 +                               }
592 +                               if (finished && opcode == TFTP_ACK) {
593 +                                       break;
594 +                               }
595                         }
596
597 -                       /* receive packet */
598 +                       /* receive reply packet */
599
600                         memset(&from, 0, sizeof(from));
601                         fromlen = sizeof(from);
602
603 -                       tv.tv_sec = TFTP_TIMEOUT;
604 +                       tv.tv_sec = option->client_timeout;
605                         tv.tv_usec = 0;
606
607                         FD_ZERO(&rfds);
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);
614 +                       }
615 +#endif
616
617 -                       switch (select(FD_SETSIZE, &rfds, NULL, NULL, &tv)) {
618 -                       case 1:
619 -                               len = recvfrom(socketfd, buf, tftp_bufsize, 0,
620 -                                               (struct sockaddr *) &from, &fromlen);
621 -
622 -                               if (len < 0) {
623 -                                       bb_perror_msg("recvfrom");
624 -                                       break;
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);
632 +
633 +                                       if (len < 0) {
634 +                                               bb_perror_msg("recvfrom");
635 +                                       } else {
636 +                                               if (sa.sin_port == port) {
637 +                                                       sa.sin_port = from.sin_port;
638 +                                               }
639 +                                               if (sa.sin_port == from.sin_port) {
640 +                                                       retry = 0;
641 +                                               } else {
642 +                                                       /* bad packet */
643 +                                                       /* discard the packet - treat as timeout */
644 +                                                       retry = bb_tftp_num_retries;
645 +                                                       bb_error_msg("timeout");
646 +                                               }
647 +                                       }
648                                 }
649
650 -                               timeout = 0;
651 -
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);
658 +                                       if (len < 0) {
659 +                                               bb_perror_msg("multicast recvfrom");
660 +                                       } else {
661 +                                               if (mcsa.sin_port == mcport) {
662 +                                                       mcsa.sin_port = from.sin_port;
663 +                                               }
664 +                                               if (mcsa.sin_port == from.sin_port) {
665 +                                                       retry = 0;
666 +                                               } else {
667 +                                                       retry = bb_tftp_num_retries;
668 +                                                       bb_error_msg("multicast timeout");
669 +                                               }
670 +                                       }
671                                 }
672 -                               if (sa.sin_port == from.sin_port) {
673 -                                       break;
674 -                               }
675 -
676 -                               /* fall-through for bad packets! */
677 -                               /* discard the packet - treat as timeout */
678 -                               timeout = bb_tftp_num_retries;
679 +#endif
680
681 -                       case 0:
682 +                       } else if (selectrc == 0) {
683 +                               /* Time out */
684 +                               dprintf("timeout\n");
685                                 bb_error_msg("timeout");
686
687 -                               timeout--;
688 -                               if (timeout == 0) {
689 +                               retry--;
690 +                               if (retry == 0) {
691                                         len = -1;
692                                         bb_error_msg("last timeout");
693                                 }
694 -                               break;
695 -
696 -                       default:
697 +                       } else {
698 +                               /* Error condition */
699 +                               dprintf("error\n");
700                                 bb_perror_msg("select");
701                                 len = -1;
702                         }
703
704 -               } while (timeout && (len >= 0));
705 +               } while (retry && len >= 0);
706
707                 if ((finished) || (len < 0)) {
708                         break;
709 @@ -370,9 +654,8 @@
710                 opcode = ntohs(*((unsigned short *) buf));
711                 tmp = ntohs(*((unsigned short *) &buf[2]));
712
713 -#ifdef CONFIG_FEATURE_TFTP_DEBUG
714 -               fprintf(stderr, "received %d bytes: %04x %04x\n", len, opcode, tmp);
715 -#endif
716 +               dprintf("received %d bytes: %04x %04x\n", len, opcode, tmp);
717 +               dprintf("master_client=%d\n", master_client);
718
719                 if (opcode == TFTP_ERROR) {
720                         char *msg = NULL;
721 @@ -393,55 +676,116 @@
722                         break;
723                 }
724
725 -#ifdef CONFIG_FEATURE_TFTP_BLOCKSIZE
726 -               if (want_option_ack) {
727 +#ifdef TFTP_OPTIONS
728
729 -                        want_option_ack = 0;
730 +               if (opcode == TFTP_OACK) {
731
732 -                        if (opcode == TFTP_OACK) {
733 +                       /* server seems to support options */
734
735 -                                /* server seems to support options */
736 +                       char *res;
737 +
738 +                       block_nr = 0;           /* acknowledge option packet with block number 0 */
739 +                       opcode = cmd_put ? TFTP_DATA : TFTP_ACK;
740
741 -                                char *res;
742
743 -                                res = tftp_option_get(&buf[2], len-2,
744 -                                                      "blksize");
745 +#ifdef CONFIG_FEATURE_TFTP_BLOCKSIZE
746 +                       res = tftp_option_get(&buf[2], len-2, "blksize");
747
748 -                                if (res) {
749 -                                        int blksize = atoi(res);
750 -                       
751 -                                        if (tftp_blocksize_check(blksize,
752 -                                                          tftp_bufsize - 4)) {
753 +                       if (res) {
754 +                               int blksize = atoi(res);
755
756 -                                                if (cmd_put) {
757 -                                                        opcode = TFTP_DATA;
758 -                                                }
759 -                                                else {
760 -                                                        opcode = TFTP_ACK;
761 -                                                }
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;
767 +                                       free(buf);
768 +                                       buf = xmalloc(tftp_bufsize);
769 +                               } else {
770 +                                       bb_error_msg("bad blksize %d", blksize);
771 +                                       break;
772 +                               }
773 +                       }
774  #endif
775 -                                                tftp_bufsize = blksize + 4;
776 -                                                block_nr = 0;
777 -                                                continue;
778 -                                        }
779 -                                }
780 -                                /* FIXME:
781 -                                 * we should send ERROR 8 */
782 -                                bb_error_msg("bad server option");
783 -                                break;
784 -                        }
785
786 -                        bb_error_msg("warning: blksize not supported by server"
787 -                                  " - reverting to 512");
788
789 -                        tftp_bufsize = TFTP_BLOCKSIZE_DEFAULT + 4;
790 +#ifdef CONFIG_FEATURE_TFTP_MULTICAST
791 +                       res = tftp_option_get(&buf[2], len-2, "multicast");
792 +
793 +                       if (res) {
794 +                               ack_oack = 1;
795 +                               if (tftp_multicast_check(res, &mchostname, &mcport, &master_client)) {
796 +                                       struct ip_mreq mreq;
797 +                                       struct in_addr mcaddr;
798 +
799 +                                       dprintf("using multicast\n");
800 +
801 +                                       mchost = xgethostbyname(mchostname);
802 +                                       if (mchost) {
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);
806 +                                                       break;
807 +                                               }
808 +                                       } else {
809 +                                               bb_error_msg("bad multicast address: %s", mchostname);
810 +                                               break;
811 +                                       }
812 +
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;
817 +
818 +                                       bind(mcfd, (struct sockaddr *)&mcsa, sizeof(mcsa));
819 +
820 +                                       mreq.imr_multiaddr.s_addr = mcaddr.s_addr;
821 +                                       mreq.imr_interface.s_addr = htonl(INADDR_ANY);
822 +
823 +                                       if (setsockopt(mcfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0)
824 +                                       {
825 +                                               bb_error_msg("setsockopt");
826 +                                               break;
827 +                                       }
828 +
829 +                                       option->multicast = 1;
830 +                               } else {
831 +                                       bb_error_msg("bad multicast option value: %s", res);
832 +                                       break;
833 +                               }
834 +                       }
835 +#endif
836 +
837                 }
838 +               else
839  #endif
840
841                 if (cmd_get && (opcode == TFTP_DATA)) {
842
843 +#ifdef CONFIG_FEATURE_TFTP_MULTICAST
844 +                       if (option->multicast) {
845 +                               int bn = tmp - 1;
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);
850 +                                       if (len < 0) {
851 +                                               bb_perror_msg("write");
852 +                                               break;
853 +                                       }
854 +                                       bit_set(bn, mcblockmap);
855 +                                       if (len != (tftp_bufsize-4)) {
856 +                                               mcmaxblock = tmp;
857 +                                               dprintf("mcmaxblock=%d, (len(%d) != tftp_bufsize-4(%d))\n", mcmaxblock, len, tftp_bufsize-4);
858 +                                       }
859 +                                       opcode = TFTP_ACK;
860 +                               }
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.
864 +                                */
865 +                       }
866 +                       else
867 +#endif
868 +
869                         if (tmp == block_nr) {
870                         
871                                 len = write(localfd, &buf[4], len - 4);
872 @@ -452,15 +796,14 @@
873                                 }
874
875                                 if (len != (tftp_bufsize - 4)) {
876 -                                       finished++;
877 +                                       finished = 1;
878                                 }
879
880                                 opcode = TFTP_ACK;
881 -                               continue;
882                         }
883                 }
884
885 -               if (cmd_put && (opcode == TFTP_ACK)) {
886 +               else if (cmd_put && opcode == TFTP_ACK) {
887
888                         if (tmp == (unsigned short)(block_nr - 1)) {
889                                 if (finished) {
890 @@ -468,15 +811,19 @@
891                                 }
892
893                                 opcode = TFTP_DATA;
894 -                               continue;
895                         }
896                 }
897         }
898
899  #ifdef CONFIG_FEATURE_CLEAN_UP
900         close(socketfd);
901 +       free(buf);
902 +
903 +#ifdef CONFIG_FEATURE_TFTP_MULTICAST
904 +       if (mcblockmap != NULL)
905 +               free(mcblockmap);
906 +#endif
907
908 -        free(buf);
909  #endif
910
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;
916 -       int port;
917 +       unsigned short port;
918         int cmd = 0;
919         int fd = -1;
920         int flags = 0;
921         int opt;
922         int result;
923 -       int blocksize = TFTP_BLOCKSIZE_DEFAULT;
924 +       struct tftp_option option = {
925 +               .multicast              = 0,
926 +               .blksize                = TFTP_BLOCKSIZE_DEFAULT,
927 +               .client_timeout = TFTP_TIMEOUT,
928 +               .server_timeout = TFTP_TIMEOUT,
929 +       };
930
931         /* figure out what to pass to getopt */
932
933 @@ -515,13 +867,45 @@
934  #define PUT
935  #endif
936
937 -       while ((opt = getopt(argc, argv, BS GET PUT "l:r:")) != -1) {
938 +#ifdef CONFIG_FEATURE_TFTP_TIMEOUT
939 +#define TO "T:t:"
940 +#else
941 +#define TO
942 +#endif
943 +
944 +#ifdef CONFIG_FEATURE_TFTP_MULTICAST
945 +#define MC "m"
946 +#else
947 +#define MC
948 +#endif
949 +
950 +#ifdef CONFIG_FEATURE_TFTP_DEBUG
951 +#define DB "D"
952 +#else
953 +#define DB
954 +#endif
955 +
956 +       while ((opt = getopt(argc, argv, BS GET PUT TO MC DB "l:r:")) != -1) {
957                 switch (opt) {
958  #ifdef CONFIG_FEATURE_TFTP_BLOCKSIZE
959                 case 'b':
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;
966 +                       }
967 +                       break;
968 +#endif
969 +#ifdef CONFIG_FEATURE_TFTP_TIMEOUT
970 +               case 'T':
971 +                       option.client_timeout = atoi(optarg);
972 +                       if (!tftp_timeout_check(option.client_timeout)) {
973 +                               return EXIT_FAILURE;
974 +                       }
975 +                       break;
976 +               case 't':
977 +                       option.server_timeout = atoi(optarg);
978 +                       if (!tftp_timeout_check(option.server_timeout)) {
979 +                               return EXIT_FAILURE;
980                         }
981                         break;
982  #endif
983 @@ -537,18 +921,34 @@
984                         flags = O_RDONLY;
985                         break;
986  #endif
987 +#ifdef CONFIG_FEATURE_TFTP_MULTICAST
988 +               case 'm':
989 +                       option.multicast = 1;   /* receive multicast file */
990 +                       break;
991 +#endif
992 +#ifdef CONFIG_FEATURE_TFTP_DEBUG
993 +               case 'D':
994 +                       debug = 1;
995 +                       break;
996 +#endif
997                 case 'l':
998                         localfile = bb_xstrdup(optarg);
999                         break;
1000                 case 'r':
1001                         remotefile = bb_xstrdup(optarg);
1002                         break;
1003 +               default:
1004 +                       bb_show_usage();
1005                 }
1006         }
1007
1008         if ((cmd == 0) || (optind == argc)) {
1009                 bb_show_usage();
1010         }
1011 +       if (cmd == tftp_cmd_put && option.multicast) {
1012 +               fprintf(stderr, "Multicast (-m) invalid option with put (-p) command\n");
1013 +               exit(EXIT_FAILURE);
1014 +       }
1015         if(localfile && strcmp(localfile, "-") == 0) {
1016             fd = fileno((cmd==tftp_cmd_get)? stdout : stdin);
1017         }
1018 @@ -566,14 +966,12 @@
1019         host = xgethostbyname(argv[optind]);
1020         port = bb_lookup_port(argv[optind + 1], "udp", 69);
1021
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);
1028 -#endif
1029
1030 -       result = tftp(cmd, host, remotefile, fd, port, blocksize);
1031 +       result = tftp(cmd, host, remotefile, fd, port, &option);
1032
1033  #ifdef CONFIG_FEATURE_CLEAN_UP
1034         if (!(fd == STDOUT_FILENO || fd == STDIN_FILENO)) {
1035 @@ -582,3 +980,18 @@
1036  #endif
1037         return(result);
1038  }
1039 +
1040 +
1041 +#ifdef CONFIG_FEATURE_TFTP_DEBUG
1042 +
1043 +#include <sys/time.h>
1044 +
1045 +static void
1046 +printtime(void)
1047 +{
1048 +       struct timeval tv;
1049 +       gettimeofday(&tv, NULL);
1050 +       printf("%11lu.%06lu ", tv.tv_sec, tv.tv_usec);
1051 +}
1052 +
1053 +#endif