tftpd: new applet (mostly using existing code for tftp)
[oweals/busybox.git] / networking / tftp.c
1 /* vi: set sw=4 ts=4: */
2 /* -------------------------------------------------------------------------
3  * tftp.c
4  *
5  * A simple tftp client/server 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  * tftpd added by Denys Vlasenko
20  *
21  * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
22  * ------------------------------------------------------------------------- */
23
24 #include "libbb.h"
25
26 #if ENABLE_FEATURE_TFTP_GET || ENABLE_FEATURE_TFTP_PUT
27
28 #define TFTP_BLOCKSIZE_DEFAULT 512      /* according to RFC 1350, don't change */
29 #define TFTP_TIMEOUT_MS         50
30 #define TFTP_MAXTIMEOUT_MS    2000
31 #define TFTP_NUM_RETRIES        12      /* number of backed-off retries */
32
33 /* opcodes we support */
34 #define TFTP_RRQ   1
35 #define TFTP_WRQ   2
36 #define TFTP_DATA  3
37 #define TFTP_ACK   4
38 #define TFTP_ERROR 5
39 #define TFTP_OACK  6
40
41 #if ENABLE_FEATURE_TFTP_GET && !ENABLE_FEATURE_TFTP_PUT
42 #define USE_GETPUT(...)
43 #define CMD_GET(cmd) 1
44 #define CMD_PUT(cmd) 0
45 #elif !ENABLE_FEATURE_TFTP_GET && ENABLE_FEATURE_TFTP_PUT
46 #define USE_GETPUT(...)
47 #define CMD_GET(cmd) 0
48 #define CMD_PUT(cmd) 1
49 #else
50 #define USE_GETPUT(...) __VA_ARGS__
51 /* masks coming from getopt32 */
52 #define CMD_GET(cmd) ((cmd) & 1)
53 #define CMD_PUT(cmd) ((cmd) & 2)
54 #endif
55 /* NB: in the code below
56  * CMD_GET(cmd) and CMD_PUT(cmd) are mutually exclusive
57  */
58
59
60 #if ENABLE_FEATURE_TFTP_BLOCKSIZE
61
62 static int tftp_blocksize_check(int blocksize, int bufsize)
63 {
64         /* Check if the blocksize is valid:
65          * RFC2348 says between 8 and 65464,
66          * but our implementation makes it impossible
67          * to use blocksizes smaller than 22 octets.
68          */
69         if ((bufsize && (blocksize > bufsize))
70          || (blocksize < 24) || (blocksize > 65564)
71         ) {
72                 bb_error_msg("bad blocksize");
73                 return 0;
74         }
75         return blocksize;
76 }
77
78 static char *tftp_option_get(char *buf, int len, const char *option)
79 {
80         int opt_val = 0;
81         int opt_found = 0;
82         int k;
83
84         /* buf points to:
85          * "opt_name<NUL>opt_val<NUL>opt_name2<NUL>opt_val2<NUL>..." */
86
87         while (len > 0) {
88                 /* Make sure options are terminated correctly */
89                 for (k = 0; k < len; k++) {
90                         if (buf[k] == '\0') {
91                                 goto nul_found;
92                         }
93                 }
94                 return NULL;
95  nul_found:
96                 if (opt_val == 0) { /* it's "name" part */
97                         if (strcasecmp(buf, option) == 0) {
98                                 opt_found = 1;
99                         }
100                 } else if (opt_found) {
101                         return buf;
102                 }
103
104                 k++;
105                 buf += k;
106                 len -= k;
107                 opt_val ^= 1;
108         }
109
110         return NULL;
111 }
112
113 #endif
114
115 static int tftp_protocol(
116                 USE_GETPUT(int cmd,)
117                 len_and_sockaddr *our_lsa,
118                 len_and_sockaddr *peer_lsa,
119                 USE_TFTP(const char *remote_file,)
120                 int local_fd,
121                 int blocksize)
122 {
123 #if !ENABLE_TFTP
124 #define remote_file NULL
125 #endif
126         struct pollfd pfd[1];
127 #define socket_fd (pfd[0].fd)
128         int len;
129         int send_len;
130         USE_FEATURE_TFTP_BLOCKSIZE(smallint want_option_ack = 0;)
131         smallint finished = 0;
132         uint16_t opcode;
133         uint16_t block_nr;
134         uint16_t recv_blk;
135         int retries, waittime_ms;
136         int tftp_bufsize = blocksize + 4;
137         char *cp;
138         /* Can't use RESERVE_CONFIG_BUFFER here since the allocation
139          * size varies meaning BUFFERS_GO_ON_STACK would fail */
140         /* We must keep the transmit and receive buffers seperate */
141         /* In case we rcv a garbage pkt and we need to rexmit the last pkt */
142         char *xbuf = xmalloc(tftp_bufsize);
143         char *rbuf = xmalloc(tftp_bufsize);
144
145         socket_fd = xsocket(peer_lsa->u.sa.sa_family, SOCK_DGRAM, 0);
146         setsockopt_reuseaddr(socket_fd);
147
148         if (!ENABLE_TFTP || our_lsa) {
149                 /* tftpd */
150                 block_nr = 0;
151
152                 /* Create a socket which is:
153                  * 1. bound to IP:port peer sent 1st datagram to,
154                  * 2. connected to peer's IP:port
155                  * This way we will answer from the IP:port peer
156                  * expects, will not get any other packets on
157                  * the socket, and also plain read/write will work. */
158                 xbind(socket_fd, &our_lsa->u.sa, our_lsa->len);
159                 xconnect(socket_fd, &peer_lsa->u.sa, peer_lsa->len);
160
161 #if ENABLE_FEATURE_TFTP_BLOCKSIZE
162                 if (blocksize != TFTP_BLOCKSIZE_DEFAULT) {
163                         /* Create and send OACK packet */
164                         opcode = TFTP_OACK;
165                         cp = xbuf + 2;
166                         goto add_blksize_opt;
167                 }
168                 /* else: just fall into while (1) loop below */
169 #endif
170         } else {
171                 /* tftp */
172                 block_nr = 1;
173
174                 /* We can't (and don't really need to) bind the socket:
175                  * we don't know from which local IP datagrams will be sent,
176                  * but kernel will pick the same IP every time (unless routing
177                  * table is changed), thus peer will see dgrams consistently
178                  * coming from the same IP.
179                  * We would like to connect the socket, but since peer's
180                  * UDP code can be less perfect than ours, _peer's_ IP:port
181                  * in replies may differ from IP:port we used to send
182                  * our first packet. We can connect() only when we get
183                  * first reply. */
184
185                 /* build opcode */
186                 opcode = TFTP_WRQ;
187                 if (CMD_GET(cmd)) {
188                         opcode = TFTP_RRQ;
189                 }
190                 cp = xbuf + 2;
191                 /* add filename and mode */
192                 /* fill in packet if the filename fits into xbuf */
193                 len = strlen(remote_file) + 1;
194                 if (2 + len + sizeof("octet") >= tftp_bufsize) {
195                         bb_error_msg("remote filename is too long");
196                         goto ret;
197                 }
198                 strcpy(cp, remote_file);
199                 cp += len;
200                 /* add "mode" part of the package */
201                 strcpy(cp, "octet");
202                 cp += sizeof("octet");
203
204 #if ENABLE_FEATURE_TFTP_BLOCKSIZE
205                 if (blocksize != TFTP_BLOCKSIZE_DEFAULT) {
206                         /* rfc2348 says that 65464 is a max allowed value */
207                         if ((&xbuf[tftp_bufsize - 1] - cp) < sizeof("blksize NNNNN")) {
208                                 bb_error_msg("remote filename is too long");
209                                 goto ret;
210                         }
211                         want_option_ack = 1;
212  add_blksize_opt:
213                         /* add "blksize", <nul>, blocksize, <nul> */
214                         strcpy(cp, "blksize");
215                         cp += sizeof("blksize");
216                         cp += snprintf(cp, 6, "%d", blocksize) + 1;
217                 }
218 #endif
219                 /* First packet is built, so skip packet generation */
220                 goto send_pkt;
221         }
222
223         /* Using mostly goto's - continue/break will be less clear
224          * in where we actually jump to */
225         while (1) {
226                 /* Build ACK or DATA */
227                 cp = xbuf + 2;
228                 *((uint16_t*)cp) = htons(block_nr);
229                 cp += 2;
230                 block_nr++;
231                 opcode = TFTP_ACK;
232                 if (CMD_PUT(cmd)) {
233                         opcode = TFTP_DATA;
234                         len = full_read(local_fd, cp, tftp_bufsize - 4);
235                         if (len < 0) {
236                                 bb_perror_msg(bb_msg_read_error);
237                                 goto ret;
238                         }
239                         if (len != (tftp_bufsize - 4)) {
240                                 finished = 1;
241                         }
242                         cp += len;
243                 }
244  send_pkt:
245                 /* Send packet */
246                 *((uint16_t*)xbuf) = htons(opcode); /* fill in opcode part */
247                 send_len = cp - xbuf;
248                 /* NB: send_len value is preserved in code below
249                  * for potential resend */
250
251                 retries = TFTP_NUM_RETRIES;     /* re-initialize */
252                 waittime_ms = TFTP_TIMEOUT_MS;
253
254  send_again:
255 #if ENABLE_DEBUG_TFTP
256                 fprintf(stderr, "sending %u bytes\n", send_len);
257                 for (cp = xbuf; cp < &xbuf[send_len]; cp++)
258                         fprintf(stderr, "%02x ", (unsigned char) *cp);
259                 fprintf(stderr, "\n");
260 #endif
261                 xsendto(socket_fd, xbuf, send_len, &peer_lsa->u.sa, peer_lsa->len);
262                 /* Was it final ACK? then exit */
263                 if (finished && (opcode == TFTP_ACK))
264                         goto ret;
265
266  recv_again:
267                 /* Receive packet */
268                 /*pfd[0].fd = socket_fd;*/
269                 pfd[0].events = POLLIN;
270                 switch (safe_poll(pfd, 1, waittime_ms)) {
271                 case 1:
272                         if (!our_lsa) {
273                                 /* tftp (not tftpd!) receiving 1st packet */
274                                 our_lsa = ((void*)(ptrdiff_t)-1); /* not NULL */
275                                 len = recvfrom(socket_fd, rbuf, tftp_bufsize, 0,
276                                                 &peer_lsa->u.sa, &peer_lsa->len);
277                                 /* Our first dgram went to port 69
278                                  * but reply may come from different one.
279                                  * Remember and use this new port (and IP) */
280                                 if (len >= 0)
281                                         xconnect(socket_fd, &peer_lsa->u.sa, peer_lsa->len);
282                         } else {
283                                 /* tftpd, or not the very first packet:
284                                  * socket is connect()ed, can just read from it. */
285                                 len = safe_read(socket_fd, rbuf, tftp_bufsize);
286                         }
287                         if (len < 0) {
288                                 bb_perror_msg("read");
289                                 goto ret;
290                         }
291                         goto process_pkt;
292                 case 0:
293                         retries--;
294                         if (retries == 0) {
295                                 bb_error_msg("timeout");
296                                 goto ret;
297                         }
298
299                         /* exponential backoff with limit */
300                         waittime_ms += waittime_ms/2;
301                         if (waittime_ms > TFTP_MAXTIMEOUT_MS) {
302                                 waittime_ms = TFTP_MAXTIMEOUT_MS;
303                         }
304
305                         goto send_again; /* resend last sent pkt */
306                 default:
307                         /*bb_perror_msg("poll"); - done in safe_poll */
308                         goto ret;
309                 }
310  process_pkt:
311                 /* Process recv'ed packet */
312                 opcode = ntohs( ((uint16_t*)rbuf)[0] );
313                 recv_blk = ntohs( ((uint16_t*)rbuf)[1] );
314
315 #if ENABLE_DEBUG_TFTP
316                 fprintf(stderr, "received %d bytes: %04x %04x\n", len, opcode, recv_blk);
317 #endif
318
319                 if (opcode == TFTP_ERROR) {
320                         static const char *const errcode_str[] = {
321                                 "",
322                                 "file not found",
323                                 "access violation",
324                                 "disk full",
325                                 "illegal TFTP operation",
326                                 "unknown transfer id",
327                                 "file already exists",
328                                 "no such user",
329                                 "bad option"
330                         };
331
332                         const char *msg = "";
333
334                         if (rbuf[4] != '\0') {
335                                 msg = &rbuf[4];
336                                 rbuf[tftp_bufsize - 1] = '\0';
337                         } else if (recv_blk < ARRAY_SIZE(errcode_str)) {
338                                 msg = errcode_str[recv_blk];
339                         }
340                         bb_error_msg("server error: (%u) %s", recv_blk, msg);
341                         goto ret;
342                 }
343
344 #if ENABLE_FEATURE_TFTP_BLOCKSIZE
345                 if (want_option_ack) {
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                                 if (res) {
354                                         int blksize = xatoi_u(res);
355                                         if (!tftp_blocksize_check(blksize, tftp_bufsize - 4)) {
356                                                 /* send ERROR 8 to server... */
357                                                 /* htons can be impossible to use in const initializer: */
358                                                 /*static const uint16_t error_8[2] = { htons(TFTP_ERROR), htons(8) };*/
359                                                 /* thus we open-code big-endian layout */
360                                                 static const uint8_t error_8[4] = { 0,TFTP_ERROR, 0,8 };
361                                                 xsendto(socket_fd, error_8, 4, &peer_lsa->u.sa, peer_lsa->len);
362                                                 bb_error_msg("server proposes bad blksize %d, exiting", blksize);
363                                                 goto ret;
364                                         }
365 #if ENABLE_DEBUG_TFTP
366                                         fprintf(stderr, "using blksize %u\n",
367                                                         blksize);
368 #endif
369                                         tftp_bufsize = blksize + 4;
370                                         /* Send ACK for OACK ("block" no: 0) */
371                                         block_nr = 0;
372                                         continue;
373                                 }
374                                 /* rfc2347:
375                                  * "An option not acknowledged by the server
376                                  *  must be ignored by the client and server
377                                  *  as if it were never requested." */
378                         }
379
380                         bb_error_msg("blksize is not supported by server"
381                                                 " - reverting to 512");
382                         tftp_bufsize = TFTP_BLOCKSIZE_DEFAULT + 4;
383                 }
384 #endif
385                 /* block_nr is already advanced to next block# we expect
386                  * to get / block# we are about to send next time */
387
388                 if (CMD_GET(cmd) && (opcode == TFTP_DATA)) {
389                         if (recv_blk == block_nr) {
390                                 len = full_write(local_fd, &rbuf[4], len - 4);
391                                 if (len < 0) {
392                                         bb_perror_msg(bb_msg_write_error);
393                                         goto ret;
394                                 }
395                                 if (len != (tftp_bufsize - 4)) {
396                                         finished = 1;
397                                 }
398                                 continue; /* send ACK */
399                         }
400                         if (recv_blk == (block_nr - 1)) {
401                                 /* Server lost our TFTP_ACK.  Resend it */
402                                 block_nr = recv_blk;
403                                 continue;
404                         }
405                 }
406
407                 if (CMD_PUT(cmd) && (opcode == TFTP_ACK)) {
408                         /* did peer ACK our last DATA pkt? */
409                         if (recv_blk == (uint16_t) (block_nr - 1)) {
410                                 if (finished)
411                                         goto ret;
412                                 continue; /* send next block */
413                         }
414                 }
415                 /* Awww... recv'd packet is not recognized! */
416                 goto recv_again;
417                 /* why recv_again? - rfc1123 says:
418                  * "The sender (i.e., the side originating the DATA packets)
419                  *  must never resend the current DATA packet on receipt
420                  *  of a duplicate ACK".
421                  * DATA pkts are resent ONLY on timeout.
422                  * Thus "goto send_again" will ba a bad mistake above.
423                  * See:
424                  * http://en.wikipedia.org/wiki/Sorcerer's_Apprentice_Syndrome
425                  */
426         } /* end of "while (1)" */
427  ret:
428         if (ENABLE_FEATURE_CLEAN_UP) {
429                 close(socket_fd);
430                 free(xbuf);
431                 free(rbuf);
432         }
433         return finished == 0; /* returns 1 on failure */
434 }
435
436 #if ENABLE_TFTP
437
438 int tftp_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
439 int tftp_main(int argc ATTRIBUTE_UNUSED, char **argv)
440 {
441         len_and_sockaddr *peer_lsa;
442         const char *local_file = NULL;
443         const char *remote_file = NULL;
444         int port;
445         USE_GETPUT(int cmd;)
446         int local_fd;
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                         USE_FEATURE_TFTP_BLOCKSIZE("b+");
455
456         USE_GETPUT(cmd =) getopt32(argv,
457                         USE_FEATURE_TFTP_GET("g") USE_FEATURE_TFTP_PUT("p")
458                                 "l:r:" USE_FEATURE_TFTP_BLOCKSIZE("b:"),
459                         &local_file, &remote_file
460                         USE_FEATURE_TFTP_BLOCKSIZE(, &blocksize));
461         argv += optind;
462
463         flags = O_RDONLY;
464         if (CMD_GET(cmd))
465                 flags = O_WRONLY | O_CREAT | O_TRUNC;
466
467 #if ENABLE_FEATURE_TFTP_BLOCKSIZE
468         if (!tftp_blocksize_check(blocksize, 0))
469                 return EXIT_FAILURE;
470 #endif
471
472         if (!local_file)
473                 local_file = remote_file;
474         if (!remote_file)
475                 remote_file = local_file;
476         /* Error if filename or host is not known */
477         if (!remote_file || !argv[0])
478                 bb_show_usage();
479
480         local_fd = CMD_GET(cmd) ? STDOUT_FILENO : STDIN_FILENO;
481         if (!LONE_DASH(local_file)) {
482                 local_fd = xopen(local_file, flags);
483         }
484
485         port = bb_lookup_port(argv[1], "udp", 69);
486         peer_lsa = xhost2sockaddr(argv[0], port);
487
488 #if ENABLE_DEBUG_TFTP
489         fprintf(stderr, "using server '%s', remote_file '%s', local_file '%s'\n",
490                         xmalloc_sockaddr2dotted(&peer_lsa->u.sa),
491                         remote_file, local_file);
492 #endif
493
494         result = tftp_protocol(
495                         USE_GETPUT(cmd,)
496                         NULL /* our_lsa*/,
497                         peer_lsa,
498                         remote_file, local_fd, blocksize);
499
500         if (ENABLE_FEATURE_CLEAN_UP)
501                 close(local_fd);
502         if (result != EXIT_SUCCESS && !LONE_DASH(local_file) && CMD_GET(cmd)) {
503                 unlink(local_file);
504         }
505         return result;
506 }
507
508 #endif /* ENABLE_TFTP */
509
510 #if ENABLE_TFTPD
511
512 /* TODO: libbb candidate? */
513 static len_and_sockaddr *get_sock_lsa(int s)
514 {
515         len_and_sockaddr *lsa;
516         socklen_t len = 0;
517
518         if (getsockname(s, NULL, &len) != 0)
519                 return NULL;
520         lsa = xzalloc(LSA_LEN_SIZE + len);
521         lsa->len = len;
522         getsockname(s, &lsa->u.sa, &lsa->len);
523         return lsa;
524 }
525
526 int tftpd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
527 int tftpd_main(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED)
528 {
529         struct stat statbuf;
530         char block_buf[TFTP_BLOCKSIZE_DEFAULT];
531         len_and_sockaddr *our_lsa;
532         len_and_sockaddr *peer_lsa;
533         char *filename, *mode, *opt_str;
534         int result, opcode, cmd, req_modebits, open_mode, local_fd, blksize;
535
536         our_lsa = get_sock_lsa(STDIN_FILENO);
537         if (!our_lsa)
538                 bb_perror_msg_and_die("stdin is not a socket");
539         peer_lsa = xzalloc(LSA_LEN_SIZE + our_lsa->len);
540         peer_lsa->len = our_lsa->len;
541
542         if (argv[1])
543                 xchdir(argv[1]);
544
545         result = recv_from_to(STDIN_FILENO, block_buf, sizeof(block_buf),
546                         0 /* flags */,
547                         &peer_lsa->u.sa, &our_lsa->u.sa, our_lsa->len);
548
549         opcode = ntohs(*(uint16_t*)block_buf);
550         if (result < 4 || result >= sizeof(block_buf)
551          || block_buf[result-1] != '\0'
552          || (opcode != TFTP_RRQ && opcode != TFTP_WRQ)
553         ) {
554                 bb_error_msg_and_die("malformed packet");
555         }
556         filename = block_buf + 2;
557         if (filename[0] == '.' || strstr(filename, "/.")) {
558                 bb_error_msg_and_die("dot in filename");
559         }
560         mode = filename + strlen(filename) + 1;
561         if (mode >= block_buf + sizeof(block_buf)
562          || strcmp(mode, "octet") != 0
563         ) {
564                 bb_error_msg_and_die("malformed packet");
565         }
566         blksize = TFTP_BLOCKSIZE_DEFAULT;
567 #if ENABLE_FEATURE_TFTP_BLOCKSIZE
568         opt_str = mode + 6;
569         if (opt_str < block_buf + sizeof(block_buf)) {
570                 char *res = tftp_option_get(opt_str, block_buf + sizeof(block_buf) - opt_str, "blksize");
571                 if (res) {
572                         int sz = xatoi_u(res);
573                         if (tftp_blocksize_check(sz, 0))
574                                 blksize = sz;
575                 }
576         }
577 #endif
578         xstat(filename, &statbuf);
579         /* if opcode == TFTP_WRQ: */
580         cmd = 1; /* CMD_GET: we will receive file's data */
581         req_modebits = 0222; /* writable by anyone */
582         open_mode = O_WRONLY | O_TRUNC;
583         if (opcode == TFTP_RRQ) {
584                 cmd = 2; /* CMD_PUT */
585                 req_modebits = 0444; /* readable by anyone */
586                 open_mode = O_RDONLY;
587         }
588         if (!S_ISREG(statbuf.st_mode)
589          || (statbuf.st_mode & req_modebits) != req_modebits
590         ) {
591                 bb_error_msg_and_die("access to '%s' is denied", filename);
592         }
593         local_fd = xopen(filename, open_mode);
594
595         close(STDIN_FILENO); /* close old, possibly wildcard socket */
596         /* tftp_protocol() will create new one, bound to particular local IP */
597         result = tftp_protocol(
598                 USE_GETPUT(cmd,)
599                 our_lsa, peer_lsa,
600                 USE_TFTP(NULL /*remote_file*/,)
601                 local_fd,
602                 blksize
603         );
604
605         return result;
606 }
607
608 #endif /* ENABLE_TFTPD */
609
610 #endif /* ENABLE_FEATURE_TFTP_GET || ENABLE_FEATURE_TFTP_PUT */