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