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 char *cwd;
620 const char *argv[] = {
621 "ftpd",
622 opt,
623 BB_MMU ? "--" : NULL,
624 G.ftp_arg,
625 NULL
626 };
627 struct fd_pair outfd;
628 pid_t pid;
629
630 cwd = xrealloc_getcwd_or_warn(NULL);
631 xpiped_pair(outfd);
632
633
634 pid = BB_MMU ? fork() : vfork();
635 if (pid < 0)
636 bb_perror_msg_and_die(BB_MMU ? "fork" : "vfork");
637
638 if (pid == 0) {
639
640#if !BB_MMU
641 if (fchdir(G.root_fd) != 0)
642 _exit(127);
643 close(G.root_fd);
644#endif
645
646 close(outfd.rd);
647 xmove_fd(outfd.wr, STDOUT_FILENO);
648
649
650
651
652 close(STDIN_FILENO);
653 dup(STDOUT_FILENO);
654#if !BB_MMU
655
656
657
658 argv[2] = cwd;
659
660
661
662 execv(bb_busybox_exec_path + 1, (char**) argv);
663 _exit(127);
664#else
665 memset(&G, 0, sizeof(G));
666 exit(ls_main(ARRAY_SIZE(argv) - 1, (char**) argv));
667#endif
668 }
669
670
671 close(outfd.wr);
672 free(cwd);
673 return outfd.rd;
674}
675
676enum {
677 USE_CTRL_CONN = 1,
678 LONG_LISTING = 2,
679};
680
681static void
682handle_dir_common(int opts)
683{
684 FILE *ls_fp;
685 char *line;
686 int ls_fd;
687
688 if (!(opts & USE_CTRL_CONN) && !port_or_pasv_was_seen())
689 return;
690
691
692
693 ls_fd = popen_ls((opts & LONG_LISTING) ? "-l" : "-1");
694 ls_fp = fdopen(ls_fd, "r");
695 if (!ls_fp)
696 bb_perror_msg_and_die("fdopen");
697
698 if (opts & USE_CTRL_CONN) {
699
700 cmdio_write_raw(STR(FTP_STATFILE_OK)"-File status:\r\n");
701 while (1) {
702 line = xmalloc_fgetline(ls_fp);
703 if (!line)
704 break;
705
706
707
708 cmdio_write(0, line);
709 free(line);
710 }
711 WRITE_OK(FTP_STATFILE_OK);
712 } else {
713
714 int remote_fd = get_remote_transfer_fd(" Directory listing");
715 if (remote_fd >= 0) {
716 while (1) {
717 line = xmalloc_fgetline(ls_fp);
718 if (!line)
719 break;
720
721
722
723
724
725 xwrite_str(remote_fd, line);
726 xwrite(remote_fd, "\r\n", 2);
727 free(line);
728 }
729 }
730 close(remote_fd);
731 WRITE_OK(FTP_TRANSFEROK);
732 }
733 fclose(ls_fp);
734}
735static void
736handle_list(void)
737{
738 handle_dir_common(LONG_LISTING);
739}
740static void
741handle_nlst(void)
742{
743
744
745
746
747
748
749
750 handle_dir_common(0);
751}
752static void
753handle_stat_file(void)
754{
755 handle_dir_common(LONG_LISTING + USE_CTRL_CONN);
756}
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787static void
788handle_size_or_mdtm(int need_size)
789{
790 struct stat statbuf;
791 struct tm broken_out;
792 char buf[(sizeof("NNN %"OFF_FMT"u\r\n") + sizeof(off_t) * 3)
793 | sizeof("NNN YYYYMMDDhhmmss\r\n")
794 ];
795
796 if (!G.ftp_arg
797 || stat(G.ftp_arg, &statbuf) != 0
798 || !S_ISREG(statbuf.st_mode)
799 ) {
800 WRITE_ERR(FTP_FILEFAIL);
801 return;
802 }
803 if (need_size) {
804 sprintf(buf, STR(FTP_STATFILE_OK)" %"OFF_FMT"u\r\n", statbuf.st_size);
805 } else {
806 gmtime_r(&statbuf.st_mtime, &broken_out);
807 sprintf(buf, STR(FTP_STATFILE_OK)" %04u%02u%02u%02u%02u%02u\r\n",
808 broken_out.tm_year + 1900,
809 broken_out.tm_mon,
810 broken_out.tm_mday,
811 broken_out.tm_hour,
812 broken_out.tm_min,
813 broken_out.tm_sec);
814 }
815 cmdio_write_raw(buf);
816}
817
818
819
820#if ENABLE_FEATURE_FTP_WRITE
821static void
822handle_mkd(void)
823{
824 if (!G.ftp_arg || mkdir(G.ftp_arg, 0777) != 0) {
825 WRITE_ERR(FTP_FILEFAIL);
826 return;
827 }
828 WRITE_OK(FTP_MKDIROK);
829}
830
831static void
832handle_rmd(void)
833{
834 if (!G.ftp_arg || rmdir(G.ftp_arg) != 0) {
835 WRITE_ERR(FTP_FILEFAIL);
836 return;
837 }
838 WRITE_OK(FTP_RMDIROK);
839}
840
841static void
842handle_dele(void)
843{
844 if (!G.ftp_arg || unlink(G.ftp_arg) != 0) {
845 WRITE_ERR(FTP_FILEFAIL);
846 return;
847 }
848 WRITE_OK(FTP_DELEOK);
849}
850
851static void
852handle_rnfr(void)
853{
854 free(G.rnfr_filename);
855 G.rnfr_filename = xstrdup(G.ftp_arg);
856 WRITE_OK(FTP_RNFROK);
857}
858
859static void
860handle_rnto(void)
861{
862 int retval;
863
864
865 if (G.rnfr_filename == NULL || G.ftp_arg == NULL) {
866 cmdio_write_raw(STR(FTP_NEEDRNFR)" Use RNFR first\r\n");
867 return;
868 }
869
870 retval = rename(G.rnfr_filename, G.ftp_arg);
871 free(G.rnfr_filename);
872 G.rnfr_filename = NULL;
873
874 if (retval) {
875 WRITE_ERR(FTP_FILEFAIL);
876 return;
877 }
878 WRITE_OK(FTP_RENAMEOK);
879}
880
881static void
882handle_upload_common(int is_append, int is_unique)
883{
884 struct stat statbuf;
885 char *tempname;
886 off_t bytes_transferred;
887 off_t offset;
888 int local_file_fd;
889 int remote_fd;
890
891 offset = G.restart_pos;
892 G.restart_pos = 0;
893
894 if (!port_or_pasv_was_seen())
895 return;
896
897 tempname = NULL;
898 local_file_fd = -1;
899 if (is_unique) {
900 tempname = xstrdup(" FILE: uniq.XXXXXX");
901 local_file_fd = mkstemp(tempname + 7);
902 } else if (G.ftp_arg) {
903 int flags = O_WRONLY | O_CREAT | O_TRUNC;
904 if (is_append)
905 flags = O_WRONLY | O_CREAT | O_APPEND;
906 if (offset)
907 flags = O_WRONLY | O_CREAT;
908 local_file_fd = open(G.ftp_arg, flags, 0666);
909 }
910
911 if (local_file_fd < 0
912 || fstat(local_file_fd, &statbuf) != 0
913 || !S_ISREG(statbuf.st_mode)
914 ) {
915 WRITE_ERR(FTP_UPLOADFAIL);
916 if (local_file_fd >= 0)
917 goto close_local_and_bail;
918 return;
919 }
920 G.local_file_fd = local_file_fd;
921
922 if (offset)
923 xlseek(local_file_fd, offset, SEEK_SET);
924
925 remote_fd = get_remote_transfer_fd(tempname ? tempname : " Ok to send data");
926 free(tempname);
927
928 if (remote_fd < 0)
929 goto close_local_and_bail;
930
931 bytes_transferred = bb_copyfd_eof(remote_fd, local_file_fd);
932 close(remote_fd);
933 if (bytes_transferred < 0)
934 WRITE_ERR(FTP_BADSENDFILE);
935 else
936 WRITE_OK(FTP_TRANSFEROK);
937
938 close_local_and_bail:
939 close(local_file_fd);
940 G.local_file_fd = 0;
941}
942
943static void
944handle_stor(void)
945{
946 handle_upload_common(0, 0);
947}
948
949static void
950handle_appe(void)
951{
952 G.restart_pos = 0;
953 handle_upload_common(1, 0);
954}
955
956static void
957handle_stou(void)
958{
959 G.restart_pos = 0;
960 handle_upload_common(0, 1);
961}
962#endif
963
964static uint32_t
965cmdio_get_cmd_and_arg(void)
966{
967 size_t len;
968 uint32_t cmdval;
969 char *cmd;
970
971 alarm(G.timeout);
972
973 free(G.ftp_cmd);
974 len = 8 * 1024;
975 G.ftp_cmd = cmd = xmalloc_fgets_str_len(stdin, "\r\n", &len);
976 if (!cmd)
977 exit(0);
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997 {
998 int dst, src;
999
1000
1001 if (len != 0 && cmd[len - 1] == '\n') {
1002 len--;
1003 if (len != 0 && cmd[len - 1] == '\r')
1004 len--;
1005 cmd[len] = '\0';
1006 }
1007 src = strchrnul(cmd, 0xff) - cmd;
1008
1009 if (src < len) {
1010 dst = src;
1011 do {
1012 if ((unsigned char)(cmd[src]) == 255) {
1013 src++;
1014
1015 if ((unsigned char)(cmd[src]) != 255) {
1016
1017 src++;
1018 continue;
1019 }
1020
1021 }
1022
1023 cmd[dst++] = cmd[src] ? cmd[src] : '\n';
1024 src++;
1025 } while (src < len);
1026 cmd[dst] = '\0';
1027 }
1028 }
1029
1030 if (G.verbose > 1)
1031 verbose_log(cmd);
1032
1033 G.ftp_arg = strchr(cmd, ' ');
1034 if (G.ftp_arg != NULL)
1035 *G.ftp_arg++ = '\0';
1036
1037
1038 cmdval = 0;
1039 while (*cmd)
1040 cmdval = (cmdval << 8) + ((unsigned char)*cmd++ & (unsigned char)~0x20);
1041
1042 return cmdval;
1043}
1044
1045#define mk_const4(a,b,c,d) (((a * 0x100 + b) * 0x100 + c) * 0x100 + d)
1046#define mk_const3(a,b,c) ((a * 0x100 + b) * 0x100 + c)
1047enum {
1048 const_ALLO = mk_const4('A', 'L', 'L', 'O'),
1049 const_APPE = mk_const4('A', 'P', 'P', 'E'),
1050 const_CDUP = mk_const4('C', 'D', 'U', 'P'),
1051 const_CWD = mk_const3('C', 'W', 'D'),
1052 const_DELE = mk_const4('D', 'E', 'L', 'E'),
1053 const_EPSV = mk_const4('E', 'P', 'S', 'V'),
1054 const_FEAT = mk_const4('F', 'E', 'A', 'T'),
1055 const_HELP = mk_const4('H', 'E', 'L', 'P'),
1056 const_LIST = mk_const4('L', 'I', 'S', 'T'),
1057 const_MDTM = mk_const4('M', 'D', 'T', 'M'),
1058 const_MKD = mk_const3('M', 'K', 'D'),
1059 const_MODE = mk_const4('M', 'O', 'D', 'E'),
1060 const_NLST = mk_const4('N', 'L', 'S', 'T'),
1061 const_NOOP = mk_const4('N', 'O', 'O', 'P'),
1062 const_PASS = mk_const4('P', 'A', 'S', 'S'),
1063 const_PASV = mk_const4('P', 'A', 'S', 'V'),
1064 const_PORT = mk_const4('P', 'O', 'R', 'T'),
1065 const_PWD = mk_const3('P', 'W', 'D'),
1066 const_QUIT = mk_const4('Q', 'U', 'I', 'T'),
1067 const_REST = mk_const4('R', 'E', 'S', 'T'),
1068 const_RETR = mk_const4('R', 'E', 'T', 'R'),
1069 const_RMD = mk_const3('R', 'M', 'D'),
1070 const_RNFR = mk_const4('R', 'N', 'F', 'R'),
1071 const_RNTO = mk_const4('R', 'N', 'T', 'O'),
1072 const_SIZE = mk_const4('S', 'I', 'Z', 'E'),
1073 const_STAT = mk_const4('S', 'T', 'A', 'T'),
1074 const_STOR = mk_const4('S', 'T', 'O', 'R'),
1075 const_STOU = mk_const4('S', 'T', 'O', 'U'),
1076 const_STRU = mk_const4('S', 'T', 'R', 'U'),
1077 const_SYST = mk_const4('S', 'Y', 'S', 'T'),
1078 const_TYPE = mk_const4('T', 'Y', 'P', 'E'),
1079 const_USER = mk_const4('U', 'S', 'E', 'R'),
1080
1081#if !BB_MMU
1082 OPT_l = (1 << 0),
1083 OPT_1 = (1 << 1),
1084#endif
1085 OPT_v = (1 << ((!BB_MMU) * 2 + 0)),
1086 OPT_S = (1 << ((!BB_MMU) * 2 + 1)),
1087 OPT_w = (1 << ((!BB_MMU) * 2 + 2)) * ENABLE_FEATURE_FTP_WRITE,
1088};
1089
1090int ftpd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
1091#if !BB_MMU
1092int ftpd_main(int argc, char **argv)
1093#else
1094int ftpd_main(int argc UNUSED_PARAM, char **argv)
1095#endif
1096{
1097 unsigned abs_timeout;
1098 smallint opts;
1099
1100 INIT_G();
1101
1102 abs_timeout = 1 * 60 * 60;
1103 G.timeout = 2 * 60;
1104 opt_complementary = "t+:T+:vv";
1105#if BB_MMU
1106 opts = getopt32(argv, "vS" USE_FEATURE_FTP_WRITE("w") "t:T:", &G.timeout, &abs_timeout, &G.verbose);
1107#else
1108 opts = getopt32(argv, "l1vS" USE_FEATURE_FTP_WRITE("w") "t:T:", &G.timeout, &abs_timeout, &G.verbose);
1109 if (opts & (OPT_l|OPT_1)) {
1110
1111
1112
1113 xchdir(argv[2]);
1114 argv[2] = (char*)"--";
1115 memset(&G, 0, sizeof(G));
1116 return ls_main(argc, argv);
1117 }
1118#endif
1119 if (abs_timeout | G.timeout) {
1120 if (abs_timeout == 0)
1121 abs_timeout = INT_MAX;
1122 G.end_time = monotonic_sec() + abs_timeout;
1123 if (G.timeout > abs_timeout)
1124 G.timeout = abs_timeout;
1125 }
1126 strcpy(G.msg_ok + 4, MSG_OK );
1127 strcpy(G.msg_err + 4, MSG_ERR);
1128
1129 G.local_addr = get_sock_lsa(STDIN_FILENO);
1130 if (!G.local_addr) {
1131
1132
1133
1134 bb_show_usage();
1135
1136
1137
1138 }
1139
1140 if (!(opts & OPT_v))
1141 logmode = LOGMODE_NONE;
1142 if (opts & OPT_S) {
1143
1144 openlog(applet_name, LOG_PID | LOG_NDELAY, LOG_DAEMON);
1145 logmode |= LOGMODE_SYSLOG;
1146 }
1147 if (logmode)
1148 applet_name = xasprintf("%s[%u]", applet_name, (int)getpid());
1149
1150#if !BB_MMU
1151 G.root_fd = xopen("/", O_RDONLY | O_DIRECTORY);
1152#endif
1153
1154 if (argv[optind]) {
1155 xchdir(argv[optind]);
1156 chroot(".");
1157 }
1158
1159
1160
1161
1162 signal(SIGPIPE, SIG_IGN);
1163
1164
1165 setsockopt(STDIN_FILENO, IPPROTO_TCP, TCP_NODELAY, &const_int_1, sizeof(const_int_1));
1166 setsockopt(STDIN_FILENO, SOL_SOCKET, SO_KEEPALIVE, &const_int_1, sizeof(const_int_1));
1167
1168
1169 setsockopt(STDIN_FILENO, SOL_SOCKET, SO_OOBINLINE, &const_int_1, sizeof(const_int_1));
1170
1171 WRITE_OK(FTP_GREET);
1172 signal(SIGALRM, timeout_handler);
1173
1174#ifdef IF_WE_WANT_TO_REQUIRE_LOGIN
1175 {
1176 smallint user_was_specified = 0;
1177 while (1) {
1178 uint32_t cmdval = cmdio_get_cmd_and_arg();
1179
1180 if (cmdval == const_USER) {
1181 if (G.ftp_arg == NULL || strcasecmp(G.ftp_arg, "anonymous") != 0)
1182 cmdio_write_raw(STR(FTP_LOGINERR)" Server is anonymous only\r\n");
1183 else {
1184 user_was_specified = 1;
1185 cmdio_write_raw(STR(FTP_GIVEPWORD)" Please specify the password\r\n");
1186 }
1187 } else if (cmdval == const_PASS) {
1188 if (user_was_specified)
1189 break;
1190 cmdio_write_raw(STR(FTP_NEEDUSER)" Login with USER\r\n");
1191 } else if (cmdval == const_QUIT) {
1192 WRITE_OK(FTP_GOODBYE);
1193 return 0;
1194 } else {
1195 cmdio_write_raw(STR(FTP_LOGINERR)" Login with USER and PASS\r\n");
1196 }
1197 }
1198 }
1199 WRITE_OK(FTP_LOGINOK);
1200#endif
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240 while (1) {
1241 uint32_t cmdval = cmdio_get_cmd_and_arg();
1242
1243 if (cmdval == const_QUIT) {
1244 WRITE_OK(FTP_GOODBYE);
1245 return 0;
1246 }
1247 else if (cmdval == const_USER)
1248
1249
1250
1251
1252
1253 WRITE_OK(FTP_LOGINOK);
1254 else if (cmdval == const_PASS)
1255 WRITE_OK(FTP_LOGINOK);
1256 else if (cmdval == const_NOOP)
1257 WRITE_OK(FTP_NOOPOK);
1258 else if (cmdval == const_TYPE)
1259 WRITE_OK(FTP_TYPEOK);
1260 else if (cmdval == const_STRU)
1261 WRITE_OK(FTP_STRUOK);
1262 else if (cmdval == const_MODE)
1263 WRITE_OK(FTP_MODEOK);
1264 else if (cmdval == const_ALLO)
1265 WRITE_OK(FTP_ALLOOK);
1266 else if (cmdval == const_SYST)
1267 cmdio_write_raw(STR(FTP_SYSTOK)" UNIX Type: L8\r\n");
1268 else if (cmdval == const_PWD)
1269 handle_pwd();
1270 else if (cmdval == const_CWD)
1271 handle_cwd();
1272 else if (cmdval == const_CDUP)
1273 handle_cdup();
1274
1275
1276 else if (cmdval == const_HELP || cmdval == const_FEAT)
1277 handle_feat(cmdval == const_HELP
1278 ? STRNUM32(FTP_HELP)
1279 : STRNUM32(FTP_STATOK)
1280 );
1281 else if (cmdval == const_LIST)
1282 handle_list();
1283 else if (cmdval == const_NLST)
1284 handle_nlst();
1285
1286
1287 else if (cmdval == const_SIZE || cmdval == const_MDTM)
1288 handle_size_or_mdtm(cmdval == const_SIZE);
1289 else if (cmdval == const_STAT) {
1290 if (G.ftp_arg == NULL)
1291 handle_stat();
1292 else
1293 handle_stat_file();
1294 }
1295 else if (cmdval == const_PASV)
1296 handle_pasv();
1297 else if (cmdval == const_EPSV)
1298 handle_epsv();
1299 else if (cmdval == const_RETR)
1300 handle_retr();
1301 else if (cmdval == const_PORT)
1302 handle_port();
1303 else if (cmdval == const_REST)
1304 handle_rest();
1305#if ENABLE_FEATURE_FTP_WRITE
1306 else if (opts & OPT_w) {
1307 if (cmdval == const_STOR)
1308 handle_stor();
1309 else if (cmdval == const_MKD)
1310 handle_mkd();
1311 else if (cmdval == const_RMD)
1312 handle_rmd();
1313 else if (cmdval == const_DELE)
1314 handle_dele();
1315 else if (cmdval == const_RNFR)
1316 handle_rnfr();
1317 else if (cmdval == const_RNTO)
1318 handle_rnto();
1319 else if (cmdval == const_APPE)
1320 handle_appe();
1321 else if (cmdval == const_STOU)
1322 handle_stou();
1323 }
1324#endif
1325#if 0
1326 else if (cmdval == const_STOR
1327 || cmdval == const_MKD
1328 || cmdval == const_RMD
1329 || cmdval == const_DELE
1330 || cmdval == const_RNFR
1331 || cmdval == const_RNTO
1332 || cmdval == const_APPE
1333 || cmdval == const_STOU
1334 ) {
1335 cmdio_write_raw(STR(FTP_NOPERM)" Permission denied\r\n");
1336 }
1337#endif
1338 else {
1339
1340
1341
1342
1343 cmdio_write_raw(STR(FTP_BADCMD)" Unknown command\r\n");
1344 }
1345 }
1346}
1347