fix infinite retry bug. also, reduce per-packet receive timeout
[oweals/busybox.git] / networking / tftp.c
1 /* vi: set sw=4 ts=4: */
2 /* -------------------------------------------------------------------------
3  * tftp.c
4  *
5  * A simple tftp client for busybox.
6  * Tries to follow RFC1350.
7  * Only "octet" mode supported.
8  * Optional blocksize negotiation (RFC2347 + RFC2348)
9  *
10  * Copyright (C) 2001 Magnus Damm <damm@opensource.se>
11  *
12  * Parts of the code based on:
13  *
14  * atftp:  Copyright (C) 2000 Jean-Pierre Lefebvre <helix@step.polymtl.ca>
15  *                        and Remi Lefebvre <remi@debian.org>
16  *
17  * utftp:  Copyright (C) 1999 Uwe Ohse <uwe@ohse.de>
18  *
19  * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
20  * ------------------------------------------------------------------------- */
21
22 #include "libbb.h"
23
24 #if ENABLE_FEATURE_TFTP_GET || ENABLE_FEATURE_TFTP_PUT
25
26 #define TFTP_BLOCKSIZE_DEFAULT 512      /* according to RFC 1350, don't change */
27 #define TFTP_TIMEOUT 50000              /* 50ms, in microseconds */
28 #define TFTP_MAXTIMEOUT 999000          /* about 1 second, in microseconds */
29 #define TFTP_NUM_RETRIES 12             /* number of backed-off retries */
30
31 /* opcodes we support */
32 #define TFTP_RRQ   1
33 #define TFTP_WRQ   2
34 #define TFTP_DATA  3
35 #define TFTP_ACK   4
36 #define TFTP_ERROR 5
37 #define TFTP_OACK  6
38
39 #if ENABLE_FEATURE_TFTP_GET && !ENABLE_FEATURE_TFTP_PUT
40 #define USE_GETPUT(...)
41 #define CMD_GET(cmd) 1
42 #define CMD_PUT(cmd) 0
43 #elif !ENABLE_FEATURE_TFTP_GET && ENABLE_FEATURE_TFTP_PUT
44 #define USE_GETPUT(...)
45 #define CMD_GET(cmd) 0
46 #define CMD_PUT(cmd) 1
47 #else
48 #define USE_GETPUT(...) __VA_ARGS__
49 /* masks coming from getpot32 */
50 #define CMD_GET(cmd) ((cmd) & 1)
51 #define CMD_PUT(cmd) ((cmd) & 2)
52 #endif
53 /* NB: in the code below
54  * CMD_GET(cmd) and CMD_PUT(cmd) are mutually exclusive
55  */
56
57
58 #if ENABLE_FEATURE_TFTP_BLOCKSIZE
59
60 static int tftp_blocksize_check(int blocksize, int bufsize)
61 {
62         /* Check if the blocksize is valid:
63          * RFC2348 says between 8 and 65464,
64          * but our implementation makes it impossible
65          * to use blocksizes smaller than 22 octets.
66          */
67
68         if ((bufsize && (blocksize > bufsize))
69          || (blocksize < 8) || (blocksize > 65564)
70         ) {
71                 bb_error_msg("bad blocksize");
72                 return 0;
73         }
74
75         return blocksize;
76 }
77
78 static char *tftp_option_get(char *buf, int len, const char *option)
79 {
80         int opt_val = 0;
81         int opt_found = 0;
82         int k;
83
84         while (len > 0) {
85                 /* Make sure the options are terminated correctly */
86                 for (k = 0; k < len; k++) {
87                         if (buf[k] == '\0') {
88                                 goto nul_found;
89                         }
90                 }
91                 return NULL;
92  nul_found:
93                 if (opt_val == 0) {
94                         if (strcasecmp(buf, option) == 0) {
95                                 opt_found = 1;
96                         }
97                 } else if (opt_found) {
98                         return buf;
99                 }
100
101                 k++;
102                 buf += k;
103                 len -= k;
104                 opt_val ^= 1;
105         }
106
107         return NULL;
108 }
109
110 #endif
111
112 static int tftp( USE_GETPUT(const int cmd,)
113                 len_and_sockaddr *peer_lsa,
114                 const char *remotefile, const int localfd,
115                 unsigned port, int tftp_bufsize)
116 {
117         struct timeval tv;
118         fd_set rfds;
119         int socketfd;
120         int len;
121         int send_len;
122         USE_FEATURE_TFTP_BLOCKSIZE(smallint want_option_ack = 0;)
123         smallint finished = 0;
124         uint16_t opcode;
125         uint16_t block_nr = 1;
126         uint16_t recv_blk;
127         int retries, waittime;
128         char *cp;
129
130         unsigned org_port;
131         len_and_sockaddr *const from = alloca(offsetof(len_and_sockaddr, sa) + peer_lsa->len);
132
133         /* Can't use RESERVE_CONFIG_BUFFER here since the allocation
134          * size varies meaning BUFFERS_GO_ON_STACK would fail */
135         /* We must keep the transmit and receive buffers seperate */
136         /* In case we rcv a garbage pkt and we need to rexmit the last pkt */
137         char *xbuf = xmalloc(tftp_bufsize += 4);
138         char *rbuf = xmalloc(tftp_bufsize);
139
140         port = org_port = htons(port);
141
142         socketfd = xsocket(peer_lsa->sa.sa_family, SOCK_DGRAM, 0);
143
144         /* build opcode */
145         opcode = TFTP_WRQ;
146         if (CMD_GET(cmd)) {
147                 opcode = TFTP_RRQ;
148         }
149         cp = xbuf + 2;
150         /* add filename and mode */
151         /* fill in packet if the filename fits into xbuf */
152         len = strlen(remotefile) + 1;
153         if (2 + len + sizeof("octet") >= tftp_bufsize) {
154                 bb_error_msg("remote filename is too long");
155                 goto ret;
156         }
157         strcpy(cp, remotefile);
158         cp += len;
159         /* add "mode" part of the package */
160         strcpy(cp, "octet");
161         cp += sizeof("octet");
162
163 #if ENABLE_FEATURE_TFTP_BLOCKSIZE
164         len = tftp_bufsize - 4; /* data block size */
165         if (len != TFTP_BLOCKSIZE_DEFAULT) {
166                 /* rfc2348 says that 65464 is a max allowed value */
167                 if ((&xbuf[tftp_bufsize - 1] - cp) < sizeof("blksize NNNNN")) {
168                         bb_error_msg("remote filename is too long");
169                         goto ret;
170                 }
171                 /* add "blksize", <nul>, blocksize */
172                 strcpy(cp, "blksize");
173                 cp += sizeof("blksize");
174                 cp += snprintf(cp, 6, "%d", len) + 1;
175                 want_option_ack = 1;
176         }
177 #endif
178         /* First packet is built, so skip packet generation */
179         goto send_pkt;
180
181         /* Using mostly goto's - continue/break will be less clear
182          * in where we actually jump to */
183
184         while (1) {
185                 /* Build ACK or DATA */
186                 cp = xbuf + 2;
187                 *((uint16_t*)cp) = htons(block_nr);
188                 cp += 2;
189                 block_nr++;
190                 opcode = TFTP_ACK;
191                 if (CMD_PUT(cmd)) {
192                         opcode = TFTP_DATA;
193                         len = full_read(localfd, cp, tftp_bufsize - 4);
194                         if (len < 0) {
195                                 bb_perror_msg(bb_msg_read_error);
196                                 goto ret;
197                         }
198                         if (len != (tftp_bufsize - 4)) {
199                                 finished = 1;
200                         }
201                         cp += len;
202                 }
203  send_pkt:
204                 /* Send packet */
205                 *((uint16_t*)xbuf) = htons(opcode); /* fill in opcode part */
206                 send_len = cp - xbuf;
207                 /* NB: send_len value is preserved in code below
208                  * for potential resend */
209
210                 retries = TFTP_NUM_RETRIES;     /* re-initialize */
211                 waittime = TFTP_TIMEOUT;
212
213  send_again:
214 #if ENABLE_DEBUG_TFTP
215                 fprintf(stderr, "sending %u bytes\n", send_len);
216                 for (cp = xbuf; cp < &xbuf[send_len]; cp++)
217                         fprintf(stderr, "%02x ", (unsigned char) *cp);
218                 fprintf(stderr, "\n");
219 #endif
220                 xsendto(socketfd, xbuf, send_len, &peer_lsa->sa, peer_lsa->len);
221                 /* Was it final ACK? then exit */
222                 if (finished && (opcode == TFTP_ACK))
223                         goto ret;
224
225  recv_again:
226                 /* Receive packet */
227                 tv.tv_sec = 0;
228                 tv.tv_usec = waittime;
229                 FD_ZERO(&rfds);
230                 FD_SET(socketfd, &rfds);
231                 switch (select(socketfd + 1, &rfds, NULL, NULL, &tv)) {
232                         unsigned from_port;
233                 case 1:
234                         from->len = peer_lsa->len;
235                         memset(&from->sa, 0, peer_lsa->len);
236                         len = recvfrom(socketfd, rbuf, tftp_bufsize, 0,
237                                                 &from->sa, &from->len);
238                         if (len < 0) {
239                                 bb_perror_msg("recvfrom");
240                                 goto ret;
241                         }
242                         from_port = get_nport(&from->sa);
243                         if (port == org_port) {
244                                 /* Our first query went to port 69
245                                  * but reply will come from different one.
246                                  * Remember and use this new port */
247                                 port = from_port;
248                                 set_nport(peer_lsa, from_port);
249                         }
250                         if (port != from_port)
251                                 goto recv_again;
252                         goto process_pkt;
253                 case 0:
254                         retries--;
255                         if (retries == 0) {
256                                 bb_error_msg("timeout");
257                                 goto ret;
258                         }
259
260                         /* exponential backoff with limit */
261                         waittime += waittime/2;
262                         if (waittime > TFTP_MAXTIMEOUT) {
263                                 waittime = TFTP_MAXTIMEOUT;
264                         }
265
266                         goto send_again; /* resend last sent pkt */
267                 default:
268                         bb_perror_msg("select");
269                         goto ret;
270                 }
271  process_pkt:
272                 /* Process recv'ed packet */
273                 opcode = ntohs( ((uint16_t*)rbuf)[0] );
274                 recv_blk = ntohs( ((uint16_t*)rbuf)[1] );
275
276 #if ENABLE_DEBUG_TFTP
277                 fprintf(stderr, "received %d bytes: %04x %04x\n", len, opcode, recv_blk);
278 #endif
279
280                 if (opcode == TFTP_ERROR) {
281                         static const char *const errcode_str[] = {
282                                 "",
283                                 "file not found",
284                                 "access violation",
285                                 "disk full",
286                                 "illegal TFTP operation",
287                                 "unknown transfer id",
288                                 "file already exists",
289                                 "no such user",
290                                 "bad option"
291                         };
292
293                         const char *msg = "";
294
295                         if (rbuf[4] != '\0') {
296                                 msg = &rbuf[4];
297                                 rbuf[tftp_bufsize - 1] = '\0';
298                         } else if (recv_blk < ARRAY_SIZE(errcode_str)) {
299                                 msg = errcode_str[recv_blk];
300                         }
301                         bb_error_msg("server error: (%u) %s", recv_blk, msg);
302                         goto ret;
303                 }
304
305 #if ENABLE_FEATURE_TFTP_BLOCKSIZE
306                 if (want_option_ack) {
307                         want_option_ack = 0;
308
309                         if (opcode == TFTP_OACK) {
310                                 /* server seems to support options */
311                                 char *res;
312
313                                 res = tftp_option_get(&rbuf[2], len - 2, "blksize");
314                                 if (res) {
315                                         int blksize = xatoi_u(res);
316                                         if (!tftp_blocksize_check(blksize, tftp_bufsize - 4)) {
317                                                 /* send ERROR 8 to server... */
318                                                 /* htons can be impossible to use in const initializer: */
319                                                 /*static const uint16_t error_8[2] = { htons(TFTP_ERROR), htons(8) };*/
320                                                 /* thus we open-code big-endian layout */
321                                                 static const uint8_t error_8[4] = { 0,TFTP_ERROR, 0,8 };
322                                                 xsendto(socketfd, error_8, 4, &peer_lsa->sa, peer_lsa->len);
323                                                 bb_error_msg("server proposes bad blksize %d, exiting", blksize);
324                                                 goto ret;
325                                         }
326 #if ENABLE_DEBUG_TFTP
327                                         fprintf(stderr, "using blksize %u\n",
328                                                         blksize);
329 #endif
330                                         tftp_bufsize = blksize + 4;
331                                         /* Send ACK for OACK ("block" no: 0) */
332                                         block_nr = 0;
333                                         continue;
334                                 }
335                                 /* rfc2347:
336                                  * "An option not acknowledged by the server
337                                  *  must be ignored by the client and server
338                                  *  as if it were never requested." */
339                         }
340
341                         bb_error_msg("blksize is not supported by server"
342                                                 " - reverting to 512");
343                         tftp_bufsize = TFTP_BLOCKSIZE_DEFAULT + 4;
344                 }
345 #endif
346                 /* block_nr is already advanced to next block# we expect
347                  * to get / block# we are about to send next time */
348
349                 if (CMD_GET(cmd) && (opcode == TFTP_DATA)) {
350                         if (recv_blk == block_nr) {
351                                 len = full_write(localfd, &rbuf[4], len - 4);
352                                 if (len < 0) {
353                                         bb_perror_msg(bb_msg_write_error);
354                                         goto ret;
355                                 }
356                                 if (len != (tftp_bufsize - 4)) {
357                                         finished = 1;
358                                 }
359                                 continue; /* send ACK */
360                         }
361                         if (recv_blk == (block_nr - 1)) {
362                                 /* Server lost our TFTP_ACK.  Resend it */
363                                 block_nr = recv_blk;
364                                 continue;
365                         }
366                 }
367
368                 if (CMD_PUT(cmd) && (opcode == TFTP_ACK)) {
369                         /* did server ACK our last DATA pkt? */
370                         if (recv_blk == (uint16_t) (block_nr - 1)) {
371                                 if (finished)
372                                         goto ret;
373                                 continue; /* send next block */
374                         }
375                 }
376                 /* Awww... recv'd packet is not recognized! */
377                 goto recv_again;
378                 /* why recv_again? - rfc1123 says:
379                  * "The sender (i.e., the side originating the DATA packets)
380                  *  must never resend the current DATA packet on receipt
381                  *  of a duplicate ACK".
382                  * DATA pkts are resent ONLY on timeout.
383                  * Thus "goto send_again" will ba a bad mistake above.
384                  * See:
385                  * http://en.wikipedia.org/wiki/Sorcerer's_Apprentice_Syndrome
386                  */
387         }
388  ret:
389         if (ENABLE_FEATURE_CLEAN_UP) {
390                 close(socketfd);
391                 free(xbuf);
392                 free(rbuf);
393         }
394         return finished == 0; /* returns 1 on failure */
395 }
396
397 int tftp_main(int argc, char **argv);
398 int tftp_main(int argc, char **argv)
399 {
400         len_and_sockaddr *peer_lsa;
401         const char *localfile = NULL;
402         const char *remotefile = NULL;
403 #if ENABLE_FEATURE_TFTP_BLOCKSIZE
404         const char *sblocksize = NULL;
405 #endif
406         int port;
407         USE_GETPUT(int cmd;)
408         int fd = -1;
409         int flags = 0;
410         int result;
411         int blocksize = TFTP_BLOCKSIZE_DEFAULT;
412
413         /* -p or -g is mandatory, and they are mutually exclusive */
414         opt_complementary = "" USE_FEATURE_TFTP_GET("g:") USE_FEATURE_TFTP_PUT("p:")
415                         USE_GETPUT("?g--p:p--g");
416
417         USE_GETPUT(cmd =) getopt32(argv,
418                         USE_FEATURE_TFTP_GET("g") USE_FEATURE_TFTP_PUT("p")
419                                 "l:r:" USE_FEATURE_TFTP_BLOCKSIZE("b:"),
420                         &localfile, &remotefile
421                         USE_FEATURE_TFTP_BLOCKSIZE(, &sblocksize));
422         argv += optind;
423
424         flags = O_RDONLY;
425         if (CMD_GET(cmd))
426                 flags = O_WRONLY | O_CREAT | O_TRUNC;
427
428 #if ENABLE_FEATURE_TFTP_BLOCKSIZE
429         if (sblocksize) {
430                 blocksize = xatoi_u(sblocksize);
431                 if (!tftp_blocksize_check(blocksize, 0)) {
432                         return EXIT_FAILURE;
433                 }
434         }
435 #endif
436
437         if (!localfile)
438                 localfile = remotefile;
439         if (!remotefile)
440                 remotefile = localfile;
441         /* Error if filename or host is not known */
442         if (!remotefile || !argv[0])
443                 bb_show_usage();
444
445         fd = CMD_GET(cmd) ? STDOUT_FILENO : STDIN_FILENO;
446         if (!LONE_DASH(localfile)) {
447                 fd = xopen(localfile, flags);
448         }
449
450         port = bb_lookup_port(argv[1], "udp", 69);
451         peer_lsa = xhost2sockaddr(argv[0], port);
452
453 #if ENABLE_DEBUG_TFTP
454         fprintf(stderr, "using server '%s', remotefile '%s', localfile '%s'\n",
455                         xmalloc_sockaddr2dotted(&peer_lsa->sa),
456                         remotefile, localfile);
457 #endif
458
459         result = tftp( USE_GETPUT(cmd,) peer_lsa, remotefile, fd, port, blocksize);
460
461         if (ENABLE_FEATURE_CLEAN_UP)
462                 close(fd);
463         if (result != EXIT_SUCCESS && !LONE_DASH(localfile) && CMD_GET(cmd)) {
464                 unlink(localfile);
465         }
466         return result;
467 }
468
469 #endif /* ENABLE_FEATURE_TFTP_GET || ENABLE_FEATURE_TFTP_PUT */