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 strcpy(G.msg_ok + 4, MSG_OK ); \
109 strcpy(G.msg_err + 4, MSG_ERR); \
114 escape_text(const char *prepend, const char *str, unsigned escapee)
116 unsigned retlen, remainlen, chunklen;
120 append = (char)escapee;
123 remainlen = strlen(str);
124 retlen = strlen(prepend);
125 ret = xmalloc(retlen + remainlen * 2 + 1 + 1);
126 strcpy(ret, prepend);
129 found = strchrnul(str, escapee);
130 chunklen = found - str + 1;
132 /* Copy chunk up to and including escapee (or NUL) to ret */
133 memcpy(ret + retlen, str, chunklen);
136 if (*found == '\0') {
137 /* It wasn't escapee, it was NUL! */
138 ret[retlen - 1] = append; /* replace NUL */
139 ret[retlen] = '\0'; /* add NUL */
142 ret[retlen++] = escapee; /* duplicate escapee */
148 /* Returns strlen as a bonus */
150 replace_char(char *str, char from, char to)
161 /* NB: status_str is char[4] packed into uint32_t */
163 cmdio_write(uint32_t status_str, const char *str)
168 /* FTP allegedly uses telnet protocol for command link.
169 * In telnet, 0xff is an escape char, and needs to be escaped: */
170 response = escape_text((char *) &status_str, str, (0xff << 8) + '\r');
172 /* ?! does FTP send embedded LFs as NULs? wow */
173 len = replace_char(response, '\n', '\0');
175 response[len++] = '\n'; /* tack on trailing '\n' */
176 xwrite(STDOUT_FILENO, response, len);
181 cmdio_write_ok(unsigned status)
183 *(uint32_t *) G.msg_ok = status;
184 xwrite(STDOUT_FILENO, G.msg_ok, sizeof("NNN " MSG_OK) - 1);
186 #define WRITE_OK(a) cmdio_write_ok(STRNUM32sp(a))
188 /* TODO: output strerr(errno) if errno != 0? */
190 cmdio_write_error(unsigned status)
192 *(uint32_t *) G.msg_err = status;
193 xwrite(STDOUT_FILENO, G.msg_err, sizeof("NNN " MSG_ERR) - 1);
195 #define WRITE_ERR(a) cmdio_write_error(STRNUM32sp(a))
198 cmdio_write_raw(const char *p_text)
200 xwrite_str(STDOUT_FILENO, p_text);
204 timeout_handler(int sig UNUSED_PARAM)
207 int sv_errno = errno;
209 if (monotonic_sec() - G.start_time > G.abs_timeout)
212 if (!G.local_file_fd)
215 pos = xlseek(G.local_file_fd, 0, SEEK_CUR);
216 if (pos == G.local_file_pos)
218 G.local_file_pos = pos;
225 cmdio_write_raw(STR(FTP_TIMEOUT)" Timeout\r\n");
226 /* TODO: do we need to abort (as opposed to usual shutdown) data transfer? */
230 /* Simple commands */
235 char *cwd, *response;
237 cwd = xrealloc_getcwd_or_warn(NULL);
241 /* We have to promote each " to "" */
242 response = escape_text(" \"", cwd, ('"' << 8) + '"');
244 cmdio_write(STRNUM32(FTP_PWDOK), response);
251 if (!G.ftp_arg || chdir(G.ftp_arg) != 0) {
252 WRITE_ERR(FTP_FILEFAIL);
261 G.ftp_arg = (char*)"..";
268 cmdio_write_raw(STR(FTP_STATOK)"-FTP server status:\r\n"
270 STR(FTP_STATOK)" Ok\r\n");
273 /* TODO: implement FEAT. Example:
274 # nc -vvv ftp.kernel.org 21
275 ftp.kernel.org (130.239.17.4:21) open
276 220 Welcome to ftp.kernel.org.
289 214-The following commands are recognized.
290 ABOR ACCT ALLO APPE CDUP CWD DELE EPRT EPSV FEAT HELP LIST MDTM MKD
291 MODE NLST NOOP OPTS PASS PASV PORT PWD QUIT REIN REST RETR RMD RNFR
292 RNTO SITE SIZE SMNT STAT STOR STOU STRU SYST TYPE USER XCUP XCWD XMKD
299 cmdio_write_raw(STR(FTP_HELP)"-Commands:\r\n"
300 " ALLO CDUP CWD EPSV HELP LIST\r\n"
301 " MODE NLST NOOP PASS PASV PORT PWD QUIT\r\n"
302 " REST RETR SIZE STAT STRU SYST TYPE USER\r\n"
303 #if ENABLE_FEATURE_FTP_WRITE
304 " APPE DELE MKD RMD RNFR RNTO STOR STOU\r\n"
306 STR(FTP_HELP)" Ok\r\n");
309 /* Download commands */
314 return (G.port_addr != NULL);
320 return (G.pasv_listen_fd > STDOUT_FILENO);
324 port_pasv_cleanup(void)
328 if (G.pasv_listen_fd > STDOUT_FILENO)
329 close(G.pasv_listen_fd);
330 G.pasv_listen_fd = -1;
333 /* On error, emits error code to the peer */
335 ftpdataio_get_pasv_fd(void)
339 remote_fd = accept(G.pasv_listen_fd, NULL, 0);
342 WRITE_ERR(FTP_BADSENDCONN);
346 setsockopt(remote_fd, SOL_SOCKET, SO_KEEPALIVE, &const_int_1, sizeof(const_int_1));
350 /* Clears port/pasv data.
351 * This means we dont waste resources, for example, keeping
352 * PASV listening socket open when it is no longer needed.
353 * On error, emits error code to the peer (or exits).
354 * On success, emits p_status_msg to the peer.
357 get_remote_transfer_fd(const char *p_status_msg)
362 /* On error, emits error code to the peer */
363 remote_fd = ftpdataio_get_pasv_fd();
366 remote_fd = xconnect_stream(G.port_addr);
373 cmdio_write(STRNUM32(FTP_DATACONN), p_status_msg);
377 /* If there were neither PASV nor PORT, emits error code to the peer */
379 port_or_pasv_was_seen(void)
381 if (!pasv_active() && !port_active()) {
382 cmdio_write_raw(STR(FTP_BADSENDCONN)" Use PORT or PASV first\r\n");
391 bind_for_passive_mode(void)
398 G.pasv_listen_fd = fd = xsocket(G.local_addr->u.sa.sa_family, SOCK_STREAM, 0);
399 setsockopt_reuseaddr(fd);
401 set_nport(G.local_addr, 0);
402 xbind(fd, &G.local_addr->u.sa, G.local_addr->len);
404 getsockname(fd, &G.local_addr->u.sa, &G.local_addr->len);
406 port = get_nport(&G.local_addr->u.sa);
416 char *addr, *response;
418 port = bind_for_passive_mode();
420 if (G.local_addr->u.sa.sa_family == AF_INET)
421 addr = xmalloc_sockaddr2dotted_noport(&G.local_addr->u.sa);
422 else /* seen this in the wild done by other ftp servers: */
423 addr = xstrdup("0.0.0.0");
424 replace_char(addr, '.', ',');
426 response = xasprintf(STR(FTP_PASVOK)" Entering Passive Mode (%s,%u,%u)\r\n",
427 addr, (int)(port >> 8), (int)(port & 255));
429 cmdio_write_raw(response);
440 port = bind_for_passive_mode();
441 response = xasprintf(STR(FTP_EPSVOK)" EPSV Ok (|||%u|)\r\n", port);
442 cmdio_write_raw(response);
446 /* libbb candidate */
448 len_and_sockaddr* get_peer_lsa(int fd)
450 len_and_sockaddr *lsa;
453 if (getpeername(fd, NULL, &len) != 0)
455 lsa = xzalloc(LSA_LEN_SIZE + len);
457 getpeername(fd, &lsa->u.sa, &lsa->len);
464 unsigned port, port_hi;
466 #ifdef WHY_BOTHER_WE_CAN_ASSUME_IP_MATCHES
467 socklen_t peer_ipv4_len;
468 struct sockaddr_in peer_ipv4;
469 struct in_addr port_ipv4_sin_addr;
476 /* PORT command format makes sense only over IPv4 */
478 #ifdef WHY_BOTHER_WE_CAN_ASSUME_IP_MATCHES
479 || G.local_addr->u.sa.sa_family != AF_INET
483 WRITE_ERR(FTP_BADCMD);
487 comma = strrchr(raw, ',');
491 port = bb_strtou(&comma[1], NULL, 10);
492 if (errno || port > 0xff)
495 comma = strrchr(raw, ',');
499 port_hi = bb_strtou(&comma[1], NULL, 10);
500 if (errno || port_hi > 0xff)
502 port |= port_hi << 8;
504 #ifdef WHY_BOTHER_WE_CAN_ASSUME_IP_MATCHES
505 replace_char(raw, ',', '.');
507 /* We are verifying that PORT's IP matches getpeername().
508 * Otherwise peer can make us open data connections
509 * to other hosts (security problem!)
510 * This code would be too simplistic:
511 * lsa = xdotted2sockaddr(raw, port);
512 * if (lsa == NULL) goto bail;
514 if (!inet_aton(raw, &port_ipv4_sin_addr))
516 peer_ipv4_len = sizeof(peer_ipv4);
517 if (getpeername(STDIN_FILENO, &peer_ipv4, &peer_ipv4_len) != 0)
519 if (memcmp(&port_ipv4_sin_addr, &peer_ipv4.sin_addr, sizeof(struct in_addr)) != 0)
522 G.port_addr = xdotted2sockaddr(raw, port);
524 G.port_addr = get_peer_lsa(STDIN_FILENO);
525 set_nport(G.port_addr, htons(port));
527 WRITE_OK(FTP_PORTOK);
533 /* When ftp_arg == NULL simply restart from beginning */
534 G.restart_pos = G.ftp_arg ? xatoi_u(G.ftp_arg) : 0;
535 WRITE_OK(FTP_RESTOK);
542 off_t bytes_transferred;
545 off_t offset = G.restart_pos;
550 if (!port_or_pasv_was_seen())
551 return; /* port_or_pasv_was_seen emitted error response */
553 /* O_NONBLOCK is useful if file happens to be a device node */
554 local_file_fd = G.ftp_arg ? open(G.ftp_arg, O_RDONLY | O_NONBLOCK) : -1;
555 if (local_file_fd < 0) {
556 WRITE_ERR(FTP_FILEFAIL);
560 if (fstat(local_file_fd, &statbuf) != 0 || !S_ISREG(statbuf.st_mode)) {
561 /* Note - pretend open failed */
562 WRITE_ERR(FTP_FILEFAIL);
565 G.local_file_fd = local_file_fd;
567 /* Now deactive O_NONBLOCK, otherwise we have a problem
568 * on DMAPI filesystems such as XFS DMAPI.
570 ndelay_off(local_file_fd);
572 /* Set the download offset (from REST) if any */
574 xlseek(local_file_fd, offset, SEEK_SET);
576 response = xasprintf(
577 " Opening BINARY mode data connection for %s (%"OFF_FMT"u bytes)",
578 G.ftp_arg, statbuf.st_size);
579 remote_fd = get_remote_transfer_fd(response);
584 bytes_transferred = bb_copyfd_eof(local_file_fd, remote_fd);
586 if (bytes_transferred < 0)
587 WRITE_ERR(FTP_BADSENDFILE);
589 WRITE_OK(FTP_TRANSFEROK);
592 close(local_file_fd);
599 popen_ls(const char *opt)
602 const char *argv[5] = { "ftpd", opt, NULL, G.ftp_arg, NULL };
603 struct fd_pair outfd;
606 cwd = xrealloc_getcwd_or_warn(NULL);
609 /*fflush(NULL); - so far we dont use stdio on output */
612 case -1: /* failure */
613 bb_perror_msg_and_die("vfork");
615 /* NB: close _first_, then move fds! */
617 xmove_fd(outfd.wr, STDOUT_FILENO);
619 /* xopen("/dev/null", O_RDONLY); - chroot may lack it! */
620 if (fchdir(G.proc_self_fd) == 0) {
621 close(G.proc_self_fd);
623 /* ftpd ls helper chdirs to argv[2],
624 * preventing peer from seeing /proc/self
626 execv("exe", (char**) argv);
643 handle_dir_common(int opts)
649 if (!(opts & USE_CTRL_CONN) && !port_or_pasv_was_seen())
650 return; /* port_or_pasv_was_seen emitted error response */
652 /* -n prevents user/groupname display,
653 * which can be problematic in chroot */
654 ls_fd = popen_ls((opts & LONG_LISTING) ? "-l" : "-1");
655 ls_fp = fdopen(ls_fd, "r");
656 if (!ls_fp) /* never happens. paranoia */
657 bb_perror_msg_and_die("fdopen");
659 if (opts & USE_CTRL_CONN) {
660 /* STAT <filename> */
661 cmdio_write_raw(STR(FTP_STATFILE_OK)"-Status follows:\r\n");
663 line = xmalloc_fgetline(ls_fp);
666 cmdio_write(0, line); /* hack: 0 results in no status at all */
669 WRITE_OK(FTP_STATFILE_OK);
671 /* LIST/NLST [<filename>] */
672 int remote_fd = get_remote_transfer_fd(" Here comes the directory listing");
673 if (remote_fd >= 0) {
675 line = xmalloc_fgetline(ls_fp);
678 /* I've seen clients complaining when they
679 * are fed with ls output with bare '\n'.
680 * Pity... that would be much simpler.
682 xwrite_str(remote_fd, line);
683 xwrite(remote_fd, "\r\n", 2);
688 WRITE_OK(FTP_TRANSFEROK);
690 fclose(ls_fp); /* closes ls_fd too */
695 handle_dir_common(LONG_LISTING);
700 handle_dir_common(0);
703 handle_stat_file(void)
705 handle_dir_common(LONG_LISTING + USE_CTRL_CONN);
712 char buf[sizeof(STR(FTP_STATFILE_OK)" %"OFF_FMT"u\r\n") + sizeof(off_t)*3];
715 || stat(G.ftp_arg, &statbuf) != 0
716 || !S_ISREG(statbuf.st_mode)
718 WRITE_ERR(FTP_FILEFAIL);
721 sprintf(buf, STR(FTP_STATFILE_OK)" %"OFF_FMT"u\r\n", statbuf.st_size);
722 cmdio_write_raw(buf);
725 /* Upload commands */
727 #if ENABLE_FEATURE_FTP_WRITE
731 if (!G.ftp_arg || mkdir(G.ftp_arg, 0777) != 0) {
732 WRITE_ERR(FTP_FILEFAIL);
735 WRITE_OK(FTP_MKDIROK);
741 if (!G.ftp_arg || rmdir(G.ftp_arg) != 0) {
742 WRITE_ERR(FTP_FILEFAIL);
745 WRITE_OK(FTP_RMDIROK);
751 if (!G.ftp_arg || unlink(G.ftp_arg) != 0) {
752 WRITE_ERR(FTP_FILEFAIL);
755 WRITE_OK(FTP_DELEOK);
761 free(G.rnfr_filename);
762 G.rnfr_filename = xstrdup(G.ftp_arg);
763 WRITE_OK(FTP_RNFROK);
771 /* If we didn't get a RNFR, throw a wobbly */
772 if (G.rnfr_filename == NULL || G.ftp_arg == NULL) {
773 cmdio_write_raw(STR(FTP_NEEDRNFR)" RNFR required first\r\n");
777 retval = rename(G.rnfr_filename, G.ftp_arg);
778 free(G.rnfr_filename);
779 G.rnfr_filename = NULL;
782 WRITE_ERR(FTP_FILEFAIL);
785 WRITE_OK(FTP_RENAMEOK);
789 handle_upload_common(int is_append, int is_unique)
793 off_t bytes_transferred;
798 offset = G.restart_pos;
801 if (!port_or_pasv_was_seen())
802 return; /* port_or_pasv_was_seen emitted error response */
807 tempname = xstrdup(" FILE: uniq.XXXXXX");
808 local_file_fd = mkstemp(tempname + 7);
809 } else if (G.ftp_arg) {
810 int flags = O_WRONLY | O_CREAT | O_TRUNC;
812 flags = O_WRONLY | O_CREAT | O_APPEND;
814 flags = O_WRONLY | O_CREAT;
815 local_file_fd = open(G.ftp_arg, flags, 0666);
818 if (local_file_fd < 0
819 || fstat(local_file_fd, &statbuf) != 0
820 || !S_ISREG(statbuf.st_mode)
822 WRITE_ERR(FTP_UPLOADFAIL);
823 if (local_file_fd >= 0)
824 goto close_local_and_bail;
827 G.local_file_fd = local_file_fd;
830 xlseek(local_file_fd, offset, SEEK_SET);
832 remote_fd = get_remote_transfer_fd(tempname ? tempname : " Ok to send data");
836 goto close_local_and_bail;
838 bytes_transferred = bb_copyfd_eof(remote_fd, local_file_fd);
840 if (bytes_transferred < 0)
841 WRITE_ERR(FTP_BADSENDFILE);
843 WRITE_OK(FTP_TRANSFEROK);
845 close_local_and_bail:
846 close(local_file_fd);
853 handle_upload_common(0, 0);
860 handle_upload_common(1, 0);
867 handle_upload_common(0, 1);
869 #endif /* ENABLE_FEATURE_FTP_WRITE */
872 cmdio_get_cmd_and_arg(void)
881 len = 8 * 1024; /* Paranoia. Peer may send 1 gigabyte long cmd... */
882 G.ftp_cmd = cmd = xmalloc_reads(STDIN_FILENO, NULL, &len);
886 /* Trailing '\n' is already stripped, strip '\r' */
887 len = strlen(cmd) - 1;
888 while ((ssize_t)len >= 0 && cmd[len] == '\r') {
893 G.ftp_arg = strchr(cmd, ' ');
894 if (G.ftp_arg != NULL)
897 /* Uppercase and pack into uint32_t first word of the command */
900 cmdval = (cmdval << 8) + ((unsigned char)*cmd++ & (unsigned char)~0x20);
905 #define mk_const4(a,b,c,d) (((a * 0x100 + b) * 0x100 + c) * 0x100 + d)
906 #define mk_const3(a,b,c) ((a * 0x100 + b) * 0x100 + c)
908 const_ALLO = mk_const4('A', 'L', 'L', 'O'),
909 const_APPE = mk_const4('A', 'P', 'P', 'E'),
910 const_CDUP = mk_const4('C', 'D', 'U', 'P'),
911 const_CWD = mk_const3('C', 'W', 'D'),
912 const_DELE = mk_const4('D', 'E', 'L', 'E'),
913 const_EPSV = mk_const4('E', 'P', 'S', 'V'),
914 const_HELP = mk_const4('H', 'E', 'L', 'P'),
915 const_LIST = mk_const4('L', 'I', 'S', 'T'),
916 const_MKD = mk_const3('M', 'K', 'D'),
917 const_MODE = mk_const4('M', 'O', 'D', 'E'),
918 const_NLST = mk_const4('N', 'L', 'S', 'T'),
919 const_NOOP = mk_const4('N', 'O', 'O', 'P'),
920 const_PASS = mk_const4('P', 'A', 'S', 'S'),
921 const_PASV = mk_const4('P', 'A', 'S', 'V'),
922 const_PORT = mk_const4('P', 'O', 'R', 'T'),
923 const_PWD = mk_const3('P', 'W', 'D'),
924 const_QUIT = mk_const4('Q', 'U', 'I', 'T'),
925 const_REST = mk_const4('R', 'E', 'S', 'T'),
926 const_RETR = mk_const4('R', 'E', 'T', 'R'),
927 const_RMD = mk_const3('R', 'M', 'D'),
928 const_RNFR = mk_const4('R', 'N', 'F', 'R'),
929 const_RNTO = mk_const4('R', 'N', 'T', 'O'),
930 const_SIZE = mk_const4('S', 'I', 'Z', 'E'),
931 const_STAT = mk_const4('S', 'T', 'A', 'T'),
932 const_STOR = mk_const4('S', 'T', 'O', 'R'),
933 const_STOU = mk_const4('S', 'T', 'O', 'U'),
934 const_STRU = mk_const4('S', 'T', 'R', 'U'),
935 const_SYST = mk_const4('S', 'Y', 'S', 'T'),
936 const_TYPE = mk_const4('T', 'Y', 'P', 'E'),
937 const_USER = mk_const4('U', 'S', 'E', 'R'),
946 int ftpd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
947 int ftpd_main(int argc, char **argv)
953 G.start_time = monotonic_sec();
954 G.abs_timeout = 1 * 60 * 60;
956 opt_complementary = "t+:T+";
957 opts = getopt32(argv, "l1vS" USE_FEATURE_FTP_WRITE("w") "t:T:", &G.timeout, &G.abs_timeout);
959 if (opts & (OPT_l|OPT_1)) {
960 /* Our secret backdoor to ls */
961 memset(&G, 0, sizeof(G));
962 /* TODO: pass -n too? */
964 argv[2] = (char*)"--";
965 return ls_main(argc, argv);
969 G.local_addr = get_sock_lsa(STDIN_FILENO);
971 /* This is confusing:
972 * bb_error_msg_and_die("stdin is not a socket");
975 /* Help text says that ftpd must be used as inetd service,
976 * which is by far the most usual cause of get_sock_lsa
981 logmode = LOGMODE_NONE;
983 /* LOG_NDELAY is needed since we may chroot later */
984 openlog(applet_name, LOG_PID | LOG_NDELAY, LOG_DAEMON);
985 logmode |= LOGMODE_SYSLOG;
988 G.proc_self_fd = xopen("/proc/self", O_RDONLY | O_DIRECTORY);
991 xchdir(argv[optind]);
995 //umask(077); - admin can set umask before starting us
997 /* Signals. We'll always take -EPIPE rather than a rude signal, thanks */
998 signal(SIGPIPE, SIG_IGN);
1000 /* Set up options on the command socket (do we need these all? why?) */
1001 setsockopt(STDIN_FILENO, IPPROTO_TCP, TCP_NODELAY, &const_int_1, sizeof(const_int_1));
1002 setsockopt(STDIN_FILENO, SOL_SOCKET, SO_KEEPALIVE, &const_int_1, sizeof(const_int_1));
1003 setsockopt(STDIN_FILENO, SOL_SOCKET, SO_OOBINLINE, &const_int_1, sizeof(const_int_1));
1005 WRITE_OK(FTP_GREET);
1006 signal(SIGALRM, timeout_handler);
1008 #ifdef IF_WE_WANT_TO_REQUIRE_LOGIN
1010 smallint user_was_specified = 0;
1012 uint32_t cmdval = cmdio_get_cmd_and_arg();
1014 if (cmdval == const_USER) {
1015 if (G.ftp_arg == NULL || strcasecmp(G.ftp_arg, "anonymous") != 0)
1016 cmdio_write_raw(STR(FTP_LOGINERR)" Server is anonymous only\r\n");
1018 user_was_specified = 1;
1019 cmdio_write_raw(STR(FTP_GIVEPWORD)" Please specify the password\r\n");
1021 } else if (cmdval == const_PASS) {
1022 if (user_was_specified)
1024 cmdio_write_raw(STR(FTP_NEEDUSER)" Login with USER\r\n");
1025 } else if (cmdval == const_QUIT) {
1026 WRITE_OK(FTP_GOODBYE);
1029 cmdio_write_raw(STR(FTP_LOGINERR)" Login with USER and PASS\r\n");
1033 WRITE_OK(FTP_LOGINOK);
1036 /* RFC-959 Section 5.1
1037 * The following commands and options MUST be supported by every
1038 * server-FTP and user-FTP, except in cases where the underlying
1039 * file system or operating system does not allow or support
1040 * a particular command.
1041 * Type: ASCII Non-print, IMAGE, LOCAL 8
1043 * Structure: File, Record*
1044 * (Record structure is REQUIRED only for hosts whose file
1045 * systems support record structure).
1047 * USER, PASS, ACCT, [bbox: ACCT not supported]
1052 * CWD, CDUP, RMD, MKD, PWD,
1058 * "The argument field is a Telnet string identifying the user's account.
1059 * The command is not necessarily related to the USER command, as some
1060 * sites may require an account for login and others only for specific
1061 * access, such as storing files. In the latter case the command may
1062 * arrive at any time.
1063 * There are reply codes to differentiate these cases for the automation:
1064 * when account information is required for login, the response to
1065 * a successful PASSword command is reply code 332. On the other hand,
1066 * if account information is NOT required for login, the reply to
1067 * a successful PASSword command is 230; and if the account information
1068 * is needed for a command issued later in the dialogue, the server
1069 * should return a 332 or 532 reply depending on whether it stores
1070 * (pending receipt of the ACCounT command) or discards the command,
1075 uint32_t cmdval = cmdio_get_cmd_and_arg();
1077 if (cmdval == const_QUIT) {
1078 WRITE_OK(FTP_GOODBYE);
1081 else if (cmdval == const_USER)
1082 WRITE_OK(FTP_GIVEPWORD);
1083 else if (cmdval == const_PASS)
1084 WRITE_OK(FTP_LOGINOK);
1085 else if (cmdval == const_NOOP)
1086 WRITE_OK(FTP_NOOPOK);
1087 else if (cmdval == const_TYPE)
1088 WRITE_OK(FTP_TYPEOK);
1089 else if (cmdval == const_STRU)
1090 WRITE_OK(FTP_STRUOK);
1091 else if (cmdval == const_MODE)
1092 WRITE_OK(FTP_MODEOK);
1093 else if (cmdval == const_ALLO)
1094 WRITE_OK(FTP_ALLOOK);
1095 else if (cmdval == const_SYST)
1096 cmdio_write_raw(STR(FTP_SYSTOK)" UNIX Type: L8\r\n");
1097 else if (cmdval == const_PWD)
1099 else if (cmdval == const_CWD)
1101 else if (cmdval == const_CDUP) /* cd .. */
1103 else if (cmdval == const_HELP)
1105 else if (cmdval == const_LIST) /* ls -l */
1107 else if (cmdval == const_NLST) /* "name list", bare ls */
1109 else if (cmdval == const_SIZE)
1111 else if (cmdval == const_STAT) {
1112 if (G.ftp_arg == NULL)
1117 else if (cmdval == const_PASV)
1119 else if (cmdval == const_EPSV)
1121 else if (cmdval == const_RETR)
1123 else if (cmdval == const_PORT)
1125 else if (cmdval == const_REST)
1127 #if ENABLE_FEATURE_FTP_WRITE
1128 else if (opts & OPT_w) {
1129 if (cmdval == const_STOR)
1131 else if (cmdval == const_MKD)
1133 else if (cmdval == const_RMD)
1135 else if (cmdval == const_DELE)
1137 else if (cmdval == const_RNFR) /* "rename from" */
1139 else if (cmdval == const_RNTO) /* "rename to" */
1141 else if (cmdval == const_APPE)
1143 else if (cmdval == const_STOU) /* "store unique" */
1148 else if (cmdval == const_STOR
1149 || cmdval == const_MKD
1150 || cmdval == const_RMD
1151 || cmdval == const_DELE
1152 || cmdval == const_RNFR
1153 || cmdval == const_RNTO
1154 || cmdval == const_APPE
1155 || cmdval == const_STOU
1157 cmdio_write_raw(STR(FTP_NOPERM)" Permission denied\r\n");
1161 /* Which unsupported commands were seen in the wild?
1162 * (doesn't necessarily mean "we must support them")
1163 * lftp 3.6.3: FEAT - is it useful?
1164 * MDTM - works fine without it anyway
1166 cmdio_write_raw(STR(FTP_BADCMD)" Unknown command\r\n");