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