inline strcmp(s, "-") [actually macro-ize it for now - gcc is too stupid]
[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         char *buf=xmalloc(tftp_bufsize += 4);
148
149         if ((socketfd = socket(PF_INET, SOCK_DGRAM, 0)) < 0) {
150                 /* need to unlink the localfile, so don't use xsocket here. */
151                 bb_perror_msg("socket");
152                 return EXIT_FAILURE;
153         }
154
155         len = sizeof(sa);
156
157         memset(&sa, 0, len);
158         xbind(socketfd, (struct sockaddr *)&sa, len);
159
160         sa.sin_family = host->h_addrtype;
161         sa.sin_port = port;
162         memcpy(&sa.sin_addr, (struct in_addr *) host->h_addr,
163                    sizeof(sa.sin_addr));
164
165         /* build opcode */
166         if (cmd & tftp_cmd_get) {
167                 opcode = TFTP_RRQ;
168         }
169         if (cmd & tftp_cmd_put) {
170                 opcode = TFTP_WRQ;
171         }
172
173         while (1) {
174
175                 cp = buf;
176
177                 /* first create the opcode part */
178                 *((unsigned short *) cp) = htons(opcode);
179                 cp += 2;
180
181                 /* add filename and mode */
182                 if (((cmd & tftp_cmd_get) && (opcode == TFTP_RRQ)) ||
183                         ((cmd & tftp_cmd_put) && (opcode == TFTP_WRQ)))
184                 {
185                         int too_long = 0;
186
187                         /* see if the filename fits into buf
188                          * and fill in packet.  */
189                         len = strlen(remotefile) + 1;
190
191                         if ((cp + len) >= &buf[tftp_bufsize - 1]) {
192                                 too_long = 1;
193                         } else {
194                                 safe_strncpy(cp, remotefile, len);
195                                 cp += len;
196                         }
197
198                         if (too_long || ((&buf[tftp_bufsize - 1] - cp) < MODE_OCTET_LEN)) {
199                                 bb_error_msg("remote filename too long");
200                                 break;
201                         }
202
203                         /* add "mode" part of the package */
204                         memcpy(cp, MODE_OCTET, MODE_OCTET_LEN);
205                         cp += MODE_OCTET_LEN;
206
207 #if ENABLE_FEATURE_TFTP_BLOCKSIZE
208
209                         len = tftp_bufsize - 4; /* data block size */
210
211                         if (len != TFTP_BLOCKSIZE_DEFAULT) {
212
213                                 if ((&buf[tftp_bufsize - 1] - cp) < 15) {
214                                         bb_error_msg("remote filename too long");
215                                         break;
216                                 }
217
218                                 /* add "blksize" + number of blocks  */
219                                 memcpy(cp, OPTION_BLOCKSIZE, OPTION_BLOCKSIZE_LEN);
220                                 cp += OPTION_BLOCKSIZE_LEN;
221                                 cp += snprintf(cp, 6, "%d", len) + 1;
222
223                                 want_option_ack = 1;
224                         }
225 #endif
226                 }
227
228                 /* add ack and data */
229
230                 if (((cmd & tftp_cmd_get) && (opcode == TFTP_ACK)) ||
231                         ((cmd & tftp_cmd_put) && (opcode == TFTP_DATA))) {
232
233                         *((unsigned short *) cp) = htons(block_nr);
234
235                         cp += 2;
236
237                         block_nr++;
238
239                         if ((cmd & tftp_cmd_put) && (opcode == TFTP_DATA)) {
240                                 len = full_read(localfd, cp, tftp_bufsize - 4);
241
242                                 if (len < 0) {
243                                         bb_perror_msg(bb_msg_read_error);
244                                         break;
245                                 }
246
247                                 if (len != (tftp_bufsize - 4)) {
248                                         finished++;
249                                 }
250
251                                 cp += len;
252                         }
253                 }
254
255
256                 /* send packet */
257
258
259                 timeout = TFTP_NUM_RETRIES;     /* re-initialize */
260                 do {
261
262                         len = cp - buf;
263
264 #if ENABLE_DEBUG_TFTP
265                         fprintf(stderr, "sending %u bytes\n", len);
266                         for (cp = buf; cp < &buf[len]; cp++)
267                                 fprintf(stderr, "%02x ", (unsigned char) *cp);
268                         fprintf(stderr, "\n");
269 #endif
270                         if (sendto(socketfd, buf, len, 0,
271                                            (struct sockaddr *) &sa, sizeof(sa)) < 0) {
272                                 bb_perror_msg("send");
273                                 len = -1;
274                                 break;
275                         }
276
277
278                         if (finished && (opcode == TFTP_ACK)) {
279                                 break;
280                         }
281
282                         /* receive packet */
283
284                         memset(&from, 0, sizeof(from));
285                         fromlen = sizeof(from);
286
287                         tv.tv_sec = TFTP_TIMEOUT;
288                         tv.tv_usec = 0;
289
290                         FD_ZERO(&rfds);
291                         FD_SET(socketfd, &rfds);
292
293                         switch (select(socketfd + 1, &rfds, NULL, NULL, &tv)) {
294                         case 1:
295                                 len = recvfrom(socketfd, buf, tftp_bufsize, 0,
296                                                            (struct sockaddr *) &from, &fromlen);
297
298                                 if (len < 0) {
299                                         bb_perror_msg("recvfrom");
300                                         break;
301                                 }
302
303                                 timeout = 0;
304
305                                 if (sa.sin_port == port) {
306                                         sa.sin_port = from.sin_port;
307                                 }
308                                 if (sa.sin_port == from.sin_port) {
309                                         break;
310                                 }
311
312                                 /* fall-through for bad packets! */
313                                 /* discard the packet - treat as timeout */
314                                 timeout = TFTP_NUM_RETRIES;
315                         case 0:
316                                 bb_error_msg("timeout");
317
318                                 timeout--;
319                                 if (timeout == 0) {
320                                         len = -1;
321                                         bb_error_msg("last timeout");
322                                 }
323                                 break;
324                         default:
325                                 bb_perror_msg("select");
326                                 len = -1;
327                         }
328
329                 } while (timeout && (len >= 0));
330
331                 if ((finished) || (len < 0)) {
332                         break;
333                 }
334
335                 /* process received packet */
336
337                 opcode = ntohs(*((unsigned short *) buf));
338                 tmp = ntohs(*((unsigned short *) &buf[2]));
339
340 #if ENABLE_DEBUG_TFTP
341                 fprintf(stderr, "received %d bytes: %04x %04x\n", len, opcode, tmp);
342 #endif
343
344                 if (opcode == TFTP_ERROR) {
345                         const char *msg = NULL;
346
347                         if (buf[4] != '\0') {
348                                 msg = &buf[4];
349                                 buf[tftp_bufsize - 1] = '\0';
350                         } else if (tmp < (sizeof(tftp_bb_error_msg)
351                                                           / sizeof(char *))) {
352
353                                 msg = tftp_bb_error_msg[tmp];
354                         }
355
356                         if (msg) {
357                                 bb_error_msg("server says: %s", msg);
358                         }
359
360                         break;
361                 }
362 #if ENABLE_FEATURE_TFTP_BLOCKSIZE
363                 if (want_option_ack) {
364
365                         want_option_ack = 0;
366
367                         if (opcode == TFTP_OACK) {
368
369                                 /* server seems to support options */
370
371                                 char *res;
372
373                                 res = tftp_option_get(&buf[2], len - 2, OPTION_BLOCKSIZE);
374
375                                 if (res) {
376                                         int blksize = xatoi_u(res);
377
378                                         if (tftp_blocksize_check(blksize, tftp_bufsize - 4)) {
379
380                                                 if (cmd & tftp_cmd_put) {
381                                                         opcode = TFTP_DATA;
382                                                 } else {
383                                                         opcode = TFTP_ACK;
384                                                 }
385 #if ENABLE_DEBUG_TFTP
386                                                 fprintf(stderr, "using %s %u\n", OPTION_BLOCKSIZE,
387                                                                 blksize);
388 #endif
389                                                 tftp_bufsize = blksize + 4;
390                                                 block_nr = 0;
391                                                 continue;
392                                         }
393                                 }
394                                 /* FIXME:
395                                  * we should send ERROR 8 */
396                                 bb_error_msg("bad server option");
397                                 break;
398                         }
399
400                         bb_error_msg("warning: blksize not supported by server"
401                                                  " - reverting to 512");
402
403                         tftp_bufsize = TFTP_BLOCKSIZE_DEFAULT + 4;
404                 }
405 #endif
406
407                 if ((cmd & tftp_cmd_get) && (opcode == TFTP_DATA)) {
408
409                         if (tmp == block_nr) {
410
411                                 len = full_write(localfd, &buf[4], len - 4);
412
413                                 if (len < 0) {
414                                         bb_perror_msg(bb_msg_write_error);
415                                         break;
416                                 }
417
418                                 if (len != (tftp_bufsize - 4)) {
419                                         finished++;
420                                 }
421
422                                 opcode = TFTP_ACK;
423                                 continue;
424                         }
425                         /* in case the last ack disappeared into the ether */
426                         if (tmp == (block_nr - 1)) {
427                                 --block_nr;
428                                 opcode = TFTP_ACK;
429                                 continue;
430                         } else if (tmp + 1 == block_nr) {
431                                 /* Server lost our TFTP_ACK.  Resend it */
432                                 block_nr = tmp;
433                                 opcode = TFTP_ACK;
434                                 continue;
435                         }
436                 }
437
438                 if ((cmd & tftp_cmd_put) && (opcode == TFTP_ACK)) {
439
440                         if (tmp == (unsigned short) (block_nr - 1)) {
441                                 if (finished) {
442                                         break;
443                                 }
444
445                                 opcode = TFTP_DATA;
446                                 continue;
447                         }
448                 }
449         }
450
451 #if ENABLE_FEATURE_CLEAN_UP
452         close(socketfd);
453         free(buf);
454 #endif
455
456         return finished ? EXIT_SUCCESS : EXIT_FAILURE;
457 }
458
459 int tftp_main(int argc, char **argv)
460 {
461         struct hostent *host = NULL;
462         const char *localfile = NULL;
463         const char *remotefile = NULL;
464         int port;
465         int cmd = 0;
466         int fd = -1;
467         int flags = 0;
468         int result;
469         int blocksize = TFTP_BLOCKSIZE_DEFAULT;
470
471         /* figure out what to pass to getopt */
472
473 #if ENABLE_FEATURE_TFTP_BLOCKSIZE
474         char *sblocksize = NULL;
475
476 #define BS "b:"
477 #define BS_ARG , &sblocksize
478 #else
479 #define BS
480 #define BS_ARG
481 #endif
482
483 #if ENABLE_FEATURE_TFTP_GET
484 #define GET "g"
485 #define GET_COMPL ":g"
486 #else
487 #define GET
488 #define GET_COMPL
489 #endif
490
491 #if ENABLE_FEATURE_TFTP_PUT
492 #define PUT "p"
493 #define PUT_COMPL ":p"
494 #else
495 #define PUT
496 #define PUT_COMPL
497 #endif
498
499 #if defined(CONFIG_FEATURE_TFTP_GET) && defined(CONFIG_FEATURE_TFTP_PUT)
500         opt_complementary = GET_COMPL PUT_COMPL ":?g--p:p--g";
501 #elif defined(CONFIG_FEATURE_TFTP_GET) || defined(CONFIG_FEATURE_TFTP_PUT)
502         opt_complementary = GET_COMPL PUT_COMPL;
503 #endif
504
505         cmd = getopt32(argc, argv, GET PUT "l:r:" BS, &localfile, &remotefile BS_ARG);
506
507         cmd &= (tftp_cmd_get | tftp_cmd_put);
508 #if ENABLE_FEATURE_TFTP_GET
509         if (cmd == tftp_cmd_get)
510                 flags = O_WRONLY | O_CREAT | O_TRUNC;
511 #endif
512 #if ENABLE_FEATURE_TFTP_PUT
513         if (cmd == tftp_cmd_put)
514                 flags = O_RDONLY;
515 #endif
516
517 #if ENABLE_FEATURE_TFTP_BLOCKSIZE
518         if (sblocksize) {
519                 blocksize = xatoi_u(sblocksize);
520                 if (!tftp_blocksize_check(blocksize, 0)) {
521                         return EXIT_FAILURE;
522                 }
523         }
524 #endif
525
526         if (localfile == NULL)
527                 localfile = remotefile;
528         if (remotefile == NULL)
529                 remotefile = localfile;
530         if ((localfile == NULL && remotefile == NULL) || (argv[optind] == NULL))
531                 bb_show_usage();
532
533         if (localfile == NULL || LONE_DASH(localfile)) {
534                 fd = (cmd == tftp_cmd_get) ? STDOUT_FILENO : STDIN_FILENO;
535         } else {
536                 fd = open(localfile, flags, 0644); /* fail below */
537         }
538         if (fd < 0) {
539                 bb_perror_msg_and_die("local file");
540         }
541
542         host = xgethostbyname(argv[optind]);
543         port = bb_lookup_port(argv[optind + 1], "udp", 69);
544
545 #if ENABLE_DEBUG_TFTP
546         fprintf(stderr, "using server \"%s\", remotefile \"%s\", "
547                         "localfile \"%s\".\n",
548                         inet_ntoa(*((struct in_addr *) host->h_addr)),
549                         remotefile, localfile);
550 #endif
551
552         result = tftp(cmd, host, remotefile, fd, port, blocksize);
553
554         if (!(fd == STDOUT_FILENO || fd == STDIN_FILENO)) {
555                 if (ENABLE_FEATURE_CLEAN_UP)
556                         close(fd);
557                 if (cmd == tftp_cmd_get && result != EXIT_SUCCESS)
558                         unlink(localfile);
559         }
560         return result;
561 }