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