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