237609fad66e20ce5eb03f2edc703990163ff087
[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         struct sockaddr_in sa;
146         struct sockaddr_in from;
147         struct timeval tv;
148         socklen_t fromlen;
149         fd_set rfds;
150         int socketfd;
151         int len;
152         int opcode = 0;
153         int finished = 0;
154         int timeout = TFTP_NUM_RETRIES;
155         unsigned short block_nr = 1;
156         unsigned short tmp;
157         char *cp;
158
159         USE_FEATURE_TFTP_BLOCKSIZE(int want_option_ack = 0;)
160
161         /* Can't use RESERVE_CONFIG_BUFFER here since the allocation
162          * size varies meaning BUFFERS_GO_ON_STACK would fail */
163         char *buf=xmalloc(tftp_bufsize += 4);
164
165         if ((socketfd = socket(PF_INET, SOCK_DGRAM, 0)) < 0) {
166                 /* need to unlink the localfile, so don't use bb_xsocket here. */
167                 bb_perror_msg("socket");
168                 return EXIT_FAILURE;
169         }
170
171         len = sizeof(sa);
172
173         memset(&sa, 0, len);
174         bb_xbind(socketfd, (struct sockaddr *)&sa, len);
175
176         sa.sin_family = host->h_addrtype;
177         sa.sin_port = port;
178         memcpy(&sa.sin_addr, (struct in_addr *) host->h_addr,
179                    sizeof(sa.sin_addr));
180
181         /* build opcode */
182         if (cmd & tftp_cmd_get) {
183                 opcode = TFTP_RRQ;
184         }
185         if (cmd & tftp_cmd_put) {
186                 opcode = TFTP_WRQ;
187         }
188
189         while (1) {
190
191                 cp = buf;
192
193                 /* first create the opcode part */
194                 *((unsigned short *) cp) = htons(opcode);
195                 cp += 2;
196
197                 /* add filename and mode */
198                 if (((cmd & tftp_cmd_get) && (opcode == TFTP_RRQ)) ||
199                         ((cmd & tftp_cmd_put) && (opcode == TFTP_WRQ)))
200                 {
201                         int too_long = 0;
202
203                         /* see if the filename fits into buf
204                          * and fill in packet.  */
205                         len = strlen(remotefile) + 1;
206
207                         if ((cp + len) >= &buf[tftp_bufsize - 1]) {
208                                 too_long = 1;
209                         } else {
210                                 safe_strncpy(cp, remotefile, len);
211                                 cp += len;
212                         }
213
214                         if (too_long || ((&buf[tftp_bufsize - 1] - cp) < MODE_OCTET_LEN)) {
215                                 bb_error_msg("remote filename too long");
216                                 break;
217                         }
218
219                         /* add "mode" part of the package */
220                         memcpy(cp, MODE_OCTET, MODE_OCTET_LEN);
221                         cp += MODE_OCTET_LEN;
222
223 #ifdef CONFIG_FEATURE_TFTP_BLOCKSIZE
224
225                         len = tftp_bufsize - 4; /* data block size */
226
227                         if (len != TFTP_BLOCKSIZE_DEFAULT) {
228
229                                 if ((&buf[tftp_bufsize - 1] - cp) < 15) {
230                                         bb_error_msg("remote filename too long");
231                                         break;
232                                 }
233
234                                 /* add "blksize" + number of blocks  */
235                                 memcpy(cp, OPTION_BLOCKSIZE, OPTION_BLOCKSIZE_LEN);
236                                 cp += OPTION_BLOCKSIZE_LEN;
237                                 cp += snprintf(cp, 6, "%d", len) + 1;
238
239                                 want_option_ack = 1;
240                         }
241 #endif
242                 }
243
244                 /* add ack and data */
245
246                 if (((cmd & tftp_cmd_get) && (opcode == TFTP_ACK)) ||
247                         ((cmd & tftp_cmd_put) && (opcode == TFTP_DATA))) {
248
249                         *((unsigned short *) cp) = htons(block_nr);
250
251                         cp += 2;
252
253                         block_nr++;
254
255                         if ((cmd & tftp_cmd_put) && (opcode == TFTP_DATA)) {
256                                 len = bb_full_read(localfd, cp, tftp_bufsize - 4);
257
258                                 if (len < 0) {
259                                         bb_perror_msg(bb_msg_read_error);
260                                         break;
261                                 }
262
263                                 if (len != (tftp_bufsize - 4)) {
264                                         finished++;
265                                 }
266
267                                 cp += len;
268                         }
269                 }
270
271
272                 /* send packet */
273
274
275                 timeout = TFTP_NUM_RETRIES;     /* re-initialize */
276                 do {
277
278                         len = cp - buf;
279
280 #ifdef CONFIG_DEBUG_TFTP
281                         fprintf(stderr, "sending %u bytes\n", len);
282                         for (cp = buf; cp < &buf[len]; cp++)
283                                 fprintf(stderr, "%02x ", (unsigned char) *cp);
284                         fprintf(stderr, "\n");
285 #endif
286                         if (sendto(socketfd, buf, len, 0,
287                                            (struct sockaddr *) &sa, sizeof(sa)) < 0) {
288                                 bb_perror_msg("send");
289                                 len = -1;
290                                 break;
291                         }
292
293
294                         if (finished && (opcode == TFTP_ACK)) {
295                                 break;
296                         }
297
298                         /* receive packet */
299
300                         memset(&from, 0, sizeof(from));
301                         fromlen = sizeof(from);
302
303                         tv.tv_sec = TFTP_TIMEOUT;
304                         tv.tv_usec = 0;
305
306                         FD_ZERO(&rfds);
307                         FD_SET(socketfd, &rfds);
308
309                         switch (select(socketfd + 1, &rfds, NULL, NULL, &tv)) {
310                         case 1:
311                                 len = recvfrom(socketfd, buf, tftp_bufsize, 0,
312                                                            (struct sockaddr *) &from, &fromlen);
313
314                                 if (len < 0) {
315                                         bb_perror_msg("recvfrom");
316                                         break;
317                                 }
318
319                                 timeout = 0;
320
321                                 if (sa.sin_port == port) {
322                                         sa.sin_port = from.sin_port;
323                                 }
324                                 if (sa.sin_port == from.sin_port) {
325                                         break;
326                                 }
327
328                                 /* fall-through for bad packets! */
329                                 /* discard the packet - treat as timeout */
330                                 timeout = TFTP_NUM_RETRIES;
331                         case 0:
332                                 bb_error_msg("timeout");
333
334                                 timeout--;
335                                 if (timeout == 0) {
336                                         len = -1;
337                                         bb_error_msg("last timeout");
338                                 }
339                                 break;
340                         default:
341                                 bb_perror_msg("select");
342                                 len = -1;
343                         }
344
345                 } while (timeout && (len >= 0));
346
347                 if ((finished) || (len < 0)) {
348                         break;
349                 }
350
351                 /* process received packet */
352
353                 opcode = ntohs(*((unsigned short *) buf));
354                 tmp = ntohs(*((unsigned short *) &buf[2]));
355
356 #ifdef CONFIG_DEBUG_TFTP
357                 fprintf(stderr, "received %d bytes: %04x %04x\n", len, opcode, tmp);
358 #endif
359
360                 if (opcode == TFTP_ERROR) {
361                         const char *msg = NULL;
362
363                         if (buf[4] != '\0') {
364                                 msg = &buf[4];
365                                 buf[tftp_bufsize - 1] = '\0';
366                         } else if (tmp < (sizeof(tftp_bb_error_msg)
367                                                           / sizeof(char *))) {
368
369                                 msg = tftp_bb_error_msg[tmp];
370                         }
371
372                         if (msg) {
373                                 bb_error_msg("server says: %s", msg);
374                         }
375
376                         break;
377                 }
378 #ifdef CONFIG_FEATURE_TFTP_BLOCKSIZE
379                 if (want_option_ack) {
380
381                         want_option_ack = 0;
382
383                         if (opcode == TFTP_OACK) {
384
385                                 /* server seems to support options */
386
387                                 char *res;
388
389                                 res = tftp_option_get(&buf[2], len - 2, OPTION_BLOCKSIZE);
390
391                                 if (res) {
392                                         int blksize = atoi(res);
393
394                                         if (tftp_blocksize_check(blksize, tftp_bufsize - 4)) {
395
396                                                 if (cmd & tftp_cmd_put) {
397                                                         opcode = TFTP_DATA;
398                                                 } else {
399                                                         opcode = TFTP_ACK;
400                                                 }
401 #ifdef CONFIG_DEBUG_TFTP
402                                                 fprintf(stderr, "using %s %u\n", OPTION_BLOCKSIZE,
403                                                                 blksize);
404 #endif
405                                                 tftp_bufsize = blksize + 4;
406                                                 block_nr = 0;
407                                                 continue;
408                                         }
409                                 }
410                                 /* FIXME:
411                                  * we should send ERROR 8 */
412                                 bb_error_msg("bad server option");
413                                 break;
414                         }
415
416                         bb_error_msg("warning: blksize not supported by server"
417                                                  " - reverting to 512");
418
419                         tftp_bufsize = TFTP_BLOCKSIZE_DEFAULT + 4;
420                 }
421 #endif
422
423                 if ((cmd & tftp_cmd_get) && (opcode == TFTP_DATA)) {
424
425                         if (tmp == block_nr) {
426
427                                 len = bb_full_write(localfd, &buf[4], len - 4);
428
429                                 if (len < 0) {
430                                         bb_perror_msg(bb_msg_write_error);
431                                         break;
432                                 }
433
434                                 if (len != (tftp_bufsize - 4)) {
435                                         finished++;
436                                 }
437
438                                 opcode = TFTP_ACK;
439                                 continue;
440                         }
441                         /* in case the last ack disappeared into the ether */
442                         if (tmp == (block_nr - 1)) {
443                                 --block_nr;
444                                 opcode = TFTP_ACK;
445                                 continue;
446                         } else if (tmp + 1 == block_nr) {
447                                 /* Server lost our TFTP_ACK.  Resend it */
448                                 block_nr = tmp;
449                                 opcode = TFTP_ACK;
450                                 continue;
451                         }
452                 }
453
454                 if ((cmd & tftp_cmd_put) && (opcode == TFTP_ACK)) {
455
456                         if (tmp == (unsigned short) (block_nr - 1)) {
457                                 if (finished) {
458                                         break;
459                                 }
460
461                                 opcode = TFTP_DATA;
462                                 continue;
463                         }
464                 }
465         }
466
467 #ifdef CONFIG_FEATURE_CLEAN_UP
468         close(socketfd);
469         free(buf);
470 #endif
471
472         return finished ? EXIT_SUCCESS : EXIT_FAILURE;
473 }
474
475 int tftp_main(int argc, char **argv)
476 {
477         struct hostent *host = NULL;
478         const char *localfile = NULL;
479         const char *remotefile = NULL;
480         int port;
481         int cmd = 0;
482         int fd = -1;
483         int flags = 0;
484         int result;
485         int blocksize = TFTP_BLOCKSIZE_DEFAULT;
486
487         /* figure out what to pass to getopt */
488
489 #ifdef CONFIG_FEATURE_TFTP_BLOCKSIZE
490         char *sblocksize = NULL;
491
492 #define BS "b:"
493 #define BS_ARG , &sblocksize
494 #else
495 #define BS
496 #define BS_ARG
497 #endif
498
499 #ifdef CONFIG_FEATURE_TFTP_GET
500 #define GET "g"
501 #define GET_COMPL ":g"
502 #else
503 #define GET
504 #define GET_COMPL
505 #endif
506
507 #ifdef CONFIG_FEATURE_TFTP_PUT
508 #define PUT "p"
509 #define PUT_COMPL ":p"
510 #else
511 #define PUT
512 #define PUT_COMPL
513 #endif
514
515 #if defined(CONFIG_FEATURE_TFTP_GET) && defined(CONFIG_FEATURE_TFTP_PUT)
516         bb_opt_complementally = GET_COMPL PUT_COMPL ":?g--p:p--g";
517 #elif defined(CONFIG_FEATURE_TFTP_GET) || defined(CONFIG_FEATURE_TFTP_PUT)
518         bb_opt_complementally = GET_COMPL PUT_COMPL;
519 #endif
520
521
522         cmd = bb_getopt_ulflags(argc, argv, GET PUT "l:r:" BS,
523                                                         &localfile, &remotefile BS_ARG);
524
525         cmd &= (tftp_cmd_get | tftp_cmd_put);
526 #ifdef CONFIG_FEATURE_TFTP_GET
527         if (cmd == tftp_cmd_get)
528                 flags = O_WRONLY | O_CREAT | O_TRUNC;
529 #endif
530 #ifdef CONFIG_FEATURE_TFTP_PUT
531         if (cmd == tftp_cmd_put)
532                 flags = O_RDONLY;
533 #endif
534
535 #ifdef CONFIG_FEATURE_TFTP_BLOCKSIZE
536         if (sblocksize) {
537                 blocksize = atoi(sblocksize);
538                 if (!tftp_blocksize_check(blocksize, 0)) {
539                         return EXIT_FAILURE;
540                 }
541         }
542 #endif
543
544         if (localfile == NULL)
545                 localfile = remotefile;
546         if (remotefile == NULL)
547                 remotefile = localfile;
548         if ((localfile == NULL && remotefile == NULL) || (argv[optind] == NULL))
549                 bb_show_usage();
550
551         if (localfile == NULL || strcmp(localfile, "-") == 0) {
552                 fd = (cmd == tftp_cmd_get) ? STDOUT_FILENO : STDIN_FILENO;
553         } else {
554                 fd = open(localfile, flags, 0644); /* fail below */
555         }
556         if (fd < 0) {
557                 bb_perror_msg_and_die("local file");
558         }
559
560         host = xgethostbyname(argv[optind]);
561         port = bb_lookup_port(argv[optind + 1], "udp", 69);
562
563 #ifdef CONFIG_DEBUG_TFTP
564         fprintf(stderr, "using server \"%s\", remotefile \"%s\", "
565                         "localfile \"%s\".\n",
566                         inet_ntoa(*((struct in_addr *) host->h_addr)),
567                         remotefile, localfile);
568 #endif
569
570         result = tftp(cmd, host, remotefile, fd, port, blocksize);
571
572         if (!(fd == STDOUT_FILENO || fd == STDIN_FILENO)) {
573                 if (ENABLE_FEATURE_CLEAN_UP)
574                         close(fd);
575                 if (cmd == tftp_cmd_get && result != EXIT_SUCCESS)
576                         unlink(localfile);
577         }
578         return (result);
579 }