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