diff options
author | Denis Vlasenko <vda.linux@googlemail.com> | 2009-03-09 16:01:08 +0300 |
---|---|---|
committer | Denis Vlasenko <vda.linux@googlemail.com> | 2009-03-09 16:01:08 +0300 |
commit | 9b2fbda53853ae651349f97bcf86c891cc137f92 (patch) | |
tree | 23dd0fe4e01328899ba94e348a206ecb13ca37ac | |
parent | 57a3b174989c7778fb255f716e090d5be807b9b7 (diff) |
ftpd: EPSV and SIZE support. Tested to work on IPv6 too.
libbb: str2sockaddr shuld accept [IPv6] addr without port -
wget 'ftp://[::1]/file' needs that to work.
function old new delta
bind_for_passive_mode - 129 +129
get_nport - 30 +30
ftpd_main 1731 1760 +29
str2sockaddr 412 431 +19
------------------------------------------------------------------------------
(add/remove: 2/0 grow/shrink: 2/0 up/down: 207/0) Total: 207 bytes
text data bss dec hex filename
808568 476 7864 816908 c770c busybox_old
808804 476 7864 817144 c77f8 busybox_unstripped
-rw-r--r-- | libbb/xconnect.c | 9 | ||||
-rw-r--r-- | networking/ftpd.c | 193 |
2 files changed, 104 insertions, 98 deletions
diff --git a/libbb/xconnect.c b/libbb/xconnect.c index 975844500..f5d7983a4 100644 --- a/libbb/xconnect.c +++ b/libbb/xconnect.c @@ -167,7 +167,8 @@ USE_FEATURE_IPV6(sa_family_t af,) /* Even uglier parsing of [xx]:nn */ host++; cp = strchr(host, ']'); - if (!cp || cp[1] != ':') { /* Malformed: must have [xx]:nn */ + if (!cp || (cp[1] != ':' && cp[1] != '\0')) { + /* Malformed: must be [xx]:nn or [xx] */ bb_error_msg("bad address '%s'", org_host); if (ai_flags & DIE_ON_ERROR) xfunc_die(); @@ -183,8 +184,11 @@ USE_FEATURE_IPV6(sa_family_t af,) if (cp) { /* points to ":" or "]:" */ int sz = cp - host + 1; host = safe_strncpy(alloca(sz), host, sz); - if (ENABLE_FEATURE_IPV6 && *cp != ':') + if (ENABLE_FEATURE_IPV6 && *cp != ':') { cp++; /* skip ']' */ + if (*cp == '\0') /* [xx] without port */ + goto skip; + } cp++; /* skip ':' */ port = bb_strtou(cp, NULL, 10); if (errno || (unsigned)port > 0xffff) { @@ -193,6 +197,7 @@ USE_FEATURE_IPV6(sa_family_t af,) xfunc_die(); return NULL; } + skip: ; } memset(&hint, 0 , sizeof(hint)); diff --git a/networking/ftpd.c b/networking/ftpd.c index b2d6ef2d2..e83066032 100644 --- a/networking/ftpd.c +++ b/networking/ftpd.c @@ -4,6 +4,8 @@ * * Author: Adam Tkac <vonsch@gmail.com> * + * Licensed under GPLv2, see file LICENSE in this tarball for details. + * * Only subset of FTP protocol is implemented but vast majority of clients * should not have any problem. You have to run this daemon via inetd. * @@ -32,6 +34,8 @@ #define FTP_GOODBYE 221 #define FTP_TRANSFEROK 226 #define FTP_PASVOK 227 +/*#define FTP_EPRTOK 228*/ +#define FTP_EPSVOK 229 #define FTP_LOGINOK 230 #define FTP_CWDOK 250 #define FTP_RMDIROK 250 @@ -70,6 +74,7 @@ enum { const_CDUP = mk_const4('C', 'D', 'U', 'P'), const_CWD = mk_const3('C', 'W', 'D'), const_DELE = mk_const4('D', 'E', 'L', 'E'), + const_EPSV = mk_const4('E', 'P', 'S', 'V'), const_HELP = mk_const4('H', 'E', 'L', 'P'), const_LIST = mk_const4('L', 'I', 'S', 'T'), const_MKD = mk_const3('M', 'K', 'D'), @@ -86,6 +91,7 @@ enum { const_RMD = mk_const3('R', 'M', 'D'), const_RNFR = mk_const4('R', 'N', 'F', 'R'), const_RNTO = mk_const4('R', 'N', 'T', 'O'), + const_SIZE = mk_const4('S', 'I', 'Z', 'E'), const_STAT = mk_const4('S', 'T', 'A', 'T'), const_STOR = mk_const4('S', 'T', 'O', 'R'), const_STOU = mk_const4('S', 'T', 'O', 'U'), @@ -232,26 +238,11 @@ handle_cdup(void) handle_cwd(); } -//static void -//handle_type(void) -//{ -// if (G.ftp_arg -// && ( ((G.ftp_arg[0] | 0x20) == 'i' && G.ftp_arg[1] == '\0') -// || !strcasecmp(G.ftp_arg, "L8") -// || !strcasecmp(G.ftp_arg, "L 8") -// ) -// ) { -// cmdio_write_ok(FTP_TYPEOK); -// } else { -// cmdio_write_error(FTP_BADCMD); -// } -//} - static void handle_stat(void) { cmdio_write_raw(STR(FTP_STATOK)"-FTP server status:\r\n" - "TYPE: BINARY\r\n" + " TYPE: BINARY\r\n" STR(FTP_STATOK)" Ok\r\n"); } @@ -259,11 +250,11 @@ static void handle_help(void) { cmdio_write_raw(STR(FTP_HELP)"-Commands:\r\n" - "ALLO CDUP CWD HELP LIST\r\n" - "MODE NLST NOOP PASS PASV PORT PWD QUIT\r\n" - "REST RETR STAT STRU SYST TYPE USER\r\n" + " ALLO CDUP CWD EPSV HELP LIST\r\n" + " MODE NLST NOOP PASS PASV PORT PWD QUIT\r\n" + " REST RETR SIZE STAT STRU SYST TYPE USER\r\n" #if ENABLE_FEATURE_FTP_WRITE - "APPE DELE MKD RMD RNFR RNTO STOR STOU\r\n" + " APPE DELE MKD RMD RNFR RNTO STOR STOU\r\n" #endif STR(FTP_HELP)" Ok\r\n"); } @@ -380,47 +371,57 @@ port_pasv_cleanup(void) G.pasv_listen_fd = -1; } -static void -handle_pasv(void) +static unsigned +bind_for_passive_mode(void) { - int bind_retries = 10; - unsigned short port; - enum { min_port = 1024, max_port = 65535 }; - char *addr, *response; + unsigned port; port_pasv_cleanup(); G.pasv_listen_fd = xsocket(G.local_addr->u.sa.sa_family, SOCK_STREAM, 0); setsockopt_reuseaddr(G.pasv_listen_fd); - /* TODO bind() with port == 0 and then call getsockname */ - while (--bind_retries) { - port = rand() % max_port; - if (port < min_port) { - port += min_port; - } + set_nport(G.local_addr, 0); + xbind(G.pasv_listen_fd, &G.local_addr->u.sa, G.local_addr->len); + xlisten(G.pasv_listen_fd, 1); + getsockname(G.pasv_listen_fd, &G.local_addr->u.sa, &G.local_addr->len); - set_nport(G.local_addr, htons(port)); - /* We don't want to use xbind, it'll die if port is in use */ - if (bind(G.pasv_listen_fd, &G.local_addr->u.sa, G.local_addr->len) != 0) { - /* do we want check if errno == EADDRINUSE ? */ - continue; - } - xlisten(G.pasv_listen_fd, 1); - break; - } + port = get_nport(&G.local_addr->u.sa); + port = ntohs(port); - if (!bind_retries) - bb_error_msg_and_die("can't create pasv socket"); + return port; +} - addr = xmalloc_sockaddr2dotted_noport(&G.local_addr->u.sa); +static void +handle_pasv(void) +{ + unsigned port; + char *addr, *response; + + port = bind_for_passive_mode(); + + if (G.local_addr->u.sa.sa_family == AF_INET) + addr = xmalloc_sockaddr2dotted_noport(&G.local_addr->u.sa); + else /* seen this in the wild done by other ftp servers: */ + addr = xstrdup("0.0.0.0"); replace_char(addr, '.', ','); - response = xasprintf(" Entering Passive Mode (%s,%u,%u)", + response = xasprintf(STR(FTP_PASVOK)" Entering Passive Mode (%s,%u,%u)\r\n", addr, (int)(port >> 8), (int)(port & 255)); free(addr); + cmdio_write_raw(response); + free(response); +} - cmdio_write(FTP_PASVOK, response); +static void +handle_epsv(void) +{ + unsigned port; + char *response; + + port = bind_for_passive_mode(); + response = xasprintf(STR(FTP_EPSVOK)" EPSV Ok (|||%u|)\r\n", port); + cmdio_write_raw(response); free(response); } @@ -477,7 +478,7 @@ static void handle_retr(void) { struct stat statbuf; - int trans_ret, retval; + int trans_ret; int remote_fd; int opened_file; off_t offset = G.restart_pos; @@ -495,8 +496,7 @@ handle_retr(void) return; } - retval = fstat(opened_file, &statbuf); - if (retval < 0 || !S_ISREG(statbuf.st_mode)) { + if (fstat(opened_file, &statbuf) != 0 || !S_ISREG(statbuf.st_mode)) { /* Note - pretend open failed */ cmdio_write_error(FTP_FILEFAIL); goto file_close_out; @@ -580,10 +580,8 @@ write_filestats(int fd, const char *filename, off_t size; char *stats, *lnkname = NULL, *perms; const char *name; - int retval; char timestr[32]; struct tm *tm; - const char *format = "%b %d %H:%M"; name = bb_get_last_path_component_nostrip(filename); @@ -595,8 +593,7 @@ write_filestats(int fd, const char *filename, lnkname = xmalloc_readlink(filename); tm = gmtime(&statbuf->st_mtime); - retval = strftime(timestr, sizeof(timestr), format, tm); - if (retval == 0) + if (strftime(timestr, sizeof(timestr), "%b %d %H:%M", tm) == 0) bb_error_msg_and_die("strftime"); timestr[sizeof(timestr) - 1] = '\0'; @@ -719,6 +716,23 @@ handle_stat_file(void) handle_dir_common(1, 1); } +static void +handle_size(void) +{ + struct stat statbuf; + char buf[sizeof(STR(FTP_STATFILE_OK)" %"OFF_FMT"u\r\n") + sizeof(off_t)*3]; + + if (!G.ftp_arg + || stat(G.ftp_arg, &statbuf) != 0 + || !S_ISREG(statbuf.st_mode) + ) { + cmdio_write_error(FTP_FILEFAIL); + return; + } + sprintf(buf, STR(FTP_STATFILE_OK)" %"OFF_FMT"u\r\n", statbuf.st_size); + cmdio_write_raw(buf); +} + /* Upload commands */ #if ENABLE_FEATURE_FTP_WRITE @@ -1003,63 +1017,52 @@ int ftpd_main(int argc UNUSED_PARAM, char **argv) cmdio_write_ok(FTP_GOODBYE); return 0; } - if (cmdval == const_PWD) + else if (cmdval == const_USER) + cmdio_write_ok(FTP_GIVEPWORD); + else if (cmdval == const_PASS) + cmdio_write_ok(FTP_LOGINOK); + else if (cmdval == const_NOOP) + cmdio_write_ok(FTP_NOOPOK); + else if (cmdval == const_TYPE) + cmdio_write_ok(FTP_TYPEOK); + else if (cmdval == const_STRU) + cmdio_write_ok(FTP_STRUOK); + else if (cmdval == const_MODE) + cmdio_write_ok(FTP_MODEOK); + else if (cmdval == const_ALLO) + cmdio_write_ok(FTP_ALLOOK); + else if (cmdval == const_SYST) + cmdio_write_raw(STR(FTP_SYSTOK)" UNIX Type: L8\r\n"); + else if (cmdval == const_PWD) handle_pwd(); else if (cmdval == const_CWD) handle_cwd(); else if (cmdval == const_CDUP) /* cd .. */ handle_cdup(); - else if (cmdval == const_PASV) - handle_pasv(); - else if (cmdval == const_RETR) - handle_retr(); - else if (cmdval == const_NOOP) - cmdio_write_ok(FTP_NOOPOK); - else if (cmdval == const_SYST) - cmdio_write_raw(STR(FTP_SYSTOK)" UNIX Type: L8\r\n"); else if (cmdval == const_HELP) handle_help(); else if (cmdval == const_LIST) /* ls -l */ handle_list(); - else if (cmdval == const_TYPE) - //handle_type(); - cmdio_write_ok(FTP_TYPEOK); - else if (cmdval == const_PORT) - handle_port(); - else if (cmdval == const_REST) - handle_rest(); else if (cmdval == const_NLST) /* "name list", bare ls */ handle_nlst(); - else if (cmdval == const_STRU) { - //if (G.ftp_arg - // && (G.ftp_arg[0] | 0x20) == 'f' - // && G.ftp_arg[1] == '\0' - //) { - cmdio_write_ok(FTP_STRUOK); - //} else - // cmdio_write_raw(STR(FTP_BADSTRU)" Bad STRU command\r\n"); - } else if (cmdval == const_MODE) { - //if (G.ftp_arg - // && (G.ftp_arg[0] | 0x20) == 's' - // && G.ftp_arg[1] == '\0' - //) { - cmdio_write_ok(FTP_MODEOK); - //} else - // cmdio_write_raw(STR(FTP_BADMODE)" Bad MODE command\r\n"); - } - else if (cmdval == const_ALLO) - cmdio_write_ok(FTP_ALLOOK); + else if (cmdval == const_SIZE) + handle_size(); else if (cmdval == const_STAT) { if (G.ftp_arg == NULL) handle_stat(); else handle_stat_file(); - } else if (cmdval == const_USER) { - /* FTP_LOGINERR confuses clients: */ - /* cmdio_write_raw(STR(FTP_LOGINERR)" Can't change to another user\r\n"); */ - cmdio_write_ok(FTP_GIVEPWORD); - } else if (cmdval == const_PASS) - cmdio_write_ok(FTP_LOGINOK); + } + else if (cmdval == const_PASV) + handle_pasv(); + else if (cmdval == const_EPSV) + handle_epsv(); + else if (cmdval == const_RETR) + handle_retr(); + else if (cmdval == const_PORT) + handle_port(); + else if (cmdval == const_REST) + handle_rest(); #if ENABLE_FEATURE_FTP_WRITE else if (G.opts & OPT_w) { if (cmdval == const_STOR) @@ -1096,10 +1099,8 @@ int ftpd_main(int argc UNUSED_PARAM, char **argv) else { /* Which unsupported commands were seen in the wild? * (doesn't necessarily mean "we must support them") - * wget 1.11.4: SIZE - todo * lftp 3.6.3: FEAT - is it useful? * MDTM - works fine without it anyway - * IPv6-style PASV: "EPSV 2" - todo */ cmdio_write_raw(STR(FTP_BADCMD)" Unknown command\r\n"); } |