blob: ca431fc60370f9c458b3eeb6213e70187075a051 [file] [log] [blame]
Glenn L McGrathdb6ee812004-03-05 15:52:57 +00001Index: AUTHORS
2===================================================================
3RCS file: /var/cvs/busybox/AUTHORS,v
4retrieving revision 1.40
5diff -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 @@
Eric Andersenc7bda1c2004-03-15 08:29:22 +00009 Original author of BusyBox in 1995, 1996. Some of his code can
Glenn L McGrathdb6ee812004-03-05 15:52:57 +000010 still be found hiding here and there...
Eric Andersenc7bda1c2004-03-15 08:29:22 +000011
Glenn L McGrathdb6ee812004-03-05 15:52:57 +000012+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
Eric Andersenc7bda1c2004-03-15 08:29:22 +000017
Glenn L McGrathdb6ee812004-03-05 15:52:57 +000018Index: include/usage.h
19===================================================================
20RCS file: /var/cvs/busybox/include/usage.h,v
21retrieving revision 1.191
22diff -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
Eric Andersenc7bda1c2004-03-15 08:29:22 +000044
Glenn L McGrathdb6ee812004-03-05 15:52:57 +000045 #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...]"
64Index: networking/Config.in
65===================================================================
66RCS file: /var/cvs/busybox/networking/Config.in,v
67retrieving revision 1.27
68diff -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.
Eric Andersenc7bda1c2004-03-15 08:29:22 +000074
Glenn L McGrathdb6ee812004-03-05 15:52:57 +000075+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.
Eric Andersenc7bda1c2004-03-15 08:29:22 +000087
Glenn L McGrathdb6ee812004-03-05 15:52:57 +000088 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.
Eric Andersenc7bda1c2004-03-15 08:29:22 +000095
Glenn L McGrathdb6ee812004-03-05 15:52:57 +000096+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
106Index: networking/tftp.c
107===================================================================
108RCS file: /var/cvs/busybox/networking/tftp.c,v
109retrieving revision 1.25
110diff -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 @@
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000141
Glenn L McGrathdb6ee812004-03-05 15:52:57 +0000142 #include "busybox.h"
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000143
Glenn L McGrathdb6ee812004-03-05 15:52:57 +0000144+#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
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000149
Glenn L McGrathdb6ee812004-03-05 15:52:57 +0000150+#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 */
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000160
Glenn L McGrathdb6ee812004-03-05 15:52:57 +0000161@@ -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 };
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000171
Glenn L McGrathdb6ee812004-03-05 15:52:57 +0000172 const int tftp_cmd_get = 1;
173 const int tftp_cmd_put = 2;
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000174
Glenn L McGrathdb6ee812004-03-05 15:52:57 +0000175+
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
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000185
186 static int tftp_blocksize_check(int blocksize, int bufsize)
Glenn L McGrathdb6ee812004-03-05 15:52:57 +0000187@@ -93,16 +132,158 @@
188 return blocksize;
189 }
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000190
Glenn L McGrathdb6ee812004-03-05 15:52:57 +0000191+#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+
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000335 static char *tftp_option_get(char *buf, int len, char *option)
Glenn L McGrathdb6ee812004-03-05 15:52:57 +0000336 {
337- int opt_val = 0;
338+ int opt_val = 0;
339 int opt_found = 0;
340 int k;
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000341-
Glenn L McGrathdb6ee812004-03-05 15:52:57 +0000342- while (len > 0) {
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000343
Glenn L McGrathdb6ee812004-03-05 15:52:57 +0000344+ 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 }
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000354- }
Glenn L McGrathdb6ee812004-03-05 15:52:57 +0000355- else {
356- if (opt_found) {
357+ } else {
358+ if (opt_found) {
359 return buf;
360 }
361 }
362@@ -138,7 +318,8 @@
363 #endif
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000364
Glenn L McGrathdb6ee812004-03-05 15:52:57 +0000365 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;
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000379
Glenn L McGrathdb6ee812004-03-05 15:52:57 +0000380-#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
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000396
Glenn L McGrathdb6ee812004-03-05 15:52:57 +0000397 /* 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);
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000401
Glenn L McGrathdb6ee812004-03-05 15:52:57 +0000402- tftp_bufsize += 4;
403+ int tftp_bufsize = option->blksize + 4;
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000404
Glenn L McGrathdb6ee812004-03-05 15:52:57 +0000405 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));
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000410
Glenn L McGrathdb6ee812004-03-05 15:52:57 +0000411- /* 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
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000426
Glenn L McGrathdb6ee812004-03-05 15:52:57 +0000427- if (cmd_put) {
428- opcode = TFTP_WRQ;
429- }
430+ /* build opcode */
431+
432+ opcode = cmd_get ? TFTP_RRQ : TFTP_WRQ;
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000433
Glenn L McGrathdb6ee812004-03-05 15:52:57 +0000434 while (1) {
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000435
Glenn L McGrathdb6ee812004-03-05 15:52:57 +0000436@@ -203,7 +401,7 @@
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000437
Glenn L McGrathdb6ee812004-03-05 15:52:57 +0000438 cp += 2;
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000439
Glenn L McGrathdb6ee812004-03-05 15:52:57 +0000440- /* add filename and mode */
441+ /* First packet of file transfer includes file name, mode, and options */
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000442
Glenn L McGrathdb6ee812004-03-05 15:52:57 +0000443 if ((cmd_get && (opcode == TFTP_RRQ)) ||
444 (cmd_put && (opcode == TFTP_WRQ))) {
445@@ -223,7 +421,7 @@
446 }
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000447
Glenn L McGrathdb6ee812004-03-05 15:52:57 +0000448 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 }
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000453
Glenn L McGrathdb6ee812004-03-05 15:52:57 +0000454@@ -238,8 +436,8 @@
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000455
Glenn L McGrathdb6ee812004-03-05 15:52:57 +0000456 if (len != TFTP_BLOCKSIZE_DEFAULT) {
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000457
Glenn L McGrathdb6ee812004-03-05 15:52:57 +0000458- 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 }
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000464
Glenn L McGrathdb6ee812004-03-05 15:52:57 +0000465@@ -249,16 +447,65 @@
466 cp += 8;
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000467
Glenn L McGrathdb6ee812004-03-05 15:52:57 +0000468 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 */
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000483
Glenn L McGrathdb6ee812004-03-05 15:52:57 +0000484- 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 }
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000512
Glenn L McGrathdb6ee812004-03-05 15:52:57 +0000513 /* add ack and data */
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000514
Glenn L McGrathdb6ee812004-03-05 15:52:57 +0000515- 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)) {
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000531
Glenn L McGrathdb6ee812004-03-05 15:52:57 +0000532 *((unsigned short *) cp) = htons(block_nr);
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000533
Glenn L McGrathdb6ee812004-03-05 15:52:57 +0000534@@ -275,7 +522,7 @@
535 }
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000536
Glenn L McGrathdb6ee812004-03-05 15:52:57 +0000537 if (len != (tftp_bufsize - 4)) {
538- finished++;
539+ finished = 1;
540 }
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000541
Glenn L McGrathdb6ee812004-03-05 15:52:57 +0000542 cp += len;
543@@ -283,82 +530,119 @@
544 }
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000545
546
Glenn L McGrathdb6ee812004-03-05 15:52:57 +0000547- /* send packet */
548+ /* send packet and receive reply */
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000549
550
Glenn L McGrathdb6ee812004-03-05 15:52:57 +0000551- 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;
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000557
Glenn L McGrathdb6ee812004-03-05 15:52:57 +0000558-#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)) {
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000573
Glenn L McGrathdb6ee812004-03-05 15:52:57 +0000574- 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 }
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000596
Glenn L McGrathdb6ee812004-03-05 15:52:57 +0000597- /* receive packet */
598+ /* receive reply packet */
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000599
Glenn L McGrathdb6ee812004-03-05 15:52:57 +0000600 memset(&from, 0, sizeof(from));
601 fromlen = sizeof(from);
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000602
Glenn L McGrathdb6ee812004-03-05 15:52:57 +0000603- tv.tv_sec = TFTP_TIMEOUT;
604+ tv.tv_sec = option->client_timeout;
605 tv.tv_usec = 0;
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000606
Glenn L McGrathdb6ee812004-03-05 15:52:57 +0000607 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
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000616
Glenn L McGrathdb6ee812004-03-05 15:52:57 +0000617- 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 }
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000649
Glenn L McGrathdb6ee812004-03-05 15:52:57 +0000650- 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
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000680
Glenn L McGrathdb6ee812004-03-05 15:52:57 +0000681- case 0:
682+ } else if (selectrc == 0) {
683+ /* Time out */
684+ dprintf("timeout\n");
685 bb_error_msg("timeout");
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000686
Glenn L McGrathdb6ee812004-03-05 15:52:57 +0000687- 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 }
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000703
Glenn L McGrathdb6ee812004-03-05 15:52:57 +0000704- } while (timeout && (len >= 0));
705+ } while (retry && len >= 0);
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000706
Glenn L McGrathdb6ee812004-03-05 15:52:57 +0000707 if ((finished) || (len < 0)) {
708 break;
709@@ -370,9 +654,8 @@
710 opcode = ntohs(*((unsigned short *) buf));
711 tmp = ntohs(*((unsigned short *) &buf[2]));
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000712
Glenn L McGrathdb6ee812004-03-05 15:52:57 +0000713-#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);
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000718
Glenn L McGrathdb6ee812004-03-05 15:52:57 +0000719 if (opcode == TFTP_ERROR) {
720 char *msg = NULL;
721@@ -393,55 +676,116 @@
722 break;
723 }
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000724
Glenn L McGrathdb6ee812004-03-05 15:52:57 +0000725-#ifdef CONFIG_FEATURE_TFTP_BLOCKSIZE
726- if (want_option_ack) {
727+#ifdef TFTP_OPTIONS
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000728
Glenn L McGrathdb6ee812004-03-05 15:52:57 +0000729- want_option_ack = 0;
730+ if (opcode == TFTP_OACK) {
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000731
Glenn L McGrathdb6ee812004-03-05 15:52:57 +0000732- if (opcode == TFTP_OACK) {
733+ /* server seems to support options */
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000734
Glenn L McGrathdb6ee812004-03-05 15:52:57 +0000735- /* server seems to support options */
736+ char *res;
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000737+
Glenn L McGrathdb6ee812004-03-05 15:52:57 +0000738+ block_nr = 0; /* acknowledge option packet with block number 0 */
739+ opcode = cmd_put ? TFTP_DATA : TFTP_ACK;
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000740
Glenn L McGrathdb6ee812004-03-05 15:52:57 +0000741- char *res;
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000742
743- res = tftp_option_get(&buf[2], len-2,
Glenn L McGrathdb6ee812004-03-05 15:52:57 +0000744- "blksize");
745+#ifdef CONFIG_FEATURE_TFTP_BLOCKSIZE
746+ res = tftp_option_get(&buf[2], len-2, "blksize");
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000747
Glenn L McGrathdb6ee812004-03-05 15:52:57 +0000748- if (res) {
749- int blksize = atoi(res);
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000750-
Glenn L McGrathdb6ee812004-03-05 15:52:57 +0000751- if (tftp_blocksize_check(blksize,
752- tftp_bufsize - 4)) {
753+ if (res) {
754+ int blksize = atoi(res);
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000755
Glenn L McGrathdb6ee812004-03-05 15:52:57 +0000756- 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- }
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000785
Glenn L McGrathdb6ee812004-03-05 15:52:57 +0000786- bb_error_msg("warning: blksize not supported by server"
787- " - reverting to 512");
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000788
Glenn L McGrathdb6ee812004-03-05 15:52:57 +0000789- 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
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000840
Glenn L McGrathdb6ee812004-03-05 15:52:57 +0000841 if (cmd_get && (opcode == TFTP_DATA)) {
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000842
Glenn L McGrathdb6ee812004-03-05 15:52:57 +0000843+#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) {
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000870
Glenn L McGrathdb6ee812004-03-05 15:52:57 +0000871 len = write(localfd, &buf[4], len - 4);
872@@ -452,15 +796,14 @@
873 }
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000874
Glenn L McGrathdb6ee812004-03-05 15:52:57 +0000875 if (len != (tftp_bufsize - 4)) {
876- finished++;
877+ finished = 1;
878 }
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000879
Glenn L McGrathdb6ee812004-03-05 15:52:57 +0000880 opcode = TFTP_ACK;
881- continue;
882 }
883 }
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000884
Glenn L McGrathdb6ee812004-03-05 15:52:57 +0000885- if (cmd_put && (opcode == TFTP_ACK)) {
886+ else if (cmd_put && opcode == TFTP_ACK) {
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000887
Glenn L McGrathdb6ee812004-03-05 15:52:57 +0000888 if (tmp == (unsigned short)(block_nr - 1)) {
889 if (finished) {
890@@ -468,15 +811,19 @@
891 }
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000892
Glenn L McGrathdb6ee812004-03-05 15:52:57 +0000893 opcode = TFTP_DATA;
894- continue;
895 }
896 }
897 }
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000898
Glenn L McGrathdb6ee812004-03-05 15:52:57 +0000899 #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
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000907
Glenn L McGrathdb6ee812004-03-05 15:52:57 +0000908- free(buf);
909 #endif
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000910
Glenn L McGrathdb6ee812004-03-05 15:52:57 +0000911 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+ };
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000930
Glenn L McGrathdb6ee812004-03-05 15:52:57 +0000931 /* figure out what to pass to getopt */
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000932
Glenn L McGrathdb6ee812004-03-05 15:52:57 +0000933@@ -515,13 +867,45 @@
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000934 #define PUT
Glenn L McGrathdb6ee812004-03-05 15:52:57 +0000935 #endif
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000936
Glenn L McGrathdb6ee812004-03-05 15:52:57 +0000937- 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
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000997 case 'l':
Glenn L McGrathdb6ee812004-03-05 15:52:57 +0000998 localfile = bb_xstrdup(optarg);
999 break;
1000 case 'r':
1001 remotefile = bb_xstrdup(optarg);
1002 break;
1003+ default:
1004+ bb_show_usage();
1005 }
1006 }
Eric Andersenc7bda1c2004-03-15 08:29:22 +00001007
Glenn L McGrathdb6ee812004-03-05 15:52:57 +00001008 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);
Eric Andersenc7bda1c2004-03-15 08:29:22 +00001021
Glenn L McGrathdb6ee812004-03-05 15:52:57 +00001022-#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
Eric Andersenc7bda1c2004-03-15 08:29:22 +00001029
Glenn L McGrathdb6ee812004-03-05 15:52:57 +00001030- result = tftp(cmd, host, remotefile, fd, port, blocksize);
1031+ result = tftp(cmd, host, remotefile, fd, port, &option);
Eric Andersenc7bda1c2004-03-15 08:29:22 +00001032
Glenn L McGrathdb6ee812004-03-05 15:52:57 +00001033 #ifdef CONFIG_FEATURE_CLEAN_UP
Eric Andersen70060d22004-03-27 10:02:48 +00001034 if (!(fd == STDOUT_FILENO || fd == STDIN_FILENO)) {
Glenn L McGrathdb6ee812004-03-05 15:52:57 +00001035@@ -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