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