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