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