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