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