1
2
3
4
5
6
7
8
9
10
11
12
13
14
15#include "libbb.h"
16#include <syslog.h>
17#include <netinet/tcp.h>
18
19#define FTP_DATACONN 150
20#define FTP_NOOPOK 200
21#define FTP_TYPEOK 200
22#define FTP_PORTOK 200
23#define FTP_STRUOK 200
24#define FTP_MODEOK 200
25#define FTP_ALLOOK 202
26#define FTP_STATOK 211
27#define FTP_STATFILE_OK 213
28#define FTP_HELP 214
29#define FTP_SYSTOK 215
30#define FTP_GREET 220
31#define FTP_GOODBYE 221
32#define FTP_TRANSFEROK 226
33#define FTP_PASVOK 227
34
35#define FTP_EPSVOK 229
36#define FTP_LOGINOK 230
37#define FTP_CWDOK 250
38#define FTP_RMDIROK 250
39#define FTP_DELEOK 250
40#define FTP_RENAMEOK 250
41#define FTP_PWDOK 257
42#define FTP_MKDIROK 257
43#define FTP_GIVEPWORD 331
44#define FTP_RESTOK 350
45#define FTP_RNFROK 350
46#define FTP_TIMEOUT 421
47#define FTP_BADSENDCONN 425
48#define FTP_BADSENDNET 426
49#define FTP_BADSENDFILE 451
50#define FTP_BADCMD 500
51#define FTP_COMMANDNOTIMPL 502
52#define FTP_NEEDUSER 503
53#define FTP_NEEDRNFR 503
54#define FTP_BADSTRU 504
55#define FTP_BADMODE 504
56#define FTP_LOGINERR 530
57#define FTP_FILEFAIL 550
58#define FTP_NOPERM 550
59#define FTP_UPLOADFAIL 553
60
61#define STR1(s) #s
62#define STR(s) STR1(s)
63
64
65enum {
66
67 SHIFT2 = 0 * BB_LITTLE_ENDIAN + 24 * BB_BIG_ENDIAN,
68 SHIFT1 = 8 * BB_LITTLE_ENDIAN + 16 * BB_BIG_ENDIAN,
69 SHIFT0 = 16 * BB_LITTLE_ENDIAN + 8 * BB_BIG_ENDIAN,
70
71 SHIFTsp = 24 * BB_LITTLE_ENDIAN + 0 * BB_BIG_ENDIAN,
72};
73#define STRNUM32(s) (uint32_t)(0 \
74 | (('0' + ((s) / 1 % 10)) << SHIFT0) \
75 | (('0' + ((s) / 10 % 10)) << SHIFT1) \
76 | (('0' + ((s) / 100 % 10)) << SHIFT2) \
77)
78#define STRNUM32sp(s) (uint32_t)(0 \
79 | (' ' << SHIFTsp) \
80 | (('0' + ((s) / 1 % 10)) << SHIFT0) \
81 | (('0' + ((s) / 10 % 10)) << SHIFT1) \
82 | (('0' + ((s) / 100 % 10)) << SHIFT2) \
83)
84
85#define MSG_OK "Operation successful\r\n"
86#define MSG_ERR "Error\r\n"
87
88struct globals {
89 int pasv_listen_fd;
90#if !BB_MMU
91 int root_fd;
92#endif
93 int local_file_fd;
94 unsigned end_time;
95 unsigned timeout;
96 unsigned verbose;
97 off_t local_file_pos;
98 off_t restart_pos;
99 len_and_sockaddr *local_addr;
100 len_and_sockaddr *port_addr;
101 char *ftp_cmd;
102 char *ftp_arg;
103#if ENABLE_FEATURE_FTP_WRITE
104 char *rnfr_filename;
105#endif
106
107 char msg_ok [(sizeof("NNN " MSG_OK ) + 3) & 0xfffc];
108 char msg_err[(sizeof("NNN " MSG_ERR) + 3) & 0xfffc];
109};
110#define G (*(struct globals*)&bb_common_bufsiz1)
111#define INIT_G() do { \
112 \
113 \
114 \
115} while (0)
116
117
118static char *
119escape_text(const char *prepend, const char *str, unsigned escapee)
120{
121 unsigned retlen, remainlen, chunklen;
122 char *ret, *found;
123 char append;
124
125 append = (char)escapee;
126 escapee >>= 8;
127
128 remainlen = strlen(str);
129 retlen = strlen(prepend);
130 ret = xmalloc(retlen + remainlen * 2 + 1 + 1);
131 strcpy(ret, prepend);
132
133 for (;;) {
134 found = strchrnul(str, escapee);
135 chunklen = found - str + 1;
136
137
138 memcpy(ret + retlen, str, chunklen);
139 retlen += chunklen;
140
141 if (*found == '\0') {
142
143 ret[retlen - 1] = append;
144 ret[retlen] = '\0';
145 break;
146 }
147 ret[retlen++] = escapee;
148 str = found + 1;
149 }
150 return ret;
151}
152
153
154static unsigned
155replace_char(char *str, char from, char to)
156{
157 char *p = str;
158 while (*p) {
159 if (*p == from)
160 *p = to;
161 p++;
162 }
163 return p - str;
164}
165
166static void
167verbose_log(const char *str)
168{
169 bb_error_msg("%.*s", (int)strcspn(str, "\r\n"), str);
170}
171
172
173static void
174cmdio_write(uint32_t status_str, const char *str)
175{
176 char *response;
177 int len;
178
179
180
181 response = escape_text((char *) &status_str, str, (0xff << 8) + '\r');
182
183
184 len = replace_char(response, '\n', '\0');
185
186 response[len++] = '\n';
187 xwrite(STDOUT_FILENO, response, len);
188 if (G.verbose > 1)
189 verbose_log(response);
190 free(response);
191}
192
193static void
194cmdio_write_ok(unsigned status)
195{
196 *(uint32_t *) G.msg_ok = status;
197 xwrite(STDOUT_FILENO, G.msg_ok, sizeof("NNN " MSG_OK) - 1);
198 if (G.verbose > 1)
199 verbose_log(G.msg_ok);
200}
201#define WRITE_OK(a) cmdio_write_ok(STRNUM32sp(a))
202
203
204static void
205cmdio_write_error(unsigned status)
206{
207 *(uint32_t *) G.msg_err = status;
208 xwrite(STDOUT_FILENO, G.msg_err, sizeof("NNN " MSG_ERR) - 1);
209 if (G.verbose > 1)
210 verbose_log(G.msg_err);
211}
212#define WRITE_ERR(a) cmdio_write_error(STRNUM32sp(a))
213
214static void
215cmdio_write_raw(const char *p_text)
216{
217 xwrite_str(STDOUT_FILENO, p_text);
218 if (G.verbose > 1)
219 verbose_log(p_text);
220}
221
222static void
223timeout_handler(int sig UNUSED_PARAM)
224{
225 off_t pos;
226 int sv_errno = errno;
227
228 if ((int)(monotonic_sec() - G.end_time) >= 0)
229 goto timed_out;
230
231 if (!G.local_file_fd)
232 goto timed_out;
233
234 pos = xlseek(G.local_file_fd, 0, SEEK_CUR);
235 if (pos == G.local_file_pos)
236 goto timed_out;
237 G.local_file_pos = pos;
238
239 alarm(G.timeout);
240 errno = sv_errno;
241 return;
242
243 timed_out:
244 cmdio_write_raw(STR(FTP_TIMEOUT)" Timeout\r\n");
245
246 exit(1);
247}
248
249
250
251static void
252handle_pwd(void)
253{
254 char *cwd, *response;
255
256 cwd = xrealloc_getcwd_or_warn(NULL);
257 if (cwd == NULL)
258 cwd = xstrdup("");
259
260
261 response = escape_text(" \"", cwd, ('"' << 8) + '"');
262 free(cwd);
263 cmdio_write(STRNUM32(FTP_PWDOK), response);
264 free(response);
265}
266
267static void
268handle_cwd(void)
269{
270 if (!G.ftp_arg || chdir(G.ftp_arg) != 0) {
271 WRITE_ERR(FTP_FILEFAIL);
272 return;
273 }
274 WRITE_OK(FTP_CWDOK);
275}
276
277static void
278handle_cdup(void)
279{
280 G.ftp_arg = (char*)"..";
281 handle_cwd();
282}
283
284static void
285handle_stat(void)
286{
287 cmdio_write_raw(STR(FTP_STATOK)"-Server status:\r\n"
288 " TYPE: BINARY\r\n"
289 STR(FTP_STATOK)" Ok\r\n");
290}
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315static void
316handle_feat(unsigned status)
317{
318 cmdio_write(status, "-Features:");
319 cmdio_write_raw(" EPSV\r\n"
320 " PASV\r\n"
321 " REST STREAM\r\n"
322 " MDTM\r\n"
323 " SIZE\r\n");
324 cmdio_write(status, " Ok");
325}
326
327
328
329static inline int
330port_active(void)
331{
332 return (G.port_addr != NULL);
333}
334
335static inline int
336pasv_active(void)
337{
338 return (G.pasv_listen_fd > STDOUT_FILENO);
339}
340
341static void
342port_pasv_cleanup(void)
343{
344 free(G.port_addr);
345 G.port_addr = NULL;
346 if (G.pasv_listen_fd > STDOUT_FILENO)
347 close(G.pasv_listen_fd);
348 G.pasv_listen_fd = -1;
349}
350
351
352static int
353ftpdataio_get_pasv_fd(void)
354{
355 int remote_fd;
356
357 remote_fd = accept(G.pasv_listen_fd, NULL, 0);
358
359 if (remote_fd < 0) {
360 WRITE_ERR(FTP_BADSENDCONN);
361 return remote_fd;
362 }
363
364 setsockopt(remote_fd, SOL_SOCKET, SO_KEEPALIVE, &const_int_1, sizeof(const_int_1));
365 return remote_fd;
366}
367
368
369
370
371
372
373
374static int
375get_remote_transfer_fd(const char *p_status_msg)
376{
377 int remote_fd;
378
379 if (pasv_active())
380
381 remote_fd = ftpdataio_get_pasv_fd();
382 else
383
384 remote_fd = xconnect_stream(G.port_addr);
385
386 port_pasv_cleanup();
387
388 if (remote_fd < 0)
389 return remote_fd;
390
391 cmdio_write(STRNUM32(FTP_DATACONN), p_status_msg);
392 return remote_fd;
393}
394
395
396static int
397port_or_pasv_was_seen(void)
398{
399 if (!pasv_active() && !port_active()) {
400 cmdio_write_raw(STR(FTP_BADSENDCONN)" Use PORT/PASV first\r\n");
401 return 0;
402 }
403
404 return 1;
405}
406
407
408static unsigned
409bind_for_passive_mode(void)
410{
411 int fd;
412 unsigned port;
413
414 port_pasv_cleanup();
415
416 G.pasv_listen_fd = fd = xsocket(G.local_addr->u.sa.sa_family, SOCK_STREAM, 0);
417 setsockopt_reuseaddr(fd);
418
419 set_nport(G.local_addr, 0);
420 xbind(fd, &G.local_addr->u.sa, G.local_addr->len);
421 xlisten(fd, 1);
422 getsockname(fd, &G.local_addr->u.sa, &G.local_addr->len);
423
424 port = get_nport(&G.local_addr->u.sa);
425 port = ntohs(port);
426 return port;
427}
428
429
430static void
431handle_pasv(void)
432{
433 unsigned port;
434 char *addr, *response;
435
436 port = bind_for_passive_mode();
437
438 if (G.local_addr->u.sa.sa_family == AF_INET)
439 addr = xmalloc_sockaddr2dotted_noport(&G.local_addr->u.sa);
440 else
441 addr = xstrdup("0.0.0.0");
442 replace_char(addr, '.', ',');
443
444 response = xasprintf(STR(FTP_PASVOK)" PASV ok (%s,%u,%u)\r\n",
445 addr, (int)(port >> 8), (int)(port & 255));
446 free(addr);
447 cmdio_write_raw(response);
448 free(response);
449}
450
451
452static void
453handle_epsv(void)
454{
455 unsigned port;
456 char *response;
457
458 port = bind_for_passive_mode();
459 response = xasprintf(STR(FTP_EPSVOK)" EPSV ok (|||%u|)\r\n", port);
460 cmdio_write_raw(response);
461 free(response);
462}
463
464
465static
466len_and_sockaddr* get_peer_lsa(int fd)
467{
468 len_and_sockaddr *lsa;
469 socklen_t len = 0;
470
471 if (getpeername(fd, NULL, &len) != 0)
472 return NULL;
473 lsa = xzalloc(LSA_LEN_SIZE + len);
474 lsa->len = len;
475 getpeername(fd, &lsa->u.sa, &lsa->len);
476 return lsa;
477}
478
479static void
480handle_port(void)
481{
482 unsigned port, port_hi;
483 char *raw, *comma;
484#ifdef WHY_BOTHER_WE_CAN_ASSUME_IP_MATCHES
485 socklen_t peer_ipv4_len;
486 struct sockaddr_in peer_ipv4;
487 struct in_addr port_ipv4_sin_addr;
488#endif
489
490 port_pasv_cleanup();
491
492 raw = G.ftp_arg;
493
494
495 if (!raw
496#ifdef WHY_BOTHER_WE_CAN_ASSUME_IP_MATCHES
497 || G.local_addr->u.sa.sa_family != AF_INET
498#endif
499 ) {
500 bail:
501 WRITE_ERR(FTP_BADCMD);
502 return;
503 }
504
505 comma = strrchr(raw, ',');
506 if (comma == NULL)
507 goto bail;
508 *comma = '\0';
509 port = bb_strtou(&comma[1], NULL, 10);
510 if (errno || port > 0xff)
511 goto bail;
512
513 comma = strrchr(raw, ',');
514 if (comma == NULL)
515 goto bail;
516 *comma = '\0';
517 port_hi = bb_strtou(&comma[1], NULL, 10);
518 if (errno || port_hi > 0xff)
519 goto bail;
520 port |= port_hi << 8;
521
522#ifdef WHY_BOTHER_WE_CAN_ASSUME_IP_MATCHES
523 replace_char(raw, ',', '.');
524
525
526
527
528
529
530
531
532 if (!inet_aton(raw, &port_ipv4_sin_addr))
533 goto bail;
534 peer_ipv4_len = sizeof(peer_ipv4);
535 if (getpeername(STDIN_FILENO, &peer_ipv4, &peer_ipv4_len) != 0)
536 goto bail;
537 if (memcmp(&port_ipv4_sin_addr, &peer_ipv4.sin_addr, sizeof(struct in_addr)) != 0)
538 goto bail;
539
540 G.port_addr = xdotted2sockaddr(raw, port);
541#else
542 G.port_addr = get_peer_lsa(STDIN_FILENO);
543 set_nport(G.port_addr, htons(port));
544#endif
545 WRITE_OK(FTP_PORTOK);
546}
547
548static void
549handle_rest(void)
550{
551
552 G.restart_pos = G.ftp_arg ? xatoi_u(G.ftp_arg) : 0;
553 WRITE_OK(FTP_RESTOK);
554}
555
556static void
557handle_retr(void)
558{
559 struct stat statbuf;
560 off_t bytes_transferred;
561 int remote_fd;
562 int local_file_fd;
563 off_t offset = G.restart_pos;
564 char *response;
565
566 G.restart_pos = 0;
567
568 if (!port_or_pasv_was_seen())
569 return;
570
571
572 local_file_fd = G.ftp_arg ? open(G.ftp_arg, O_RDONLY | O_NONBLOCK) : -1;
573 if (local_file_fd < 0) {
574 WRITE_ERR(FTP_FILEFAIL);
575 return;
576 }
577
578 if (fstat(local_file_fd, &statbuf) != 0 || !S_ISREG(statbuf.st_mode)) {
579
580 WRITE_ERR(FTP_FILEFAIL);
581 goto file_close_out;
582 }
583 G.local_file_fd = local_file_fd;
584
585
586
587
588 ndelay_off(local_file_fd);
589
590
591 if (offset != 0)
592 xlseek(local_file_fd, offset, SEEK_SET);
593
594 response = xasprintf(
595 " Opening BINARY connection for %s (%"OFF_FMT"u bytes)",
596 G.ftp_arg, statbuf.st_size);
597 remote_fd = get_remote_transfer_fd(response);
598 free(response);
599 if (remote_fd < 0)
600 goto file_close_out;
601
602 bytes_transferred = bb_copyfd_eof(local_file_fd, remote_fd);
603 close(remote_fd);
604 if (bytes_transferred < 0)
605 WRITE_ERR(FTP_BADSENDFILE);
606 else
607 WRITE_OK(FTP_TRANSFEROK);
608
609 file_close_out:
610 close(local_file_fd);
611 G.local_file_fd = 0;
612}
613
614
615
616static int
617popen_ls(const char *opt)
618{
619 const char *argv[5];
620 struct fd_pair outfd;
621 pid_t pid;
622
623 argv[0] = "ftpd";
624 argv[1] = opt;
625#if BB_MMU
626 argv[2] = "--";
627#else
628
629
630 argv[2] = xrealloc_getcwd_or_warn(NULL);
631#endif
632 argv[3] = G.ftp_arg;
633 argv[4] = NULL;
634
635
636
637
638 if (ENABLE_FEATURE_FTPD_ACCEPT_BROKEN_LIST
639 && G.ftp_arg && G.ftp_arg[0] == '-' && G.ftp_arg[1] == 'l'
640 ) {
641 const char *tmp = strchr(G.ftp_arg, ' ');
642 if (tmp)
643 tmp++;
644 argv[3] = tmp;
645 }
646
647 xpiped_pair(outfd);
648
649
650 pid = BB_MMU ? fork() : vfork();
651 if (pid < 0)
652 bb_perror_msg_and_die(BB_MMU ? "fork" : "vfork");
653
654 if (pid == 0) {
655
656#if !BB_MMU
657
658
659
660
661
662 if (fchdir(G.root_fd) != 0)
663 _exit(127);
664
665#endif
666
667 close(outfd.rd);
668 xmove_fd(outfd.wr, STDOUT_FILENO);
669
670
671
672
673 close(STDIN_FILENO);
674 dup(STDOUT_FILENO);
675#if BB_MMU
676
677 exit(ls_main(ARRAY_SIZE(argv) - 1, (char**) argv));
678#else
679
680
681
682 execv(bb_busybox_exec_path + 1, (char**) argv);
683 _exit(127);
684#endif
685 }
686
687
688 close(outfd.wr);
689#if !BB_MMU
690 free((char*)argv[2]);
691#endif
692 return outfd.rd;
693}
694
695enum {
696 USE_CTRL_CONN = 1,
697 LONG_LISTING = 2,
698};
699
700static void
701handle_dir_common(int opts)
702{
703 FILE *ls_fp;
704 char *line;
705 int ls_fd;
706
707 if (!(opts & USE_CTRL_CONN) && !port_or_pasv_was_seen())
708 return;
709
710
711
712 ls_fd = popen_ls((opts & LONG_LISTING) ? "-l" : "-1");
713 ls_fp = xfdopen_for_read(ls_fd);
714
715 if (opts & USE_CTRL_CONN) {
716
717 cmdio_write_raw(STR(FTP_STATFILE_OK)"-File status:\r\n");
718 while (1) {
719 line = xmalloc_fgetline(ls_fp);
720 if (!line)
721 break;
722
723
724
725 cmdio_write(0, line);
726 free(line);
727 }
728 WRITE_OK(FTP_STATFILE_OK);
729 } else {
730
731 int remote_fd = get_remote_transfer_fd(" Directory listing");
732 if (remote_fd >= 0) {
733 while (1) {
734 line = xmalloc_fgetline(ls_fp);
735 if (!line)
736 break;
737
738
739
740
741
742 xwrite_str(remote_fd, line);
743 xwrite(remote_fd, "\r\n", 2);
744 free(line);
745 }
746 }
747 close(remote_fd);
748 WRITE_OK(FTP_TRANSFEROK);
749 }
750 fclose(ls_fp);
751}
752static void
753handle_list(void)
754{
755 handle_dir_common(LONG_LISTING);
756}
757static void
758handle_nlst(void)
759{
760
761
762
763
764
765
766
767 handle_dir_common(0);
768}
769static void
770handle_stat_file(void)
771{
772 handle_dir_common(LONG_LISTING + USE_CTRL_CONN);
773}
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804static void
805handle_size_or_mdtm(int need_size)
806{
807 struct stat statbuf;
808 struct tm broken_out;
809 char buf[(sizeof("NNN %"OFF_FMT"u\r\n") + sizeof(off_t) * 3)
810 | sizeof("NNN YYYYMMDDhhmmss\r\n")
811 ];
812
813 if (!G.ftp_arg
814 || stat(G.ftp_arg, &statbuf) != 0
815 || !S_ISREG(statbuf.st_mode)
816 ) {
817 WRITE_ERR(FTP_FILEFAIL);
818 return;
819 }
820 if (need_size) {
821 sprintf(buf, STR(FTP_STATFILE_OK)" %"OFF_FMT"u\r\n", statbuf.st_size);
822 } else {
823 gmtime_r(&statbuf.st_mtime, &broken_out);
824 sprintf(buf, STR(FTP_STATFILE_OK)" %04u%02u%02u%02u%02u%02u\r\n",
825 broken_out.tm_year + 1900,
826 broken_out.tm_mon,
827 broken_out.tm_mday,
828 broken_out.tm_hour,
829 broken_out.tm_min,
830 broken_out.tm_sec);
831 }
832 cmdio_write_raw(buf);
833}
834
835
836
837#if ENABLE_FEATURE_FTP_WRITE
838static void
839handle_mkd(void)
840{
841 if (!G.ftp_arg || mkdir(G.ftp_arg, 0777) != 0) {
842 WRITE_ERR(FTP_FILEFAIL);
843 return;
844 }
845 WRITE_OK(FTP_MKDIROK);
846}
847
848static void
849handle_rmd(void)
850{
851 if (!G.ftp_arg || rmdir(G.ftp_arg) != 0) {
852 WRITE_ERR(FTP_FILEFAIL);
853 return;
854 }
855 WRITE_OK(FTP_RMDIROK);
856}
857
858static void
859handle_dele(void)
860{
861 if (!G.ftp_arg || unlink(G.ftp_arg) != 0) {
862 WRITE_ERR(FTP_FILEFAIL);
863 return;
864 }
865 WRITE_OK(FTP_DELEOK);
866}
867
868static void
869handle_rnfr(void)
870{
871 free(G.rnfr_filename);
872 G.rnfr_filename = xstrdup(G.ftp_arg);
873 WRITE_OK(FTP_RNFROK);
874}
875
876static void
877handle_rnto(void)
878{
879 int retval;
880
881
882 if (G.rnfr_filename == NULL || G.ftp_arg == NULL) {
883 cmdio_write_raw(STR(FTP_NEEDRNFR)" Use RNFR first\r\n");
884 return;
885 }
886
887 retval = rename(G.rnfr_filename, G.ftp_arg);
888 free(G.rnfr_filename);
889 G.rnfr_filename = NULL;
890
891 if (retval) {
892 WRITE_ERR(FTP_FILEFAIL);
893 return;
894 }
895 WRITE_OK(FTP_RENAMEOK);
896}
897
898static void
899handle_upload_common(int is_append, int is_unique)
900{
901 struct stat statbuf;
902 char *tempname;
903 off_t bytes_transferred;
904 off_t offset;
905 int local_file_fd;
906 int remote_fd;
907
908 offset = G.restart_pos;
909 G.restart_pos = 0;
910
911 if (!port_or_pasv_was_seen())
912 return;
913
914 tempname = NULL;
915 local_file_fd = -1;
916 if (is_unique) {
917 tempname = xstrdup(" FILE: uniq.XXXXXX");
918 local_file_fd = mkstemp(tempname + 7);
919 } else if (G.ftp_arg) {
920 int flags = O_WRONLY | O_CREAT | O_TRUNC;
921 if (is_append)
922 flags = O_WRONLY | O_CREAT | O_APPEND;
923 if (offset)
924 flags = O_WRONLY | O_CREAT;
925 local_file_fd = open(G.ftp_arg, flags, 0666);
926 }
927
928 if (local_file_fd < 0
929 || fstat(local_file_fd, &statbuf) != 0
930 || !S_ISREG(statbuf.st_mode)
931 ) {
932 WRITE_ERR(FTP_UPLOADFAIL);
933 if (local_file_fd >= 0)
934 goto close_local_and_bail;
935 return;
936 }
937 G.local_file_fd = local_file_fd;
938
939 if (offset)
940 xlseek(local_file_fd, offset, SEEK_SET);
941
942 remote_fd = get_remote_transfer_fd(tempname ? tempname : " Ok to send data");
943 free(tempname);
944
945 if (remote_fd < 0)
946 goto close_local_and_bail;
947
948 bytes_transferred = bb_copyfd_eof(remote_fd, local_file_fd);
949 close(remote_fd);
950 if (bytes_transferred < 0)
951 WRITE_ERR(FTP_BADSENDFILE);
952 else
953 WRITE_OK(FTP_TRANSFEROK);
954
955 close_local_and_bail:
956 close(local_file_fd);
957 G.local_file_fd = 0;
958}
959
960static void
961handle_stor(void)
962{
963 handle_upload_common(0, 0);
964}
965
966static void
967handle_appe(void)
968{
969 G.restart_pos = 0;
970 handle_upload_common(1, 0);
971}
972
973static void
974handle_stou(void)
975{
976 G.restart_pos = 0;
977 handle_upload_common(0, 1);
978}
979#endif
980
981static uint32_t
982cmdio_get_cmd_and_arg(void)
983{
984 size_t len;
985 uint32_t cmdval;
986 char *cmd;
987
988 alarm(G.timeout);
989
990 free(G.ftp_cmd);
991 len = 8 * 1024;
992 G.ftp_cmd = cmd = xmalloc_fgets_str_len(stdin, "\r\n", &len);
993 if (!cmd)
994 exit(0);
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014 {
1015 int dst, src;
1016
1017
1018 if (len != 0 && cmd[len - 1] == '\n') {
1019 len--;
1020 if (len != 0 && cmd[len - 1] == '\r')
1021 len--;
1022 cmd[len] = '\0';
1023 }
1024 src = strchrnul(cmd, 0xff) - cmd;
1025
1026 if (src < len) {
1027 dst = src;
1028 do {
1029 if ((unsigned char)(cmd[src]) == 255) {
1030 src++;
1031
1032 if ((unsigned char)(cmd[src]) != 255) {
1033
1034 src++;
1035 continue;
1036 }
1037
1038 }
1039
1040 cmd[dst++] = cmd[src] ? cmd[src] : '\n';
1041 src++;
1042 } while (src < len);
1043 cmd[dst] = '\0';
1044 }
1045 }
1046
1047 if (G.verbose > 1)
1048 verbose_log(cmd);
1049
1050 G.ftp_arg = strchr(cmd, ' ');
1051 if (G.ftp_arg != NULL)
1052 *G.ftp_arg++ = '\0';
1053
1054
1055 cmdval = 0;
1056 while (*cmd)
1057 cmdval = (cmdval << 8) + ((unsigned char)*cmd++ & (unsigned char)~0x20);
1058
1059 return cmdval;
1060}
1061
1062#define mk_const4(a,b,c,d) (((a * 0x100 + b) * 0x100 + c) * 0x100 + d)
1063#define mk_const3(a,b,c) ((a * 0x100 + b) * 0x100 + c)
1064enum {
1065 const_ALLO = mk_const4('A', 'L', 'L', 'O'),
1066 const_APPE = mk_const4('A', 'P', 'P', 'E'),
1067 const_CDUP = mk_const4('C', 'D', 'U', 'P'),
1068 const_CWD = mk_const3('C', 'W', 'D'),
1069 const_DELE = mk_const4('D', 'E', 'L', 'E'),
1070 const_EPSV = mk_const4('E', 'P', 'S', 'V'),
1071 const_FEAT = mk_const4('F', 'E', 'A', 'T'),
1072 const_HELP = mk_const4('H', 'E', 'L', 'P'),
1073 const_LIST = mk_const4('L', 'I', 'S', 'T'),
1074 const_MDTM = mk_const4('M', 'D', 'T', 'M'),
1075 const_MKD = mk_const3('M', 'K', 'D'),
1076 const_MODE = mk_const4('M', 'O', 'D', 'E'),
1077 const_NLST = mk_const4('N', 'L', 'S', 'T'),
1078 const_NOOP = mk_const4('N', 'O', 'O', 'P'),
1079 const_PASS = mk_const4('P', 'A', 'S', 'S'),
1080 const_PASV = mk_const4('P', 'A', 'S', 'V'),
1081 const_PORT = mk_const4('P', 'O', 'R', 'T'),
1082 const_PWD = mk_const3('P', 'W', 'D'),
1083 const_QUIT = mk_const4('Q', 'U', 'I', 'T'),
1084 const_REST = mk_const4('R', 'E', 'S', 'T'),
1085 const_RETR = mk_const4('R', 'E', 'T', 'R'),
1086 const_RMD = mk_const3('R', 'M', 'D'),
1087 const_RNFR = mk_const4('R', 'N', 'F', 'R'),
1088 const_RNTO = mk_const4('R', 'N', 'T', 'O'),
1089 const_SIZE = mk_const4('S', 'I', 'Z', 'E'),
1090 const_STAT = mk_const4('S', 'T', 'A', 'T'),
1091 const_STOR = mk_const4('S', 'T', 'O', 'R'),
1092 const_STOU = mk_const4('S', 'T', 'O', 'U'),
1093 const_STRU = mk_const4('S', 'T', 'R', 'U'),
1094 const_SYST = mk_const4('S', 'Y', 'S', 'T'),
1095 const_TYPE = mk_const4('T', 'Y', 'P', 'E'),
1096 const_USER = mk_const4('U', 'S', 'E', 'R'),
1097
1098#if !BB_MMU
1099 OPT_l = (1 << 0),
1100 OPT_1 = (1 << 1),
1101#endif
1102 OPT_v = (1 << ((!BB_MMU) * 2 + 0)),
1103 OPT_S = (1 << ((!BB_MMU) * 2 + 1)),
1104 OPT_w = (1 << ((!BB_MMU) * 2 + 2)) * ENABLE_FEATURE_FTP_WRITE,
1105};
1106
1107int ftpd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
1108#if !BB_MMU
1109int ftpd_main(int argc, char **argv)
1110#else
1111int ftpd_main(int argc UNUSED_PARAM, char **argv)
1112#endif
1113{
1114 unsigned abs_timeout;
1115 unsigned verbose_S;
1116 smallint opts;
1117
1118 INIT_G();
1119
1120 abs_timeout = 1 * 60 * 60;
1121 verbose_S = 0;
1122 G.timeout = 2 * 60;
1123 opt_complementary = "t+:T+:vv:SS";
1124#if BB_MMU
1125 opts = getopt32(argv, "vS" IF_FEATURE_FTP_WRITE("w") "t:T:", &G.timeout, &abs_timeout, &G.verbose, &verbose_S);
1126#else
1127 opts = getopt32(argv, "l1vS" IF_FEATURE_FTP_WRITE("w") "t:T:", &G.timeout, &abs_timeout, &G.verbose, &verbose_S);
1128 if (opts & (OPT_l|OPT_1)) {
1129
1130
1131
1132
1133 xchdir(argv[2]);
1134 argv[2] = (char*)"--";
1135
1136 return ls_main(argc, argv);
1137 }
1138#endif
1139 if (G.verbose < verbose_S)
1140 G.verbose = verbose_S;
1141 if (abs_timeout | G.timeout) {
1142 if (abs_timeout == 0)
1143 abs_timeout = INT_MAX;
1144 G.end_time = monotonic_sec() + abs_timeout;
1145 if (G.timeout > abs_timeout)
1146 G.timeout = abs_timeout;
1147 }
1148 strcpy(G.msg_ok + 4, MSG_OK );
1149 strcpy(G.msg_err + 4, MSG_ERR);
1150
1151 G.local_addr = get_sock_lsa(STDIN_FILENO);
1152 if (!G.local_addr) {
1153
1154
1155
1156 bb_show_usage();
1157
1158
1159
1160 }
1161
1162 if (!(opts & OPT_v))
1163 logmode = LOGMODE_NONE;
1164 if (opts & OPT_S) {
1165
1166 openlog(applet_name, LOG_PID | LOG_NDELAY, LOG_DAEMON);
1167 logmode |= LOGMODE_SYSLOG;
1168 }
1169 if (logmode)
1170 applet_name = xasprintf("%s[%u]", applet_name, (int)getpid());
1171
1172#if !BB_MMU
1173 G.root_fd = xopen("/", O_RDONLY | O_DIRECTORY);
1174 close_on_exec_on(G.root_fd);
1175#endif
1176
1177 if (argv[optind]) {
1178 xchdir(argv[optind]);
1179 chroot(".");
1180 }
1181
1182
1183
1184
1185 signal(SIGPIPE, SIG_IGN);
1186
1187
1188 setsockopt(STDIN_FILENO, IPPROTO_TCP, TCP_NODELAY, &const_int_1, sizeof(const_int_1));
1189 setsockopt(STDIN_FILENO, SOL_SOCKET, SO_KEEPALIVE, &const_int_1, sizeof(const_int_1));
1190
1191
1192 setsockopt(STDIN_FILENO, SOL_SOCKET, SO_OOBINLINE, &const_int_1, sizeof(const_int_1));
1193
1194 WRITE_OK(FTP_GREET);
1195 signal(SIGALRM, timeout_handler);
1196
1197#ifdef IF_WE_WANT_TO_REQUIRE_LOGIN
1198 {
1199 smallint user_was_specified = 0;
1200 while (1) {
1201 uint32_t cmdval = cmdio_get_cmd_and_arg();
1202
1203 if (cmdval == const_USER) {
1204 if (G.ftp_arg == NULL || strcasecmp(G.ftp_arg, "anonymous") != 0)
1205 cmdio_write_raw(STR(FTP_LOGINERR)" Server is anonymous only\r\n");
1206 else {
1207 user_was_specified = 1;
1208 cmdio_write_raw(STR(FTP_GIVEPWORD)" Please specify the password\r\n");
1209 }
1210 } else if (cmdval == const_PASS) {
1211 if (user_was_specified)
1212 break;
1213 cmdio_write_raw(STR(FTP_NEEDUSER)" Login with USER\r\n");
1214 } else if (cmdval == const_QUIT) {
1215 WRITE_OK(FTP_GOODBYE);
1216 return 0;
1217 } else {
1218 cmdio_write_raw(STR(FTP_LOGINERR)" Login with USER and PASS\r\n");
1219 }
1220 }
1221 }
1222 WRITE_OK(FTP_LOGINOK);
1223#endif
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263 while (1) {
1264 uint32_t cmdval = cmdio_get_cmd_and_arg();
1265
1266 if (cmdval == const_QUIT) {
1267 WRITE_OK(FTP_GOODBYE);
1268 return 0;
1269 }
1270 else if (cmdval == const_USER)
1271
1272
1273
1274
1275
1276 WRITE_OK(FTP_LOGINOK);
1277 else if (cmdval == const_PASS)
1278 WRITE_OK(FTP_LOGINOK);
1279 else if (cmdval == const_NOOP)
1280 WRITE_OK(FTP_NOOPOK);
1281 else if (cmdval == const_TYPE)
1282 WRITE_OK(FTP_TYPEOK);
1283 else if (cmdval == const_STRU)
1284 WRITE_OK(FTP_STRUOK);
1285 else if (cmdval == const_MODE)
1286 WRITE_OK(FTP_MODEOK);
1287 else if (cmdval == const_ALLO)
1288 WRITE_OK(FTP_ALLOOK);
1289 else if (cmdval == const_SYST)
1290 cmdio_write_raw(STR(FTP_SYSTOK)" UNIX Type: L8\r\n");
1291 else if (cmdval == const_PWD)
1292 handle_pwd();
1293 else if (cmdval == const_CWD)
1294 handle_cwd();
1295 else if (cmdval == const_CDUP)
1296 handle_cdup();
1297
1298
1299 else if (cmdval == const_HELP || cmdval == const_FEAT)
1300 handle_feat(cmdval == const_HELP
1301 ? STRNUM32(FTP_HELP)
1302 : STRNUM32(FTP_STATOK)
1303 );
1304 else if (cmdval == const_LIST)
1305 handle_list();
1306 else if (cmdval == const_NLST)
1307 handle_nlst();
1308
1309
1310 else if (cmdval == const_SIZE || cmdval == const_MDTM)
1311 handle_size_or_mdtm(cmdval == const_SIZE);
1312 else if (cmdval == const_STAT) {
1313 if (G.ftp_arg == NULL)
1314 handle_stat();
1315 else
1316 handle_stat_file();
1317 }
1318 else if (cmdval == const_PASV)
1319 handle_pasv();
1320 else if (cmdval == const_EPSV)
1321 handle_epsv();
1322 else if (cmdval == const_RETR)
1323 handle_retr();
1324 else if (cmdval == const_PORT)
1325 handle_port();
1326 else if (cmdval == const_REST)
1327 handle_rest();
1328#if ENABLE_FEATURE_FTP_WRITE
1329 else if (opts & OPT_w) {
1330 if (cmdval == const_STOR)
1331 handle_stor();
1332 else if (cmdval == const_MKD)
1333 handle_mkd();
1334 else if (cmdval == const_RMD)
1335 handle_rmd();
1336 else if (cmdval == const_DELE)
1337 handle_dele();
1338 else if (cmdval == const_RNFR)
1339 handle_rnfr();
1340 else if (cmdval == const_RNTO)
1341 handle_rnto();
1342 else if (cmdval == const_APPE)
1343 handle_appe();
1344 else if (cmdval == const_STOU)
1345 handle_stou();
1346 else
1347 goto bad_cmd;
1348 }
1349#endif
1350#if 0
1351 else if (cmdval == const_STOR
1352 || cmdval == const_MKD
1353 || cmdval == const_RMD
1354 || cmdval == const_DELE
1355 || cmdval == const_RNFR
1356 || cmdval == const_RNTO
1357 || cmdval == const_APPE
1358 || cmdval == const_STOU
1359 ) {
1360 cmdio_write_raw(STR(FTP_NOPERM)" Permission denied\r\n");
1361 }
1362#endif
1363 else {
1364
1365
1366
1367
1368#if ENABLE_FEATURE_FTP_WRITE
1369 bad_cmd:
1370#endif
1371 cmdio_write_raw(STR(FTP_BADCMD)" Unknown command\r\n");
1372 }
1373 }
1374}
1375