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