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 /* Examples of HELP and FEAT:
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
312 handle_feat(unsigned status)
314 cmdio_write(status, "-Features:");
315 cmdio_write_raw(" EPSV\r\n"
319 cmdio_write(status, " Ok");
322 /* Download commands */
327 return (G.port_addr != NULL);
333 return (G.pasv_listen_fd > STDOUT_FILENO);
337 port_pasv_cleanup(void)
341 if (G.pasv_listen_fd > STDOUT_FILENO)
342 close(G.pasv_listen_fd);
343 G.pasv_listen_fd = -1;
346 /* On error, emits error code to the peer */
348 ftpdataio_get_pasv_fd(void)
352 remote_fd = accept(G.pasv_listen_fd, NULL, 0);
355 WRITE_ERR(FTP_BADSENDCONN);
359 setsockopt(remote_fd, SOL_SOCKET, SO_KEEPALIVE, &const_int_1, sizeof(const_int_1));
363 /* Clears port/pasv data.
364 * This means we dont waste resources, for example, keeping
365 * PASV listening socket open when it is no longer needed.
366 * On error, emits error code to the peer (or exits).
367 * On success, emits p_status_msg to the peer.
370 get_remote_transfer_fd(const char *p_status_msg)
375 /* On error, emits error code to the peer */
376 remote_fd = ftpdataio_get_pasv_fd();
379 remote_fd = xconnect_stream(G.port_addr);
386 cmdio_write(STRNUM32(FTP_DATACONN), p_status_msg);
390 /* If there were neither PASV nor PORT, emits error code to the peer */
392 port_or_pasv_was_seen(void)
394 if (!pasv_active() && !port_active()) {
395 cmdio_write_raw(STR(FTP_BADSENDCONN)" Use PORT or PASV first\r\n");
404 bind_for_passive_mode(void)
411 G.pasv_listen_fd = fd = xsocket(G.local_addr->u.sa.sa_family, SOCK_STREAM, 0);
412 setsockopt_reuseaddr(fd);
414 set_nport(G.local_addr, 0);
415 xbind(fd, &G.local_addr->u.sa, G.local_addr->len);
417 getsockname(fd, &G.local_addr->u.sa, &G.local_addr->len);
419 port = get_nport(&G.local_addr->u.sa);
429 char *addr, *response;
431 port = bind_for_passive_mode();
433 if (G.local_addr->u.sa.sa_family == AF_INET)
434 addr = xmalloc_sockaddr2dotted_noport(&G.local_addr->u.sa);
435 else /* seen this in the wild done by other ftp servers: */
436 addr = xstrdup("0.0.0.0");
437 replace_char(addr, '.', ',');
439 response = xasprintf(STR(FTP_PASVOK)" Entering Passive Mode (%s,%u,%u)\r\n",
440 addr, (int)(port >> 8), (int)(port & 255));
442 cmdio_write_raw(response);
453 port = bind_for_passive_mode();
454 response = xasprintf(STR(FTP_EPSVOK)" EPSV Ok (|||%u|)\r\n", port);
455 cmdio_write_raw(response);
459 /* libbb candidate */
461 len_and_sockaddr* get_peer_lsa(int fd)
463 len_and_sockaddr *lsa;
466 if (getpeername(fd, NULL, &len) != 0)
468 lsa = xzalloc(LSA_LEN_SIZE + len);
470 getpeername(fd, &lsa->u.sa, &lsa->len);
477 unsigned port, port_hi;
479 #ifdef WHY_BOTHER_WE_CAN_ASSUME_IP_MATCHES
480 socklen_t peer_ipv4_len;
481 struct sockaddr_in peer_ipv4;
482 struct in_addr port_ipv4_sin_addr;
489 /* PORT command format makes sense only over IPv4 */
491 #ifdef WHY_BOTHER_WE_CAN_ASSUME_IP_MATCHES
492 || G.local_addr->u.sa.sa_family != AF_INET
496 WRITE_ERR(FTP_BADCMD);
500 comma = strrchr(raw, ',');
504 port = bb_strtou(&comma[1], NULL, 10);
505 if (errno || port > 0xff)
508 comma = strrchr(raw, ',');
512 port_hi = bb_strtou(&comma[1], NULL, 10);
513 if (errno || port_hi > 0xff)
515 port |= port_hi << 8;
517 #ifdef WHY_BOTHER_WE_CAN_ASSUME_IP_MATCHES
518 replace_char(raw, ',', '.');
520 /* We are verifying that PORT's IP matches getpeername().
521 * Otherwise peer can make us open data connections
522 * to other hosts (security problem!)
523 * This code would be too simplistic:
524 * lsa = xdotted2sockaddr(raw, port);
525 * if (lsa == NULL) goto bail;
527 if (!inet_aton(raw, &port_ipv4_sin_addr))
529 peer_ipv4_len = sizeof(peer_ipv4);
530 if (getpeername(STDIN_FILENO, &peer_ipv4, &peer_ipv4_len) != 0)
532 if (memcmp(&port_ipv4_sin_addr, &peer_ipv4.sin_addr, sizeof(struct in_addr)) != 0)
535 G.port_addr = xdotted2sockaddr(raw, port);
537 G.port_addr = get_peer_lsa(STDIN_FILENO);
538 set_nport(G.port_addr, htons(port));
540 WRITE_OK(FTP_PORTOK);
546 /* When ftp_arg == NULL simply restart from beginning */
547 G.restart_pos = G.ftp_arg ? xatoi_u(G.ftp_arg) : 0;
548 WRITE_OK(FTP_RESTOK);
555 off_t bytes_transferred;
558 off_t offset = G.restart_pos;
563 if (!port_or_pasv_was_seen())
564 return; /* port_or_pasv_was_seen emitted error response */
566 /* O_NONBLOCK is useful if file happens to be a device node */
567 local_file_fd = G.ftp_arg ? open(G.ftp_arg, O_RDONLY | O_NONBLOCK) : -1;
568 if (local_file_fd < 0) {
569 WRITE_ERR(FTP_FILEFAIL);
573 if (fstat(local_file_fd, &statbuf) != 0 || !S_ISREG(statbuf.st_mode)) {
574 /* Note - pretend open failed */
575 WRITE_ERR(FTP_FILEFAIL);
578 G.local_file_fd = local_file_fd;
580 /* Now deactive O_NONBLOCK, otherwise we have a problem
581 * on DMAPI filesystems such as XFS DMAPI.
583 ndelay_off(local_file_fd);
585 /* Set the download offset (from REST) if any */
587 xlseek(local_file_fd, offset, SEEK_SET);
589 response = xasprintf(
590 " Opening BINARY mode data connection for %s (%"OFF_FMT"u bytes)",
591 G.ftp_arg, statbuf.st_size);
592 remote_fd = get_remote_transfer_fd(response);
597 bytes_transferred = bb_copyfd_eof(local_file_fd, remote_fd);
599 if (bytes_transferred < 0)
600 WRITE_ERR(FTP_BADSENDFILE);
602 WRITE_OK(FTP_TRANSFEROK);
605 close(local_file_fd);
612 popen_ls(const char *opt)
615 const char *argv[5] = { "ftpd", opt, NULL, G.ftp_arg, NULL };
616 struct fd_pair outfd;
619 cwd = xrealloc_getcwd_or_warn(NULL);
622 /*fflush(NULL); - so far we dont use stdio on output */
625 case -1: /* failure */
626 bb_perror_msg_and_die("vfork");
628 /* NB: close _first_, then move fds! */
630 xmove_fd(outfd.wr, STDOUT_FILENO);
632 /* xopen("/dev/null", O_RDONLY); - chroot may lack it! */
633 if (fchdir(G.proc_self_fd) == 0) {
634 close(G.proc_self_fd);
636 /* ftpd ls helper chdirs to argv[2],
637 * preventing peer from seeing /proc/self
639 execv("exe", (char**) argv);
656 handle_dir_common(int opts)
662 if (!(opts & USE_CTRL_CONN) && !port_or_pasv_was_seen())
663 return; /* port_or_pasv_was_seen emitted error response */
665 /* -n prevents user/groupname display,
666 * which can be problematic in chroot */
667 ls_fd = popen_ls((opts & LONG_LISTING) ? "-l" : "-1");
668 ls_fp = fdopen(ls_fd, "r");
669 if (!ls_fp) /* never happens. paranoia */
670 bb_perror_msg_and_die("fdopen");
672 if (opts & USE_CTRL_CONN) {
673 /* STAT <filename> */
674 cmdio_write_raw(STR(FTP_STATFILE_OK)"-Status follows:\r\n");
676 line = xmalloc_fgetline(ls_fp);
679 cmdio_write(0, line); /* hack: 0 results in no status at all */
682 WRITE_OK(FTP_STATFILE_OK);
684 /* LIST/NLST [<filename>] */
685 int remote_fd = get_remote_transfer_fd(" Here comes the directory listing");
686 if (remote_fd >= 0) {
688 line = xmalloc_fgetline(ls_fp);
691 /* I've seen clients complaining when they
692 * are fed with ls output with bare '\n'.
693 * Pity... that would be much simpler.
695 /* TODO: need to s/LF/NUL/g here */
696 xwrite_str(remote_fd, line);
697 xwrite(remote_fd, "\r\n", 2);
702 WRITE_OK(FTP_TRANSFEROK);
704 fclose(ls_fp); /* closes ls_fd too */
709 handle_dir_common(LONG_LISTING);
714 /* NLST returns list of names, "\r\n" terminated without regard
715 * to the current binary flag. Names may start with "/",
716 * then they represent full names (we don't produce such names),
717 * otherwise names are relative to current directory.
718 * Embedded "\n" are replaced by NULs. This is safe since names
719 * can never contain NUL.
721 handle_dir_common(0);
724 handle_stat_file(void)
726 handle_dir_common(LONG_LISTING + USE_CTRL_CONN);
733 char buf[sizeof(STR(FTP_STATFILE_OK)" %"OFF_FMT"u\r\n") + sizeof(off_t)*3];
736 || stat(G.ftp_arg, &statbuf) != 0
737 || !S_ISREG(statbuf.st_mode)
739 WRITE_ERR(FTP_FILEFAIL);
742 sprintf(buf, STR(FTP_STATFILE_OK)" %"OFF_FMT"u\r\n", statbuf.st_size);
743 cmdio_write_raw(buf);
746 /* Upload commands */
748 #if ENABLE_FEATURE_FTP_WRITE
752 if (!G.ftp_arg || mkdir(G.ftp_arg, 0777) != 0) {
753 WRITE_ERR(FTP_FILEFAIL);
756 WRITE_OK(FTP_MKDIROK);
762 if (!G.ftp_arg || rmdir(G.ftp_arg) != 0) {
763 WRITE_ERR(FTP_FILEFAIL);
766 WRITE_OK(FTP_RMDIROK);
772 if (!G.ftp_arg || unlink(G.ftp_arg) != 0) {
773 WRITE_ERR(FTP_FILEFAIL);
776 WRITE_OK(FTP_DELEOK);
782 free(G.rnfr_filename);
783 G.rnfr_filename = xstrdup(G.ftp_arg);
784 WRITE_OK(FTP_RNFROK);
792 /* If we didn't get a RNFR, throw a wobbly */
793 if (G.rnfr_filename == NULL || G.ftp_arg == NULL) {
794 cmdio_write_raw(STR(FTP_NEEDRNFR)" RNFR required first\r\n");
798 retval = rename(G.rnfr_filename, G.ftp_arg);
799 free(G.rnfr_filename);
800 G.rnfr_filename = NULL;
803 WRITE_ERR(FTP_FILEFAIL);
806 WRITE_OK(FTP_RENAMEOK);
810 handle_upload_common(int is_append, int is_unique)
814 off_t bytes_transferred;
819 offset = G.restart_pos;
822 if (!port_or_pasv_was_seen())
823 return; /* port_or_pasv_was_seen emitted error response */
828 tempname = xstrdup(" FILE: uniq.XXXXXX");
829 local_file_fd = mkstemp(tempname + 7);
830 } else if (G.ftp_arg) {
831 int flags = O_WRONLY | O_CREAT | O_TRUNC;
833 flags = O_WRONLY | O_CREAT | O_APPEND;
835 flags = O_WRONLY | O_CREAT;
836 local_file_fd = open(G.ftp_arg, flags, 0666);
839 if (local_file_fd < 0
840 || fstat(local_file_fd, &statbuf) != 0
841 || !S_ISREG(statbuf.st_mode)
843 WRITE_ERR(FTP_UPLOADFAIL);
844 if (local_file_fd >= 0)
845 goto close_local_and_bail;
848 G.local_file_fd = local_file_fd;
851 xlseek(local_file_fd, offset, SEEK_SET);
853 remote_fd = get_remote_transfer_fd(tempname ? tempname : " Ok to send data");
857 goto close_local_and_bail;
859 bytes_transferred = bb_copyfd_eof(remote_fd, local_file_fd);
861 if (bytes_transferred < 0)
862 WRITE_ERR(FTP_BADSENDFILE);
864 WRITE_OK(FTP_TRANSFEROK);
866 close_local_and_bail:
867 close(local_file_fd);
874 handle_upload_common(0, 0);
881 handle_upload_common(1, 0);
888 handle_upload_common(0, 1);
890 #endif /* ENABLE_FEATURE_FTP_WRITE */
893 cmdio_get_cmd_and_arg(void)
902 len = 8 * 1024; /* Paranoia. Peer may send 1 gigabyte long cmd... */
903 G.ftp_cmd = cmd = xmalloc_reads(STDIN_FILENO, NULL, &len);
907 /* Trailing '\n' is already stripped, strip '\r' */
908 len = strlen(cmd) - 1;
909 if ((ssize_t)len >= 0 && cmd[len] == '\r')
915 G.ftp_arg = strchr(cmd, ' ');
916 if (G.ftp_arg != NULL)
919 /* Uppercase and pack into uint32_t first word of the command */
922 cmdval = (cmdval << 8) + ((unsigned char)*cmd++ & (unsigned char)~0x20);
927 #define mk_const4(a,b,c,d) (((a * 0x100 + b) * 0x100 + c) * 0x100 + d)
928 #define mk_const3(a,b,c) ((a * 0x100 + b) * 0x100 + c)
930 const_ALLO = mk_const4('A', 'L', 'L', 'O'),
931 const_APPE = mk_const4('A', 'P', 'P', 'E'),
932 const_CDUP = mk_const4('C', 'D', 'U', 'P'),
933 const_CWD = mk_const3('C', 'W', 'D'),
934 const_DELE = mk_const4('D', 'E', 'L', 'E'),
935 const_EPSV = mk_const4('E', 'P', 'S', 'V'),
936 const_FEAT = mk_const4('F', 'E', 'A', 'T'),
937 const_HELP = mk_const4('H', 'E', 'L', 'P'),
938 const_LIST = mk_const4('L', 'I', 'S', 'T'),
939 const_MKD = mk_const3('M', 'K', 'D'),
940 const_MODE = mk_const4('M', 'O', 'D', 'E'),
941 const_NLST = mk_const4('N', 'L', 'S', 'T'),
942 const_NOOP = mk_const4('N', 'O', 'O', 'P'),
943 const_PASS = mk_const4('P', 'A', 'S', 'S'),
944 const_PASV = mk_const4('P', 'A', 'S', 'V'),
945 const_PORT = mk_const4('P', 'O', 'R', 'T'),
946 const_PWD = mk_const3('P', 'W', 'D'),
947 const_QUIT = mk_const4('Q', 'U', 'I', 'T'),
948 const_REST = mk_const4('R', 'E', 'S', 'T'),
949 const_RETR = mk_const4('R', 'E', 'T', 'R'),
950 const_RMD = mk_const3('R', 'M', 'D'),
951 const_RNFR = mk_const4('R', 'N', 'F', 'R'),
952 const_RNTO = mk_const4('R', 'N', 'T', 'O'),
953 const_SIZE = mk_const4('S', 'I', 'Z', 'E'),
954 const_STAT = mk_const4('S', 'T', 'A', 'T'),
955 const_STOR = mk_const4('S', 'T', 'O', 'R'),
956 const_STOU = mk_const4('S', 'T', 'O', 'U'),
957 const_STRU = mk_const4('S', 'T', 'R', 'U'),
958 const_SYST = mk_const4('S', 'Y', 'S', 'T'),
959 const_TYPE = mk_const4('T', 'Y', 'P', 'E'),
960 const_USER = mk_const4('U', 'S', 'E', 'R'),
969 int ftpd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
970 int ftpd_main(int argc, char **argv)
972 unsigned abs_timeout;
977 abs_timeout = 1 * 60 * 60;
979 opt_complementary = "t+:T+:vv";
980 opts = getopt32(argv, "l1vS" USE_FEATURE_FTP_WRITE("w") "t:T:", &G.timeout, &abs_timeout, &G.verbose);
981 if (opts & (OPT_l|OPT_1)) {
982 /* Our secret backdoor to ls */
983 memset(&G, 0, sizeof(G));
984 /* TODO: pass -n too? */
985 /* --group-directories-first would be nice, but ls don't do that yet */
987 argv[2] = (char*)"--";
988 return ls_main(argc, argv);
990 if (abs_timeout | G.timeout) {
991 if (abs_timeout == 0)
992 abs_timeout = INT_MAX;
993 G.end_time = monotonic_sec() + abs_timeout;
994 if (G.timeout > abs_timeout)
995 G.timeout = abs_timeout;
997 strcpy(G.msg_ok + 4, MSG_OK );
998 strcpy(G.msg_err + 4, MSG_ERR);
1000 G.local_addr = get_sock_lsa(STDIN_FILENO);
1001 if (!G.local_addr) {
1002 /* This is confusing:
1003 * bb_error_msg_and_die("stdin is not a socket");
1006 /* Help text says that ftpd must be used as inetd service,
1007 * which is by far the most usual cause of get_sock_lsa
1011 if (!(opts & OPT_v))
1012 logmode = LOGMODE_NONE;
1014 /* LOG_NDELAY is needed since we may chroot later */
1015 openlog(applet_name, LOG_PID | LOG_NDELAY, LOG_DAEMON);
1016 logmode |= LOGMODE_SYSLOG;
1019 applet_name = xasprintf("%s[%u]", applet_name, (int)getpid());
1021 G.proc_self_fd = xopen("/proc/self", O_RDONLY | O_DIRECTORY);
1024 xchdir(argv[optind]);
1028 //umask(077); - admin can set umask before starting us
1030 /* Signals. We'll always take -EPIPE rather than a rude signal, thanks */
1031 signal(SIGPIPE, SIG_IGN);
1033 /* Set up options on the command socket (do we need these all? why?) */
1034 setsockopt(STDIN_FILENO, IPPROTO_TCP, TCP_NODELAY, &const_int_1, sizeof(const_int_1));
1035 setsockopt(STDIN_FILENO, SOL_SOCKET, SO_KEEPALIVE, &const_int_1, sizeof(const_int_1));
1036 setsockopt(STDIN_FILENO, SOL_SOCKET, SO_OOBINLINE, &const_int_1, sizeof(const_int_1));
1038 WRITE_OK(FTP_GREET);
1039 signal(SIGALRM, timeout_handler);
1041 #ifdef IF_WE_WANT_TO_REQUIRE_LOGIN
1043 smallint user_was_specified = 0;
1045 uint32_t cmdval = cmdio_get_cmd_and_arg();
1047 if (cmdval == const_USER) {
1048 if (G.ftp_arg == NULL || strcasecmp(G.ftp_arg, "anonymous") != 0)
1049 cmdio_write_raw(STR(FTP_LOGINERR)" Server is anonymous only\r\n");
1051 user_was_specified = 1;
1052 cmdio_write_raw(STR(FTP_GIVEPWORD)" Please specify the password\r\n");
1054 } else if (cmdval == const_PASS) {
1055 if (user_was_specified)
1057 cmdio_write_raw(STR(FTP_NEEDUSER)" Login with USER\r\n");
1058 } else if (cmdval == const_QUIT) {
1059 WRITE_OK(FTP_GOODBYE);
1062 cmdio_write_raw(STR(FTP_LOGINERR)" Login with USER and PASS\r\n");
1066 WRITE_OK(FTP_LOGINOK);
1069 /* RFC-959 Section 5.1
1070 * The following commands and options MUST be supported by every
1071 * server-FTP and user-FTP, except in cases where the underlying
1072 * file system or operating system does not allow or support
1073 * a particular command.
1074 * Type: ASCII Non-print, IMAGE, LOCAL 8
1076 * Structure: File, Record*
1077 * (Record structure is REQUIRED only for hosts whose file
1078 * systems support record structure).
1080 * USER, PASS, ACCT, [bbox: ACCT not supported]
1085 * CWD, CDUP, RMD, MKD, PWD,
1091 * "The argument field is a Telnet string identifying the user's account.
1092 * The command is not necessarily related to the USER command, as some
1093 * sites may require an account for login and others only for specific
1094 * access, such as storing files. In the latter case the command may
1095 * arrive at any time.
1096 * There are reply codes to differentiate these cases for the automation:
1097 * when account information is required for login, the response to
1098 * a successful PASSword command is reply code 332. On the other hand,
1099 * if account information is NOT required for login, the reply to
1100 * a successful PASSword command is 230; and if the account information
1101 * is needed for a command issued later in the dialogue, the server
1102 * should return a 332 or 532 reply depending on whether it stores
1103 * (pending receipt of the ACCounT command) or discards the command,
1108 uint32_t cmdval = cmdio_get_cmd_and_arg();
1110 if (cmdval == const_QUIT) {
1111 WRITE_OK(FTP_GOODBYE);
1114 else if (cmdval == const_USER)
1115 WRITE_OK(FTP_GIVEPWORD);
1116 else if (cmdval == const_PASS)
1117 WRITE_OK(FTP_LOGINOK);
1118 else if (cmdval == const_NOOP)
1119 WRITE_OK(FTP_NOOPOK);
1120 else if (cmdval == const_TYPE)
1121 WRITE_OK(FTP_TYPEOK);
1122 else if (cmdval == const_STRU)
1123 WRITE_OK(FTP_STRUOK);
1124 else if (cmdval == const_MODE)
1125 WRITE_OK(FTP_MODEOK);
1126 else if (cmdval == const_ALLO)
1127 WRITE_OK(FTP_ALLOOK);
1128 else if (cmdval == const_SYST)
1129 cmdio_write_raw(STR(FTP_SYSTOK)" UNIX Type: L8\r\n");
1130 else if (cmdval == const_PWD)
1132 else if (cmdval == const_CWD)
1134 else if (cmdval == const_CDUP) /* cd .. */
1136 /* HELP is nearly useless, but we can reuse FEAT for it */
1137 else if (cmdval == const_HELP || cmdval == const_FEAT)
1138 handle_feat(cmdval == const_HELP ? STRNUM32(FTP_HELP) : STRNUM32(FTP_STATOK));
1139 else if (cmdval == const_LIST) /* ls -l */
1141 else if (cmdval == const_NLST) /* "name list", bare ls */
1143 else if (cmdval == const_SIZE)
1145 else if (cmdval == const_STAT) {
1146 if (G.ftp_arg == NULL)
1151 else if (cmdval == const_PASV)
1153 else if (cmdval == const_EPSV)
1155 else if (cmdval == const_RETR)
1157 else if (cmdval == const_PORT)
1159 else if (cmdval == const_REST)
1161 #if ENABLE_FEATURE_FTP_WRITE
1162 else if (opts & OPT_w) {
1163 if (cmdval == const_STOR)
1165 else if (cmdval == const_MKD)
1167 else if (cmdval == const_RMD)
1169 else if (cmdval == const_DELE)
1171 else if (cmdval == const_RNFR) /* "rename from" */
1173 else if (cmdval == const_RNTO) /* "rename to" */
1175 else if (cmdval == const_APPE)
1177 else if (cmdval == const_STOU) /* "store unique" */
1182 else if (cmdval == const_STOR
1183 || cmdval == const_MKD
1184 || cmdval == const_RMD
1185 || cmdval == const_DELE
1186 || cmdval == const_RNFR
1187 || cmdval == const_RNTO
1188 || cmdval == const_APPE
1189 || cmdval == const_STOU
1191 cmdio_write_raw(STR(FTP_NOPERM)" Permission denied\r\n");
1195 /* Which unsupported commands were seen in the wild?
1196 * (doesn't necessarily mean "we must support them")
1197 * lftp 3.6.3: MDTM - works fine without it anyway
1199 cmdio_write_raw(STR(FTP_BADCMD)" Unknown command\r\n");