fixing bugs revealed by randomconfig runs
[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 "busybox.h"
23
24
25 #if ENABLE_FEATURE_TFTP_GET || ENABLE_FEATURE_TFTP_PUT
26
27 #define TFTP_BLOCKSIZE_DEFAULT 512      /* according to RFC 1350, don't change */
28 #define TFTP_TIMEOUT 5  /* seconds */
29 #define TFTP_NUM_RETRIES 5 /* number of 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 static const char *const tftp_bb_error_msg[] = {
40         "Undefined error",
41         "File not found",
42         "Access violation",
43         "Disk full or allocation error",
44         "Illegal TFTP operation",
45         "Unknown transfer ID",
46         "File already exists",
47         "No such user"
48 };
49
50 #if ENABLE_FEATURE_TFTP_GET && !ENABLE_FEATURE_TFTP_PUT
51 #define USE_GETPUT(a)
52 #define CMD_GET(cmd) 1
53 #define CMD_PUT(cmd) 0
54 #elif !ENABLE_FEATURE_TFTP_GET && ENABLE_FEATURE_TFTP_PUT
55 #define USE_GETPUT(a)
56 #define CMD_GET(cmd) 0
57 #define CMD_PUT(cmd) 1
58 #else
59 #define USE_GETPUT(a) a
60 /* masks coming from getpot32 */
61 #define CMD_GET(cmd) ((cmd) & 1)
62 #define CMD_PUT(cmd) ((cmd) & 2)
63 #endif
64 /* NB: in the code below
65  * CMD_GET(cmd) and CMD_GET(cmd) are mutually exclusive
66  */
67
68
69 #if ENABLE_FEATURE_TFTP_BLOCKSIZE
70
71 static int tftp_blocksize_check(int blocksize, int bufsize)
72 {
73         /* Check if the blocksize is valid:
74          * RFC2348 says between 8 and 65464,
75          * but our implementation makes it impossible
76          * to use blocksizes smaller than 22 octets.
77          */
78
79         if ((bufsize && (blocksize > bufsize))
80          || (blocksize < 8) || (blocksize > 65564)
81         ) {
82                 bb_error_msg("bad blocksize");
83                 return 0;
84         }
85
86         return blocksize;
87 }
88
89 static char *tftp_option_get(char *buf, int len, const char * const option)
90 {
91         int opt_val = 0;
92         int opt_found = 0;
93         int k;
94
95         while (len > 0) {
96                 /* Make sure the options are terminated correctly */
97
98                 for (k = 0; k < len; k++) {
99                         if (buf[k] == '\0') {
100                                 break;
101                         }
102                 }
103
104                 if (k >= len) {
105                         break;
106                 }
107
108                 if (opt_val == 0) {
109                         if (strcasecmp(buf, option) == 0) {
110                                 opt_found = 1;
111                         }
112                 } else {
113                         if (opt_found) {
114                                 return buf;
115                         }
116                 }
117
118                 k++;
119
120                 buf += k;
121                 len -= k;
122
123                 opt_val ^= 1;
124         }
125
126         return NULL;
127 }
128
129 #endif
130
131 static int tftp(
132 #if ENABLE_FEATURE_TFTP_GET && ENABLE_FEATURE_TFTP_PUT
133                 const int cmd,
134 #endif
135                 const len_and_sockaddr *peer_lsa,
136                 const char *remotefile, const int localfd,
137                 unsigned port, int tftp_bufsize)
138 {
139         struct timeval tv;
140         fd_set rfds;
141         int socketfd;
142         int len;
143         int opcode = 0;
144         int finished = 0;
145         int timeout = TFTP_NUM_RETRIES;
146         uint16_t block_nr = 1;
147         uint16_t tmp;
148         char *cp;
149
150         USE_FEATURE_TFTP_BLOCKSIZE(int want_option_ack = 0;)
151
152         /* Can't use RESERVE_CONFIG_BUFFER here since the allocation
153          * size varies meaning BUFFERS_GO_ON_STACK would fail */
154         /* We must keep the transmit and receive buffers seperate */
155         /* In case we rcv a garbage pkt and we need to rexmit the last pkt */
156         char *xbuf = xmalloc(tftp_bufsize += 4);
157         char *rbuf = xmalloc(tftp_bufsize);
158
159         port = htons(port);
160
161         socketfd = xsocket(peer_lsa->sa.sa_family, SOCK_DGRAM, 0);
162
163         /* build opcode */
164         opcode = TFTP_WRQ;
165         if (CMD_GET(cmd)) {
166                 opcode = TFTP_RRQ;
167         }
168
169         while (1) {
170
171                 cp = xbuf;
172
173                 /* first create the opcode part */
174                 *((uint16_t*)cp) = htons(opcode);
175                 cp += 2;
176
177                 /* add filename and mode */
178                 if (CMD_GET(cmd) ? (opcode == TFTP_RRQ) : (opcode == TFTP_WRQ)) {
179                         int too_long = 0;
180
181                         /* see if the filename fits into xbuf
182                          * and fill in packet.  */
183                         len = strlen(remotefile) + 1;
184
185                         if ((cp + len) >= &xbuf[tftp_bufsize - 1]) {
186                                 too_long = 1;
187                         } else {
188                                 safe_strncpy(cp, remotefile, len);
189                                 cp += len;
190                         }
191
192                         if (too_long || (&xbuf[tftp_bufsize - 1] - cp) < sizeof("octet")) {
193                                 bb_error_msg("remote filename too long");
194                                 break;
195                         }
196
197                         /* add "mode" part of the package */
198                         memcpy(cp, "octet", sizeof("octet"));
199                         cp += sizeof("octet");
200
201 #if ENABLE_FEATURE_TFTP_BLOCKSIZE
202
203                         len = tftp_bufsize - 4; /* data block size */
204
205                         if (len != TFTP_BLOCKSIZE_DEFAULT) {
206
207                                 if ((&xbuf[tftp_bufsize - 1] - cp) < 15) {
208                                         bb_error_msg("remote filename too long");
209                                         break;
210                                 }
211
212                                 /* add "blksize" + number of blocks  */
213                                 memcpy(cp, "blksize", sizeof("blksize"));
214                                 cp += sizeof("blksize");
215                                 cp += snprintf(cp, 6, "%d", len) + 1;
216
217                                 want_option_ack = 1;
218                         }
219 #endif
220                 }
221
222                 /* add ack and data */
223
224                 if (CMD_GET(cmd) ? (opcode == TFTP_ACK) : (opcode == TFTP_DATA)) {
225                         *((uint16_t*)cp) = htons(block_nr);
226                         cp += 2;
227                         block_nr++;
228
229                         if (CMD_PUT(cmd) && (opcode == TFTP_DATA)) {
230                                 len = full_read(localfd, cp, tftp_bufsize - 4);
231
232                                 if (len < 0) {
233                                         bb_perror_msg(bb_msg_read_error);
234                                         break;
235                                 }
236
237                                 if (len != (tftp_bufsize - 4)) {
238                                         finished++;
239                                 }
240
241                                 cp += len;
242                         }
243                 }
244
245                 /* send packet */
246
247                 timeout = TFTP_NUM_RETRIES;     /* re-initialize */
248                 do {
249                         len = cp - xbuf;
250 #if ENABLE_DEBUG_TFTP
251                         fprintf(stderr, "sending %u bytes\n", len);
252                         for (cp = xbuf; cp < &xbuf[len]; cp++)
253                                 fprintf(stderr, "%02x ", (unsigned char) *cp);
254                         fprintf(stderr, "\n");
255 #endif
256                         if (sendto(socketfd, xbuf, len, 0,
257                                         &peer_lsa->sa, peer_lsa->len) < 0) {
258                                 bb_perror_msg("send");
259                                 len = -1;
260                                 break;
261                         }
262
263                         if (finished && (opcode == TFTP_ACK)) {
264                                 break;
265                         }
266
267                         /* receive packet */
268  recv_again:
269                         tv.tv_sec = TFTP_TIMEOUT;
270                         tv.tv_usec = 0;
271
272                         FD_ZERO(&rfds);
273                         FD_SET(socketfd, &rfds);
274
275                         switch (select(socketfd + 1, &rfds, NULL, NULL, &tv)) {
276                                 struct sockaddr *from;
277                                 socklen_t fromlen;
278
279                         case 1:
280                                 fromlen = peer_lsa->len;
281                                 from = alloca(fromlen);
282                                 memset(from, 0, fromlen);
283
284                                 len = recvfrom(socketfd, rbuf, tftp_bufsize, 0,
285                                                         from, &fromlen);
286                                 if (len < 0) {
287                                         bb_perror_msg("recvfrom");
288                                         break;
289                                 }
290 #if ENABLE_FEATURE_IPV6
291                                 if (from->sa_family == AF_INET6)
292                                         if (((struct sockaddr_in6*)from)->sin6_port != port)
293                                                 goto recv_again;
294 #endif
295                                 if (from->sa_family == AF_INET)
296                                         if (((struct sockaddr_in*)from)->sin_port != port)
297                                                 goto recv_again;
298                                 timeout = 0;
299                                 break;
300                         case 0:
301                                 bb_error_msg("timeout");
302                                 timeout--;
303                                 if (timeout == 0) {
304                                         len = -1;
305                                         bb_error_msg("last timeout");
306                                 }
307                                 break;
308                         default:
309                                 bb_perror_msg("select");
310                                 len = -1;
311                         }
312
313                 } while (timeout && (len >= 0));
314
315                 if (finished || (len < 0)) {
316                         break;
317                 }
318
319                 /* process received packet */
320
321                 opcode = ntohs( ((uint16_t*)rbuf)[0] );
322                 tmp = ntohs( ((uint16_t*)rbuf)[1] );
323
324 #if ENABLE_DEBUG_TFTP
325                 fprintf(stderr, "received %d bytes: %04x %04x\n", len, opcode, tmp);
326 #endif
327
328                 if (opcode == TFTP_ERROR) {
329                         const char *msg = NULL;
330
331                         if (rbuf[4] != '\0') {
332                                 msg = &rbuf[4];
333                                 rbuf[tftp_bufsize - 1] = '\0';
334                         } else if (tmp < (sizeof(tftp_bb_error_msg)
335                                                           / sizeof(char *))) {
336                                 msg = tftp_bb_error_msg[tmp];
337                         }
338
339                         if (msg) {
340                                 bb_error_msg("server says: %s", msg);
341                         }
342
343                         break;
344                 }
345 #if ENABLE_FEATURE_TFTP_BLOCKSIZE
346                 if (want_option_ack) {
347
348                         want_option_ack = 0;
349
350                         if (opcode == TFTP_OACK) {
351                                 /* server seems to support options */
352                                 char *res;
353
354                                 res = tftp_option_get(&rbuf[2], len - 2, "blksize");
355
356                                 if (res) {
357                                         int blksize = xatoi_u(res);
358
359                                         if (tftp_blocksize_check(blksize, tftp_bufsize - 4)) {
360                                                 if (CMD_PUT(cmd)) {
361                                                         opcode = TFTP_DATA;
362                                                 } else {
363                                                         opcode = TFTP_ACK;
364                                                 }
365 #if ENABLE_DEBUG_TFTP
366                                                 fprintf(stderr, "using blksize %u\n",
367                                                                 blksize);
368 #endif
369                                                 tftp_bufsize = blksize + 4;
370                                                 block_nr = 0;
371                                                 continue;
372                                         }
373                                 }
374                                 /* FIXME:
375                                  * we should send ERROR 8 */
376                                 bb_error_msg("bad server option");
377                                 break;
378                         }
379
380                         bb_error_msg("warning: blksize not supported by server"
381                                                  " - reverting to 512");
382
383                         tftp_bufsize = TFTP_BLOCKSIZE_DEFAULT + 4;
384                 }
385 #endif
386
387                 if (CMD_GET(cmd) && (opcode == TFTP_DATA)) {
388                         if (tmp == block_nr) {
389                                 len = full_write(localfd, &rbuf[4], len - 4);
390
391                                 if (len < 0) {
392                                         bb_perror_msg(bb_msg_write_error);
393                                         break;
394                                 }
395
396                                 if (len != (tftp_bufsize - 4)) {
397                                         finished++;
398                                 }
399
400                                 opcode = TFTP_ACK;
401                                 continue;
402                         }
403                         /* in case the last ack disappeared into the ether */
404                         if (tmp == (block_nr - 1)) {
405                                 --block_nr;
406                                 opcode = TFTP_ACK;
407                                 continue;
408 // tmp==(block_nr-1) and (tmp+1)==block_nr is always same, I think. wtf?
409                         } else if (tmp + 1 == block_nr) {
410                                 /* Server lost our TFTP_ACK.  Resend it */
411                                 block_nr = tmp;
412                                 opcode = TFTP_ACK;
413                                 continue;
414                         }
415                 }
416
417                 if (CMD_PUT(cmd) && (opcode == TFTP_ACK)) {
418                         if (tmp == (uint16_t) (block_nr - 1)) {
419                                 if (finished) {
420                                         break;
421                                 }
422
423                                 opcode = TFTP_DATA;
424                                 continue;
425                         }
426                 }
427         }
428
429         if (ENABLE_FEATURE_CLEAN_UP) {
430                 close(socketfd);
431                 free(xbuf);
432                 free(rbuf);
433         }
434
435         return finished ? EXIT_SUCCESS : EXIT_FAILURE;
436 }
437
438 int tftp_main(int argc, char **argv)
439 {
440         len_and_sockaddr *peer_lsa;
441         const char *localfile = NULL;
442         const char *remotefile = NULL;
443 #if ENABLE_FEATURE_TFTP_BLOCKSIZE
444         const char *sblocksize = NULL;
445 #endif
446         int port;
447         USE_GETPUT(int cmd;)
448         int fd = -1;
449         int flags = 0;
450         int result;
451         int blocksize = TFTP_BLOCKSIZE_DEFAULT;
452
453         /* -p or -g is mandatory, and they are mutually exclusive */
454         opt_complementary = "" USE_FEATURE_TFTP_GET("g:") USE_FEATURE_TFTP_PUT("p:")
455                         USE_GETPUT("?g--p:p--g");
456
457         USE_GETPUT(cmd =) getopt32(argc, argv,
458                         USE_FEATURE_TFTP_GET("g") USE_FEATURE_TFTP_PUT("p")
459                                 "l:r:" USE_FEATURE_TFTP_BLOCKSIZE("b:"),
460                         &localfile, &remotefile
461                         USE_FEATURE_TFTP_BLOCKSIZE(, &sblocksize));
462
463         flags = O_RDONLY;
464         if (CMD_GET(cmd))
465                 flags = O_WRONLY | O_CREAT | O_TRUNC;
466
467 #if ENABLE_FEATURE_TFTP_BLOCKSIZE
468         if (sblocksize) {
469                 blocksize = xatoi_u(sblocksize);
470                 if (!tftp_blocksize_check(blocksize, 0)) {
471                         return EXIT_FAILURE;
472                 }
473         }
474 #endif
475
476         if (localfile == NULL)
477                 localfile = remotefile;
478         if (remotefile == NULL)
479                 remotefile = localfile;
480         if ((localfile == NULL && remotefile == NULL) || (argv[optind] == NULL))
481                 bb_show_usage();
482
483         if (localfile == NULL || LONE_DASH(localfile)) {
484                 fd = CMD_GET(cmd) ? STDOUT_FILENO : STDIN_FILENO;
485         } else {
486                 fd = xopen3(localfile, flags, 0644);
487         }
488
489         port = bb_lookup_port(argv[optind + 1], "udp", 69);
490         peer_lsa = host2sockaddr(argv[optind], port);
491
492 #if ENABLE_DEBUG_TFTP
493         fprintf(stderr, "using server \"%s\", "
494                         "remotefile \"%s\", localfile \"%s\".\n",
495                         xmalloc_sockaddr2dotted(&peer_lsa->sa, peer_lsa->len),
496                         remotefile, localfile);
497 #endif
498
499         result = tftp(
500 #if ENABLE_FEATURE_TFTP_GET && ENABLE_FEATURE_TFTP_PUT
501                 cmd,
502 #endif
503                 peer_lsa, remotefile, fd, port, blocksize);
504
505         if (fd > 1) {
506                 if (ENABLE_FEATURE_CLEAN_UP)
507                         close(fd);
508                 if (CMD_GET(cmd) && result != EXIT_SUCCESS)
509                         unlink(localfile);
510         }
511         return result;
512 }
513
514 #endif /* ENABLE_FEATURE_TFTP_GET || ENABLE_FEATURE_TFTP_PUT */