aboutsummaryrefslogtreecommitdiff
path: root/networking/ftpd.c
diff options
context:
space:
mode:
authorDenis Vlasenko <vda.linux@googlemail.com>2009-03-09 13:01:08 +0000
committerDenis Vlasenko <vda.linux@googlemail.com>2009-03-09 13:01:08 +0000
commit9b2fbda53853ae651349f97bcf86c891cc137f92 (patch)
tree23dd0fe4e01328899ba94e348a206ecb13ca37ac /networking/ftpd.c
parent57a3b174989c7778fb255f716e090d5be807b9b7 (diff)
downloadbusybox-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.c193
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
250static void 241static void
251handle_stat(void) 242handle_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
259handle_help(void) 250handle_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
383static void 374static unsigned
384handle_pasv(void) 375bind_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); 395static void
396handle_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); 416static void
417handle_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
477handle_retr(void) 478handle_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
719static void
720handle_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 }