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