diff options
author | Denis Vlasenko <vda.linux@googlemail.com> | 2009-03-09 13:01:08 +0000 |
---|---|---|
committer | Denis Vlasenko <vda.linux@googlemail.com> | 2009-03-09 13:01:08 +0000 |
commit | 9b2fbda53853ae651349f97bcf86c891cc137f92 (patch) | |
tree | 23dd0fe4e01328899ba94e348a206ecb13ca37ac /networking/ftpd.c | |
parent | 57a3b174989c7778fb255f716e090d5be807b9b7 (diff) | |
download | busybox-w32-9b2fbda53853ae651349f97bcf86c891cc137f92.tar.gz busybox-w32-9b2fbda53853ae651349f97bcf86c891cc137f92.tar.bz2 busybox-w32-9b2fbda53853ae651349f97bcf86c891cc137f92.zip |
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
Diffstat (limited to 'networking/ftpd.c')
-rw-r--r-- | networking/ftpd.c | 193 |
1 files changed, 97 insertions, 96 deletions
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 @@ | |||
4 | * | 4 | * |
5 | * Author: Adam Tkac <vonsch@gmail.com> | 5 | * Author: Adam Tkac <vonsch@gmail.com> |
6 | * | 6 | * |
7 | * Licensed under GPLv2, see file LICENSE in this tarball for details. | ||
8 | * | ||
7 | * Only subset of FTP protocol is implemented but vast majority of clients | 9 | * Only subset of FTP protocol is implemented but vast majority of clients |
8 | * should not have any problem. You have to run this daemon via inetd. | 10 | * should not have any problem. You have to run this daemon via inetd. |
9 | * | 11 | * |
@@ -32,6 +34,8 @@ | |||
32 | #define FTP_GOODBYE 221 | 34 | #define FTP_GOODBYE 221 |
33 | #define FTP_TRANSFEROK 226 | 35 | #define FTP_TRANSFEROK 226 |
34 | #define FTP_PASVOK 227 | 36 | #define FTP_PASVOK 227 |
37 | /*#define FTP_EPRTOK 228*/ | ||
38 | #define FTP_EPSVOK 229 | ||
35 | #define FTP_LOGINOK 230 | 39 | #define FTP_LOGINOK 230 |
36 | #define FTP_CWDOK 250 | 40 | #define FTP_CWDOK 250 |
37 | #define FTP_RMDIROK 250 | 41 | #define FTP_RMDIROK 250 |
@@ -70,6 +74,7 @@ enum { | |||
70 | const_CDUP = mk_const4('C', 'D', 'U', 'P'), | 74 | const_CDUP = mk_const4('C', 'D', 'U', 'P'), |
71 | const_CWD = mk_const3('C', 'W', 'D'), | 75 | const_CWD = mk_const3('C', 'W', 'D'), |
72 | const_DELE = mk_const4('D', 'E', 'L', 'E'), | 76 | const_DELE = mk_const4('D', 'E', 'L', 'E'), |
77 | const_EPSV = mk_const4('E', 'P', 'S', 'V'), | ||
73 | const_HELP = mk_const4('H', 'E', 'L', 'P'), | 78 | const_HELP = mk_const4('H', 'E', 'L', 'P'), |
74 | const_LIST = mk_const4('L', 'I', 'S', 'T'), | 79 | const_LIST = mk_const4('L', 'I', 'S', 'T'), |
75 | const_MKD = mk_const3('M', 'K', 'D'), | 80 | const_MKD = mk_const3('M', 'K', 'D'), |
@@ -86,6 +91,7 @@ enum { | |||
86 | const_RMD = mk_const3('R', 'M', 'D'), | 91 | const_RMD = mk_const3('R', 'M', 'D'), |
87 | const_RNFR = mk_const4('R', 'N', 'F', 'R'), | 92 | const_RNFR = mk_const4('R', 'N', 'F', 'R'), |
88 | const_RNTO = mk_const4('R', 'N', 'T', 'O'), | 93 | const_RNTO = mk_const4('R', 'N', 'T', 'O'), |
94 | const_SIZE = mk_const4('S', 'I', 'Z', 'E'), | ||
89 | const_STAT = mk_const4('S', 'T', 'A', 'T'), | 95 | const_STAT = mk_const4('S', 'T', 'A', 'T'), |
90 | const_STOR = mk_const4('S', 'T', 'O', 'R'), | 96 | const_STOR = mk_const4('S', 'T', 'O', 'R'), |
91 | const_STOU = mk_const4('S', 'T', 'O', 'U'), | 97 | const_STOU = mk_const4('S', 'T', 'O', 'U'), |
@@ -232,26 +238,11 @@ handle_cdup(void) | |||
232 | handle_cwd(); | 238 | handle_cwd(); |
233 | } | 239 | } |
234 | 240 | ||
235 | //static void | ||
236 | //handle_type(void) | ||
237 | //{ | ||
238 | // if (G.ftp_arg | ||
239 | // && ( ((G.ftp_arg[0] | 0x20) == 'i' && G.ftp_arg[1] == '\0') | ||
240 | // || !strcasecmp(G.ftp_arg, "L8") | ||
241 | // || !strcasecmp(G.ftp_arg, "L 8") | ||
242 | // ) | ||
243 | // ) { | ||
244 | // cmdio_write_ok(FTP_TYPEOK); | ||
245 | // } else { | ||
246 | // cmdio_write_error(FTP_BADCMD); | ||
247 | // } | ||
248 | //} | ||
249 | |||
250 | static void | 241 | static void |
251 | handle_stat(void) | 242 | handle_stat(void) |
252 | { | 243 | { |
253 | cmdio_write_raw(STR(FTP_STATOK)"-FTP server status:\r\n" | 244 | cmdio_write_raw(STR(FTP_STATOK)"-FTP server status:\r\n" |
254 | "TYPE: BINARY\r\n" | 245 | " TYPE: BINARY\r\n" |
255 | STR(FTP_STATOK)" Ok\r\n"); | 246 | STR(FTP_STATOK)" Ok\r\n"); |
256 | } | 247 | } |
257 | 248 | ||
@@ -259,11 +250,11 @@ static void | |||
259 | handle_help(void) | 250 | handle_help(void) |
260 | { | 251 | { |
261 | cmdio_write_raw(STR(FTP_HELP)"-Commands:\r\n" | 252 | cmdio_write_raw(STR(FTP_HELP)"-Commands:\r\n" |
262 | "ALLO CDUP CWD HELP LIST\r\n" | 253 | " ALLO CDUP CWD EPSV HELP LIST\r\n" |
263 | "MODE NLST NOOP PASS PASV PORT PWD QUIT\r\n" | 254 | " MODE NLST NOOP PASS PASV PORT PWD QUIT\r\n" |
264 | "REST RETR STAT STRU SYST TYPE USER\r\n" | 255 | " REST RETR SIZE STAT STRU SYST TYPE USER\r\n" |
265 | #if ENABLE_FEATURE_FTP_WRITE | 256 | #if ENABLE_FEATURE_FTP_WRITE |
266 | "APPE DELE MKD RMD RNFR RNTO STOR STOU\r\n" | 257 | " APPE DELE MKD RMD RNFR RNTO STOR STOU\r\n" |
267 | #endif | 258 | #endif |
268 | STR(FTP_HELP)" Ok\r\n"); | 259 | STR(FTP_HELP)" Ok\r\n"); |
269 | } | 260 | } |
@@ -380,47 +371,57 @@ port_pasv_cleanup(void) | |||
380 | G.pasv_listen_fd = -1; | 371 | G.pasv_listen_fd = -1; |
381 | } | 372 | } |
382 | 373 | ||
383 | static void | 374 | static unsigned |
384 | handle_pasv(void) | 375 | bind_for_passive_mode(void) |
385 | { | 376 | { |
386 | int bind_retries = 10; | 377 | unsigned port; |
387 | unsigned short port; | ||
388 | enum { min_port = 1024, max_port = 65535 }; | ||
389 | char *addr, *response; | ||
390 | 378 | ||
391 | port_pasv_cleanup(); | 379 | port_pasv_cleanup(); |
392 | 380 | ||
393 | G.pasv_listen_fd = xsocket(G.local_addr->u.sa.sa_family, SOCK_STREAM, 0); | 381 | G.pasv_listen_fd = xsocket(G.local_addr->u.sa.sa_family, SOCK_STREAM, 0); |
394 | setsockopt_reuseaddr(G.pasv_listen_fd); | 382 | setsockopt_reuseaddr(G.pasv_listen_fd); |
395 | 383 | ||
396 | /* TODO bind() with port == 0 and then call getsockname */ | 384 | set_nport(G.local_addr, 0); |
397 | while (--bind_retries) { | 385 | xbind(G.pasv_listen_fd, &G.local_addr->u.sa, G.local_addr->len); |
398 | port = rand() % max_port; | 386 | xlisten(G.pasv_listen_fd, 1); |
399 | if (port < min_port) { | 387 | getsockname(G.pasv_listen_fd, &G.local_addr->u.sa, &G.local_addr->len); |
400 | port += min_port; | ||
401 | } | ||
402 | 388 | ||
403 | set_nport(G.local_addr, htons(port)); | 389 | port = get_nport(&G.local_addr->u.sa); |
404 | /* We don't want to use xbind, it'll die if port is in use */ | 390 | port = ntohs(port); |
405 | if (bind(G.pasv_listen_fd, &G.local_addr->u.sa, G.local_addr->len) != 0) { | ||
406 | /* do we want check if errno == EADDRINUSE ? */ | ||
407 | continue; | ||
408 | } | ||
409 | xlisten(G.pasv_listen_fd, 1); | ||
410 | break; | ||
411 | } | ||
412 | 391 | ||
413 | if (!bind_retries) | 392 | return port; |
414 | bb_error_msg_and_die("can't create pasv socket"); | 393 | } |
415 | 394 | ||
416 | addr = xmalloc_sockaddr2dotted_noport(&G.local_addr->u.sa); | 395 | static void |
396 | handle_pasv(void) | ||
397 | { | ||
398 | unsigned port; | ||
399 | char *addr, *response; | ||
400 | |||
401 | port = bind_for_passive_mode(); | ||
402 | |||
403 | if (G.local_addr->u.sa.sa_family == AF_INET) | ||
404 | addr = xmalloc_sockaddr2dotted_noport(&G.local_addr->u.sa); | ||
405 | else /* seen this in the wild done by other ftp servers: */ | ||
406 | addr = xstrdup("0.0.0.0"); | ||
417 | replace_char(addr, '.', ','); | 407 | replace_char(addr, '.', ','); |
418 | 408 | ||
419 | response = xasprintf(" Entering Passive Mode (%s,%u,%u)", | 409 | response = xasprintf(STR(FTP_PASVOK)" Entering Passive Mode (%s,%u,%u)\r\n", |
420 | addr, (int)(port >> 8), (int)(port & 255)); | 410 | addr, (int)(port >> 8), (int)(port & 255)); |
421 | free(addr); | 411 | free(addr); |
412 | cmdio_write_raw(response); | ||
413 | free(response); | ||
414 | } | ||
422 | 415 | ||
423 | cmdio_write(FTP_PASVOK, response); | 416 | static void |
417 | handle_epsv(void) | ||
418 | { | ||
419 | unsigned port; | ||
420 | char *response; | ||
421 | |||
422 | port = bind_for_passive_mode(); | ||
423 | response = xasprintf(STR(FTP_EPSVOK)" EPSV Ok (|||%u|)\r\n", port); | ||
424 | cmdio_write_raw(response); | ||
424 | free(response); | 425 | free(response); |
425 | } | 426 | } |
426 | 427 | ||
@@ -477,7 +478,7 @@ static void | |||
477 | handle_retr(void) | 478 | handle_retr(void) |
478 | { | 479 | { |
479 | struct stat statbuf; | 480 | struct stat statbuf; |
480 | int trans_ret, retval; | 481 | int trans_ret; |
481 | int remote_fd; | 482 | int remote_fd; |
482 | int opened_file; | 483 | int opened_file; |
483 | off_t offset = G.restart_pos; | 484 | off_t offset = G.restart_pos; |
@@ -495,8 +496,7 @@ handle_retr(void) | |||
495 | return; | 496 | return; |
496 | } | 497 | } |
497 | 498 | ||
498 | retval = fstat(opened_file, &statbuf); | 499 | if (fstat(opened_file, &statbuf) != 0 || !S_ISREG(statbuf.st_mode)) { |
499 | if (retval < 0 || !S_ISREG(statbuf.st_mode)) { | ||
500 | /* Note - pretend open failed */ | 500 | /* Note - pretend open failed */ |
501 | cmdio_write_error(FTP_FILEFAIL); | 501 | cmdio_write_error(FTP_FILEFAIL); |
502 | goto file_close_out; | 502 | goto file_close_out; |
@@ -580,10 +580,8 @@ write_filestats(int fd, const char *filename, | |||
580 | off_t size; | 580 | off_t size; |
581 | char *stats, *lnkname = NULL, *perms; | 581 | char *stats, *lnkname = NULL, *perms; |
582 | const char *name; | 582 | const char *name; |
583 | int retval; | ||
584 | char timestr[32]; | 583 | char timestr[32]; |
585 | struct tm *tm; | 584 | struct tm *tm; |
586 | const char *format = "%b %d %H:%M"; | ||
587 | 585 | ||
588 | name = bb_get_last_path_component_nostrip(filename); | 586 | name = bb_get_last_path_component_nostrip(filename); |
589 | 587 | ||
@@ -595,8 +593,7 @@ write_filestats(int fd, const char *filename, | |||
595 | lnkname = xmalloc_readlink(filename); | 593 | lnkname = xmalloc_readlink(filename); |
596 | 594 | ||
597 | tm = gmtime(&statbuf->st_mtime); | 595 | tm = gmtime(&statbuf->st_mtime); |
598 | retval = strftime(timestr, sizeof(timestr), format, tm); | 596 | if (strftime(timestr, sizeof(timestr), "%b %d %H:%M", tm) == 0) |
599 | if (retval == 0) | ||
600 | bb_error_msg_and_die("strftime"); | 597 | bb_error_msg_and_die("strftime"); |
601 | 598 | ||
602 | timestr[sizeof(timestr) - 1] = '\0'; | 599 | timestr[sizeof(timestr) - 1] = '\0'; |
@@ -719,6 +716,23 @@ handle_stat_file(void) | |||
719 | handle_dir_common(1, 1); | 716 | handle_dir_common(1, 1); |
720 | } | 717 | } |
721 | 718 | ||
719 | static void | ||
720 | handle_size(void) | ||
721 | { | ||
722 | struct stat statbuf; | ||
723 | char buf[sizeof(STR(FTP_STATFILE_OK)" %"OFF_FMT"u\r\n") + sizeof(off_t)*3]; | ||
724 | |||
725 | if (!G.ftp_arg | ||
726 | || stat(G.ftp_arg, &statbuf) != 0 | ||
727 | || !S_ISREG(statbuf.st_mode) | ||
728 | ) { | ||
729 | cmdio_write_error(FTP_FILEFAIL); | ||
730 | return; | ||
731 | } | ||
732 | sprintf(buf, STR(FTP_STATFILE_OK)" %"OFF_FMT"u\r\n", statbuf.st_size); | ||
733 | cmdio_write_raw(buf); | ||
734 | } | ||
735 | |||
722 | /* Upload commands */ | 736 | /* Upload commands */ |
723 | 737 | ||
724 | #if ENABLE_FEATURE_FTP_WRITE | 738 | #if ENABLE_FEATURE_FTP_WRITE |
@@ -1003,63 +1017,52 @@ int ftpd_main(int argc UNUSED_PARAM, char **argv) | |||
1003 | cmdio_write_ok(FTP_GOODBYE); | 1017 | cmdio_write_ok(FTP_GOODBYE); |
1004 | return 0; | 1018 | return 0; |
1005 | } | 1019 | } |
1006 | if (cmdval == const_PWD) | 1020 | else if (cmdval == const_USER) |
1021 | cmdio_write_ok(FTP_GIVEPWORD); | ||
1022 | else if (cmdval == const_PASS) | ||
1023 | cmdio_write_ok(FTP_LOGINOK); | ||
1024 | else if (cmdval == const_NOOP) | ||
1025 | cmdio_write_ok(FTP_NOOPOK); | ||
1026 | else if (cmdval == const_TYPE) | ||
1027 | cmdio_write_ok(FTP_TYPEOK); | ||
1028 | else if (cmdval == const_STRU) | ||
1029 | cmdio_write_ok(FTP_STRUOK); | ||
1030 | else if (cmdval == const_MODE) | ||
1031 | cmdio_write_ok(FTP_MODEOK); | ||
1032 | else if (cmdval == const_ALLO) | ||
1033 | cmdio_write_ok(FTP_ALLOOK); | ||
1034 | else if (cmdval == const_SYST) | ||
1035 | cmdio_write_raw(STR(FTP_SYSTOK)" UNIX Type: L8\r\n"); | ||
1036 | else if (cmdval == const_PWD) | ||
1007 | handle_pwd(); | 1037 | handle_pwd(); |
1008 | else if (cmdval == const_CWD) | 1038 | else if (cmdval == const_CWD) |
1009 | handle_cwd(); | 1039 | handle_cwd(); |
1010 | else if (cmdval == const_CDUP) /* cd .. */ | 1040 | else if (cmdval == const_CDUP) /* cd .. */ |
1011 | handle_cdup(); | 1041 | handle_cdup(); |
1012 | else if (cmdval == const_PASV) | ||
1013 | handle_pasv(); | ||
1014 | else if (cmdval == const_RETR) | ||
1015 | handle_retr(); | ||
1016 | else if (cmdval == const_NOOP) | ||
1017 | cmdio_write_ok(FTP_NOOPOK); | ||
1018 | else if (cmdval == const_SYST) | ||
1019 | cmdio_write_raw(STR(FTP_SYSTOK)" UNIX Type: L8\r\n"); | ||
1020 | else if (cmdval == const_HELP) | 1042 | else if (cmdval == const_HELP) |
1021 | handle_help(); | 1043 | handle_help(); |
1022 | else if (cmdval == const_LIST) /* ls -l */ | 1044 | else if (cmdval == const_LIST) /* ls -l */ |
1023 | handle_list(); | 1045 | handle_list(); |
1024 | else if (cmdval == const_TYPE) | ||
1025 | //handle_type(); | ||
1026 | cmdio_write_ok(FTP_TYPEOK); | ||
1027 | else if (cmdval == const_PORT) | ||
1028 | handle_port(); | ||
1029 | else if (cmdval == const_REST) | ||
1030 | handle_rest(); | ||
1031 | else if (cmdval == const_NLST) /* "name list", bare ls */ | 1046 | else if (cmdval == const_NLST) /* "name list", bare ls */ |
1032 | handle_nlst(); | 1047 | handle_nlst(); |
1033 | else if (cmdval == const_STRU) { | 1048 | else if (cmdval == const_SIZE) |
1034 | //if (G.ftp_arg | 1049 | handle_size(); |
1035 | // && (G.ftp_arg[0] | 0x20) == 'f' | ||
1036 | // && G.ftp_arg[1] == '\0' | ||
1037 | //) { | ||
1038 | cmdio_write_ok(FTP_STRUOK); | ||
1039 | //} else | ||
1040 | // cmdio_write_raw(STR(FTP_BADSTRU)" Bad STRU command\r\n"); | ||
1041 | } else if (cmdval == const_MODE) { | ||
1042 | //if (G.ftp_arg | ||
1043 | // && (G.ftp_arg[0] | 0x20) == 's' | ||
1044 | // && G.ftp_arg[1] == '\0' | ||
1045 | //) { | ||
1046 | cmdio_write_ok(FTP_MODEOK); | ||
1047 | //} else | ||
1048 | // cmdio_write_raw(STR(FTP_BADMODE)" Bad MODE command\r\n"); | ||
1049 | } | ||
1050 | else if (cmdval == const_ALLO) | ||
1051 | cmdio_write_ok(FTP_ALLOOK); | ||
1052 | else if (cmdval == const_STAT) { | 1050 | else if (cmdval == const_STAT) { |
1053 | if (G.ftp_arg == NULL) | 1051 | if (G.ftp_arg == NULL) |
1054 | handle_stat(); | 1052 | handle_stat(); |
1055 | else | 1053 | else |
1056 | handle_stat_file(); | 1054 | handle_stat_file(); |
1057 | } else if (cmdval == const_USER) { | 1055 | } |
1058 | /* FTP_LOGINERR confuses clients: */ | 1056 | else if (cmdval == const_PASV) |
1059 | /* cmdio_write_raw(STR(FTP_LOGINERR)" Can't change to another user\r\n"); */ | 1057 | handle_pasv(); |
1060 | cmdio_write_ok(FTP_GIVEPWORD); | 1058 | else if (cmdval == const_EPSV) |
1061 | } else if (cmdval == const_PASS) | 1059 | handle_epsv(); |
1062 | cmdio_write_ok(FTP_LOGINOK); | 1060 | else if (cmdval == const_RETR) |
1061 | handle_retr(); | ||
1062 | else if (cmdval == const_PORT) | ||
1063 | handle_port(); | ||
1064 | else if (cmdval == const_REST) | ||
1065 | handle_rest(); | ||
1063 | #if ENABLE_FEATURE_FTP_WRITE | 1066 | #if ENABLE_FEATURE_FTP_WRITE |
1064 | else if (G.opts & OPT_w) { | 1067 | else if (G.opts & OPT_w) { |
1065 | if (cmdval == const_STOR) | 1068 | if (cmdval == const_STOR) |
@@ -1096,10 +1099,8 @@ int ftpd_main(int argc UNUSED_PARAM, char **argv) | |||
1096 | else { | 1099 | else { |
1097 | /* Which unsupported commands were seen in the wild? | 1100 | /* Which unsupported commands were seen in the wild? |
1098 | * (doesn't necessarily mean "we must support them") | 1101 | * (doesn't necessarily mean "we must support them") |
1099 | * wget 1.11.4: SIZE - todo | ||
1100 | * lftp 3.6.3: FEAT - is it useful? | 1102 | * lftp 3.6.3: FEAT - is it useful? |
1101 | * MDTM - works fine without it anyway | 1103 | * MDTM - works fine without it anyway |
1102 | * IPv6-style PASV: "EPSV 2" - todo | ||
1103 | */ | 1104 | */ |
1104 | cmdio_write_raw(STR(FTP_BADCMD)" Unknown command\r\n"); | 1105 | cmdio_write_raw(STR(FTP_BADCMD)" Unknown command\r\n"); |
1105 | } | 1106 | } |