1 /* vi: set sw=4 ts=4: */
3 * Simple FTP daemon, based on vsftpd 2.0.7 (written by Chris Evans)
5 * Author: Adam Tkac <vonsch@gmail.com>
7 * Licensed under GPLv2, see file LICENSE in this tarball for details.
9 * Only subset of FTP protocol is implemented but vast majority of clients
10 * should not have any problem. You have to run this daemon via inetd.
15 #include <netinet/tcp.h>
17 #define FTP_DATACONN 150
18 #define FTP_NOOPOK 200
19 #define FTP_TYPEOK 200
20 #define FTP_PORTOK 200
21 #define FTP_STRUOK 200
22 #define FTP_MODEOK 200
23 #define FTP_ALLOOK 202
24 #define FTP_STATOK 211
25 #define FTP_STATFILE_OK 213
27 #define FTP_SYSTOK 215
29 #define FTP_GOODBYE 221
30 #define FTP_TRANSFEROK 226
31 #define FTP_PASVOK 227
32 /*#define FTP_EPRTOK 228*/
33 #define FTP_EPSVOK 229
34 #define FTP_LOGINOK 230
36 #define FTP_RMDIROK 250
37 #define FTP_DELEOK 250
38 #define FTP_RENAMEOK 250
40 #define FTP_MKDIROK 257
41 #define FTP_GIVEPWORD 331
42 #define FTP_RESTOK 350
43 #define FTP_RNFROK 350
44 #define FTP_TIMEOUT 421
45 #define FTP_BADSENDCONN 425
46 #define FTP_BADSENDNET 426
47 #define FTP_BADSENDFILE 451
48 #define FTP_BADCMD 500
49 #define FTP_COMMANDNOTIMPL 502
50 #define FTP_NEEDUSER 503
51 #define FTP_NEEDRNFR 503
52 #define FTP_BADSTRU 504
53 #define FTP_BADMODE 504
54 #define FTP_LOGINERR 530
55 #define FTP_FILEFAIL 550
56 #define FTP_NOPERM 550
57 #define FTP_UPLOADFAIL 553
60 #define STR(s) STR1(s)
62 /* Convert a constant to 3-digit string, packed into uint32_t */
64 /* Shift for Nth decimal digit */
65 SHIFT2 = 0 * BB_LITTLE_ENDIAN + 24 * BB_BIG_ENDIAN,
66 SHIFT1 = 8 * BB_LITTLE_ENDIAN + 16 * BB_BIG_ENDIAN,
67 SHIFT0 = 16 * BB_LITTLE_ENDIAN + 8 * BB_BIG_ENDIAN,
68 /* And for 4th position (space) */
69 SHIFTsp = 24 * BB_LITTLE_ENDIAN + 0 * BB_BIG_ENDIAN,
71 #define STRNUM32(s) (uint32_t)(0 \
72 | (('0' + ((s) / 1 % 10)) << SHIFT0) \
73 | (('0' + ((s) / 10 % 10)) << SHIFT1) \
74 | (('0' + ((s) / 100 % 10)) << SHIFT2) \
76 #define STRNUM32sp(s) (uint32_t)(0 \
78 | (('0' + ((s) / 1 % 10)) << SHIFT0) \
79 | (('0' + ((s) / 10 % 10)) << SHIFT1) \
80 | (('0' + ((s) / 100 % 10)) << SHIFT2) \
83 #define MSG_OK "Operation successful\r\n"
84 #define MSG_ERR "Error\r\n"
95 len_and_sockaddr *local_addr;
96 len_and_sockaddr *port_addr;
99 #if ENABLE_FEATURE_FTP_WRITE
102 /* We need these aligned to uint32_t */
103 char msg_ok [(sizeof("NNN " MSG_OK ) + 3) & 0xfffc];
104 char msg_err[(sizeof("NNN " MSG_ERR) + 3) & 0xfffc];
106 #define G (*(struct globals*)&bb_common_bufsiz1)
107 #define INIT_G() do { \
108 /* Moved to main */ \
109 /*strcpy(G.msg_ok + 4, MSG_OK );*/ \
110 /*strcpy(G.msg_err + 4, MSG_ERR);*/ \
115 escape_text(const char *prepend, const char *str, unsigned escapee)
117 unsigned retlen, remainlen, chunklen;
121 append = (char)escapee;
124 remainlen = strlen(str);
125 retlen = strlen(prepend);
126 ret = xmalloc(retlen + remainlen * 2 + 1 + 1);
127 strcpy(ret, prepend);
130 found = strchrnul(str, escapee);
131 chunklen = found - str + 1;
133 /* Copy chunk up to and including escapee (or NUL) to ret */
134 memcpy(ret + retlen, str, chunklen);
137 if (*found == '\0') {
138 /* It wasn't escapee, it was NUL! */
139 ret[retlen - 1] = append; /* replace NUL */
140 ret[retlen] = '\0'; /* add NUL */
143 ret[retlen++] = escapee; /* duplicate escapee */
149 /* Returns strlen as a bonus */
151 replace_char(char *str, char from, char to)
163 verbose_log(const char *str)
165 bb_error_msg("%.*s", (int)strcspn(str, "\r\n"), str);
168 /* NB: status_str is char[4] packed into uint32_t */
170 cmdio_write(uint32_t status_str, const char *str)
175 /* FTP allegedly uses telnet protocol for command link.
176 * In telnet, 0xff is an escape char, and needs to be escaped: */
177 response = escape_text((char *) &status_str, str, (0xff << 8) + '\r');
179 /* ?! does FTP send embedded LFs as NULs? wow */
180 len = replace_char(response, '\n', '\0');
182 response[len++] = '\n'; /* tack on trailing '\n' */
183 xwrite(STDOUT_FILENO, response, len);
185 verbose_log(response);
190 cmdio_write_ok(unsigned status)
192 *(uint32_t *) G.msg_ok = status;
193 xwrite(STDOUT_FILENO, G.msg_ok, sizeof("NNN " MSG_OK) - 1);
195 verbose_log(G.msg_ok);
197 #define WRITE_OK(a) cmdio_write_ok(STRNUM32sp(a))
199 /* TODO: output strerr(errno) if errno != 0? */
201 cmdio_write_error(unsigned status)
203 *(uint32_t *) G.msg_err = status;
204 xwrite(STDOUT_FILENO, G.msg_err, sizeof("NNN " MSG_ERR) - 1);
206 verbose_log(G.msg_err);
208 #define WRITE_ERR(a) cmdio_write_error(STRNUM32sp(a))
211 cmdio_write_raw(const char *p_text)
213 xwrite_str(STDOUT_FILENO, p_text);
219 timeout_handler(int sig UNUSED_PARAM)
222 int sv_errno = errno;
224 if ((int)(monotonic_sec() - G.end_time) >= 0)
227 if (!G.local_file_fd)
230 pos = xlseek(G.local_file_fd, 0, SEEK_CUR);
231 if (pos == G.local_file_pos)
233 G.local_file_pos = pos;
240 cmdio_write_raw(STR(FTP_TIMEOUT)" Timeout\r\n");
241 /* TODO: do we need to abort (as opposed to usual shutdown) data transfer? */
245 /* Simple commands */
250 char *cwd, *response;
252 cwd = xrealloc_getcwd_or_warn(NULL);
256 /* We have to promote each " to "" */
257 response = escape_text(" \"", cwd, ('"' << 8) + '"');
259 cmdio_write(STRNUM32(FTP_PWDOK), response);
266 if (!G.ftp_arg || chdir(G.ftp_arg) != 0) {
267 WRITE_ERR(FTP_FILEFAIL);
276 G.ftp_arg = (char*)"..";
283 cmdio_write_raw(STR(FTP_STATOK)"-FTP server status:\r\n"
285 STR(FTP_STATOK)" Ok\r\n");
288 /* TODO: implement FEAT. Example:
289 # nc -vvv ftp.kernel.org 21
290 ftp.kernel.org (130.239.17.4:21) open
291 220 Welcome to ftp.kernel.org.
304 214-The following commands are recognized.
305 ABOR ACCT ALLO APPE CDUP CWD DELE EPRT EPSV FEAT HELP LIST MDTM MKD
306 MODE NLST NOOP OPTS PASS PASV PORT PWD QUIT REIN REST RETR RMD RNFR
307 RNTO SITE SIZE SMNT STAT STOR STOU STRU SYST TYPE USER XCUP XCWD XMKD
314 cmdio_write_raw(STR(FTP_HELP)"-Commands:\r\n"
315 " ALLO CDUP CWD EPSV HELP LIST\r\n"
316 " MODE NLST NOOP PASS PASV PORT PWD QUIT\r\n"
317 " REST RETR SIZE STAT STRU SYST TYPE USER\r\n"
318 #if ENABLE_FEATURE_FTP_WRITE
319 " APPE DELE MKD RMD RNFR RNTO STOR STOU\r\n"
321 STR(FTP_HELP)" Ok\r\n");
324 /* Download commands */
329 return (G.port_addr != NULL);
335 return (G.pasv_listen_fd > STDOUT_FILENO);
339 port_pasv_cleanup(void)
343 if (G.pasv_listen_fd > STDOUT_FILENO)
344 close(G.pasv_listen_fd);
345 G.pasv_listen_fd = -1;
348 /* On error, emits error code to the peer */
350 ftpdataio_get_pasv_fd(void)
354 remote_fd = accept(G.pasv_listen_fd, NULL, 0);
357 WRITE_ERR(FTP_BADSENDCONN);
361 setsockopt(remote_fd, SOL_SOCKET, SO_KEEPALIVE, &const_int_1, sizeof(const_int_1));
365 /* Clears port/pasv data.
366 * This means we dont waste resources, for example, keeping
367 * PASV listening socket open when it is no longer needed.
368 * On error, emits error code to the peer (or exits).
369 * On success, emits p_status_msg to the peer.
372 get_remote_transfer_fd(const char *p_status_msg)
377 /* On error, emits error code to the peer */
378 remote_fd = ftpdataio_get_pasv_fd();
381 remote_fd = xconnect_stream(G.port_addr);
388 cmdio_write(STRNUM32(FTP_DATACONN), p_status_msg);
392 /* If there were neither PASV nor PORT, emits error code to the peer */
394 port_or_pasv_was_seen(void)
396 if (!pasv_active() && !port_active()) {
397 cmdio_write_raw(STR(FTP_BADSENDCONN)" Use PORT or PASV first\r\n");
406 bind_for_passive_mode(void)
413 G.pasv_listen_fd = fd = xsocket(G.local_addr->u.sa.sa_family, SOCK_STREAM, 0);
414 setsockopt_reuseaddr(fd);
416 set_nport(G.local_addr, 0);
417 xbind(fd, &G.local_addr->u.sa, G.local_addr->len);
419 getsockname(fd, &G.local_addr->u.sa, &G.local_addr->len);
421 port = get_nport(&G.local_addr->u.sa);
431 char *addr, *response;
433 port = bind_for_passive_mode();
435 if (G.local_addr->u.sa.sa_family == AF_INET)
436 addr = xmalloc_sockaddr2dotted_noport(&G.local_addr->u.sa);
437 else /* seen this in the wild done by other ftp servers: */
438 addr = xstrdup("0.0.0.0");
439 replace_char(addr, '.', ',');
441 response = xasprintf(STR(FTP_PASVOK)" Entering Passive Mode (%s,%u,%u)\r\n",
442 addr, (int)(port >> 8), (int)(port & 255));
444 cmdio_write_raw(response);
455 port = bind_for_passive_mode();
456 response = xasprintf(STR(FTP_EPSVOK)" EPSV Ok (|||%u|)\r\n", port);
457 cmdio_write_raw(response);
461 /* libbb candidate */
463 len_and_sockaddr* get_peer_lsa(int fd)
465 len_and_sockaddr *lsa;
468 if (getpeername(fd, NULL, &len) != 0)
470 lsa = xzalloc(LSA_LEN_SIZE + len);
472 getpeername(fd, &lsa->u.sa, &lsa->len);
479 unsigned port, port_hi;
481 #ifdef WHY_BOTHER_WE_CAN_ASSUME_IP_MATCHES
482 socklen_t peer_ipv4_len;
483 struct sockaddr_in peer_ipv4;
484 struct in_addr port_ipv4_sin_addr;
491 /* PORT command format makes sense only over IPv4 */
493 #ifdef WHY_BOTHER_WE_CAN_ASSUME_IP_MATCHES
494 || G.local_addr->u.sa.sa_family != AF_INET
498 WRITE_ERR(FTP_BADCMD);
502 comma = strrchr(raw, ',');
506 port = bb_strtou(&comma[1], NULL, 10);
507 if (errno || port > 0xff)
510 comma = strrchr(raw, ',');
514 port_hi = bb_strtou(&comma[1], NULL, 10);
515 if (errno || port_hi > 0xff)
517 port |= port_hi << 8;
519 #ifdef WHY_BOTHER_WE_CAN_ASSUME_IP_MATCHES
520 replace_char(raw, ',', '.');
522 /* We are verifying that PORT's IP matches getpeername().
523 * Otherwise peer can make us open data connections
524 * to other hosts (security problem!)
525 * This code would be too simplistic:
526 * lsa = xdotted2sockaddr(raw, port);
527 * if (lsa == NULL) goto bail;
529 if (!inet_aton(raw, &port_ipv4_sin_addr))
531 peer_ipv4_len = sizeof(peer_ipv4);
532 if (getpeername(STDIN_FILENO, &peer_ipv4, &peer_ipv4_len) != 0)
534 if (memcmp(&port_ipv4_sin_addr, &peer_ipv4.sin_addr, sizeof(struct in_addr)) != 0)
537 G.port_addr = xdotted2sockaddr(raw, port);
539 G.port_addr = get_peer_lsa(STDIN_FILENO);
540 set_nport(G.port_addr, htons(port));
542 WRITE_OK(FTP_PORTOK);
548 /* When ftp_arg == NULL simply restart from beginning */
549 G.restart_pos = G.ftp_arg ? xatoi_u(G.ftp_arg) : 0;
550 WRITE_OK(FTP_RESTOK);
557 off_t bytes_transferred;
560 off_t offset = G.restart_pos;
565 if (!port_or_pasv_was_seen())
566 return; /* port_or_pasv_was_seen emitted error response */
568 /* O_NONBLOCK is useful if file happens to be a device node */
569 local_file_fd = G.ftp_arg ? open(G.ftp_arg, O_RDONLY | O_NONBLOCK) : -1;
570 if (local_file_fd < 0) {
571 WRITE_ERR(FTP_FILEFAIL);
575 if (fstat(local_file_fd, &statbuf) != 0 || !S_ISREG(statbuf.st_mode)) {
576 /* Note - pretend open failed */
577 WRITE_ERR(FTP_FILEFAIL);
580 G.local_file_fd = local_file_fd;
582 /* Now deactive O_NONBLOCK, otherwise we have a problem
583 * on DMAPI filesystems such as XFS DMAPI.
585 ndelay_off(local_file_fd);
587 /* Set the download offset (from REST) if any */
589 xlseek(local_file_fd, offset, SEEK_SET);
591 response = xasprintf(
592 " Opening BINARY mode data connection for %s (%"OFF_FMT"u bytes)",
593 G.ftp_arg, statbuf.st_size);
594 remote_fd = get_remote_transfer_fd(response);
599 bytes_transferred = bb_copyfd_eof(local_file_fd, remote_fd);
601 if (bytes_transferred < 0)
602 WRITE_ERR(FTP_BADSENDFILE);
604 WRITE_OK(FTP_TRANSFEROK);
607 close(local_file_fd);
614 popen_ls(const char *opt)
617 const char *argv[5] = { "ftpd", opt, NULL, G.ftp_arg, NULL };
618 struct fd_pair outfd;
621 cwd = xrealloc_getcwd_or_warn(NULL);
624 /*fflush(NULL); - so far we dont use stdio on output */
627 case -1: /* failure */
628 bb_perror_msg_and_die("vfork");
630 /* NB: close _first_, then move fds! */
632 xmove_fd(outfd.wr, STDOUT_FILENO);
634 /* xopen("/dev/null", O_RDONLY); - chroot may lack it! */
635 if (fchdir(G.proc_self_fd) == 0) {
636 close(G.proc_self_fd);
638 /* ftpd ls helper chdirs to argv[2],
639 * preventing peer from seeing /proc/self
641 execv("exe", (char**) argv);
658 handle_dir_common(int opts)
664 if (!(opts & USE_CTRL_CONN) && !port_or_pasv_was_seen())
665 return; /* port_or_pasv_was_seen emitted error response */
667 /* -n prevents user/groupname display,
668 * which can be problematic in chroot */
669 ls_fd = popen_ls((opts & LONG_LISTING) ? "-l" : "-1");
670 ls_fp = fdopen(ls_fd, "r");
671 if (!ls_fp) /* never happens. paranoia */
672 bb_perror_msg_and_die("fdopen");
674 if (opts & USE_CTRL_CONN) {
675 /* STAT <filename> */
676 cmdio_write_raw(STR(FTP_STATFILE_OK)"-Status follows:\r\n");
678 line = xmalloc_fgetline(ls_fp);
681 cmdio_write(0, line); /* hack: 0 results in no status at all */
684 WRITE_OK(FTP_STATFILE_OK);
686 /* LIST/NLST [<filename>] */
687 int remote_fd = get_remote_transfer_fd(" Here comes the directory listing");
688 if (remote_fd >= 0) {
690 line = xmalloc_fgetline(ls_fp);
693 /* I've seen clients complaining when they
694 * are fed with ls output with bare '\n'.
695 * Pity... that would be much simpler.
697 xwrite_str(remote_fd, line);
698 xwrite(remote_fd, "\r\n", 2);
703 WRITE_OK(FTP_TRANSFEROK);
705 fclose(ls_fp); /* closes ls_fd too */
710 handle_dir_common(LONG_LISTING);
715 handle_dir_common(0);
718 handle_stat_file(void)
720 handle_dir_common(LONG_LISTING + USE_CTRL_CONN);
727 char buf[sizeof(STR(FTP_STATFILE_OK)" %"OFF_FMT"u\r\n") + sizeof(off_t)*3];
730 || stat(G.ftp_arg, &statbuf) != 0
731 || !S_ISREG(statbuf.st_mode)
733 WRITE_ERR(FTP_FILEFAIL);
736 sprintf(buf, STR(FTP_STATFILE_OK)" %"OFF_FMT"u\r\n", statbuf.st_size);
737 cmdio_write_raw(buf);
740 /* Upload commands */
742 #if ENABLE_FEATURE_FTP_WRITE
746 if (!G.ftp_arg || mkdir(G.ftp_arg, 0777) != 0) {
747 WRITE_ERR(FTP_FILEFAIL);
750 WRITE_OK(FTP_MKDIROK);
756 if (!G.ftp_arg || rmdir(G.ftp_arg) != 0) {
757 WRITE_ERR(FTP_FILEFAIL);
760 WRITE_OK(FTP_RMDIROK);
766 if (!G.ftp_arg || unlink(G.ftp_arg) != 0) {
767 WRITE_ERR(FTP_FILEFAIL);
770 WRITE_OK(FTP_DELEOK);
776 free(G.rnfr_filename);
777 G.rnfr_filename = xstrdup(G.ftp_arg);
778 WRITE_OK(FTP_RNFROK);
786 /* If we didn't get a RNFR, throw a wobbly */
787 if (G.rnfr_filename == NULL || G.ftp_arg == NULL) {
788 cmdio_write_raw(STR(FTP_NEEDRNFR)" RNFR required first\r\n");
792 retval = rename(G.rnfr_filename, G.ftp_arg);
793 free(G.rnfr_filename);
794 G.rnfr_filename = NULL;
797 WRITE_ERR(FTP_FILEFAIL);
800 WRITE_OK(FTP_RENAMEOK);
804 handle_upload_common(int is_append, int is_unique)
808 off_t bytes_transferred;
813 offset = G.restart_pos;
816 if (!port_or_pasv_was_seen())
817 return; /* port_or_pasv_was_seen emitted error response */
822 tempname = xstrdup(" FILE: uniq.XXXXXX");
823 local_file_fd = mkstemp(tempname + 7);
824 } else if (G.ftp_arg) {
825 int flags = O_WRONLY | O_CREAT | O_TRUNC;
827 flags = O_WRONLY | O_CREAT | O_APPEND;
829 flags = O_WRONLY | O_CREAT;
830 local_file_fd = open(G.ftp_arg, flags, 0666);
833 if (local_file_fd < 0
834 || fstat(local_file_fd, &statbuf) != 0
835 || !S_ISREG(statbuf.st_mode)
837 WRITE_ERR(FTP_UPLOADFAIL);
838 if (local_file_fd >= 0)
839 goto close_local_and_bail;
842 G.local_file_fd = local_file_fd;
845 xlseek(local_file_fd, offset, SEEK_SET);
847 remote_fd = get_remote_transfer_fd(tempname ? tempname : " Ok to send data");
851 goto close_local_and_bail;
853 bytes_transferred = bb_copyfd_eof(remote_fd, local_file_fd);
855 if (bytes_transferred < 0)
856 WRITE_ERR(FTP_BADSENDFILE);
858 WRITE_OK(FTP_TRANSFEROK);
860 close_local_and_bail:
861 close(local_file_fd);
868 handle_upload_common(0, 0);
875 handle_upload_common(1, 0);
882 handle_upload_common(0, 1);
884 #endif /* ENABLE_FEATURE_FTP_WRITE */
887 cmdio_get_cmd_and_arg(void)
896 len = 8 * 1024; /* Paranoia. Peer may send 1 gigabyte long cmd... */
897 G.ftp_cmd = cmd = xmalloc_reads(STDIN_FILENO, NULL, &len);
901 /* Trailing '\n' is already stripped, strip '\r' */
902 len = strlen(cmd) - 1;
903 if ((ssize_t)len >= 0 && cmd[len] == '\r')
907 bb_error_msg("%s", cmd);
909 G.ftp_arg = strchr(cmd, ' ');
910 if (G.ftp_arg != NULL)
913 /* Uppercase and pack into uint32_t first word of the command */
916 cmdval = (cmdval << 8) + ((unsigned char)*cmd++ & (unsigned char)~0x20);
921 #define mk_const4(a,b,c,d) (((a * 0x100 + b) * 0x100 + c) * 0x100 + d)
922 #define mk_const3(a,b,c) ((a * 0x100 + b) * 0x100 + c)
924 const_ALLO = mk_const4('A', 'L', 'L', 'O'),
925 const_APPE = mk_const4('A', 'P', 'P', 'E'),
926 const_CDUP = mk_const4('C', 'D', 'U', 'P'),
927 const_CWD = mk_const3('C', 'W', 'D'),
928 const_DELE = mk_const4('D', 'E', 'L', 'E'),
929 const_EPSV = mk_const4('E', 'P', 'S', 'V'),
930 const_HELP = mk_const4('H', 'E', 'L', 'P'),
931 const_LIST = mk_const4('L', 'I', 'S', 'T'),
932 const_MKD = mk_const3('M', 'K', 'D'),
933 const_MODE = mk_const4('M', 'O', 'D', 'E'),
934 const_NLST = mk_const4('N', 'L', 'S', 'T'),
935 const_NOOP = mk_const4('N', 'O', 'O', 'P'),
936 const_PASS = mk_const4('P', 'A', 'S', 'S'),
937 const_PASV = mk_const4('P', 'A', 'S', 'V'),
938 const_PORT = mk_const4('P', 'O', 'R', 'T'),
939 const_PWD = mk_const3('P', 'W', 'D'),
940 const_QUIT = mk_const4('Q', 'U', 'I', 'T'),
941 const_REST = mk_const4('R', 'E', 'S', 'T'),
942 const_RETR = mk_const4('R', 'E', 'T', 'R'),
943 const_RMD = mk_const3('R', 'M', 'D'),
944 const_RNFR = mk_const4('R', 'N', 'F', 'R'),
945 const_RNTO = mk_const4('R', 'N', 'T', 'O'),
946 const_SIZE = mk_const4('S', 'I', 'Z', 'E'),
947 const_STAT = mk_const4('S', 'T', 'A', 'T'),
948 const_STOR = mk_const4('S', 'T', 'O', 'R'),
949 const_STOU = mk_const4('S', 'T', 'O', 'U'),
950 const_STRU = mk_const4('S', 'T', 'R', 'U'),
951 const_SYST = mk_const4('S', 'Y', 'S', 'T'),
952 const_TYPE = mk_const4('T', 'Y', 'P', 'E'),
953 const_USER = mk_const4('U', 'S', 'E', 'R'),
962 int ftpd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
963 int ftpd_main(int argc, char **argv)
965 unsigned abs_timeout;
970 abs_timeout = 1 * 60 * 60;
972 opt_complementary = "t+:T+:vv";
973 opts = getopt32(argv, "l1vS" USE_FEATURE_FTP_WRITE("w") "t:T:", &G.timeout, &abs_timeout, &G.verbose);
974 if (opts & (OPT_l|OPT_1)) {
975 /* Our secret backdoor to ls */
976 memset(&G, 0, sizeof(G));
977 /* TODO: pass -n too? */
978 /* --group-directories-first would be nice, but ls don't do that yet */
980 argv[2] = (char*)"--";
981 return ls_main(argc, argv);
983 if (abs_timeout | G.timeout) {
984 if (abs_timeout == 0)
985 abs_timeout = INT_MAX;
986 G.end_time = monotonic_sec() + abs_timeout;
987 if (G.timeout > abs_timeout)
988 G.timeout = abs_timeout;
990 strcpy(G.msg_ok + 4, MSG_OK );
991 strcpy(G.msg_err + 4, MSG_ERR);
993 G.local_addr = get_sock_lsa(STDIN_FILENO);
995 /* This is confusing:
996 * bb_error_msg_and_die("stdin is not a socket");
999 /* Help text says that ftpd must be used as inetd service,
1000 * which is by far the most usual cause of get_sock_lsa
1004 if (!(opts & OPT_v))
1005 logmode = LOGMODE_NONE;
1007 /* LOG_NDELAY is needed since we may chroot later */
1008 openlog(applet_name, LOG_PID | LOG_NDELAY, LOG_DAEMON);
1009 logmode |= LOGMODE_SYSLOG;
1012 applet_name = xasprintf("%s[%u]", applet_name, (int)getpid());
1014 G.proc_self_fd = xopen("/proc/self", O_RDONLY | O_DIRECTORY);
1017 xchdir(argv[optind]);
1021 //umask(077); - admin can set umask before starting us
1023 /* Signals. We'll always take -EPIPE rather than a rude signal, thanks */
1024 signal(SIGPIPE, SIG_IGN);
1026 /* Set up options on the command socket (do we need these all? why?) */
1027 setsockopt(STDIN_FILENO, IPPROTO_TCP, TCP_NODELAY, &const_int_1, sizeof(const_int_1));
1028 setsockopt(STDIN_FILENO, SOL_SOCKET, SO_KEEPALIVE, &const_int_1, sizeof(const_int_1));
1029 setsockopt(STDIN_FILENO, SOL_SOCKET, SO_OOBINLINE, &const_int_1, sizeof(const_int_1));
1031 WRITE_OK(FTP_GREET);
1032 signal(SIGALRM, timeout_handler);
1034 #ifdef IF_WE_WANT_TO_REQUIRE_LOGIN
1036 smallint user_was_specified = 0;
1038 uint32_t cmdval = cmdio_get_cmd_and_arg();
1040 if (cmdval == const_USER) {
1041 if (G.ftp_arg == NULL || strcasecmp(G.ftp_arg, "anonymous") != 0)
1042 cmdio_write_raw(STR(FTP_LOGINERR)" Server is anonymous only\r\n");
1044 user_was_specified = 1;
1045 cmdio_write_raw(STR(FTP_GIVEPWORD)" Please specify the password\r\n");
1047 } else if (cmdval == const_PASS) {
1048 if (user_was_specified)
1050 cmdio_write_raw(STR(FTP_NEEDUSER)" Login with USER\r\n");
1051 } else if (cmdval == const_QUIT) {
1052 WRITE_OK(FTP_GOODBYE);
1055 cmdio_write_raw(STR(FTP_LOGINERR)" Login with USER and PASS\r\n");
1059 WRITE_OK(FTP_LOGINOK);
1062 /* RFC-959 Section 5.1
1063 * The following commands and options MUST be supported by every
1064 * server-FTP and user-FTP, except in cases where the underlying
1065 * file system or operating system does not allow or support
1066 * a particular command.
1067 * Type: ASCII Non-print, IMAGE, LOCAL 8
1069 * Structure: File, Record*
1070 * (Record structure is REQUIRED only for hosts whose file
1071 * systems support record structure).
1073 * USER, PASS, ACCT, [bbox: ACCT not supported]
1078 * CWD, CDUP, RMD, MKD, PWD,
1084 * "The argument field is a Telnet string identifying the user's account.
1085 * The command is not necessarily related to the USER command, as some
1086 * sites may require an account for login and others only for specific
1087 * access, such as storing files. In the latter case the command may
1088 * arrive at any time.
1089 * There are reply codes to differentiate these cases for the automation:
1090 * when account information is required for login, the response to
1091 * a successful PASSword command is reply code 332. On the other hand,
1092 * if account information is NOT required for login, the reply to
1093 * a successful PASSword command is 230; and if the account information
1094 * is needed for a command issued later in the dialogue, the server
1095 * should return a 332 or 532 reply depending on whether it stores
1096 * (pending receipt of the ACCounT command) or discards the command,
1101 uint32_t cmdval = cmdio_get_cmd_and_arg();
1103 if (cmdval == const_QUIT) {
1104 WRITE_OK(FTP_GOODBYE);
1107 else if (cmdval == const_USER)
1108 WRITE_OK(FTP_GIVEPWORD);
1109 else if (cmdval == const_PASS)
1110 WRITE_OK(FTP_LOGINOK);
1111 else if (cmdval == const_NOOP)
1112 WRITE_OK(FTP_NOOPOK);
1113 else if (cmdval == const_TYPE)
1114 WRITE_OK(FTP_TYPEOK);
1115 else if (cmdval == const_STRU)
1116 WRITE_OK(FTP_STRUOK);
1117 else if (cmdval == const_MODE)
1118 WRITE_OK(FTP_MODEOK);
1119 else if (cmdval == const_ALLO)
1120 WRITE_OK(FTP_ALLOOK);
1121 else if (cmdval == const_SYST)
1122 cmdio_write_raw(STR(FTP_SYSTOK)" UNIX Type: L8\r\n");
1123 else if (cmdval == const_PWD)
1125 else if (cmdval == const_CWD)
1127 else if (cmdval == const_CDUP) /* cd .. */
1129 else if (cmdval == const_HELP)
1131 else if (cmdval == const_LIST) /* ls -l */
1133 else if (cmdval == const_NLST) /* "name list", bare ls */
1135 else if (cmdval == const_SIZE)
1137 else if (cmdval == const_STAT) {
1138 if (G.ftp_arg == NULL)
1143 else if (cmdval == const_PASV)
1145 else if (cmdval == const_EPSV)
1147 else if (cmdval == const_RETR)
1149 else if (cmdval == const_PORT)
1151 else if (cmdval == const_REST)
1153 #if ENABLE_FEATURE_FTP_WRITE
1154 else if (opts & OPT_w) {
1155 if (cmdval == const_STOR)
1157 else if (cmdval == const_MKD)
1159 else if (cmdval == const_RMD)
1161 else if (cmdval == const_DELE)
1163 else if (cmdval == const_RNFR) /* "rename from" */
1165 else if (cmdval == const_RNTO) /* "rename to" */
1167 else if (cmdval == const_APPE)
1169 else if (cmdval == const_STOU) /* "store unique" */
1174 else if (cmdval == const_STOR
1175 || cmdval == const_MKD
1176 || cmdval == const_RMD
1177 || cmdval == const_DELE
1178 || cmdval == const_RNFR
1179 || cmdval == const_RNTO
1180 || cmdval == const_APPE
1181 || cmdval == const_STOU
1183 cmdio_write_raw(STR(FTP_NOPERM)" Permission denied\r\n");
1187 /* Which unsupported commands were seen in the wild?
1188 * (doesn't necessarily mean "we must support them")
1189 * lftp 3.6.3: FEAT - is it useful?
1190 * MDTM - works fine without it anyway
1192 cmdio_write_raw(STR(FTP_BADCMD)" Unknown command\r\n");