tftp_timeout_multicast.diff 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053
  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. +John Powers <jpp@ti.com>
  12. + Added multicast option (rfc2090) and timeout option (rfc2349) to tftp.
  13. +
  14. Tim Riker <Tim@Rikers.org>
  15. bug fixes, member of fan club
  16. Index: include/usage.h
  17. ===================================================================
  18. RCS file: /var/cvs/busybox/include/usage.h,v
  19. retrieving revision 1.191
  20. diff -u -r1.191 usage.h
  21. --- a/include/usage.h 25 Feb 2004 10:35:55 -0000 1.191
  22. +++ b/include/usage.h 5 Mar 2004 15:45:59 -0000
  23. @@ -2492,6 +2492,21 @@
  24. #else
  25. #define USAGE_TFTP_BS(a)
  26. #endif
  27. +#ifdef CONFIG_FEATURE_TFTP_TIMEOUT
  28. + #define USAGE_TFTP_TIMEOUT(a) a
  29. +#else
  30. + #define USAGE_TFTP_TIMEOUT(a)
  31. +#endif
  32. +#ifdef CONFIG_FEATURE_TFTP_MULTICAST
  33. + #define USAGE_TFTP_MULTICAST(a) a
  34. +#else
  35. + #define USAGE_TFTP_MULTICAST(a)
  36. +#endif
  37. +#ifdef CONFIG_FEATURE_TFTP_DEBUG
  38. + #define USAGE_TFTP_DEBUG(a) a
  39. +#else
  40. + #define USAGE_TFTP_DEBUG(a)
  41. +#endif
  42. #define tftp_trivial_usage \
  43. "[OPTION]... HOST [PORT]"
  44. @@ -2508,6 +2523,16 @@
  45. ) \
  46. USAGE_TFTP_BS( \
  47. "\t-b SIZE\tTransfer blocks of SIZE octets.\n" \
  48. + ) \
  49. + USAGE_TFTP_TIMEOUT( \
  50. + "\t-T SEC\tClient timeout SEC seconds (default: 5).\n" \
  51. + "\t-t SEC\tServer timeout SEC seconds\n" \
  52. + ) \
  53. + USAGE_TFTP_MULTICAST( \
  54. + "\t-m\tMulticast get file.\n" \
  55. + ) \
  56. + USAGE_TFTP_DEBUG( \
  57. + "\t-D\tPrint debug messages.\n" \
  58. )
  59. #define time_trivial_usage \
  60. "[OPTION]... COMMAND [ARGS...]"
  61. Index: networking/Config.in
  62. ===================================================================
  63. RCS file: /var/cvs/busybox/networking/Config.in,v
  64. retrieving revision 1.27
  65. diff -u -r1.27 Config.in
  66. --- a/networking/Config.in 22 Feb 2004 12:25:47 -0000 1.27
  67. +++ b/networking/Config.in 5 Mar 2004 15:45:59 -0000
  68. @@ -522,6 +522,13 @@
  69. Add support for the GET command within the TFTP client. This allows
  70. a client to retrieve a file from a TFTP server.
  71. +config CONFIG_FEATURE_TFTP_MULTICAST
  72. + bool " Enable \"multicast\" option"
  73. + default n
  74. + depends on CONFIG_FEATURE_TFTP_GET
  75. + help
  76. + Allow the client to receive multicast file transfers.
  77. +
  78. config CONFIG_FEATURE_TFTP_PUT
  79. bool " Enable \"put\" command"
  80. default y
  81. @@ -531,12 +538,19 @@
  82. a client to transfer a file to a TFTP server.
  83. config CONFIG_FEATURE_TFTP_BLOCKSIZE
  84. - bool " Enable \"blocksize\" command"
  85. + bool " Enable \"blksize\" option"
  86. default n
  87. depends on CONFIG_TFTP
  88. help
  89. Allow the client to specify the desired block size for transfers.
  90. +config CONFIG_FEATURE_TFTP_TIMEOUT
  91. + bool " Enable \"timeout\" option"
  92. + default n
  93. + depends on CONFIG_TFTP
  94. + help
  95. + Allow the client to negotiate timeout option with server.
  96. +
  97. config CONFIG_FEATURE_TFTP_DEBUG
  98. bool " Enable debug"
  99. default n
  100. Index: networking/tftp.c
  101. ===================================================================
  102. RCS file: /var/cvs/busybox/networking/tftp.c,v
  103. retrieving revision 1.25
  104. diff -u -r1.25 tftp.c
  105. --- a/networking/tftp.c 5 Mar 2004 13:04:39 -0000 1.25
  106. +++ b/networking/tftp.c 5 Mar 2004 15:46:00 -0000
  107. @@ -1,11 +1,26 @@
  108. +/* vi: set sw=4 ts=4: */
  109. /* ------------------------------------------------------------------------- */
  110. /* tftp.c */
  111. +/* Copyright (c) 2003, 2004 Texas Instruments */
  112. +/* */
  113. +/* This package is free software; you can redistribute it and/or */
  114. +/* modify it under the terms of the license found in the file */
  115. +/* named COPYING that should have accompanied this file. */
  116. +/* */
  117. +/* THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR */
  118. +/* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED */
  119. +/* WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. */
  120. /* */
  121. /* A simple tftp client for busybox. */
  122. /* Tries to follow RFC1350. */
  123. /* Only "octet" mode supported. */
  124. /* Optional blocksize negotiation (RFC2347 + RFC2348) */
  125. /* */
  126. +/* New features added at Texas Instruments, October 2003 */
  127. +/* Author: John Powers */
  128. +/* Multicast option: rfc2090 */
  129. +/* Timeout option: rfc2349 */
  130. +/* */
  131. /* Copyright (C) 2001 Magnus Damm <damm@opensource.se> */
  132. /* */
  133. /* Parts of the code based on: */
  134. @@ -46,8 +61,20 @@
  135. #include "busybox.h"
  136. +#if defined(CONFIG_FEATURE_TFTP_BLOCKSIZE) || defined(CONFIG_FEATURE_TFTP_MULTICAST) || defined(CONFIG_FEATURE_TFTP_TIMEOUT)
  137. + #define TFTP_OPTIONS
  138. +#endif
  139. +
  140. //#define CONFIG_FEATURE_TFTP_DEBUG
  141. +#ifdef CONFIG_FEATURE_TFTP_DEBUG
  142. + static void printtime(void);
  143. + #define dprintf(fmt...) if (debug) {printtime(); printf(fmt);}
  144. + int debug = 0;
  145. +#else
  146. + #define dprintf(fmt...)
  147. +#endif
  148. +
  149. #define TFTP_BLOCKSIZE_DEFAULT 512 /* according to RFC 1350, don't change */
  150. #define TFTP_TIMEOUT 5 /* seconds */
  151. @@ -68,12 +95,24 @@
  152. "Illegal TFTP operation",
  153. "Unknown transfer ID",
  154. "File already exists",
  155. - "No such user"
  156. + "No such user",
  157. +#ifdef TFTP_OPTIONS
  158. + "Unsupported option",
  159. +#endif
  160. };
  161. const int tftp_cmd_get = 1;
  162. const int tftp_cmd_put = 2;
  163. +
  164. +struct tftp_option {
  165. + int multicast;
  166. + int blksize;
  167. + int client_timeout;
  168. + int server_timeout;
  169. +};
  170. +
  171. +
  172. #ifdef CONFIG_FEATURE_TFTP_BLOCKSIZE
  173. static int tftp_blocksize_check(int blocksize, int bufsize)
  174. @@ -93,16 +132,158 @@
  175. return blocksize;
  176. }
  177. +#endif
  178. +
  179. +#ifdef CONFIG_FEATURE_TFTP_TIMEOUT
  180. +
  181. +static int
  182. +tftp_timeout_check(int timeout)
  183. +{
  184. + /* Check if timeout seconds is valid:
  185. + * RFC2349 says between 1 and 255.
  186. + */
  187. +
  188. + if (timeout < 1 || timeout > 255) {
  189. + bb_error_msg("bad timeout value");
  190. + return 0;
  191. + }
  192. + return timeout;
  193. +}
  194. +
  195. +#endif
  196. +
  197. +#ifdef CONFIG_FEATURE_TFTP_MULTICAST
  198. +static int
  199. +tftp_multicast_check(const char *opt, char **phost, unsigned short *pport, int *pactive)
  200. +{
  201. + /* Option string contains comma delimited addr,port,active.
  202. + * addr = multicast IP address
  203. + * port = port number
  204. + * active = 1 if active client
  205. + * 0 if passive client
  206. + *
  207. + * Addr and port will be empty fields when the server notifies a
  208. + * passive client that it is now the active client.
  209. + *
  210. + * The host address string must be freed by the caller. Neither host
  211. + * nor port will be set/changed if the input fields are empty.
  212. + *
  213. + * If any tokenization errors occur in the opt string, the host
  214. + * address string is automatically freed.
  215. + *
  216. + * Return 0 if any tokenization error, 1 if all parameters are good.
  217. + */
  218. +
  219. + char *token = NULL;
  220. + char *parse_buf = NULL;
  221. + char *tokenv = NULL;
  222. + char *host = NULL;
  223. + int port;
  224. + int active;
  225. +
  226. + parse_buf = bb_xstrdup(opt);
  227. +
  228. + dprintf("multicast option=%s\n", opt);
  229. +
  230. + /* IP address */
  231. + if ((token = strtok_r(parse_buf, ",", &tokenv)) == NULL) {
  232. + dprintf("tftp_multicast_check: cannot parse IP address from %s\n", parse_buf);
  233. + free(parse_buf);
  234. + return 0;
  235. + }
  236. + if (strlen(token) > 0)
  237. + *phost = host = bb_xstrdup(token);
  238. +
  239. + /* Port */
  240. + if ((token = strtok_r(NULL, ",", &tokenv)) == NULL) {
  241. + dprintf("tftp_multicast_check: cannot parse port number from %s\n", tokenv);
  242. + goto token_error;
  243. + }
  244. + if (strlen(token) > 0) {
  245. + port = atoi(token);
  246. + if (port < 0 || port > 0xFFFF) {
  247. + dprintf("tftp_multicast_check: bad port number (%d)\n", port);
  248. + goto token_error;
  249. + }
  250. + *pport = htons(port);
  251. + }
  252. +
  253. + /* Active/passive */
  254. + if ((token = strtok_r(NULL, ",", &tokenv)) == NULL) {
  255. + dprintf("tftp_multicast_check: cannot parse active/passive from %s\n", tokenv);
  256. + goto token_error;
  257. + }
  258. + active = atoi(token);
  259. + if (active != 0 && active != 1) {
  260. + dprintf("tftp_multicast_check: bad active/passive flag (%d)\n", active);
  261. + goto token_error;
  262. + }
  263. + *pactive = active;
  264. +
  265. + free(parse_buf);
  266. + return 1;
  267. +
  268. +token_error:
  269. + free(parse_buf);
  270. + if (host != NULL)
  271. + free(host);
  272. + *phost = NULL;
  273. + return 0;
  274. +
  275. +}
  276. +
  277. +#define VECTOR_QUANTUM_WIDTH 8
  278. +#define VECTOR_QUANTUM_ALL_ONES ((1<<VECTOR_QUANTUM_WIDTH)-1)
  279. +
  280. +static void inline
  281. +bit_set(int bit, unsigned char *vector)
  282. +{
  283. + int offset = bit / VECTOR_QUANTUM_WIDTH;
  284. + int mask = 1 << (bit % VECTOR_QUANTUM_WIDTH);
  285. + vector[offset] |= mask;
  286. +}
  287. +
  288. +static int inline
  289. +bit_isset(int bit, const unsigned char *vector)
  290. +{
  291. + int offset = bit / VECTOR_QUANTUM_WIDTH;
  292. + int mask = 1 << (bit % VECTOR_QUANTUM_WIDTH);
  293. + return vector[offset] & mask ? 1 : 0;
  294. +}
  295. +
  296. +static int inline
  297. +bit_lmz(const unsigned char *vector)
  298. +{
  299. + /* Return number of left-most zero in bit vector */
  300. + const unsigned char *vp = vector;
  301. + int i;
  302. + unsigned char velem;
  303. +
  304. + while (*vp == VECTOR_QUANTUM_ALL_ONES)
  305. + vp++;
  306. + velem = *vp;
  307. + for (i = 0; i < VECTOR_QUANTUM_WIDTH; i++) {
  308. + if ((velem & (1 << i)) == 0)
  309. + break;
  310. + }
  311. + dprintf("bit_lmz: block=%d\n", (vp - vector)*VECTOR_QUANTUM_WIDTH + i);
  312. + return (vp - vector)*VECTOR_QUANTUM_WIDTH + i;
  313. +}
  314. +
  315. +#endif
  316. +
  317. +
  318. +
  319. +#ifdef TFTP_OPTIONS
  320. +
  321. static char *tftp_option_get(char *buf, int len, char *option)
  322. {
  323. - int opt_val = 0;
  324. + int opt_val = 0;
  325. int opt_found = 0;
  326. int k;
  327. -
  328. - while (len > 0) {
  329. + while (len > 0) {
  330. /* Make sure the options are terminated correctly */
  331. -
  332. for (k = 0; k < len; k++) {
  333. if (buf[k] == '\0') {
  334. break;
  335. @@ -117,9 +298,8 @@
  336. if (strcasecmp(buf, option) == 0) {
  337. opt_found = 1;
  338. }
  339. - }
  340. - else {
  341. - if (opt_found) {
  342. + } else {
  343. + if (opt_found) {
  344. return buf;
  345. }
  346. }
  347. @@ -138,7 +318,8 @@
  348. #endif
  349. static inline int tftp(const int cmd, const struct hostent *host,
  350. - const char *remotefile, int localfd, const unsigned short port, int tftp_bufsize)
  351. + const char *remotefile, int localfd, const unsigned short port,
  352. + struct tftp_option *option)
  353. {
  354. const int cmd_get = cmd & tftp_cmd_get;
  355. const int cmd_put = cmd & tftp_cmd_put;
  356. @@ -155,18 +336,29 @@
  357. int len;
  358. int opcode = 0;
  359. int finished = 0;
  360. - int timeout = bb_tftp_num_retries;
  361. + int retry = bb_tftp_num_retries;
  362. unsigned short block_nr = 1;
  363. -#ifdef CONFIG_FEATURE_TFTP_BLOCKSIZE
  364. - int want_option_ack = 0;
  365. +#ifdef CONFIG_FEATURE_TFTP_MULTICAST
  366. + struct hostent *mchost;
  367. + struct sockaddr_in mcsa;
  368. + char *mchostname;
  369. + unsigned short mcport;
  370. + unsigned char *mcblockmap = NULL;
  371. + int master_client = 1;
  372. + int mcfd = -1;
  373. + int mcmaxblock = 0x10000;
  374. + int ack_oack = 0;
  375. +#else
  376. + #define master_client 1
  377. + #define ack_oack 0
  378. #endif
  379. /* Can't use RESERVE_CONFIG_BUFFER here since the allocation
  380. * size varies meaning BUFFERS_GO_ON_STACK would fail */
  381. - char *buf=xmalloc(tftp_bufsize + 4);
  382. + char *buf=xmalloc(option->blksize + 4);
  383. - tftp_bufsize += 4;
  384. + int tftp_bufsize = option->blksize + 4;
  385. if ((socketfd = socket(PF_INET, SOCK_DGRAM, 0)) < 0) {
  386. bb_perror_msg("socket");
  387. @@ -183,15 +375,21 @@
  388. memcpy(&sa.sin_addr, (struct in_addr *) host->h_addr,
  389. sizeof(sa.sin_addr));
  390. - /* build opcode */
  391. -
  392. - if (cmd_get) {
  393. - opcode = TFTP_RRQ;
  394. +#ifdef CONFIG_FEATURE_TFTP_MULTICAST
  395. + if (option->multicast) {
  396. + const int bmsize = 0x10000 / VECTOR_QUANTUM_WIDTH;
  397. + if ((mcfd = socket(PF_INET, SOCK_DGRAM, 0)) < 0) {
  398. + bb_perror_msg("multicast socket");
  399. + return EXIT_FAILURE;
  400. + }
  401. + mcblockmap = xmalloc(bmsize+1);
  402. + memset(mcblockmap, 0, bmsize+1);
  403. }
  404. +#endif
  405. - if (cmd_put) {
  406. - opcode = TFTP_WRQ;
  407. - }
  408. + /* build opcode */
  409. +
  410. + opcode = cmd_get ? TFTP_RRQ : TFTP_WRQ;
  411. while (1) {
  412. @@ -203,7 +401,7 @@
  413. cp += 2;
  414. - /* add filename and mode */
  415. + /* First packet of file transfer includes file name, mode, and options */
  416. if ((cmd_get && (opcode == TFTP_RRQ)) ||
  417. (cmd_put && (opcode == TFTP_WRQ))) {
  418. @@ -223,7 +421,7 @@
  419. }
  420. if (too_long || ((&buf[tftp_bufsize - 1] - cp) < 6)) {
  421. - bb_error_msg("too long remote-filename");
  422. + bb_error_msg("too long: remote filename");
  423. break;
  424. }
  425. @@ -238,8 +436,8 @@
  426. if (len != TFTP_BLOCKSIZE_DEFAULT) {
  427. - if ((&buf[tftp_bufsize - 1] - cp) < 15) {
  428. - bb_error_msg("too long remote-filename");
  429. + if ((&buf[tftp_bufsize - 1] - cp) < 15) {
  430. + bb_error_msg("buffer too small for blksize option");
  431. break;
  432. }
  433. @@ -249,16 +447,65 @@
  434. cp += 8;
  435. cp += snprintf(cp, 6, "%d", len) + 1;
  436. + }
  437. +#endif
  438. +
  439. +
  440. +
  441. +#ifdef CONFIG_FEATURE_TFTP_MULTICAST
  442. +
  443. + if (option->multicast) {
  444. + if ((&buf[tftp_bufsize - 1] - cp) < 12) {
  445. + bb_error_msg("buffer too small for multicast option");
  446. + break;
  447. + }
  448. +
  449. + /* add "multicast" option */
  450. - want_option_ack = 1;
  451. + memcpy(cp, "multicast\0", 11);
  452. + cp += 11;
  453. +
  454. + option->multicast = 0; /* turn back on when server accepts option */
  455. + ack_oack = 1; /* acknowledge OACK */
  456. }
  457. +
  458. #endif
  459. +
  460. +#ifdef CONFIG_FEATURE_TFTP_TIMEOUT
  461. +
  462. + if (option->server_timeout != TFTP_TIMEOUT) {
  463. + if ((&buf[tftp_bufsize - 1] - cp) < 12) {
  464. + bb_error_msg("buffer too small for timeout option");
  465. + break;
  466. + }
  467. +
  468. + /* add "timeout" option */
  469. +
  470. + memcpy(cp, "timeout", 8);
  471. + cp += 8;
  472. +
  473. + cp += snprintf(cp, 4, "%d", option->server_timeout) + 1;
  474. + }
  475. +#endif
  476. +
  477. }
  478. /* add ack and data */
  479. - if ((cmd_get && (opcode == TFTP_ACK)) ||
  480. - (cmd_put && (opcode == TFTP_DATA))) {
  481. +#ifdef CONFIG_FEATURE_TFTP_MULTICAST
  482. + else if (option->multicast && opcode == TFTP_ACK) {
  483. + if (master_client || ack_oack) {
  484. + int blocknum = bit_lmz(mcblockmap);
  485. + *((unsigned short *) cp) = htons(blocknum);
  486. + cp += 2;
  487. + if (blocknum >= mcmaxblock)
  488. + finished = 1;
  489. + dprintf("ack block %d/%d %s\n", blocknum, mcmaxblock, finished? "finished": "");
  490. + }
  491. + }
  492. +#endif
  493. + else if ((cmd_get && opcode == TFTP_ACK) ||
  494. + (cmd_put && opcode == TFTP_DATA)) {
  495. *((unsigned short *) cp) = htons(block_nr);
  496. @@ -275,7 +522,7 @@
  497. }
  498. if (len != (tftp_bufsize - 4)) {
  499. - finished++;
  500. + finished = 1;
  501. }
  502. cp += len;
  503. @@ -283,82 +530,119 @@
  504. }
  505. - /* send packet */
  506. + /* send packet and receive reply */
  507. - timeout = bb_tftp_num_retries; /* re-initialize */
  508. + retry = bb_tftp_num_retries; /* re-initialize */
  509. do {
  510. -
  511. + int selectrc;
  512. len = cp - buf;
  513. -#ifdef CONFIG_FEATURE_TFTP_DEBUG
  514. - fprintf(stderr, "sending %u bytes\n", len);
  515. - for (cp = buf; cp < &buf[len]; cp++)
  516. - fprintf(stderr, "%02x ", (unsigned char)*cp);
  517. - fprintf(stderr, "\n");
  518. -#endif
  519. - if (sendto(socketfd, buf, len, 0,
  520. - (struct sockaddr *) &sa, sizeof(sa)) < 0) {
  521. - bb_perror_msg("send");
  522. - len = -1;
  523. - break;
  524. - }
  525. -
  526. + /* send packet */
  527. + if ((len > 2) && (! option->multicast || master_client || ack_oack)) {
  528. - if (finished && (opcode == TFTP_ACK)) {
  529. - break;
  530. +#ifdef CONFIG_FEATURE_TFTP_DEBUG
  531. + dprintf("sending %u bytes\n", len);
  532. + for (cp = buf; cp < &buf[len]; cp++)
  533. + if (debug)
  534. + printf("%02x ", *(unsigned char *)cp);
  535. + if (debug)
  536. + printf("\n");
  537. +#endif
  538. +#ifdef CONFIG_FEATURE_TFTP_MULTICAST
  539. + ack_oack = 0;
  540. +#endif
  541. + if (sendto(socketfd, buf, len, 0, (struct sockaddr *) &sa, sizeof(sa)) < 0) {
  542. + bb_perror_msg("send");
  543. + len = -1;
  544. + break;
  545. + }
  546. + if (finished && opcode == TFTP_ACK) {
  547. + break;
  548. + }
  549. }
  550. - /* receive packet */
  551. + /* receive reply packet */
  552. memset(&from, 0, sizeof(from));
  553. fromlen = sizeof(from);
  554. - tv.tv_sec = TFTP_TIMEOUT;
  555. + tv.tv_sec = option->client_timeout;
  556. tv.tv_usec = 0;
  557. FD_ZERO(&rfds);
  558. FD_SET(socketfd, &rfds);
  559. + dprintf("set to receive from socketfd (%d)\n", socketfd);
  560. +#ifdef CONFIG_FEATURE_TFTP_MULTICAST
  561. + if (option->multicast) {
  562. + FD_SET(mcfd, &rfds);
  563. + dprintf("set to receive from mcfd (%d)\n", mcfd);
  564. + }
  565. +#endif
  566. - switch (select(FD_SETSIZE, &rfds, NULL, NULL, &tv)) {
  567. - case 1:
  568. - len = recvfrom(socketfd, buf, tftp_bufsize, 0,
  569. - (struct sockaddr *) &from, &fromlen);
  570. -
  571. - if (len < 0) {
  572. - bb_perror_msg("recvfrom");
  573. - break;
  574. + dprintf("select\n");
  575. + selectrc = select(FD_SETSIZE, &rfds, NULL, NULL, &tv);
  576. + if (selectrc > 0) {
  577. + /* A packet was received */
  578. + if (FD_ISSET(socketfd, &rfds)) { /* Unicast packet */
  579. + dprintf("from socketfd\n");
  580. + len = recvfrom(socketfd, buf, tftp_bufsize, 0, (struct sockaddr *) &from, &fromlen);
  581. +
  582. + if (len < 0) {
  583. + bb_perror_msg("recvfrom");
  584. + } else {
  585. + if (sa.sin_port == port) {
  586. + sa.sin_port = from.sin_port;
  587. + }
  588. + if (sa.sin_port == from.sin_port) {
  589. + retry = 0;
  590. + } else {
  591. + /* bad packet */
  592. + /* discard the packet - treat as timeout */
  593. + retry = bb_tftp_num_retries;
  594. + bb_error_msg("timeout");
  595. + }
  596. + }
  597. }
  598. - timeout = 0;
  599. -
  600. - if (sa.sin_port == port) {
  601. - sa.sin_port = from.sin_port;
  602. +#ifdef CONFIG_FEATURE_TFTP_MULTICAST
  603. + else if (option->multicast && FD_ISSET(mcfd, &rfds)) { /* Multicast packet */
  604. + dprintf("from mcfd\n");
  605. + len = recvfrom(mcfd, buf, tftp_bufsize, 0, (struct sockaddr *) &from, &fromlen);
  606. + if (len < 0) {
  607. + bb_perror_msg("multicast recvfrom");
  608. + } else {
  609. + if (mcsa.sin_port == mcport) {
  610. + mcsa.sin_port = from.sin_port;
  611. + }
  612. + if (mcsa.sin_port == from.sin_port) {
  613. + retry = 0;
  614. + } else {
  615. + retry = bb_tftp_num_retries;
  616. + bb_error_msg("multicast timeout");
  617. + }
  618. + }
  619. }
  620. - if (sa.sin_port == from.sin_port) {
  621. - break;
  622. - }
  623. -
  624. - /* fall-through for bad packets! */
  625. - /* discard the packet - treat as timeout */
  626. - timeout = bb_tftp_num_retries;
  627. +#endif
  628. - case 0:
  629. + } else if (selectrc == 0) {
  630. + /* Time out */
  631. + dprintf("timeout\n");
  632. bb_error_msg("timeout");
  633. - timeout--;
  634. - if (timeout == 0) {
  635. + retry--;
  636. + if (retry == 0) {
  637. len = -1;
  638. bb_error_msg("last timeout");
  639. }
  640. - break;
  641. -
  642. - default:
  643. + } else {
  644. + /* Error condition */
  645. + dprintf("error\n");
  646. bb_perror_msg("select");
  647. len = -1;
  648. }
  649. - } while (timeout && (len >= 0));
  650. + } while (retry && len >= 0);
  651. if ((finished) || (len < 0)) {
  652. break;
  653. @@ -370,9 +654,8 @@
  654. opcode = ntohs(*((unsigned short *) buf));
  655. tmp = ntohs(*((unsigned short *) &buf[2]));
  656. -#ifdef CONFIG_FEATURE_TFTP_DEBUG
  657. - fprintf(stderr, "received %d bytes: %04x %04x\n", len, opcode, tmp);
  658. -#endif
  659. + dprintf("received %d bytes: %04x %04x\n", len, opcode, tmp);
  660. + dprintf("master_client=%d\n", master_client);
  661. if (opcode == TFTP_ERROR) {
  662. char *msg = NULL;
  663. @@ -393,55 +676,116 @@
  664. break;
  665. }
  666. -#ifdef CONFIG_FEATURE_TFTP_BLOCKSIZE
  667. - if (want_option_ack) {
  668. +#ifdef TFTP_OPTIONS
  669. - want_option_ack = 0;
  670. + if (opcode == TFTP_OACK) {
  671. - if (opcode == TFTP_OACK) {
  672. + /* server seems to support options */
  673. - /* server seems to support options */
  674. + char *res;
  675. +
  676. + block_nr = 0; /* acknowledge option packet with block number 0 */
  677. + opcode = cmd_put ? TFTP_DATA : TFTP_ACK;
  678. - char *res;
  679. - res = tftp_option_get(&buf[2], len-2,
  680. - "blksize");
  681. +#ifdef CONFIG_FEATURE_TFTP_BLOCKSIZE
  682. + res = tftp_option_get(&buf[2], len-2, "blksize");
  683. - if (res) {
  684. - int blksize = atoi(res);
  685. -
  686. - if (tftp_blocksize_check(blksize,
  687. - tftp_bufsize - 4)) {
  688. + if (res) {
  689. + int blksize = atoi(res);
  690. - if (cmd_put) {
  691. - opcode = TFTP_DATA;
  692. - }
  693. - else {
  694. - opcode = TFTP_ACK;
  695. - }
  696. -#ifdef CONFIG_FEATURE_TFTP_DEBUG
  697. - fprintf(stderr, "using blksize %u\n", blksize);
  698. + if (tftp_blocksize_check(blksize, tftp_bufsize - 4)) {
  699. + dprintf("using blksize %d\n", blksize);
  700. + tftp_bufsize = blksize + 4;
  701. + free(buf);
  702. + buf = xmalloc(tftp_bufsize);
  703. + } else {
  704. + bb_error_msg("bad blksize %d", blksize);
  705. + break;
  706. + }
  707. + }
  708. #endif
  709. - tftp_bufsize = blksize + 4;
  710. - block_nr = 0;
  711. - continue;
  712. - }
  713. - }
  714. - /* FIXME:
  715. - * we should send ERROR 8 */
  716. - bb_error_msg("bad server option");
  717. - break;
  718. - }
  719. - bb_error_msg("warning: blksize not supported by server"
  720. - " - reverting to 512");
  721. - tftp_bufsize = TFTP_BLOCKSIZE_DEFAULT + 4;
  722. +#ifdef CONFIG_FEATURE_TFTP_MULTICAST
  723. + res = tftp_option_get(&buf[2], len-2, "multicast");
  724. +
  725. + if (res) {
  726. + ack_oack = 1;
  727. + if (tftp_multicast_check(res, &mchostname, &mcport, &master_client)) {
  728. + struct ip_mreq mreq;
  729. + struct in_addr mcaddr;
  730. +
  731. + dprintf("using multicast\n");
  732. +
  733. + mchost = xgethostbyname(mchostname);
  734. + if (mchost) {
  735. + memcpy(&mcaddr, mchost->h_addr, mchost->h_length);
  736. + if (! IN_MULTICAST(ntohl(mcaddr.s_addr))) {
  737. + bb_error_msg("bad multicast address: %s", mchostname);
  738. + break;
  739. + }
  740. + } else {
  741. + bb_error_msg("bad multicast address: %s", mchostname);
  742. + break;
  743. + }
  744. +
  745. + memset(&mcsa, 0, sizeof(mcsa));
  746. + mcsa.sin_family = AF_INET;
  747. + mcsa.sin_addr.s_addr = htonl(INADDR_ANY);
  748. + mcsa.sin_port = mcport;
  749. +
  750. + bind(mcfd, (struct sockaddr *)&mcsa, sizeof(mcsa));
  751. +
  752. + mreq.imr_multiaddr.s_addr = mcaddr.s_addr;
  753. + mreq.imr_interface.s_addr = htonl(INADDR_ANY);
  754. +
  755. + if (setsockopt(mcfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0)
  756. + {
  757. + bb_error_msg("setsockopt");
  758. + break;
  759. + }
  760. +
  761. + option->multicast = 1;
  762. + } else {
  763. + bb_error_msg("bad multicast option value: %s", res);
  764. + break;
  765. + }
  766. + }
  767. +#endif
  768. +
  769. }
  770. + else
  771. #endif
  772. if (cmd_get && (opcode == TFTP_DATA)) {
  773. +#ifdef CONFIG_FEATURE_TFTP_MULTICAST
  774. + if (option->multicast) {
  775. + int bn = tmp - 1;
  776. + /* Do I need this block? */
  777. + if (! bit_isset(bn, mcblockmap)) {
  778. + lseek(localfd, bn*(tftp_bufsize-4), SEEK_SET);
  779. + len = write(localfd, &buf[4], len-4);
  780. + if (len < 0) {
  781. + bb_perror_msg("write");
  782. + break;
  783. + }
  784. + bit_set(bn, mcblockmap);
  785. + if (len != (tftp_bufsize-4)) {
  786. + mcmaxblock = tmp;
  787. + dprintf("mcmaxblock=%d, (len(%d) != tftp_bufsize-4(%d))\n", mcmaxblock, len, tftp_bufsize-4);
  788. + }
  789. + opcode = TFTP_ACK;
  790. + }
  791. + /* Do not acknowledge block if I already have a copy of the block. A situation can arise when the server
  792. + * and client timeout nearly simultaneously. The server retransmits the block at the same time the client
  793. + * re-requests the block. From then on out, each block is transmitted twice--not a good use of bandwidth.
  794. + */
  795. + }
  796. + else
  797. +#endif
  798. +
  799. if (tmp == block_nr) {
  800. len = write(localfd, &buf[4], len - 4);
  801. @@ -452,15 +796,14 @@
  802. }
  803. if (len != (tftp_bufsize - 4)) {
  804. - finished++;
  805. + finished = 1;
  806. }
  807. opcode = TFTP_ACK;
  808. - continue;
  809. }
  810. }
  811. - if (cmd_put && (opcode == TFTP_ACK)) {
  812. + else if (cmd_put && opcode == TFTP_ACK) {
  813. if (tmp == (unsigned short)(block_nr - 1)) {
  814. if (finished) {
  815. @@ -468,15 +811,19 @@
  816. }
  817. opcode = TFTP_DATA;
  818. - continue;
  819. }
  820. }
  821. }
  822. #ifdef CONFIG_FEATURE_CLEAN_UP
  823. close(socketfd);
  824. + free(buf);
  825. +
  826. +#ifdef CONFIG_FEATURE_TFTP_MULTICAST
  827. + if (mcblockmap != NULL)
  828. + free(mcblockmap);
  829. +#endif
  830. - free(buf);
  831. #endif
  832. return finished ? EXIT_SUCCESS : EXIT_FAILURE;
  833. @@ -487,13 +834,18 @@
  834. struct hostent *host = NULL;
  835. char *localfile = NULL;
  836. char *remotefile = NULL;
  837. - int port;
  838. + unsigned short port;
  839. int cmd = 0;
  840. int fd = -1;
  841. int flags = 0;
  842. int opt;
  843. int result;
  844. - int blocksize = TFTP_BLOCKSIZE_DEFAULT;
  845. + struct tftp_option option = {
  846. + .multicast = 0,
  847. + .blksize = TFTP_BLOCKSIZE_DEFAULT,
  848. + .client_timeout = TFTP_TIMEOUT,
  849. + .server_timeout = TFTP_TIMEOUT,
  850. + };
  851. /* figure out what to pass to getopt */
  852. @@ -515,13 +867,45 @@
  853. #define PUT
  854. #endif
  855. - while ((opt = getopt(argc, argv, BS GET PUT "l:r:")) != -1) {
  856. +#ifdef CONFIG_FEATURE_TFTP_TIMEOUT
  857. +#define TO "T:t:"
  858. +#else
  859. +#define TO
  860. +#endif
  861. +
  862. +#ifdef CONFIG_FEATURE_TFTP_MULTICAST
  863. +#define MC "m"
  864. +#else
  865. +#define MC
  866. +#endif
  867. +
  868. +#ifdef CONFIG_FEATURE_TFTP_DEBUG
  869. +#define DB "D"
  870. +#else
  871. +#define DB
  872. +#endif
  873. +
  874. + while ((opt = getopt(argc, argv, BS GET PUT TO MC DB "l:r:")) != -1) {
  875. switch (opt) {
  876. #ifdef CONFIG_FEATURE_TFTP_BLOCKSIZE
  877. case 'b':
  878. - blocksize = atoi(optarg);
  879. - if (!tftp_blocksize_check(blocksize, 0)) {
  880. - return EXIT_FAILURE;
  881. + option.blksize = atoi(optarg);
  882. + if (!tftp_blocksize_check(option.blksize, 0)) {
  883. + return EXIT_FAILURE;
  884. + }
  885. + break;
  886. +#endif
  887. +#ifdef CONFIG_FEATURE_TFTP_TIMEOUT
  888. + case 'T':
  889. + option.client_timeout = atoi(optarg);
  890. + if (!tftp_timeout_check(option.client_timeout)) {
  891. + return EXIT_FAILURE;
  892. + }
  893. + break;
  894. + case 't':
  895. + option.server_timeout = atoi(optarg);
  896. + if (!tftp_timeout_check(option.server_timeout)) {
  897. + return EXIT_FAILURE;
  898. }
  899. break;
  900. #endif
  901. @@ -537,18 +921,34 @@
  902. flags = O_RDONLY;
  903. break;
  904. #endif
  905. +#ifdef CONFIG_FEATURE_TFTP_MULTICAST
  906. + case 'm':
  907. + option.multicast = 1; /* receive multicast file */
  908. + break;
  909. +#endif
  910. +#ifdef CONFIG_FEATURE_TFTP_DEBUG
  911. + case 'D':
  912. + debug = 1;
  913. + break;
  914. +#endif
  915. case 'l':
  916. localfile = bb_xstrdup(optarg);
  917. break;
  918. case 'r':
  919. remotefile = bb_xstrdup(optarg);
  920. break;
  921. + default:
  922. + bb_show_usage();
  923. }
  924. }
  925. if ((cmd == 0) || (optind == argc)) {
  926. bb_show_usage();
  927. }
  928. + if (cmd == tftp_cmd_put && option.multicast) {
  929. + fprintf(stderr, "Multicast (-m) invalid option with put (-p) command\n");
  930. + exit(EXIT_FAILURE);
  931. + }
  932. if(localfile && strcmp(localfile, "-") == 0) {
  933. fd = fileno((cmd==tftp_cmd_get)? stdout : stdin);
  934. }
  935. @@ -566,14 +966,12 @@
  936. host = xgethostbyname(argv[optind]);
  937. port = bb_lookup_port(argv[optind + 1], "udp", 69);
  938. -#ifdef CONFIG_FEATURE_TFTP_DEBUG
  939. - fprintf(stderr, "using server \"%s\", remotefile \"%s\", "
  940. + dprintf("using server \"%s\", remotefile \"%s\", "
  941. "localfile \"%s\".\n",
  942. inet_ntoa(*((struct in_addr *) host->h_addr)),
  943. remotefile, localfile);
  944. -#endif
  945. - result = tftp(cmd, host, remotefile, fd, port, blocksize);
  946. + result = tftp(cmd, host, remotefile, fd, port, &option);
  947. #ifdef CONFIG_FEATURE_CLEAN_UP
  948. if (!(fd == STDOUT_FILENO || fd == STDIN_FILENO)) {
  949. @@ -582,3 +980,18 @@
  950. #endif
  951. return(result);
  952. }
  953. +
  954. +
  955. +#ifdef CONFIG_FEATURE_TFTP_DEBUG
  956. +
  957. +#include <sys/time.h>
  958. +
  959. +static void
  960. +printtime(void)
  961. +{
  962. + struct timeval tv;
  963. + gettimeofday(&tv, NULL);
  964. + printf("%11lu.%06lu ", tv.tv_sec, tv.tv_usec);
  965. +}
  966. +
  967. +#endif