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