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.
12 * You have to run this daemon via inetd.
17 #include <netinet/tcp.h>
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
29 #define FTP_SYSTOK 215
31 #define FTP_GOODBYE 221
32 #define FTP_TRANSFEROK 226
33 #define FTP_PASVOK 227
34 /*#define FTP_EPRTOK 228*/
35 #define FTP_EPSVOK 229
36 #define FTP_LOGINOK 230
38 #define FTP_RMDIROK 250
39 #define FTP_DELEOK 250
40 #define FTP_RENAMEOK 250
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
62 #define STR(s) STR1(s)
64 /* Convert a constant to 3-digit string, packed into uint32_t */
66 /* Shift for Nth decimal digit */
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 /* And for 4th position (space) */
71 SHIFTsp = 24 * BB_LITTLE_ENDIAN + 0 * BB_BIG_ENDIAN,
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) \
78 #define STRNUM32sp(s) (uint32_t)(0 \
80 | (('0' + ((s) / 1 % 10)) << SHIFT0) \
81 | (('0' + ((s) / 10 % 10)) << SHIFT1) \
82 | (('0' + ((s) / 100 % 10)) << SHIFT2) \
85 #define MSG_OK "Operation successful\r\n"
86 #define MSG_ERR "Error\r\n"
97 len_and_sockaddr *local_addr;
98 len_and_sockaddr *port_addr;
101 #if ENABLE_FEATURE_FTP_WRITE
104 /* We need these aligned to uint32_t */
105 char msg_ok [(sizeof("NNN " MSG_OK ) + 3) & 0xfffc];
106 char msg_err[(sizeof("NNN " MSG_ERR) + 3) & 0xfffc];
108 #define G (*(struct globals*)&bb_common_bufsiz1)
109 #define INIT_G() do { \
110 /* Moved to main */ \
111 /*strcpy(G.msg_ok + 4, MSG_OK );*/ \
112 /*strcpy(G.msg_err + 4, MSG_ERR);*/ \
117 escape_text(const char *prepend, const char *str, unsigned escapee)
119 unsigned retlen, remainlen, chunklen;
123 append = (char)escapee;
126 remainlen = strlen(str);
127 retlen = strlen(prepend);
128 ret = xmalloc(retlen + remainlen * 2 + 1 + 1);
129 strcpy(ret, prepend);
132 found = strchrnul(str, escapee);
133 chunklen = found - str + 1;
135 /* Copy chunk up to and including escapee (or NUL) to ret */
136 memcpy(ret + retlen, str, chunklen);
139 if (*found == '\0') {
140 /* It wasn't escapee, it was NUL! */
141 ret[retlen - 1] = append; /* replace NUL */
142 ret[retlen] = '\0'; /* add NUL */
145 ret[retlen++] = escapee; /* duplicate escapee */
151 /* Returns strlen as a bonus */
153 replace_char(char *str, char from, char to)
165 verbose_log(const char *str)
167 bb_error_msg("%.*s", (int)strcspn(str, "\r\n"), str);
170 /* NB: status_str is char[4] packed into uint32_t */
172 cmdio_write(uint32_t status_str, const char *str)
177 /* FTP uses telnet protocol for command link.
178 * In telnet, 0xff is an escape char, and needs to be escaped: */
179 response = escape_text((char *) &status_str, str, (0xff << 8) + '\r');
181 /* FTP sends embedded LFs as NULs */
182 len = replace_char(response, '\n', '\0');
184 response[len++] = '\n'; /* tack on trailing '\n' */
185 xwrite(STDOUT_FILENO, response, len);
187 verbose_log(response);
192 cmdio_write_ok(unsigned status)
194 *(uint32_t *) G.msg_ok = status;
195 xwrite(STDOUT_FILENO, G.msg_ok, sizeof("NNN " MSG_OK) - 1);
197 verbose_log(G.msg_ok);
199 #define WRITE_OK(a) cmdio_write_ok(STRNUM32sp(a))
201 /* TODO: output strerr(errno) if errno != 0? */
203 cmdio_write_error(unsigned status)
205 *(uint32_t *) G.msg_err = status;
206 xwrite(STDOUT_FILENO, G.msg_err, sizeof("NNN " MSG_ERR) - 1);
208 verbose_log(G.msg_err);
210 #define WRITE_ERR(a) cmdio_write_error(STRNUM32sp(a))
213 cmdio_write_raw(const char *p_text)
215 xwrite_str(STDOUT_FILENO, p_text);
221 timeout_handler(int sig UNUSED_PARAM)
224 int sv_errno = errno;
226 if ((int)(monotonic_sec() - G.end_time) >= 0)
229 if (!G.local_file_fd)
232 pos = xlseek(G.local_file_fd, 0, SEEK_CUR);
233 if (pos == G.local_file_pos)
235 G.local_file_pos = pos;
242 cmdio_write_raw(STR(FTP_TIMEOUT)" Timeout\r\n");
243 /* TODO: do we need to abort (as opposed to usual shutdown) data transfer? */
247 /* Simple commands */
252 char *cwd, *response;
254 cwd = xrealloc_getcwd_or_warn(NULL);
258 /* We have to promote each " to "" */
259 response = escape_text(" \"", cwd, ('"' << 8) + '"');
261 cmdio_write(STRNUM32(FTP_PWDOK), response);
268 if (!G.ftp_arg || chdir(G.ftp_arg) != 0) {
269 WRITE_ERR(FTP_FILEFAIL);
278 G.ftp_arg = (char*)"..";
285 cmdio_write_raw(STR(FTP_STATOK)"-FTP server status:\r\n"
287 STR(FTP_STATOK)" Ok\r\n");
290 /* Examples of HELP and FEAT:
291 # nc -vvv ftp.kernel.org 21
292 ftp.kernel.org (130.239.17.4:21) open
293 220 Welcome to ftp.kernel.org.
306 214-The following commands are recognized.
307 ABOR ACCT ALLO APPE CDUP CWD DELE EPRT EPSV FEAT HELP LIST MDTM MKD
308 MODE NLST NOOP OPTS PASS PASV PORT PWD QUIT REIN REST RETR RMD RNFR
309 RNTO SITE SIZE SMNT STAT STOR STOU STRU SYST TYPE USER XCUP XCWD XMKD
314 handle_feat(unsigned status)
316 cmdio_write(status, "-Features:");
317 cmdio_write_raw(" EPSV\r\n"
322 cmdio_write(status, " Ok");
325 /* Download commands */
330 return (G.port_addr != NULL);
336 return (G.pasv_listen_fd > STDOUT_FILENO);
340 port_pasv_cleanup(void)
344 if (G.pasv_listen_fd > STDOUT_FILENO)
345 close(G.pasv_listen_fd);
346 G.pasv_listen_fd = -1;
349 /* On error, emits error code to the peer */
351 ftpdataio_get_pasv_fd(void)
355 remote_fd = accept(G.pasv_listen_fd, NULL, 0);
358 WRITE_ERR(FTP_BADSENDCONN);
362 setsockopt(remote_fd, SOL_SOCKET, SO_KEEPALIVE, &const_int_1, sizeof(const_int_1));
366 /* Clears port/pasv data.
367 * This means we dont waste resources, for example, keeping
368 * PASV listening socket open when it is no longer needed.
369 * On error, emits error code to the peer (or exits).
370 * On success, emits p_status_msg to the peer.
373 get_remote_transfer_fd(const char *p_status_msg)
378 /* On error, emits error code to the peer */
379 remote_fd = ftpdataio_get_pasv_fd();
382 remote_fd = xconnect_stream(G.port_addr);
389 cmdio_write(STRNUM32(FTP_DATACONN), p_status_msg);
393 /* If there were neither PASV nor PORT, emits error code to the peer */
395 port_or_pasv_was_seen(void)
397 if (!pasv_active() && !port_active()) {
398 cmdio_write_raw(STR(FTP_BADSENDCONN)" Use PORT or PASV first\r\n");
407 bind_for_passive_mode(void)
414 G.pasv_listen_fd = fd = xsocket(G.local_addr->u.sa.sa_family, SOCK_STREAM, 0);
415 setsockopt_reuseaddr(fd);
417 set_nport(G.local_addr, 0);
418 xbind(fd, &G.local_addr->u.sa, G.local_addr->len);
420 getsockname(fd, &G.local_addr->u.sa, &G.local_addr->len);
422 port = get_nport(&G.local_addr->u.sa);
432 char *addr, *response;
434 port = bind_for_passive_mode();
436 if (G.local_addr->u.sa.sa_family == AF_INET)
437 addr = xmalloc_sockaddr2dotted_noport(&G.local_addr->u.sa);
438 else /* seen this in the wild done by other ftp servers: */
439 addr = xstrdup("0.0.0.0");
440 replace_char(addr, '.', ',');
442 response = xasprintf(STR(FTP_PASVOK)" Entering Passive Mode (%s,%u,%u)\r\n",
443 addr, (int)(port >> 8), (int)(port & 255));
445 cmdio_write_raw(response);
456 port = bind_for_passive_mode();
457 response = xasprintf(STR(FTP_EPSVOK)" EPSV Ok (|||%u|)\r\n", port);
458 cmdio_write_raw(response);
462 /* libbb candidate */
464 len_and_sockaddr* get_peer_lsa(int fd)
466 len_and_sockaddr *lsa;
469 if (getpeername(fd, NULL, &len) != 0)
471 lsa = xzalloc(LSA_LEN_SIZE + len);
473 getpeername(fd, &lsa->u.sa, &lsa->len);
480 unsigned port, port_hi;
482 #ifdef WHY_BOTHER_WE_CAN_ASSUME_IP_MATCHES
483 socklen_t peer_ipv4_len;
484 struct sockaddr_in peer_ipv4;
485 struct in_addr port_ipv4_sin_addr;
492 /* PORT command format makes sense only over IPv4 */
494 #ifdef WHY_BOTHER_WE_CAN_ASSUME_IP_MATCHES
495 || G.local_addr->u.sa.sa_family != AF_INET
499 WRITE_ERR(FTP_BADCMD);
503 comma = strrchr(raw, ',');
507 port = bb_strtou(&comma[1], NULL, 10);
508 if (errno || port > 0xff)
511 comma = strrchr(raw, ',');
515 port_hi = bb_strtou(&comma[1], NULL, 10);
516 if (errno || port_hi > 0xff)
518 port |= port_hi << 8;
520 #ifdef WHY_BOTHER_WE_CAN_ASSUME_IP_MATCHES
521 replace_char(raw, ',', '.');
523 /* We are verifying that PORT's IP matches getpeername().
524 * Otherwise peer can make us open data connections
525 * to other hosts (security problem!)
526 * This code would be too simplistic:
527 * lsa = xdotted2sockaddr(raw, port);
528 * if (lsa == NULL) goto bail;
530 if (!inet_aton(raw, &port_ipv4_sin_addr))
532 peer_ipv4_len = sizeof(peer_ipv4);
533 if (getpeername(STDIN_FILENO, &peer_ipv4, &peer_ipv4_len) != 0)
535 if (memcmp(&port_ipv4_sin_addr, &peer_ipv4.sin_addr, sizeof(struct in_addr)) != 0)
538 G.port_addr = xdotted2sockaddr(raw, port);
540 G.port_addr = get_peer_lsa(STDIN_FILENO);
541 set_nport(G.port_addr, htons(port));
543 WRITE_OK(FTP_PORTOK);
549 /* When ftp_arg == NULL simply restart from beginning */
550 G.restart_pos = G.ftp_arg ? xatoi_u(G.ftp_arg) : 0;
551 WRITE_OK(FTP_RESTOK);
558 off_t bytes_transferred;
561 off_t offset = G.restart_pos;
566 if (!port_or_pasv_was_seen())
567 return; /* port_or_pasv_was_seen emitted error response */
569 /* O_NONBLOCK is useful if file happens to be a device node */
570 local_file_fd = G.ftp_arg ? open(G.ftp_arg, O_RDONLY | O_NONBLOCK) : -1;
571 if (local_file_fd < 0) {
572 WRITE_ERR(FTP_FILEFAIL);
576 if (fstat(local_file_fd, &statbuf) != 0 || !S_ISREG(statbuf.st_mode)) {
577 /* Note - pretend open failed */
578 WRITE_ERR(FTP_FILEFAIL);
581 G.local_file_fd = local_file_fd;
583 /* Now deactive O_NONBLOCK, otherwise we have a problem
584 * on DMAPI filesystems such as XFS DMAPI.
586 ndelay_off(local_file_fd);
588 /* Set the download offset (from REST) if any */
590 xlseek(local_file_fd, offset, SEEK_SET);
592 response = xasprintf(
593 " Opening BINARY mode data connection for %s (%"OFF_FMT"u bytes)",
594 G.ftp_arg, statbuf.st_size);
595 remote_fd = get_remote_transfer_fd(response);
600 bytes_transferred = bb_copyfd_eof(local_file_fd, remote_fd);
602 if (bytes_transferred < 0)
603 WRITE_ERR(FTP_BADSENDFILE);
605 WRITE_OK(FTP_TRANSFEROK);
608 close(local_file_fd);
615 popen_ls(const char *opt)
618 const char *argv[5] = { "ftpd", opt, NULL, G.ftp_arg, NULL };
619 struct fd_pair outfd;
622 cwd = xrealloc_getcwd_or_warn(NULL);
625 /*fflush(NULL); - so far we dont use stdio on output */
628 case -1: /* failure */
629 bb_perror_msg_and_die("vfork");
631 /* NB: close _first_, then move fds! */
633 xmove_fd(outfd.wr, STDOUT_FILENO);
635 /* xopen("/dev/null", O_RDONLY); - chroot may lack it! */
636 if (fchdir(G.proc_self_fd) == 0) {
637 close(G.proc_self_fd);
639 /* ftpd ls helper chdirs to argv[2],
640 * preventing peer from seeing /proc/self
642 execv("exe", (char**) argv);
659 handle_dir_common(int opts)
665 if (!(opts & USE_CTRL_CONN) && !port_or_pasv_was_seen())
666 return; /* port_or_pasv_was_seen emitted error response */
668 /* -n prevents user/groupname display,
669 * which can be problematic in chroot */
670 ls_fd = popen_ls((opts & LONG_LISTING) ? "-l" : "-1");
671 ls_fp = fdopen(ls_fd, "r");
672 if (!ls_fp) /* never happens. paranoia */
673 bb_perror_msg_and_die("fdopen");
675 if (opts & USE_CTRL_CONN) {
676 /* STAT <filename> */
677 cmdio_write_raw(STR(FTP_STATFILE_OK)"-Status follows:\r\n");
679 line = xmalloc_fgetline(ls_fp);
682 cmdio_write(0, line); /* hack: 0 results in no status at all */
685 WRITE_OK(FTP_STATFILE_OK);
687 /* LIST/NLST [<filename>] */
688 int remote_fd = get_remote_transfer_fd(" Here comes the directory listing");
689 if (remote_fd >= 0) {
691 line = xmalloc_fgetline(ls_fp);
694 /* I've seen clients complaining when they
695 * are fed with ls output with bare '\n'.
696 * Pity... that would be much simpler.
698 /* TODO: need to s/LF/NUL/g here */
699 xwrite_str(remote_fd, line);
700 xwrite(remote_fd, "\r\n", 2);
705 WRITE_OK(FTP_TRANSFEROK);
707 fclose(ls_fp); /* closes ls_fd too */
712 handle_dir_common(LONG_LISTING);
717 /* NLST returns list of names, "\r\n" terminated without regard
718 * to the current binary flag. Names may start with "/",
719 * then they represent full names (we don't produce such names),
720 * otherwise names are relative to current directory.
721 * Embedded "\n" are replaced by NULs. This is safe since names
722 * can never contain NUL.
724 handle_dir_common(0);
727 handle_stat_file(void)
729 handle_dir_common(LONG_LISTING + USE_CTRL_CONN);
732 /* This can be extended to handle MLST, as all info is available
733 * in struct stat for that:
735 * 250-Listing file_name
736 * type=file;size=4161;modify=19970214165800; /dir/dir/file_name
739 * MLST [<file or dir name, "." assumed if not given>]
740 * Returned name should be either the same as requested, or fully qualified.
741 * If there was no parameter, return "" or (preferred) fully-qualified name.
742 * Returned "facts" (case is not important):
743 * size - size in octets
744 * modify - last modification time
745 * type - entry type (file,dir,OS.unix=block)
746 * (+ cdir and pdir types for MLSD)
747 * unique - unique id of file/directory (inode#)
749 * a: can be appended to (APPE)
750 * d: can be deleted (RMD/DELE)
751 * f: can be renamed (RNFR)
752 * r: can be read (RETR)
753 * w: can be written (STOR)
754 * e: can CWD into this dir
755 * l: this dir can be listed (dir only!)
756 * c: can create files in this dir
757 * m: can create dirs in this dir (MKD)
758 * p: can delete files in this dir
759 * UNIX.mode - unix file mode
762 handle_size_or_mdtm(int need_size)
765 struct tm broken_out;
766 char buf[(sizeof("NNN %"OFF_FMT"u\r\n") + sizeof(off_t) * 3)
767 | sizeof("NNN YYYYMMDDhhmmss\r\n")
771 || stat(G.ftp_arg, &statbuf) != 0
772 || !S_ISREG(statbuf.st_mode)
774 WRITE_ERR(FTP_FILEFAIL);
778 sprintf(buf, STR(FTP_STATFILE_OK)" %"OFF_FMT"u\r\n", statbuf.st_size);
780 gmtime_r(&statbuf.st_mtime, &broken_out);
781 sprintf(buf, STR(FTP_STATFILE_OK)" %04u%02u%02u%02u%02u%02u\r\n",
782 broken_out.tm_year + 1900,
789 cmdio_write_raw(buf);
792 /* Upload commands */
794 #if ENABLE_FEATURE_FTP_WRITE
798 if (!G.ftp_arg || mkdir(G.ftp_arg, 0777) != 0) {
799 WRITE_ERR(FTP_FILEFAIL);
802 WRITE_OK(FTP_MKDIROK);
808 if (!G.ftp_arg || rmdir(G.ftp_arg) != 0) {
809 WRITE_ERR(FTP_FILEFAIL);
812 WRITE_OK(FTP_RMDIROK);
818 if (!G.ftp_arg || unlink(G.ftp_arg) != 0) {
819 WRITE_ERR(FTP_FILEFAIL);
822 WRITE_OK(FTP_DELEOK);
828 free(G.rnfr_filename);
829 G.rnfr_filename = xstrdup(G.ftp_arg);
830 WRITE_OK(FTP_RNFROK);
838 /* If we didn't get a RNFR, throw a wobbly */
839 if (G.rnfr_filename == NULL || G.ftp_arg == NULL) {
840 cmdio_write_raw(STR(FTP_NEEDRNFR)" RNFR required first\r\n");
844 retval = rename(G.rnfr_filename, G.ftp_arg);
845 free(G.rnfr_filename);
846 G.rnfr_filename = NULL;
849 WRITE_ERR(FTP_FILEFAIL);
852 WRITE_OK(FTP_RENAMEOK);
856 handle_upload_common(int is_append, int is_unique)
860 off_t bytes_transferred;
865 offset = G.restart_pos;
868 if (!port_or_pasv_was_seen())
869 return; /* port_or_pasv_was_seen emitted error response */
874 tempname = xstrdup(" FILE: uniq.XXXXXX");
875 local_file_fd = mkstemp(tempname + 7);
876 } else if (G.ftp_arg) {
877 int flags = O_WRONLY | O_CREAT | O_TRUNC;
879 flags = O_WRONLY | O_CREAT | O_APPEND;
881 flags = O_WRONLY | O_CREAT;
882 local_file_fd = open(G.ftp_arg, flags, 0666);
885 if (local_file_fd < 0
886 || fstat(local_file_fd, &statbuf) != 0
887 || !S_ISREG(statbuf.st_mode)
889 WRITE_ERR(FTP_UPLOADFAIL);
890 if (local_file_fd >= 0)
891 goto close_local_and_bail;
894 G.local_file_fd = local_file_fd;
897 xlseek(local_file_fd, offset, SEEK_SET);
899 remote_fd = get_remote_transfer_fd(tempname ? tempname : " Ok to send data");
903 goto close_local_and_bail;
905 bytes_transferred = bb_copyfd_eof(remote_fd, local_file_fd);
907 if (bytes_transferred < 0)
908 WRITE_ERR(FTP_BADSENDFILE);
910 WRITE_OK(FTP_TRANSFEROK);
912 close_local_and_bail:
913 close(local_file_fd);
920 handle_upload_common(0, 0);
927 handle_upload_common(1, 0);
934 handle_upload_common(0, 1);
936 #endif /* ENABLE_FEATURE_FTP_WRITE */
939 cmdio_get_cmd_and_arg(void)
948 len = 8 * 1024; /* Paranoia. Peer may send 1 gigabyte long cmd... */
949 G.ftp_cmd = cmd = xmalloc_reads(STDIN_FILENO, NULL, &len);
953 /* Trailing '\n' is already stripped, strip '\r' */
954 len = strlen(cmd) - 1;
955 if ((ssize_t)len >= 0 && cmd[len] == '\r')
961 G.ftp_arg = strchr(cmd, ' ');
962 if (G.ftp_arg != NULL)
965 /* Uppercase and pack into uint32_t first word of the command */
968 cmdval = (cmdval << 8) + ((unsigned char)*cmd++ & (unsigned char)~0x20);
973 #define mk_const4(a,b,c,d) (((a * 0x100 + b) * 0x100 + c) * 0x100 + d)
974 #define mk_const3(a,b,c) ((a * 0x100 + b) * 0x100 + c)
976 const_ALLO = mk_const4('A', 'L', 'L', 'O'),
977 const_APPE = mk_const4('A', 'P', 'P', 'E'),
978 const_CDUP = mk_const4('C', 'D', 'U', 'P'),
979 const_CWD = mk_const3('C', 'W', 'D'),
980 const_DELE = mk_const4('D', 'E', 'L', 'E'),
981 const_EPSV = mk_const4('E', 'P', 'S', 'V'),
982 const_FEAT = mk_const4('F', 'E', 'A', 'T'),
983 const_HELP = mk_const4('H', 'E', 'L', 'P'),
984 const_LIST = mk_const4('L', 'I', 'S', 'T'),
985 const_MDTM = mk_const4('M', 'D', 'T', 'M'),
986 const_MKD = mk_const3('M', 'K', 'D'),
987 const_MODE = mk_const4('M', 'O', 'D', 'E'),
988 const_NLST = mk_const4('N', 'L', 'S', 'T'),
989 const_NOOP = mk_const4('N', 'O', 'O', 'P'),
990 const_PASS = mk_const4('P', 'A', 'S', 'S'),
991 const_PASV = mk_const4('P', 'A', 'S', 'V'),
992 const_PORT = mk_const4('P', 'O', 'R', 'T'),
993 const_PWD = mk_const3('P', 'W', 'D'),
994 const_QUIT = mk_const4('Q', 'U', 'I', 'T'),
995 const_REST = mk_const4('R', 'E', 'S', 'T'),
996 const_RETR = mk_const4('R', 'E', 'T', 'R'),
997 const_RMD = mk_const3('R', 'M', 'D'),
998 const_RNFR = mk_const4('R', 'N', 'F', 'R'),
999 const_RNTO = mk_const4('R', 'N', 'T', 'O'),
1000 const_SIZE = mk_const4('S', 'I', 'Z', 'E'),
1001 const_STAT = mk_const4('S', 'T', 'A', 'T'),
1002 const_STOR = mk_const4('S', 'T', 'O', 'R'),
1003 const_STOU = mk_const4('S', 'T', 'O', 'U'),
1004 const_STRU = mk_const4('S', 'T', 'R', 'U'),
1005 const_SYST = mk_const4('S', 'Y', 'S', 'T'),
1006 const_TYPE = mk_const4('T', 'Y', 'P', 'E'),
1007 const_USER = mk_const4('U', 'S', 'E', 'R'),
1016 int ftpd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
1017 int ftpd_main(int argc, char **argv)
1019 unsigned abs_timeout;
1024 abs_timeout = 1 * 60 * 60;
1026 opt_complementary = "t+:T+:vv";
1027 opts = getopt32(argv, "l1vS" USE_FEATURE_FTP_WRITE("w") "t:T:", &G.timeout, &abs_timeout, &G.verbose);
1028 if (opts & (OPT_l|OPT_1)) {
1029 /* Our secret backdoor to ls */
1030 memset(&G, 0, sizeof(G));
1031 /* TODO: pass -n too? */
1032 /* --group-directories-first would be nice, but ls don't do that yet */
1034 argv[2] = (char*)"--";
1035 return ls_main(argc, argv);
1037 if (abs_timeout | G.timeout) {
1038 if (abs_timeout == 0)
1039 abs_timeout = INT_MAX;
1040 G.end_time = monotonic_sec() + abs_timeout;
1041 if (G.timeout > abs_timeout)
1042 G.timeout = abs_timeout;
1044 strcpy(G.msg_ok + 4, MSG_OK );
1045 strcpy(G.msg_err + 4, MSG_ERR);
1047 G.local_addr = get_sock_lsa(STDIN_FILENO);
1048 if (!G.local_addr) {
1049 /* This is confusing:
1050 * bb_error_msg_and_die("stdin is not a socket");
1053 /* Help text says that ftpd must be used as inetd service,
1054 * which is by far the most usual cause of get_sock_lsa
1058 if (!(opts & OPT_v))
1059 logmode = LOGMODE_NONE;
1061 /* LOG_NDELAY is needed since we may chroot later */
1062 openlog(applet_name, LOG_PID | LOG_NDELAY, LOG_DAEMON);
1063 logmode |= LOGMODE_SYSLOG;
1066 applet_name = xasprintf("%s[%u]", applet_name, (int)getpid());
1068 G.proc_self_fd = xopen("/proc/self", O_RDONLY | O_DIRECTORY);
1071 xchdir(argv[optind]);
1075 //umask(077); - admin can set umask before starting us
1077 /* Signals. We'll always take -EPIPE rather than a rude signal, thanks */
1078 signal(SIGPIPE, SIG_IGN);
1080 /* Set up options on the command socket (do we need these all? why?) */
1081 setsockopt(STDIN_FILENO, IPPROTO_TCP, TCP_NODELAY, &const_int_1, sizeof(const_int_1));
1082 setsockopt(STDIN_FILENO, SOL_SOCKET, SO_KEEPALIVE, &const_int_1, sizeof(const_int_1));
1083 setsockopt(STDIN_FILENO, SOL_SOCKET, SO_OOBINLINE, &const_int_1, sizeof(const_int_1));
1085 WRITE_OK(FTP_GREET);
1086 signal(SIGALRM, timeout_handler);
1088 #ifdef IF_WE_WANT_TO_REQUIRE_LOGIN
1090 smallint user_was_specified = 0;
1092 uint32_t cmdval = cmdio_get_cmd_and_arg();
1094 if (cmdval == const_USER) {
1095 if (G.ftp_arg == NULL || strcasecmp(G.ftp_arg, "anonymous") != 0)
1096 cmdio_write_raw(STR(FTP_LOGINERR)" Server is anonymous only\r\n");
1098 user_was_specified = 1;
1099 cmdio_write_raw(STR(FTP_GIVEPWORD)" Please specify the password\r\n");
1101 } else if (cmdval == const_PASS) {
1102 if (user_was_specified)
1104 cmdio_write_raw(STR(FTP_NEEDUSER)" Login with USER\r\n");
1105 } else if (cmdval == const_QUIT) {
1106 WRITE_OK(FTP_GOODBYE);
1109 cmdio_write_raw(STR(FTP_LOGINERR)" Login with USER and PASS\r\n");
1113 WRITE_OK(FTP_LOGINOK);
1116 /* RFC-959 Section 5.1
1117 * The following commands and options MUST be supported by every
1118 * server-FTP and user-FTP, except in cases where the underlying
1119 * file system or operating system does not allow or support
1120 * a particular command.
1121 * Type: ASCII Non-print, IMAGE, LOCAL 8
1123 * Structure: File, Record*
1124 * (Record structure is REQUIRED only for hosts whose file
1125 * systems support record structure).
1127 * USER, PASS, ACCT, [bbox: ACCT not supported]
1132 * CWD, CDUP, RMD, MKD, PWD,
1138 * "The argument field is a Telnet string identifying the user's account.
1139 * The command is not necessarily related to the USER command, as some
1140 * sites may require an account for login and others only for specific
1141 * access, such as storing files. In the latter case the command may
1142 * arrive at any time.
1143 * There are reply codes to differentiate these cases for the automation:
1144 * when account information is required for login, the response to
1145 * a successful PASSword command is reply code 332. On the other hand,
1146 * if account information is NOT required for login, the reply to
1147 * a successful PASSword command is 230; and if the account information
1148 * is needed for a command issued later in the dialogue, the server
1149 * should return a 332 or 532 reply depending on whether it stores
1150 * (pending receipt of the ACCounT command) or discards the command,
1155 uint32_t cmdval = cmdio_get_cmd_and_arg();
1157 if (cmdval == const_QUIT) {
1158 WRITE_OK(FTP_GOODBYE);
1161 else if (cmdval == const_USER)
1162 /* This would mean "ok, now give me PASS". */
1163 /*WRITE_OK(FTP_GIVEPWORD);*/
1164 /* vsftpd can be configured to not require that,
1165 * and this also saves one roundtrip:
1167 WRITE_OK(FTP_LOGINOK);
1168 else if (cmdval == const_PASS)
1169 WRITE_OK(FTP_LOGINOK);
1170 else if (cmdval == const_NOOP)
1171 WRITE_OK(FTP_NOOPOK);
1172 else if (cmdval == const_TYPE)
1173 WRITE_OK(FTP_TYPEOK);
1174 else if (cmdval == const_STRU)
1175 WRITE_OK(FTP_STRUOK);
1176 else if (cmdval == const_MODE)
1177 WRITE_OK(FTP_MODEOK);
1178 else if (cmdval == const_ALLO)
1179 WRITE_OK(FTP_ALLOOK);
1180 else if (cmdval == const_SYST)
1181 cmdio_write_raw(STR(FTP_SYSTOK)" UNIX Type: L8\r\n");
1182 else if (cmdval == const_PWD)
1184 else if (cmdval == const_CWD)
1186 else if (cmdval == const_CDUP) /* cd .. */
1188 /* HELP is nearly useless, but we can reuse FEAT for it */
1189 /* lftp uses FEAT */
1190 else if (cmdval == const_HELP || cmdval == const_FEAT)
1191 handle_feat(cmdval == const_HELP
1192 ? STRNUM32(FTP_HELP)
1193 : STRNUM32(FTP_STATOK)
1195 else if (cmdval == const_LIST) /* ls -l */
1197 else if (cmdval == const_NLST) /* "name list", bare ls */
1199 /* SIZE is crucial for wget's download indicator etc */
1200 /* Mozilla, lftp use MDTM (presumably for caching) */
1201 else if (cmdval == const_SIZE || cmdval == const_MDTM)
1202 handle_size_or_mdtm(cmdval == const_SIZE);
1203 else if (cmdval == const_STAT) {
1204 if (G.ftp_arg == NULL)
1209 else if (cmdval == const_PASV)
1211 else if (cmdval == const_EPSV)
1213 else if (cmdval == const_RETR)
1215 else if (cmdval == const_PORT)
1217 else if (cmdval == const_REST)
1219 #if ENABLE_FEATURE_FTP_WRITE
1220 else if (opts & OPT_w) {
1221 if (cmdval == const_STOR)
1223 else if (cmdval == const_MKD)
1225 else if (cmdval == const_RMD)
1227 else if (cmdval == const_DELE)
1229 else if (cmdval == const_RNFR) /* "rename from" */
1231 else if (cmdval == const_RNTO) /* "rename to" */
1233 else if (cmdval == const_APPE)
1235 else if (cmdval == const_STOU) /* "store unique" */
1240 else if (cmdval == const_STOR
1241 || cmdval == const_MKD
1242 || cmdval == const_RMD
1243 || cmdval == const_DELE
1244 || cmdval == const_RNFR
1245 || cmdval == const_RNTO
1246 || cmdval == const_APPE
1247 || cmdval == const_STOU
1249 cmdio_write_raw(STR(FTP_NOPERM)" Permission denied\r\n");
1253 /* Which unsupported commands were seen in the wild?
1254 * (doesn't necessarily mean "we must support them")
1255 * foo 1.2.3: XXXX - comment
1257 cmdio_write_raw(STR(FTP_BADCMD)" Unknown command\r\n");