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} FIX_ALIASING;
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
464static void
465handle_port(void)
466{
467 unsigned port, port_hi;
468 char *raw, *comma;
469#ifdef WHY_BOTHER_WE_CAN_ASSUME_IP_MATCHES
470 socklen_t peer_ipv4_len;
471 struct sockaddr_in peer_ipv4;
472 struct in_addr port_ipv4_sin_addr;
473#endif
474
475 port_pasv_cleanup();
476
477 raw = G.ftp_arg;
478
479
480 if (!raw
481#ifdef WHY_BOTHER_WE_CAN_ASSUME_IP_MATCHES
482 || G.local_addr->u.sa.sa_family != AF_INET
483#endif
484 ) {
485 bail:
486 WRITE_ERR(FTP_BADCMD);
487 return;
488 }
489
490 comma = strrchr(raw, ',');
491 if (comma == NULL)
492 goto bail;
493 *comma = '\0';
494 port = bb_strtou(&comma[1], NULL, 10);
495 if (errno || port > 0xff)
496 goto bail;
497
498 comma = strrchr(raw, ',');
499 if (comma == NULL)
500 goto bail;
501 *comma = '\0';
502 port_hi = bb_strtou(&comma[1], NULL, 10);
503 if (errno || port_hi > 0xff)
504 goto bail;
505 port |= port_hi << 8;
506
507#ifdef WHY_BOTHER_WE_CAN_ASSUME_IP_MATCHES
508 replace_char(raw, ',', '.');
509
510
511
512
513
514
515
516
517 if (!inet_aton(raw, &port_ipv4_sin_addr))
518 goto bail;
519 peer_ipv4_len = sizeof(peer_ipv4);
520 if (getpeername(STDIN_FILENO, &peer_ipv4, &peer_ipv4_len) != 0)
521 goto bail;
522 if (memcmp(&port_ipv4_sin_addr, &peer_ipv4.sin_addr, sizeof(struct in_addr)) != 0)
523 goto bail;
524
525 G.port_addr = xdotted2sockaddr(raw, port);
526#else
527 G.port_addr = get_peer_lsa(STDIN_FILENO);
528 set_nport(G.port_addr, htons(port));
529#endif
530 WRITE_OK(FTP_PORTOK);
531}
532
533static void
534handle_rest(void)
535{
536
537 G.restart_pos = G.ftp_arg ? xatoi_u(G.ftp_arg) : 0;
538 WRITE_OK(FTP_RESTOK);
539}
540
541static void
542handle_retr(void)
543{
544 struct stat statbuf;
545 off_t bytes_transferred;
546 int remote_fd;
547 int local_file_fd;
548 off_t offset = G.restart_pos;
549 char *response;
550
551 G.restart_pos = 0;
552
553 if (!port_or_pasv_was_seen())
554 return;
555
556
557 local_file_fd = G.ftp_arg ? open(G.ftp_arg, O_RDONLY | O_NONBLOCK) : -1;
558 if (local_file_fd < 0) {
559 WRITE_ERR(FTP_FILEFAIL);
560 return;
561 }
562
563 if (fstat(local_file_fd, &statbuf) != 0 || !S_ISREG(statbuf.st_mode)) {
564
565 WRITE_ERR(FTP_FILEFAIL);
566 goto file_close_out;
567 }
568 G.local_file_fd = local_file_fd;
569
570
571
572
573 ndelay_off(local_file_fd);
574
575
576 if (offset != 0)
577 xlseek(local_file_fd, offset, SEEK_SET);
578
579 response = xasprintf(
580 " Opening BINARY connection for %s (%"OFF_FMT"u bytes)",
581 G.ftp_arg, statbuf.st_size);
582 remote_fd = get_remote_transfer_fd(response);
583 free(response);
584 if (remote_fd < 0)
585 goto file_close_out;
586
587 bytes_transferred = bb_copyfd_eof(local_file_fd, remote_fd);
588 close(remote_fd);
589 if (bytes_transferred < 0)
590 WRITE_ERR(FTP_BADSENDFILE);
591 else
592 WRITE_OK(FTP_TRANSFEROK);
593
594 file_close_out:
595 close(local_file_fd);
596 G.local_file_fd = 0;
597}
598
599
600
601static int
602popen_ls(const char *opt)
603{
604 const char *argv[5];
605 struct fd_pair outfd;
606 pid_t pid;
607
608 argv[0] = "ftpd";
609 argv[1] = opt;
610#if BB_MMU
611 argv[2] = "--";
612#else
613
614
615 argv[2] = xrealloc_getcwd_or_warn(NULL);
616#endif
617 argv[3] = G.ftp_arg;
618 argv[4] = NULL;
619
620
621
622
623 if (ENABLE_FEATURE_FTPD_ACCEPT_BROKEN_LIST
624 && G.ftp_arg && G.ftp_arg[0] == '-'
625 ) {
626 const char *tmp = strchr(G.ftp_arg, ' ');
627 if (tmp)
628 tmp++;
629 argv[3] = tmp;
630 }
631
632 xpiped_pair(outfd);
633
634
635 pid = BB_MMU ? xfork() : xvfork();
636 if (pid == 0) {
637
638#if !BB_MMU
639
640
641
642
643
644 if (fchdir(G.root_fd) != 0)
645 _exit(127);
646
647#endif
648
649 close(outfd.rd);
650 xmove_fd(outfd.wr, STDOUT_FILENO);
651
652
653
654
655 close(STDIN_FILENO);
656 dup(STDOUT_FILENO);
657#if BB_MMU
658
659 exit(ls_main(ARRAY_SIZE(argv) - 1, (char**) argv));
660#else
661
662
663
664 execv(bb_busybox_exec_path + 1, (char**) argv);
665 _exit(127);
666#endif
667 }
668
669
670 close(outfd.wr);
671#if !BB_MMU
672 free((char*)argv[2]);
673#endif
674 return outfd.rd;
675}
676
677enum {
678 USE_CTRL_CONN = 1,
679 LONG_LISTING = 2,
680};
681
682static void
683handle_dir_common(int opts)
684{
685 FILE *ls_fp;
686 char *line;
687 int ls_fd;
688
689 if (!(opts & USE_CTRL_CONN) && !port_or_pasv_was_seen())
690 return;
691
692
693
694 ls_fd = popen_ls((opts & LONG_LISTING) ? "-l" : "-1");
695 ls_fp = xfdopen_for_read(ls_fd);
696
697 if (opts & USE_CTRL_CONN) {
698
699 cmdio_write_raw(STR(FTP_STATFILE_OK)"-File status:\r\n");
700 while (1) {
701 line = xmalloc_fgetline(ls_fp);
702 if (!line)
703 break;
704
705
706
707 cmdio_write(0, line);
708 free(line);
709 }
710 WRITE_OK(FTP_STATFILE_OK);
711 } else {
712
713 int remote_fd = get_remote_transfer_fd(" Directory listing");
714 if (remote_fd >= 0) {
715 while (1) {
716 line = xmalloc_fgetline(ls_fp);
717 if (!line)
718 break;
719
720
721
722
723
724 xwrite_str(remote_fd, line);
725 xwrite(remote_fd, "\r\n", 2);
726 free(line);
727 }
728 }
729 close(remote_fd);
730 WRITE_OK(FTP_TRANSFEROK);
731 }
732 fclose(ls_fp);
733}
734static void
735handle_list(void)
736{
737 handle_dir_common(LONG_LISTING);
738}
739static void
740handle_nlst(void)
741{
742
743
744
745
746
747
748
749 handle_dir_common(0);
750}
751static void
752handle_stat_file(void)
753{
754 handle_dir_common(LONG_LISTING + USE_CTRL_CONN);
755}
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
786static void
787handle_size_or_mdtm(int need_size)
788{
789 struct stat statbuf;
790 struct tm broken_out;
791 char buf[(sizeof("NNN %"OFF_FMT"u\r\n") + sizeof(off_t) * 3)
792 | sizeof("NNN YYYYMMDDhhmmss\r\n")
793 ];
794
795 if (!G.ftp_arg
796 || stat(G.ftp_arg, &statbuf) != 0
797 || !S_ISREG(statbuf.st_mode)
798 ) {
799 WRITE_ERR(FTP_FILEFAIL);
800 return;
801 }
802 if (need_size) {
803 sprintf(buf, STR(FTP_STATFILE_OK)" %"OFF_FMT"u\r\n", statbuf.st_size);
804 } else {
805 gmtime_r(&statbuf.st_mtime, &broken_out);
806 sprintf(buf, STR(FTP_STATFILE_OK)" %04u%02u%02u%02u%02u%02u\r\n",
807 broken_out.tm_year + 1900,
808 broken_out.tm_mon,
809 broken_out.tm_mday,
810 broken_out.tm_hour,
811 broken_out.tm_min,
812 broken_out.tm_sec);
813 }
814 cmdio_write_raw(buf);
815}
816
817
818
819#if ENABLE_FEATURE_FTP_WRITE
820static void
821handle_mkd(void)
822{
823 if (!G.ftp_arg || mkdir(G.ftp_arg, 0777) != 0) {
824 WRITE_ERR(FTP_FILEFAIL);
825 return;
826 }
827 WRITE_OK(FTP_MKDIROK);
828}
829
830static void
831handle_rmd(void)
832{
833 if (!G.ftp_arg || rmdir(G.ftp_arg) != 0) {
834 WRITE_ERR(FTP_FILEFAIL);
835 return;
836 }
837 WRITE_OK(FTP_RMDIROK);
838}
839
840static void
841handle_dele(void)
842{
843 if (!G.ftp_arg || unlink(G.ftp_arg) != 0) {
844 WRITE_ERR(FTP_FILEFAIL);
845 return;
846 }
847 WRITE_OK(FTP_DELEOK);
848}
849
850static void
851handle_rnfr(void)
852{
853 free(G.rnfr_filename);
854 G.rnfr_filename = xstrdup(G.ftp_arg);
855 WRITE_OK(FTP_RNFROK);
856}
857
858static void
859handle_rnto(void)
860{
861 int retval;
862
863
864 if (G.rnfr_filename == NULL || G.ftp_arg == NULL) {
865 cmdio_write_raw(STR(FTP_NEEDRNFR)" Use RNFR first\r\n");
866 return;
867 }
868
869 retval = rename(G.rnfr_filename, G.ftp_arg);
870 free(G.rnfr_filename);
871 G.rnfr_filename = NULL;
872
873 if (retval) {
874 WRITE_ERR(FTP_FILEFAIL);
875 return;
876 }
877 WRITE_OK(FTP_RENAMEOK);
878}
879
880static void
881handle_upload_common(int is_append, int is_unique)
882{
883 struct stat statbuf;
884 char *tempname;
885 off_t bytes_transferred;
886 off_t offset;
887 int local_file_fd;
888 int remote_fd;
889
890 offset = G.restart_pos;
891 G.restart_pos = 0;
892
893 if (!port_or_pasv_was_seen())
894 return;
895
896 tempname = NULL;
897 local_file_fd = -1;
898 if (is_unique) {
899 tempname = xstrdup(" FILE: uniq.XXXXXX");
900 local_file_fd = mkstemp(tempname + 7);
901 } else if (G.ftp_arg) {
902 int flags = O_WRONLY | O_CREAT | O_TRUNC;
903 if (is_append)
904 flags = O_WRONLY | O_CREAT | O_APPEND;
905 if (offset)
906 flags = O_WRONLY | O_CREAT;
907 local_file_fd = open(G.ftp_arg, flags, 0666);
908 }
909
910 if (local_file_fd < 0
911 || fstat(local_file_fd, &statbuf) != 0
912 || !S_ISREG(statbuf.st_mode)
913 ) {
914 WRITE_ERR(FTP_UPLOADFAIL);
915 if (local_file_fd >= 0)
916 goto close_local_and_bail;
917 return;
918 }
919 G.local_file_fd = local_file_fd;
920
921 if (offset)
922 xlseek(local_file_fd, offset, SEEK_SET);
923
924 remote_fd = get_remote_transfer_fd(tempname ? tempname : " Ok to send data");
925 free(tempname);
926
927 if (remote_fd < 0)
928 goto close_local_and_bail;
929
930 bytes_transferred = bb_copyfd_eof(remote_fd, local_file_fd);
931 close(remote_fd);
932 if (bytes_transferred < 0)
933 WRITE_ERR(FTP_BADSENDFILE);
934 else
935 WRITE_OK(FTP_TRANSFEROK);
936
937 close_local_and_bail:
938 close(local_file_fd);
939 G.local_file_fd = 0;
940}
941
942static void
943handle_stor(void)
944{
945 handle_upload_common(0, 0);
946}
947
948static void
949handle_appe(void)
950{
951 G.restart_pos = 0;
952 handle_upload_common(1, 0);
953}
954
955static void
956handle_stou(void)
957{
958 G.restart_pos = 0;
959 handle_upload_common(0, 1);
960}
961#endif
962
963static uint32_t
964cmdio_get_cmd_and_arg(void)
965{
966 int len;
967 uint32_t cmdval;
968 char *cmd;
969
970 alarm(G.timeout);
971
972 free(G.ftp_cmd);
973 {
974
975
976
977 size_t len_on_stk = 8 * 1024;
978 G.ftp_cmd = cmd = xmalloc_fgets_str_len(stdin, "\r\n", &len_on_stk);
979 if (!cmd)
980 exit(0);
981 len = len_on_stk;
982 }
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002 {
1003 int dst, src;
1004
1005
1006 if (len != 0 && cmd[len - 1] == '\n') {
1007 len--;
1008 if (len != 0 && cmd[len - 1] == '\r')
1009 len--;
1010 cmd[len] = '\0';
1011 }
1012 src = strchrnul(cmd, 0xff) - cmd;
1013
1014 if (src < len) {
1015 dst = src;
1016 do {
1017 if ((unsigned char)(cmd[src]) == 255) {
1018 src++;
1019
1020 if ((unsigned char)(cmd[src]) != 255) {
1021
1022 src++;
1023 continue;
1024 }
1025
1026 }
1027
1028 cmd[dst++] = cmd[src] ? cmd[src] : '\n';
1029 src++;
1030 } while (src < len);
1031 cmd[dst] = '\0';
1032 }
1033 }
1034
1035 if (G.verbose > 1)
1036 verbose_log(cmd);
1037
1038 G.ftp_arg = strchr(cmd, ' ');
1039 if (G.ftp_arg != NULL)
1040 *G.ftp_arg++ = '\0';
1041
1042
1043 cmdval = 0;
1044 while (*cmd)
1045 cmdval = (cmdval << 8) + ((unsigned char)*cmd++ & (unsigned char)~0x20);
1046
1047 return cmdval;
1048}
1049
1050#define mk_const4(a,b,c,d) (((a * 0x100 + b) * 0x100 + c) * 0x100 + d)
1051#define mk_const3(a,b,c) ((a * 0x100 + b) * 0x100 + c)
1052enum {
1053 const_ALLO = mk_const4('A', 'L', 'L', 'O'),
1054 const_APPE = mk_const4('A', 'P', 'P', 'E'),
1055 const_CDUP = mk_const4('C', 'D', 'U', 'P'),
1056 const_CWD = mk_const3('C', 'W', 'D'),
1057 const_DELE = mk_const4('D', 'E', 'L', 'E'),
1058 const_EPSV = mk_const4('E', 'P', 'S', 'V'),
1059 const_FEAT = mk_const4('F', 'E', 'A', 'T'),
1060 const_HELP = mk_const4('H', 'E', 'L', 'P'),
1061 const_LIST = mk_const4('L', 'I', 'S', 'T'),
1062 const_MDTM = mk_const4('M', 'D', 'T', 'M'),
1063 const_MKD = mk_const3('M', 'K', 'D'),
1064 const_MODE = mk_const4('M', 'O', 'D', 'E'),
1065 const_NLST = mk_const4('N', 'L', 'S', 'T'),
1066 const_NOOP = mk_const4('N', 'O', 'O', 'P'),
1067 const_PASS = mk_const4('P', 'A', 'S', 'S'),
1068 const_PASV = mk_const4('P', 'A', 'S', 'V'),
1069 const_PORT = mk_const4('P', 'O', 'R', 'T'),
1070 const_PWD = mk_const3('P', 'W', 'D'),
1071 const_QUIT = mk_const4('Q', 'U', 'I', 'T'),
1072 const_REST = mk_const4('R', 'E', 'S', 'T'),
1073 const_RETR = mk_const4('R', 'E', 'T', 'R'),
1074 const_RMD = mk_const3('R', 'M', 'D'),
1075 const_RNFR = mk_const4('R', 'N', 'F', 'R'),
1076 const_RNTO = mk_const4('R', 'N', 'T', 'O'),
1077 const_SIZE = mk_const4('S', 'I', 'Z', 'E'),
1078 const_STAT = mk_const4('S', 'T', 'A', 'T'),
1079 const_STOR = mk_const4('S', 'T', 'O', 'R'),
1080 const_STOU = mk_const4('S', 'T', 'O', 'U'),
1081 const_STRU = mk_const4('S', 'T', 'R', 'U'),
1082 const_SYST = mk_const4('S', 'Y', 'S', 'T'),
1083 const_TYPE = mk_const4('T', 'Y', 'P', 'E'),
1084 const_USER = mk_const4('U', 'S', 'E', 'R'),
1085
1086#if !BB_MMU
1087 OPT_l = (1 << 0),
1088 OPT_1 = (1 << 1),
1089#endif
1090 OPT_v = (1 << ((!BB_MMU) * 2 + 0)),
1091 OPT_S = (1 << ((!BB_MMU) * 2 + 1)),
1092 OPT_w = (1 << ((!BB_MMU) * 2 + 2)) * ENABLE_FEATURE_FTP_WRITE,
1093};
1094
1095int ftpd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
1096#if !BB_MMU
1097int ftpd_main(int argc, char **argv)
1098#else
1099int ftpd_main(int argc UNUSED_PARAM, char **argv)
1100#endif
1101{
1102 unsigned abs_timeout;
1103 unsigned verbose_S;
1104 smallint opts;
1105
1106 INIT_G();
1107
1108 abs_timeout = 1 * 60 * 60;
1109 verbose_S = 0;
1110 G.timeout = 2 * 60;
1111 opt_complementary = "t+:T+:vv:SS";
1112#if BB_MMU
1113 opts = getopt32(argv, "vS" IF_FEATURE_FTP_WRITE("w") "t:T:", &G.timeout, &abs_timeout, &G.verbose, &verbose_S);
1114#else
1115 opts = getopt32(argv, "l1vS" IF_FEATURE_FTP_WRITE("w") "t:T:", &G.timeout, &abs_timeout, &G.verbose, &verbose_S);
1116 if (opts & (OPT_l|OPT_1)) {
1117
1118
1119
1120
1121 xchdir(argv[2]);
1122 argv[2] = (char*)"--";
1123
1124 return ls_main(argc, argv);
1125 }
1126#endif
1127 if (G.verbose < verbose_S)
1128 G.verbose = verbose_S;
1129 if (abs_timeout | G.timeout) {
1130 if (abs_timeout == 0)
1131 abs_timeout = INT_MAX;
1132 G.end_time = monotonic_sec() + abs_timeout;
1133 if (G.timeout > abs_timeout)
1134 G.timeout = abs_timeout;
1135 }
1136 strcpy(G.msg_ok + 4, MSG_OK );
1137 strcpy(G.msg_err + 4, MSG_ERR);
1138
1139 G.local_addr = get_sock_lsa(STDIN_FILENO);
1140 if (!G.local_addr) {
1141
1142
1143
1144 bb_show_usage();
1145
1146
1147
1148 }
1149
1150 if (!(opts & OPT_v))
1151 logmode = LOGMODE_NONE;
1152 if (opts & OPT_S) {
1153
1154 openlog(applet_name, LOG_PID | LOG_NDELAY, LOG_DAEMON);
1155 logmode |= LOGMODE_SYSLOG;
1156 }
1157 if (logmode)
1158 applet_name = xasprintf("%s[%u]", applet_name, (int)getpid());
1159
1160#if !BB_MMU
1161 G.root_fd = xopen("/", O_RDONLY | O_DIRECTORY);
1162 close_on_exec_on(G.root_fd);
1163#endif
1164
1165 if (argv[optind]) {
1166 xchdir(argv[optind]);
1167 chroot(".");
1168 }
1169
1170
1171
1172
1173 signal(SIGPIPE, SIG_IGN);
1174
1175
1176 setsockopt(STDIN_FILENO, IPPROTO_TCP, TCP_NODELAY, &const_int_1, sizeof(const_int_1));
1177 setsockopt(STDIN_FILENO, SOL_SOCKET, SO_KEEPALIVE, &const_int_1, sizeof(const_int_1));
1178
1179
1180 setsockopt(STDIN_FILENO, SOL_SOCKET, SO_OOBINLINE, &const_int_1, sizeof(const_int_1));
1181
1182 WRITE_OK(FTP_GREET);
1183 signal(SIGALRM, timeout_handler);
1184
1185#ifdef IF_WE_WANT_TO_REQUIRE_LOGIN
1186 {
1187 smallint user_was_specified = 0;
1188 while (1) {
1189 uint32_t cmdval = cmdio_get_cmd_and_arg();
1190
1191 if (cmdval == const_USER) {
1192 if (G.ftp_arg == NULL || strcasecmp(G.ftp_arg, "anonymous") != 0)
1193 cmdio_write_raw(STR(FTP_LOGINERR)" Server is anonymous only\r\n");
1194 else {
1195 user_was_specified = 1;
1196 cmdio_write_raw(STR(FTP_GIVEPWORD)" Please specify the password\r\n");
1197 }
1198 } else if (cmdval == const_PASS) {
1199 if (user_was_specified)
1200 break;
1201 cmdio_write_raw(STR(FTP_NEEDUSER)" Login with USER\r\n");
1202 } else if (cmdval == const_QUIT) {
1203 WRITE_OK(FTP_GOODBYE);
1204 return 0;
1205 } else {
1206 cmdio_write_raw(STR(FTP_LOGINERR)" Login with USER and PASS\r\n");
1207 }
1208 }
1209 }
1210 WRITE_OK(FTP_LOGINOK);
1211#endif
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
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251 while (1) {
1252 uint32_t cmdval = cmdio_get_cmd_and_arg();
1253
1254 if (cmdval == const_QUIT) {
1255 WRITE_OK(FTP_GOODBYE);
1256 return 0;
1257 }
1258 else if (cmdval == const_USER)
1259
1260
1261
1262
1263
1264 WRITE_OK(FTP_LOGINOK);
1265 else if (cmdval == const_PASS)
1266 WRITE_OK(FTP_LOGINOK);
1267 else if (cmdval == const_NOOP)
1268 WRITE_OK(FTP_NOOPOK);
1269 else if (cmdval == const_TYPE)
1270 WRITE_OK(FTP_TYPEOK);
1271 else if (cmdval == const_STRU)
1272 WRITE_OK(FTP_STRUOK);
1273 else if (cmdval == const_MODE)
1274 WRITE_OK(FTP_MODEOK);
1275 else if (cmdval == const_ALLO)
1276 WRITE_OK(FTP_ALLOOK);
1277 else if (cmdval == const_SYST)
1278 cmdio_write_raw(STR(FTP_SYSTOK)" UNIX Type: L8\r\n");
1279 else if (cmdval == const_PWD)
1280 handle_pwd();
1281 else if (cmdval == const_CWD)
1282 handle_cwd();
1283 else if (cmdval == const_CDUP)
1284 handle_cdup();
1285
1286
1287 else if (cmdval == const_HELP || cmdval == const_FEAT)
1288 handle_feat(cmdval == const_HELP
1289 ? STRNUM32(FTP_HELP)
1290 : STRNUM32(FTP_STATOK)
1291 );
1292 else if (cmdval == const_LIST)
1293 handle_list();
1294 else if (cmdval == const_NLST)
1295 handle_nlst();
1296
1297
1298 else if (cmdval == const_SIZE || cmdval == const_MDTM)
1299 handle_size_or_mdtm(cmdval == const_SIZE);
1300 else if (cmdval == const_STAT) {
1301 if (G.ftp_arg == NULL)
1302 handle_stat();
1303 else
1304 handle_stat_file();
1305 }
1306 else if (cmdval == const_PASV)
1307 handle_pasv();
1308 else if (cmdval == const_EPSV)
1309 handle_epsv();
1310 else if (cmdval == const_RETR)
1311 handle_retr();
1312 else if (cmdval == const_PORT)
1313 handle_port();
1314 else if (cmdval == const_REST)
1315 handle_rest();
1316#if ENABLE_FEATURE_FTP_WRITE
1317 else if (opts & OPT_w) {
1318 if (cmdval == const_STOR)
1319 handle_stor();
1320 else if (cmdval == const_MKD)
1321 handle_mkd();
1322 else if (cmdval == const_RMD)
1323 handle_rmd();
1324 else if (cmdval == const_DELE)
1325 handle_dele();
1326 else if (cmdval == const_RNFR)
1327 handle_rnfr();
1328 else if (cmdval == const_RNTO)
1329 handle_rnto();
1330 else if (cmdval == const_APPE)
1331 handle_appe();
1332 else if (cmdval == const_STOU)
1333 handle_stou();
1334 else
1335 goto bad_cmd;
1336 }
1337#endif
1338#if 0
1339 else if (cmdval == const_STOR
1340 || cmdval == const_MKD
1341 || cmdval == const_RMD
1342 || cmdval == const_DELE
1343 || cmdval == const_RNFR
1344 || cmdval == const_RNTO
1345 || cmdval == const_APPE
1346 || cmdval == const_STOU
1347 ) {
1348 cmdio_write_raw(STR(FTP_NOPERM)" Permission denied\r\n");
1349 }
1350#endif
1351 else {
1352
1353
1354
1355
1356#if ENABLE_FEATURE_FTP_WRITE
1357 bad_cmd:
1358#endif
1359 cmdio_write_raw(STR(FTP_BADCMD)" Unknown command\r\n");
1360 }
1361 }
1362}
1363