Major rework of the directory structure and the entire build system.
[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 *tftp_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 const int tftp_cmd_get = 1;
75 const int tftp_cmd_put = 2;
76
77 #ifdef CONFIG_FEATURE_TFTP_BLOCKSIZE
78
79 static int tftp_blocksize_check(int blocksize, int bufsize)  
80 {
81         /* Check if the blocksize is valid: 
82          * RFC2348 says between 8 and 65464,
83          * but our implementation makes it impossible
84          * to use blocksizes smaller than 22 octets.
85          */
86
87         if ((bufsize && (blocksize > bufsize)) || 
88             (blocksize < 8) || (blocksize > 65464)) {
89                 error_msg("bad blocksize");
90                 return 0;
91         }
92
93         return blocksize;
94 }
95
96 static char *tftp_option_get(char *buf, int len, char *option)  
97 {
98         int opt_val = 0;
99         int opt_found = 0;
100         int k;
101   
102         while (len > 0) {
103
104                 /* Make sure the options are terminated correctly */
105
106                 for (k = 0; k < len; k++) {
107                         if (buf[k] == '\0') {
108                                 break;
109                         }
110                 }
111
112                 if (k >= len) {
113                         break;
114                 }
115
116                 if (opt_val == 0) {
117                         if (strcasecmp(buf, option) == 0) {
118                                 opt_found = 1;
119                         }
120                 }      
121                 else {
122                         if (opt_found) {
123                                 return buf;
124                         }
125                 }
126     
127                 k++;
128                 
129                 buf += k;
130                 len -= k;
131                 
132                 opt_val ^= 1;
133         }
134         
135         return NULL;
136 }
137
138 #endif
139
140 static inline int tftp(const int cmd, const struct hostent *host,
141         const char *remotefile, int localfd, const int port, int tftp_bufsize)
142 {
143         const int cmd_get = cmd & tftp_cmd_get;
144         const int cmd_put = cmd & tftp_cmd_put;
145         const int bb_tftp_num_retries = 5;
146
147         struct sockaddr_in sa;
148         struct sockaddr_in from;
149         struct timeval tv;
150         socklen_t fromlen;
151         fd_set rfds;
152         char *cp;
153         unsigned short tmp;
154         int socketfd;
155         int len;
156         int opcode = 0;
157         int finished = 0;
158         int timeout = bb_tftp_num_retries;
159         int block_nr = 1;
160
161 #ifdef CONFIG_FEATURE_TFTP_BLOCKSIZE
162         int want_option_ack = 0;
163 #endif
164
165         RESERVE_CONFIG_BUFFER(buf, tftp_bufsize + 4); /* Opcode + Block # + Data */
166
167         tftp_bufsize += 4;
168
169         if ((socketfd = socket(PF_INET, SOCK_DGRAM, 0)) < 0) {
170                 perror_msg("socket");
171                 return EXIT_FAILURE;
172         }
173
174         len = sizeof(sa);
175
176         memset(&sa, 0, len);
177         bind(socketfd, (struct sockaddr *)&sa, len);
178
179         sa.sin_family = host->h_addrtype;
180         sa.sin_port = htons(port);
181         memcpy(&sa.sin_addr, (struct in_addr *) host->h_addr,
182                    sizeof(sa.sin_addr));
183
184         /* build opcode */
185
186         if (cmd_get) {
187                 opcode = TFTP_RRQ;
188         }
189
190         if (cmd_put) {
191                 opcode = TFTP_WRQ;
192         }
193
194         while (1) {
195
196                 cp = buf;
197
198                 /* first create the opcode part */
199
200                 *((unsigned short *) cp) = htons(opcode);
201
202                 cp += 2;
203
204                 /* add filename and mode */
205
206                 if ((cmd_get && (opcode == TFTP_RRQ)) ||
207                         (cmd_put && (opcode == TFTP_WRQ))) {
208                         int too_long = 0; 
209
210                         /* see if the filename fits into buf */
211                         /* and fill in packet                */
212
213                         len = strlen(remotefile) + 1;
214
215                         if ((cp + len) >= &buf[tftp_bufsize - 1]) {
216                                 too_long = 1;
217                         }
218                         else {
219                                 safe_strncpy(cp, remotefile, len);
220                                 cp += len;
221                         }
222
223                         if (too_long || ((&buf[tftp_bufsize - 1] - cp) < 6)) {
224                                 error_msg("too long remote-filename");
225                                 break;
226                         }
227
228                         /* add "mode" part of the package */
229
230                         memcpy(cp, "octet", 6);
231                         cp += 6;
232
233 #ifdef CONFIG_FEATURE_TFTP_BLOCKSIZE
234
235                         len = tftp_bufsize - 4; /* data block size */
236
237                         if (len != TFTP_BLOCKSIZE_DEFAULT) {
238
239                                 if ((&buf[tftp_bufsize - 1] - cp) < 15) {
240                                         error_msg("too long remote-filename");
241                                         break;
242                                 }
243
244                                 /* add "blksize" + number of blocks  */
245
246                                 memcpy(cp, "blksize", 8);
247                                 cp += 8;
248
249                                 cp += snprintf(cp, 6, "%d", len) + 1;
250
251                                 want_option_ack = 1;
252                         }
253 #endif
254                 }
255
256                 /* add ack and data */
257
258                 if ((cmd_get && (opcode == TFTP_ACK)) ||
259                         (cmd_put && (opcode == TFTP_DATA))) {
260
261                         *((unsigned short *) cp) = htons(block_nr);
262
263                         cp += 2;
264
265                         block_nr++;
266
267                         if (cmd_put && (opcode == TFTP_DATA)) {
268                                 len = read(localfd, cp, tftp_bufsize - 4);
269
270                                 if (len < 0) {
271                                         perror_msg("read");
272                                         break;
273                                 }
274
275                                 if (len != (tftp_bufsize - 4)) {
276                                         finished++;
277                                 }
278
279                                 cp += len;
280                         } else if (finished) {
281                                 break;
282                         }
283                 }
284
285
286                 /* send packet */
287
288
289                 do {
290
291                         len = cp - buf;
292
293 #ifdef CONFIG_FEATURE_TFTP_DEBUG
294                         printf("sending %u bytes\n", len);
295                         for (cp = buf; cp < &buf[len]; cp++)
296                                 printf("%02x ", *cp);
297                         printf("\n");
298 #endif
299                         if (sendto(socketfd, buf, len, 0,
300                                         (struct sockaddr *) &sa, sizeof(sa)) < 0) {
301                                 perror_msg("send");
302                                 len = -1;
303                                 break;
304                         }
305
306
307                         /* receive packet */
308
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(FD_SETSIZE, &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                                         perror_msg("recvfrom");
326                                         break;
327                                 }
328
329                                 timeout = 0;
330
331                                 if (sa.sin_port == htons(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                                 error_msg("timeout");
344
345                                 if (timeout == 0) {
346                                         len = -1;
347                                         error_msg("last timeout");
348                                 } else {
349                                         timeout--;
350                                 }
351                                 break;
352
353                         default:
354                                 perror_msg("select");
355                                 len = -1;
356                         }
357
358                 } while (timeout && (len >= 0));
359
360                 if (len < 0) {
361                         break;
362                 }
363
364                 /* process received packet */
365
366
367                 opcode = ntohs(*((unsigned short *) buf));
368                 tmp = ntohs(*((unsigned short *) &buf[2]));
369
370 #ifdef CONFIG_FEATURE_TFTP_DEBUG
371                 printf("received %d bytes: %04x %04x\n", len, opcode, tmp);
372 #endif
373
374                 if (opcode == TFTP_ERROR) {
375                         char *msg = NULL;
376
377                         if (buf[4] != '\0') {
378                                 msg = &buf[4];
379                                 buf[tftp_bufsize - 1] = '\0';
380                         } else if (tmp < (sizeof(tftp_error_msg) 
381                                           / sizeof(char *))) {
382
383                                 msg = (char *) tftp_error_msg[tmp];
384                         }
385
386                         if (msg) {
387                                 error_msg("server says: %s", msg);
388                         }
389
390                         break;
391                 }
392
393 #ifdef CONFIG_FEATURE_TFTP_BLOCKSIZE
394                 if (want_option_ack) {
395
396                          want_option_ack = 0;
397
398                          if (opcode == TFTP_OACK) {
399
400                                  /* server seems to support options */
401
402                                  char *res;
403
404                                  res = tftp_option_get(&buf[2], len-2, 
405                                                        "blksize");
406
407                                  if (res) {
408                                          int foo = atoi(res);
409                              
410                                          if (tftp_blocksize_check(foo,
411                                                            tftp_bufsize - 4)) {
412
413                                                  if (cmd_put) {
414                                                          opcode = TFTP_DATA;
415                                                  }
416                                                  else {
417                                                          opcode = TFTP_ACK;
418                                                  }
419 #ifdef CONFIG_FEATURE_TFTP_DEBUG
420                                                  printf("using blksize %u\n");
421 #endif
422                                                  tftp_bufsize = foo + 4;
423                                                  block_nr = 0;
424                                                  continue;
425                                          }
426                                  }
427                                  /* FIXME:
428                                   * we should send ERROR 8 */
429                                  error_msg("bad server option");
430                                  break;
431                          }
432
433                          error_msg("warning: blksize not supported by server"
434                                    " - reverting to 512");
435
436                          tftp_bufsize = TFTP_BLOCKSIZE_DEFAULT + 4;
437                 }
438 #endif
439
440                 if (cmd_get && (opcode == TFTP_DATA)) {
441
442                         if (tmp == block_nr) {
443                             
444                                 len = write(localfd, &buf[4], len - 4);
445
446                                 if (len < 0) {
447                                         perror_msg("write");
448                                         break;
449                                 }
450
451                                 if (len != (tftp_bufsize - 4)) {
452                                         finished++;
453                                 }
454
455                                 opcode = TFTP_ACK;
456                                 continue;
457                         }
458                 }
459
460                 if (cmd_put && (opcode == TFTP_ACK)) {
461
462                         if (tmp == (block_nr - 1)) {
463                                 if (finished) {
464                                         break;
465                                 }
466
467                                 opcode = TFTP_DATA;
468                                 continue;
469                         }
470                 }
471         }
472
473 #ifdef CONFIG_FEATURE_CLEAN_UP
474         close(socketfd);
475
476         RELEASE_CONFIG_BUFFER(buf);
477 #endif
478
479         return finished ? EXIT_SUCCESS : EXIT_FAILURE;
480 }
481
482 int tftp_main(int argc, char **argv)
483 {
484         struct hostent *host = NULL;
485         char *localfile = NULL;
486         char *remotefile = NULL;
487         int port = 69;
488         int cmd = 0;
489         int fd = -1;
490         int flags = 0;
491         int opt;
492         int result;
493         int blocksize = TFTP_BLOCKSIZE_DEFAULT;
494
495         /* figure out what to pass to getopt */
496
497 #ifdef CONFIG_FEATURE_TFTP_BLOCKSIZE
498 #define BS "b:"
499 #else
500 #define BS
501 #endif
502
503 #ifdef CONFIG_FEATURE_TFTP_GET
504 #define GET "g"
505 #else
506 #define GET 
507 #endif
508
509 #ifdef CONFIG_FEATURE_TFTP_PUT
510 #define PUT "p"
511 #else
512 #define PUT 
513 #endif
514
515         while ((opt = getopt(argc, argv, BS GET PUT "l:r:")) != -1) {
516                 switch (opt) {
517 #ifdef CONFIG_FEATURE_TFTP_BLOCKSIZE
518                 case 'b':
519                         blocksize = atoi(optarg);
520                         if (!tftp_blocksize_check(blocksize, 0)) {
521                                 return EXIT_FAILURE;
522                         }
523                         break;
524 #endif
525 #ifdef CONFIG_FEATURE_TFTP_GET
526                 case 'g':
527                         cmd = tftp_cmd_get;
528                         flags = O_WRONLY | O_CREAT;
529                         break;
530 #endif
531 #ifdef CONFIG_FEATURE_TFTP_PUT
532                 case 'p':
533                         cmd = tftp_cmd_put;
534                         flags = O_RDONLY;
535                         break;
536 #endif
537                 case 'l': 
538                         localfile = xstrdup(optarg);
539                         break;
540                 case 'r':
541                         remotefile = xstrdup(optarg);
542                         break;
543                 }
544         }
545
546         if ((cmd == 0) || (optind == argc)) {
547                 show_usage();
548         }
549
550         fd = open(localfile, flags, 0644);
551         if (fd < 0) {
552                 perror_msg_and_die("local file");
553         }
554
555         host = xgethostbyname(argv[optind]);
556
557         if (optind + 2 == argc) {
558                 port = atoi(argv[optind + 1]);
559         }
560
561 #ifdef CONFIG_FEATURE_TFTP_DEBUG
562         printf("using server \"%s\", remotefile \"%s\", "
563                 "localfile \"%s\".\n",
564                 inet_ntoa(*((struct in_addr *) host->h_addr)),
565                 remotefile, localfile);
566 #endif
567
568         result = tftp(cmd, host, remotefile, fd, port, blocksize);
569
570 #ifdef CONFIG_FEATURE_CLEAN_UP
571         close(fd);
572 #endif
573         return(result);
574 }