e1b6f403c74575cbc3a96388f395a8fb3376bd5b
[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 <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <sys/types.h>
26 #include <sys/socket.h>
27 #include <sys/stat.h>
28 #include <netdb.h>
29 #include <netinet/in.h>
30 #include <arpa/inet.h>
31 #include <unistd.h>
32 #include <fcntl.h>
33
34 #include "busybox.h"
35
36
37 #define TFTP_BLOCKSIZE_DEFAULT 512      /* according to RFC 1350, don't change */
38 #define TFTP_TIMEOUT 5  /* seconds */
39 #define TFTP_NUM_RETRIES 5 /* number of retries */
40
41 /* RFC2348 says between 8 and 65464 */
42 #define TFTP_OCTECTS_MIN 8
43 #define TFTP_OCTECTS_MAX 65464
44
45 static const char * const MODE_OCTET = "octet";
46 #define MODE_OCTET_LEN 6 /* sizeof(MODE_OCTET)*/
47
48 static const char * const OPTION_BLOCKSIZE = "blksize";
49 #define OPTION_BLOCKSIZE_LEN 8 /* sizeof(OPTION_BLOCKSIZE) */
50
51 /* opcodes we support */
52 #define TFTP_RRQ   1
53 #define TFTP_WRQ   2
54 #define TFTP_DATA  3
55 #define TFTP_ACK   4
56 #define TFTP_ERROR 5
57 #define TFTP_OACK  6
58
59 static const char *const tftp_bb_error_msg[] = {
60         "Undefined error",
61         "File not found",
62         "Access violation",
63         "Disk full or allocation error",
64         "Illegal TFTP operation",
65         "Unknown transfer ID",
66         "File already exists",
67         "No such user"
68 };
69
70 #define tftp_cmd_get ENABLE_FEATURE_TFTP_GET
71
72 #if ENABLE_FEATURE_TFTP_PUT
73 # define tftp_cmd_put (tftp_cmd_get+ENABLE_FEATURE_TFTP_PUT)
74 #else
75 # define tftp_cmd_put 0
76 #endif
77
78
79 #ifdef CONFIG_FEATURE_TFTP_BLOCKSIZE
80
81 static int tftp_blocksize_check(int blocksize, int bufsize)
82 {
83         /* Check if the blocksize is valid:
84          * RFC2348 says between 8 and 65464,
85          * but our implementation makes it impossible
86          * to use blocksizes smaller than 22 octets.
87          */
88
89         if ((bufsize && (blocksize > bufsize)) ||
90                 (blocksize < TFTP_OCTECTS_MIN) || (blocksize > TFTP_OCTECTS_MAX)) {
91                 bb_error_msg("bad blocksize");
92                 return 0;
93         }
94
95         return blocksize;
96 }
97
98 static char *tftp_option_get(char *buf, int len, const char const *option)
99 {
100         int opt_val = 0;
101         int opt_found = 0;
102         int k;
103
104         while (len > 0) {
105
106                 /* Make sure the options are terminated correctly */
107
108                 for (k = 0; k < len; k++) {
109                         if (buf[k] == '\0') {
110                                 break;
111                         }
112                 }
113
114                 if (k >= len) {
115                         break;
116                 }
117
118                 if (opt_val == 0) {
119                         if (strcasecmp(buf, option) == 0) {
120                                 opt_found = 1;
121                         }
122                 } else {
123                         if (opt_found) {
124                                 return buf;
125                         }
126                 }
127
128                 k++;
129
130                 buf += k;
131                 len -= k;
132
133                 opt_val ^= 1;
134         }
135
136         return NULL;
137 }
138
139 #endif
140
141 static int tftp(const int cmd, const struct hostent *host,
142                    const char *remotefile, const int localfd,
143                    const unsigned short port, int tftp_bufsize)
144 {
145 #define cmd_get  cmd & tftp_cmd_get
146 #define cmd_put  cmd & tftp_cmd_put
147         struct sockaddr_in sa;
148         struct sockaddr_in from;
149         struct timeval tv;
150         socklen_t fromlen;
151         fd_set rfds;
152         int socketfd;
153         int len;
154         int opcode = 0;
155         int finished = 0;
156         int timeout = TFTP_NUM_RETRIES;
157         unsigned short block_nr = 1;
158         unsigned short tmp;
159         char *cp;
160
161         USE_FEATURE_TFTP_BLOCKSIZE(int want_option_ack = 0;)
162
163         /* Can't use RESERVE_CONFIG_BUFFER here since the allocation
164          * size varies meaning BUFFERS_GO_ON_STACK would fail */
165         char *buf=xmalloc(tftp_bufsize += 4);
166
167         if ((socketfd = socket(PF_INET, SOCK_DGRAM, 0)) < 0) {
168                 /* need to unlink the localfile, so don't use bb_xsocket here. */
169                 bb_perror_msg("socket");
170                 return EXIT_FAILURE;
171         }
172
173         len = sizeof(sa);
174
175         memset(&sa, 0, len);
176         bb_xbind(socketfd, (struct sockaddr *)&sa, len);
177
178         sa.sin_family = host->h_addrtype;
179         sa.sin_port = port;
180         memcpy(&sa.sin_addr, (struct in_addr *) host->h_addr,
181                    sizeof(sa.sin_addr));
182
183         /* build opcode */
184         if (cmd_get) {
185                 opcode = TFTP_RRQ;
186         }
187         if (cmd_put) {
188                 opcode = TFTP_WRQ;
189         }
190
191         while (1) {
192
193                 cp = buf;
194
195                 /* first create the opcode part */
196                 *((unsigned short *) cp) = htons(opcode);
197                 cp += 2;
198
199                 /* add filename and mode */
200                 if (((cmd_get) && (opcode == TFTP_RRQ)) ||
201                         ((cmd_put) && (opcode == TFTP_WRQ)))
202                 {
203                         int too_long = 0;
204
205                         /* see if the filename fits into buf
206                          * and fill in packet.  */
207                         len = strlen(remotefile) + 1;
208
209                         if ((cp + len) >= &buf[tftp_bufsize - 1]) {
210                                 too_long = 1;
211                         } else {
212                                 safe_strncpy(cp, remotefile, len);
213                                 cp += len;
214                         }
215
216                         if (too_long || ((&buf[tftp_bufsize - 1] - cp) < MODE_OCTET_LEN)) {
217                                 bb_error_msg("remote filename too long");
218                                 break;
219                         }
220
221                         /* add "mode" part of the package */
222                         memcpy(cp, MODE_OCTET, MODE_OCTET_LEN);
223                         cp += MODE_OCTET_LEN;
224
225 #ifdef CONFIG_FEATURE_TFTP_BLOCKSIZE
226
227                         len = tftp_bufsize - 4; /* data block size */
228
229                         if (len != TFTP_BLOCKSIZE_DEFAULT) {
230
231                                 if ((&buf[tftp_bufsize - 1] - cp) < 15) {
232                                         bb_error_msg("remote filename too long");
233                                         break;
234                                 }
235
236                                 /* add "blksize" + number of blocks  */
237                                 memcpy(cp, OPTION_BLOCKSIZE, OPTION_BLOCKSIZE_LEN);
238                                 cp += OPTION_BLOCKSIZE_LEN;
239                                 cp += snprintf(cp, 6, "%d", len) + 1;
240
241                                 want_option_ack = 1;
242                         }
243 #endif
244                 }
245
246                 /* add ack and data */
247
248                 if (((cmd_get) && (opcode == TFTP_ACK)) ||
249                         ((cmd_put) && (opcode == TFTP_DATA))) {
250
251                         *((unsigned short *) cp) = htons(block_nr);
252
253                         cp += 2;
254
255                         block_nr++;
256
257                         if ((cmd_put) && (opcode == TFTP_DATA)) {
258                                 len = bb_full_read(localfd, cp, tftp_bufsize - 4);
259
260                                 if (len < 0) {
261                                         bb_perror_msg(bb_msg_read_error);
262                                         break;
263                                 }
264
265                                 if (len != (tftp_bufsize - 4)) {
266                                         finished++;
267                                 }
268
269                                 cp += len;
270                         }
271                 }
272
273
274                 /* send packet */
275
276
277                 timeout = TFTP_NUM_RETRIES;     /* re-initialize */
278                 do {
279
280                         len = cp - buf;
281
282 #ifdef CONFIG_FEATURE_TFTP_DEBUG
283                         fprintf(stderr, "sending %u bytes\n", len);
284                         for (cp = buf; cp < &buf[len]; cp++)
285                                 fprintf(stderr, "%02x ", (unsigned char) *cp);
286                         fprintf(stderr, "\n");
287 #endif
288                         if (sendto(socketfd, buf, len, 0,
289                                            (struct sockaddr *) &sa, sizeof(sa)) < 0) {
290                                 bb_perror_msg("send");
291                                 len = -1;
292                                 break;
293                         }
294
295
296                         if (finished && (opcode == TFTP_ACK)) {
297                                 break;
298                         }
299
300                         /* receive packet */
301
302                         memset(&from, 0, sizeof(from));
303                         fromlen = sizeof(from);
304
305                         tv.tv_sec = TFTP_TIMEOUT;
306                         tv.tv_usec = 0;
307
308                         FD_ZERO(&rfds);
309                         FD_SET(socketfd, &rfds);
310
311                         switch (select(socketfd + 1, &rfds, NULL, NULL, &tv)) {
312                         case 1:
313                                 len = recvfrom(socketfd, buf, tftp_bufsize, 0,
314                                                            (struct sockaddr *) &from, &fromlen);
315
316                                 if (len < 0) {
317                                         bb_perror_msg("recvfrom");
318                                         break;
319                                 }
320
321                                 timeout = 0;
322
323                                 if (sa.sin_port == port) {
324                                         sa.sin_port = from.sin_port;
325                                 }
326                                 if (sa.sin_port == from.sin_port) {
327                                         break;
328                                 }
329
330                                 /* fall-through for bad packets! */
331                                 /* discard the packet - treat as timeout */
332                                 timeout = TFTP_NUM_RETRIES;
333                         case 0:
334                                 bb_error_msg("timeout");
335
336                                 timeout--;
337                                 if (timeout == 0) {
338                                         len = -1;
339                                         bb_error_msg("last timeout");
340                                 }
341                                 break;
342                         default:
343                                 bb_perror_msg("select");
344                                 len = -1;
345                         }
346
347                 } while (timeout && (len >= 0));
348
349                 if ((finished) || (len < 0)) {
350                         break;
351                 }
352
353                 /* process received packet */
354
355                 opcode = ntohs(*((unsigned short *) buf));
356                 tmp = ntohs(*((unsigned short *) &buf[2]));
357
358 #ifdef CONFIG_FEATURE_TFTP_DEBUG
359                 fprintf(stderr, "received %d bytes: %04x %04x\n", len, opcode, tmp);
360 #endif
361
362                 if (opcode == TFTP_ERROR) {
363                         const char *msg = NULL;
364
365                         if (buf[4] != '\0') {
366                                 msg = &buf[4];
367                                 buf[tftp_bufsize - 1] = '\0';
368                         } else if (tmp < (sizeof(tftp_bb_error_msg)
369                                                           / sizeof(char *))) {
370
371                                 msg = tftp_bb_error_msg[tmp];
372                         }
373
374                         if (msg) {
375                                 bb_error_msg("server says: %s", msg);
376                         }
377
378                         break;
379                 }
380 #ifdef CONFIG_FEATURE_TFTP_BLOCKSIZE
381                 if (want_option_ack) {
382
383                         want_option_ack = 0;
384
385                         if (opcode == TFTP_OACK) {
386
387                                 /* server seems to support options */
388
389                                 char *res;
390
391                                 res = tftp_option_get(&buf[2], len - 2, OPTION_BLOCKSIZE);
392
393                                 if (res) {
394                                         int blksize = atoi(res);
395
396                                         if (tftp_blocksize_check(blksize, tftp_bufsize - 4)) {
397
398                                                 if (cmd_put) {
399                                                         opcode = TFTP_DATA;
400                                                 } else {
401                                                         opcode = TFTP_ACK;
402                                                 }
403 #ifdef CONFIG_FEATURE_TFTP_DEBUG
404                                                 fprintf(stderr, "using %s %u\n", OPTION_BLOCKSIZE,
405                                                                 blksize);
406 #endif
407                                                 tftp_bufsize = blksize + 4;
408                                                 block_nr = 0;
409                                                 continue;
410                                         }
411                                 }
412                                 /* FIXME:
413                                  * we should send ERROR 8 */
414                                 bb_error_msg("bad server option");
415                                 break;
416                         }
417
418                         bb_error_msg("warning: blksize not supported by server"
419                                                  " - reverting to 512");
420
421                         tftp_bufsize = TFTP_BLOCKSIZE_DEFAULT + 4;
422                 }
423 #endif
424
425                 if ((cmd_get) && (opcode == TFTP_DATA)) {
426
427                         if (tmp == block_nr) {
428
429                                 len = bb_full_write(localfd, &buf[4], len - 4);
430
431                                 if (len < 0) {
432                                         bb_perror_msg(bb_msg_write_error);
433                                         break;
434                                 }
435
436                                 if (len != (tftp_bufsize - 4)) {
437                                         finished++;
438                                 }
439
440                                 opcode = TFTP_ACK;
441                                 continue;
442                         }
443                         /* in case the last ack disappeared into the ether */
444                         if (tmp == (block_nr - 1)) {
445                                 --block_nr;
446                                 opcode = TFTP_ACK;
447                                 continue;
448                         } else if (tmp + 1 == block_nr) {
449                                 /* Server lost our TFTP_ACK.  Resend it */
450                                 block_nr = tmp;
451                                 opcode = TFTP_ACK;
452                                 continue;
453                         }
454                 }
455
456                 if ((cmd_put) && (opcode == TFTP_ACK)) {
457
458                         if (tmp == (unsigned short) (block_nr - 1)) {
459                                 if (finished) {
460                                         break;
461                                 }
462
463                                 opcode = TFTP_DATA;
464                                 continue;
465                         }
466                 }
467         }
468
469 #ifdef CONFIG_FEATURE_CLEAN_UP
470         close(socketfd);
471         free(buf);
472 #endif
473
474         return finished ? EXIT_SUCCESS : EXIT_FAILURE;
475 }
476
477 int tftp_main(int argc, char **argv)
478 {
479         struct hostent *host = NULL;
480         const char *localfile = NULL;
481         const char *remotefile = NULL;
482         int port;
483         int cmd = 0;
484         int fd = -1;
485         int flags = 0;
486         int result;
487         int blocksize = TFTP_BLOCKSIZE_DEFAULT;
488
489         /* figure out what to pass to getopt */
490
491 #ifdef CONFIG_FEATURE_TFTP_BLOCKSIZE
492         char *sblocksize = NULL;
493
494 #define BS "b:"
495 #define BS_ARG , &sblocksize
496 #else
497 #define BS
498 #define BS_ARG
499 #endif
500
501 #ifdef CONFIG_FEATURE_TFTP_GET
502 #define GET "g"
503 #define GET_COMPL ":g"
504 #else
505 #define GET
506 #define GET_COMPL
507 #endif
508
509 #ifdef CONFIG_FEATURE_TFTP_PUT
510 #define PUT "p"
511 #define PUT_COMPL ":p"
512 #else
513 #define PUT
514 #define PUT_COMPL
515 #endif
516
517 #if defined(CONFIG_FEATURE_TFTP_GET) && defined(CONFIG_FEATURE_TFTP_PUT)
518         bb_opt_complementally = GET_COMPL PUT_COMPL ":?g--p:p--g";
519 #elif defined(CONFIG_FEATURE_TFTP_GET) || defined(CONFIG_FEATURE_TFTP_PUT)
520         bb_opt_complementally = GET_COMPL PUT_COMPL;
521 #else
522 #error "Either CONFIG_FEATURE_TFTP_GET or CONFIG_FEATURE_TFTP_PUT must be defined"
523 #endif
524
525
526         cmd = bb_getopt_ulflags(argc, argv, GET PUT "l:r:" BS,
527                                                         &localfile, &remotefile BS_ARG);
528
529         cmd &= (tftp_cmd_get | tftp_cmd_put);
530 #ifdef CONFIG_FEATURE_TFTP_GET
531         if (cmd == tftp_cmd_get)
532                 flags = O_WRONLY | O_CREAT | O_TRUNC;
533 #endif
534 #ifdef CONFIG_FEATURE_TFTP_PUT
535         if (cmd == tftp_cmd_put)
536                 flags = O_RDONLY;
537 #endif
538
539 #ifdef CONFIG_FEATURE_TFTP_BLOCKSIZE
540         if (sblocksize) {
541                 blocksize = atoi(sblocksize);
542                 if (!tftp_blocksize_check(blocksize, 0)) {
543                         return EXIT_FAILURE;
544                 }
545         }
546 #endif
547
548         if (localfile == NULL)
549                 localfile = remotefile;
550         if (remotefile == NULL)
551                 remotefile = localfile;
552         if ((localfile == NULL && remotefile == NULL) || (argv[optind] == NULL))
553                 bb_show_usage();
554
555         if (localfile == NULL || strcmp(localfile, "-") == 0) {
556                 fd = (cmd == tftp_cmd_get) ? STDOUT_FILENO : STDIN_FILENO;
557         } else {
558                 fd = open(localfile, flags, 0644); /* fail below */
559         }
560         if (fd < 0) {
561                 bb_perror_msg_and_die("local file");
562         }
563
564         host = xgethostbyname(argv[optind]);
565         port = bb_lookup_port(argv[optind + 1], "udp", 69);
566
567 #ifdef CONFIG_FEATURE_TFTP_DEBUG
568         fprintf(stderr, "using server \"%s\", remotefile \"%s\", "
569                         "localfile \"%s\".\n",
570                         inet_ntoa(*((struct in_addr *) host->h_addr)),
571                         remotefile, localfile);
572 #endif
573
574         result = tftp(cmd, host, remotefile, fd, port, blocksize);
575
576 #ifdef CONFIG_FEATURE_CLEAN_UP
577         if (!(fd == STDOUT_FILENO || fd == STDIN_FILENO)) {
578                 close(fd);
579         }
580 #endif
581         if (cmd == tftp_cmd_get && result != EXIT_SUCCESS)
582                 unlink(localfile);
583         return (result);
584 }