1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21#include "libbb.h"
22
23#if ENABLE_FEATURE_TFTP_GET || ENABLE_FEATURE_TFTP_PUT
24
25#define TFTP_BLKSIZE_DEFAULT 512
26#define TFTP_BLKSIZE_DEFAULT_STR "512"
27
28#define TFTP_TIMEOUT_MS 100
29#define TFTP_MAXTIMEOUT_MS 2000
30#define TFTP_NUM_RETRIES 12
31
32
33#define TFTP_RRQ 1
34#define TFTP_WRQ 2
35#define TFTP_DATA 3
36#define TFTP_ACK 4
37#define TFTP_ERROR 5
38#define TFTP_OACK 6
39
40
41
42#define ERR_UNSPEC 0
43#define ERR_NOFILE 1
44#define ERR_ACCESS 2
45
46#define ERR_WRITE 3
47#define ERR_OP 4
48#define ERR_BAD_ID 5
49#define ERR_EXIST 6
50#define ERR_BAD_USER 7
51#define ERR_BAD_OPT 8
52
53
54enum {
55 TFTP_OPT_GET = (1 << 0),
56 TFTP_OPT_PUT = (1 << 1),
57
58 TFTPD_OPT = (1 << 7) * ENABLE_TFTPD,
59 TFTPD_OPT_r = (1 << 8) * ENABLE_TFTPD,
60 TFTPD_OPT_c = (1 << 9) * ENABLE_TFTPD,
61 TFTPD_OPT_u = (1 << 10) * ENABLE_TFTPD,
62};
63
64#if ENABLE_FEATURE_TFTP_GET && !ENABLE_FEATURE_TFTP_PUT
65#define IF_GETPUT(...)
66#define CMD_GET(cmd) 1
67#define CMD_PUT(cmd) 0
68#elif !ENABLE_FEATURE_TFTP_GET && ENABLE_FEATURE_TFTP_PUT
69#define IF_GETPUT(...)
70#define CMD_GET(cmd) 0
71#define CMD_PUT(cmd) 1
72#else
73#define IF_GETPUT(...) __VA_ARGS__
74#define CMD_GET(cmd) ((cmd) & TFTP_OPT_GET)
75#define CMD_PUT(cmd) ((cmd) & TFTP_OPT_PUT)
76#endif
77
78
79
80
81
82struct globals {
83
84 uint8_t error_pkt[4 + 32];
85 char *user_opt;
86
87 char block_buf[TFTP_BLKSIZE_DEFAULT];
88#if ENABLE_FEATURE_TFTP_PROGRESS_BAR
89 off_t pos;
90 off_t size;
91 const char *file;
92 bb_progress_t pmt;
93#endif
94} FIX_ALIASING;
95#define G (*(struct globals*)&bb_common_bufsiz1)
96struct BUG_G_too_big {
97 char BUG_G_too_big[sizeof(G) <= COMMON_BUFSIZE ? 1 : -1];
98};
99#define block_buf (G.block_buf )
100#define user_opt (G.user_opt )
101#define error_pkt (G.error_pkt )
102#define INIT_G() do { } while (0)
103
104#define error_pkt_reason (error_pkt[3])
105#define error_pkt_str (error_pkt + 4)
106
107#if ENABLE_FEATURE_TFTP_PROGRESS_BAR
108static void tftp_progress_update(void)
109{
110 bb_progress_update(&G.pmt, G.file, 0, G.pos, G.size);
111}
112static void tftp_progress_init(void)
113{
114 bb_progress_init(&G.pmt);
115 tftp_progress_update();
116}
117static void tftp_progress_done(void)
118{
119 if (G.pmt.inited) {
120 tftp_progress_update();
121 bb_putchar_stderr('\n');
122 G.pmt.inited = 0;
123 }
124}
125#else
126# define tftp_progress_init() ((void)0)
127# define tftp_progress_done() ((void)0)
128#endif
129
130#if ENABLE_FEATURE_TFTP_BLOCKSIZE
131
132static int tftp_blksize_check(const char *blksize_str, int maxsize)
133{
134
135
136
137
138 unsigned blksize = bb_strtou(blksize_str, NULL, 10);
139 if (errno
140 || (blksize < 24) || (blksize > maxsize)
141 ) {
142 bb_error_msg("bad blocksize '%s'", blksize_str);
143 return -1;
144 }
145# if ENABLE_TFTP_DEBUG
146 bb_error_msg("using blksize %u", blksize);
147# endif
148 return blksize;
149}
150
151static char *tftp_get_option(const char *option, char *buf, int len)
152{
153 int opt_val = 0;
154 int opt_found = 0;
155 int k;
156
157
158
159
160 while (len > 0) {
161
162 for (k = 0; k < len; k++) {
163 if (buf[k] == '\0') {
164 goto nul_found;
165 }
166 }
167 return NULL;
168 nul_found:
169 if (opt_val == 0) {
170 if (strcasecmp(buf, option) == 0) {
171 opt_found = 1;
172 }
173 } else if (opt_found) {
174 return buf;
175 }
176
177 k++;
178 buf += k;
179 len -= k;
180 opt_val ^= 1;
181 }
182
183 return NULL;
184}
185
186#endif
187
188static int tftp_protocol(
189
190 len_and_sockaddr *our_lsa,
191 len_and_sockaddr *peer_lsa,
192 const char *local_file
193 IF_TFTP(, const char *remote_file)
194#if !ENABLE_TFTP
195# define remote_file NULL
196#endif
197
198 IF_FEATURE_TFTP_BLOCKSIZE(, int want_transfer_size)
199 IF_FEATURE_TFTP_BLOCKSIZE(, int blksize))
200{
201#if !ENABLE_FEATURE_TFTP_BLOCKSIZE
202 enum { blksize = TFTP_BLKSIZE_DEFAULT };
203#endif
204
205 struct pollfd pfd[1];
206#define socket_fd (pfd[0].fd)
207 int len;
208 int send_len;
209 IF_FEATURE_TFTP_BLOCKSIZE(smallint expect_OACK = 0;)
210 smallint finished = 0;
211 uint16_t opcode;
212 uint16_t block_nr;
213 uint16_t recv_blk;
214 int open_mode, local_fd;
215 int retries, waittime_ms;
216 int io_bufsize = blksize + 4;
217 char *cp;
218
219
220
221
222
223
224 char *xbuf = xmalloc(io_bufsize);
225 char *rbuf = xmalloc(io_bufsize);
226
227 socket_fd = xsocket(peer_lsa->u.sa.sa_family, SOCK_DGRAM, 0);
228 setsockopt_reuseaddr(socket_fd);
229
230 if (!ENABLE_TFTP || our_lsa) {
231
232
233
234
235
236
237 xbind(socket_fd, &our_lsa->u.sa, our_lsa->len);
238 xconnect(socket_fd, &peer_lsa->u.sa, peer_lsa->len);
239
240
241 if (error_pkt_reason || error_pkt_str[0])
242 goto send_err_pkt;
243
244 if (user_opt) {
245 struct passwd *pw = xgetpwnam(user_opt);
246 change_identity(pw);
247 }
248 }
249
250
251 if (CMD_PUT(option_mask32)) {
252 open_mode = O_RDONLY;
253 } else {
254 open_mode = O_WRONLY | O_TRUNC | O_CREAT;
255#if ENABLE_TFTPD
256 if ((option_mask32 & (TFTPD_OPT+TFTPD_OPT_c)) == TFTPD_OPT) {
257
258 open_mode = O_WRONLY | O_TRUNC;
259 }
260#endif
261 }
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291 block_nr = 1;
292 cp = xbuf + 2;
293
294 if (!ENABLE_TFTP || our_lsa) {
295
296 local_fd = open(local_file, open_mode, 0666);
297 if (local_fd < 0) {
298 error_pkt_reason = ERR_NOFILE;
299 strcpy((char*)error_pkt_str, "can't open file");
300 goto send_err_pkt;
301 }
302
303#if ENABLE_FEATURE_TFTP_BLOCKSIZE
304 if (blksize != TFTP_BLKSIZE_DEFAULT || want_transfer_size) {
305
306
307
308
309 opcode = TFTP_OACK;
310 goto add_blksize_opt;
311 }
312#endif
313 if (CMD_GET(option_mask32)) {
314
315
316
317 block_nr = 0;
318 }
319
320 } else {
321
322 local_fd = CMD_GET(option_mask32) ? STDOUT_FILENO : STDIN_FILENO;
323 if (NOT_LONE_DASH(local_file))
324 local_fd = xopen(local_file, open_mode);
325
326
327#if ENABLE_TFTP
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342 opcode = TFTP_WRQ;
343 if (CMD_GET(option_mask32)) {
344 opcode = TFTP_RRQ;
345 }
346
347
348 len = strlen(remote_file) + 1;
349 if (2 + len + sizeof("octet") >= io_bufsize) {
350 bb_error_msg("remote filename is too long");
351 goto ret;
352 }
353 strcpy(cp, remote_file);
354 cp += len;
355
356 strcpy(cp, "octet");
357 cp += sizeof("octet");
358
359# if ENABLE_FEATURE_TFTP_BLOCKSIZE
360 if (blksize == TFTP_BLKSIZE_DEFAULT && !want_transfer_size)
361 goto send_pkt;
362
363
364 if ((&xbuf[io_bufsize - 1] - cp) < sizeof("blksize NNNNN tsize ") + sizeof(off_t)*3) {
365 bb_error_msg("remote filename is too long");
366 goto ret;
367 }
368 expect_OACK = 1;
369# endif
370#endif
371
372#if ENABLE_FEATURE_TFTP_BLOCKSIZE
373 add_blksize_opt:
374 if (blksize != TFTP_BLKSIZE_DEFAULT) {
375
376 strcpy(cp, "blksize");
377 cp += sizeof("blksize");
378 cp += snprintf(cp, 6, "%d", blksize) + 1;
379 }
380 if (want_transfer_size) {
381
382
383
384
385
386
387 struct stat st;
388 strcpy(cp, "tsize");
389 cp += sizeof("tsize");
390 st.st_size = 0;
391 fstat(local_fd, &st);
392 cp += sprintf(cp, "%"OFF_FMT"u", (off_t)st.st_size) + 1;
393# if ENABLE_FEATURE_TFTP_PROGRESS_BAR
394
395
396 G.size = st.st_size;
397 if (remote_file && st.st_size)
398 tftp_progress_init();
399# endif
400 }
401#endif
402
403 goto send_pkt;
404 }
405
406
407
408 while (1) {
409
410 cp = xbuf + 2;
411 *((uint16_t*)cp) = htons(block_nr);
412 cp += 2;
413 block_nr++;
414 opcode = TFTP_ACK;
415 if (CMD_PUT(option_mask32)) {
416 opcode = TFTP_DATA;
417 len = full_read(local_fd, cp, blksize);
418 if (len < 0) {
419 goto send_read_err_pkt;
420 }
421 if (len != blksize) {
422 finished = 1;
423 }
424 cp += len;
425 }
426 send_pkt:
427
428 *((uint16_t*)xbuf) = htons(opcode);
429 send_len = cp - xbuf;
430
431
432
433 retries = TFTP_NUM_RETRIES;
434 waittime_ms = TFTP_TIMEOUT_MS;
435
436 send_again:
437#if ENABLE_TFTP_DEBUG
438 fprintf(stderr, "sending %u bytes\n", send_len);
439 for (cp = xbuf; cp < &xbuf[send_len]; cp++)
440 fprintf(stderr, "%02x ", (unsigned char) *cp);
441 fprintf(stderr, "\n");
442#endif
443 xsendto(socket_fd, xbuf, send_len, &peer_lsa->u.sa, peer_lsa->len);
444
445#if ENABLE_FEATURE_TFTP_PROGRESS_BAR
446 if (ENABLE_TFTP && remote_file)
447 G.pos = (block_nr - 1) * (uoff_t)blksize;
448 if (G.pmt.inited)
449 tftp_progress_update();
450#endif
451
452 if (finished && (opcode == TFTP_ACK))
453 goto ret;
454
455 recv_again:
456
457
458 pfd[0].events = POLLIN;
459 switch (safe_poll(pfd, 1, waittime_ms)) {
460 default:
461
462 goto ret;
463 case 0:
464 retries--;
465 if (retries == 0) {
466 tftp_progress_done();
467 bb_error_msg("timeout");
468 goto ret;
469 }
470
471
472 waittime_ms += waittime_ms/2;
473 if (waittime_ms > TFTP_MAXTIMEOUT_MS) {
474 waittime_ms = TFTP_MAXTIMEOUT_MS;
475 }
476
477 goto send_again;
478 case 1:
479 if (!our_lsa) {
480
481 our_lsa = ((void*)(ptrdiff_t)-1);
482 len = recvfrom(socket_fd, rbuf, io_bufsize, 0,
483 &peer_lsa->u.sa, &peer_lsa->len);
484
485
486
487 if (len >= 0)
488 xconnect(socket_fd, &peer_lsa->u.sa, peer_lsa->len);
489 } else {
490
491
492
493
494 len = safe_read(socket_fd, rbuf, io_bufsize);
495 }
496 if (len < 0) {
497 goto send_read_err_pkt;
498 }
499 if (len < 4) {
500 goto recv_again;
501 }
502 }
503
504
505 opcode = ntohs( ((uint16_t*)rbuf)[0] );
506 recv_blk = ntohs( ((uint16_t*)rbuf)[1] );
507#if ENABLE_TFTP_DEBUG
508 fprintf(stderr, "received %d bytes: %04x %04x\n", len, opcode, recv_blk);
509#endif
510 if (opcode == TFTP_ERROR) {
511 static const char errcode_str[] ALIGN1 =
512 "\0"
513 "file not found\0"
514 "access violation\0"
515 "disk full\0"
516 "bad operation\0"
517 "unknown transfer id\0"
518 "file already exists\0"
519 "no such user\0"
520 "bad option";
521
522 const char *msg = "";
523
524 if (len > 4 && rbuf[4] != '\0') {
525 msg = &rbuf[4];
526 rbuf[io_bufsize - 1] = '\0';
527 } else if (recv_blk <= 8) {
528 msg = nth_string(errcode_str, recv_blk);
529 }
530 bb_error_msg("server error: (%u) %s", recv_blk, msg);
531 goto ret;
532 }
533
534#if ENABLE_FEATURE_TFTP_BLOCKSIZE
535 if (expect_OACK) {
536 expect_OACK = 0;
537 if (opcode == TFTP_OACK) {
538
539 char *res;
540
541 res = tftp_get_option("blksize", &rbuf[2], len - 2);
542 if (res) {
543 blksize = tftp_blksize_check(res, blksize);
544 if (blksize < 0) {
545 error_pkt_reason = ERR_BAD_OPT;
546 goto send_err_pkt;
547 }
548 io_bufsize = blksize + 4;
549 }
550# if ENABLE_FEATURE_TFTP_PROGRESS_BAR
551 if (remote_file && G.size == 0) {
552 res = tftp_get_option("tsize", &rbuf[2], len - 2);
553 if (res) {
554 G.size = bb_strtoull(res, NULL, 10);
555 if (G.size)
556 tftp_progress_init();
557 }
558 }
559# endif
560 if (CMD_GET(option_mask32)) {
561
562
563 block_nr = 0;
564 }
565 continue;
566 }
567
568
569
570
571 if (blksize != TFTP_BLKSIZE_DEFAULT)
572 bb_error_msg("falling back to blocksize "TFTP_BLKSIZE_DEFAULT_STR);
573 blksize = TFTP_BLKSIZE_DEFAULT;
574 io_bufsize = TFTP_BLKSIZE_DEFAULT + 4;
575 }
576#endif
577
578
579
580 if (CMD_GET(option_mask32) && (opcode == TFTP_DATA)) {
581 if (recv_blk == block_nr) {
582 int sz = full_write(local_fd, &rbuf[4], len - 4);
583 if (sz != len - 4) {
584 strcpy((char*)error_pkt_str, bb_msg_write_error);
585 error_pkt_reason = ERR_WRITE;
586 goto send_err_pkt;
587 }
588 if (sz != blksize) {
589 finished = 1;
590 }
591 continue;
592 }
593
594#if 0
595 if (recv_blk == (block_nr - 1)) {
596
597 block_nr = recv_blk;
598 continue;
599 }
600#endif
601 }
602
603 if (CMD_PUT(option_mask32) && (opcode == TFTP_ACK)) {
604
605 if (recv_blk == (uint16_t) (block_nr - 1)) {
606 if (finished)
607 goto ret;
608 continue;
609 }
610 }
611
612 goto recv_again;
613
614
615
616
617
618
619
620
621
622 }
623 ret:
624 if (ENABLE_FEATURE_CLEAN_UP) {
625 close(local_fd);
626 close(socket_fd);
627 free(xbuf);
628 free(rbuf);
629 }
630 return finished == 0;
631
632 send_read_err_pkt:
633 strcpy((char*)error_pkt_str, bb_msg_read_error);
634 send_err_pkt:
635 if (error_pkt_str[0])
636 bb_error_msg("%s", (char*)error_pkt_str);
637 error_pkt[1] = TFTP_ERROR;
638 xsendto(socket_fd, error_pkt, 4 + 1 + strlen((char*)error_pkt_str),
639 &peer_lsa->u.sa, peer_lsa->len);
640 return EXIT_FAILURE;
641#undef remote_file
642}
643
644#if ENABLE_TFTP
645
646int tftp_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
647int tftp_main(int argc UNUSED_PARAM, char **argv)
648{
649 len_and_sockaddr *peer_lsa;
650 const char *local_file = NULL;
651 const char *remote_file = NULL;
652# if ENABLE_FEATURE_TFTP_BLOCKSIZE
653 const char *blksize_str = TFTP_BLKSIZE_DEFAULT_STR;
654 int blksize;
655# endif
656 int result;
657 int port;
658 IF_GETPUT(int opt;)
659
660 INIT_G();
661
662
663 opt_complementary = "" IF_FEATURE_TFTP_GET("g:") IF_FEATURE_TFTP_PUT("p:")
664 IF_GETPUT("g--p:p--g:");
665
666 IF_GETPUT(opt =) getopt32(argv,
667 IF_FEATURE_TFTP_GET("g") IF_FEATURE_TFTP_PUT("p")
668 "l:r:" IF_FEATURE_TFTP_BLOCKSIZE("b:"),
669 &local_file, &remote_file
670 IF_FEATURE_TFTP_BLOCKSIZE(, &blksize_str));
671 argv += optind;
672
673# if ENABLE_FEATURE_TFTP_BLOCKSIZE
674
675
676 blksize = tftp_blksize_check(blksize_str, 65564);
677 if (blksize < 0) {
678
679 return EXIT_FAILURE;
680 }
681# endif
682
683 if (remote_file) {
684 if (!local_file) {
685 const char *slash = strrchr(remote_file, '/');
686 local_file = slash ? slash + 1 : remote_file;
687 }
688 } else {
689 remote_file = local_file;
690 }
691
692
693 if (!remote_file || !argv[0])
694 bb_show_usage();
695
696 port = bb_lookup_port(argv[1], "udp", 69);
697 peer_lsa = xhost2sockaddr(argv[0], port);
698
699# if ENABLE_TFTP_DEBUG
700 fprintf(stderr, "using server '%s', remote_file '%s', local_file '%s'\n",
701 xmalloc_sockaddr2dotted(&peer_lsa->u.sa),
702 remote_file, local_file);
703# endif
704
705# if ENABLE_FEATURE_TFTP_PROGRESS_BAR
706 G.file = remote_file;
707# endif
708 result = tftp_protocol(
709 NULL , peer_lsa,
710 local_file, remote_file
711 IF_FEATURE_TFTP_BLOCKSIZE(, 1 )
712 IF_FEATURE_TFTP_BLOCKSIZE(, blksize)
713 );
714 tftp_progress_done();
715
716 if (result != EXIT_SUCCESS && NOT_LONE_DASH(local_file) && CMD_GET(opt)) {
717 unlink(local_file);
718 }
719 return result;
720}
721
722#endif
723
724#if ENABLE_TFTPD
725int tftpd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
726int tftpd_main(int argc UNUSED_PARAM, char **argv)
727{
728 len_and_sockaddr *our_lsa;
729 len_and_sockaddr *peer_lsa;
730 char *local_file, *mode;
731 const char *error_msg;
732 int opt, result, opcode;
733 IF_FEATURE_TFTP_BLOCKSIZE(int blksize = TFTP_BLKSIZE_DEFAULT;)
734 IF_FEATURE_TFTP_BLOCKSIZE(int want_transfer_size = 0;)
735
736 INIT_G();
737
738 our_lsa = get_sock_lsa(STDIN_FILENO);
739 if (!our_lsa) {
740
741
742
743 bb_show_usage();
744
745
746
747 }
748 peer_lsa = xzalloc(LSA_LEN_SIZE + our_lsa->len);
749 peer_lsa->len = our_lsa->len;
750
751
752 opt = option_mask32 = TFTPD_OPT | (getopt32(argv, "rcu:", &user_opt) << 8);
753 argv += optind;
754 if (argv[0])
755 xchdir(argv[0]);
756
757 result = recv_from_to(STDIN_FILENO, block_buf, sizeof(block_buf),
758 0 ,
759 &peer_lsa->u.sa, &our_lsa->u.sa, our_lsa->len);
760
761 error_msg = "malformed packet";
762 opcode = ntohs(*(uint16_t*)block_buf);
763 if (result < 4 || result >= sizeof(block_buf)
764 || block_buf[result-1] != '\0'
765 || (IF_FEATURE_TFTP_PUT(opcode != TFTP_RRQ)
766 IF_GETPUT(&&)
767 IF_FEATURE_TFTP_GET(opcode != TFTP_WRQ)
768 )
769 ) {
770 goto err;
771 }
772 local_file = block_buf + 2;
773 if (local_file[0] == '.' || strstr(local_file, "/.")) {
774 error_msg = "dot in file name";
775 goto err;
776 }
777 mode = local_file + strlen(local_file) + 1;
778 if (mode >= block_buf + result || strcmp(mode, "octet") != 0) {
779 goto err;
780 }
781# if ENABLE_FEATURE_TFTP_BLOCKSIZE
782 {
783 char *res;
784 char *opt_str = mode + sizeof("octet");
785 int opt_len = block_buf + result - opt_str;
786 if (opt_len > 0) {
787 res = tftp_get_option("blksize", opt_str, opt_len);
788 if (res) {
789 blksize = tftp_blksize_check(res, 65564);
790 if (blksize < 0) {
791 error_pkt_reason = ERR_BAD_OPT;
792
793 goto do_proto;
794 }
795 }
796 if (opcode != TFTP_WRQ
797
798 && tftp_get_option("tsize", opt_str, opt_len)
799 ) {
800 want_transfer_size = 1;
801 }
802 }
803 }
804# endif
805
806 if (!ENABLE_FEATURE_TFTP_PUT || opcode == TFTP_WRQ) {
807 if (opt & TFTPD_OPT_r) {
808
809
810 error_msg = bb_msg_write_error;
811 goto err;
812 }
813 IF_GETPUT(option_mask32 |= TFTP_OPT_GET;)
814 } else {
815 IF_GETPUT(option_mask32 |= TFTP_OPT_PUT;)
816 }
817
818
819
820
821 do_proto:
822 close(STDIN_FILENO);
823
824 result = tftp_protocol(
825 our_lsa, peer_lsa,
826 local_file IF_TFTP(, NULL )
827 IF_FEATURE_TFTP_BLOCKSIZE(, want_transfer_size)
828 IF_FEATURE_TFTP_BLOCKSIZE(, blksize)
829 );
830
831 return result;
832 err:
833 strcpy((char*)error_pkt_str, error_msg);
834 goto do_proto;
835}
836
837#endif
838
839#endif
840