diff --git a/components/net/lwip/apps/ftpd.c b/components/net/lwip/apps/ftpd.c index af48237e4..df1609fe1 100644 --- a/components/net/lwip/apps/ftpd.c +++ b/components/net/lwip/apps/ftpd.c @@ -11,17 +11,24 @@ #define FTP_MAX_CONNECTION 2 #define FTP_USER "rtt" #define FTP_PASSWORD "demo" -#define FTP_WELCOME_MSG "220-= welcome on RT-Thread FTP server =-\r\n220 \r\n" +#define FTP_WELCOME_MSG "220 welcome on RT-Thread FTP server.\r\n" #define FTP_BUFFER_SIZE 1024 +#define INET_ADDRSTRLEN 16 + + struct ftp_session { rt_bool_t is_anonymous; int sockfd; struct sockaddr_in remote; + struct sockaddr_in server; + + char serveraddr[INET_ADDRSTRLEN]; /* pasv data */ + int pasv_listen_sockfd; char pasv_active; int pasv_sockfd; @@ -43,6 +50,7 @@ struct ftp_session* ftp_new_session() struct ftp_session* session; session = (struct ftp_session*)rt_malloc(sizeof(struct ftp_session)); + rt_memset((void *)session, 0, sizeof(struct ftp_session)); session->next = session_list; session_list = session; @@ -71,6 +79,83 @@ void ftp_close_session(struct ftp_session* session) rt_free(session); } +static int open_data_connection(struct ftp_session* session) +{ + socklen_t len = sizeof(struct sockaddr); + struct sockaddr_in sin; +#if 0 + /* Previous PORT command from client */ + if (ctrl->data_address[0]) { + ctrl->data_sd = socket(AF_INET, SOCK_STREAM, 0); + if (-1 == ctrl->data_sd) { + printf("Failed creating data socket"); + return -1; + } + + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_port = htons(ctrl->data_port); + inet_aton(ctrl->data_address, &(sin.sin_addr)); + + if (connect(ctrl->data_sd, (struct sockaddr *)&sin, len) == -1) { + printf("Failed connecting data socket to client"); + close(ctrl->data_sd); + ctrl->data_sd = -1; + + return -1; + } + + DBG("Connected successfully to client's previously requested address:PORT %s:%d", ctrl->data_address, ctrl->data_port); + return 0; + } +#endif + /* Previous PASV command, accept connect from client */ + if (session->pasv_listen_sockfd > 0) { + char client_ip[100]; + + session->pasv_sockfd = accept(session->pasv_listen_sockfd, (struct sockaddr *)&sin, &len); + if (-1 == session->pasv_sockfd) { + printf("Failed accepting connection from client"); + return -1; + } + + len = sizeof(struct sockaddr); + if (-1 == getpeername(session->pasv_sockfd, (struct sockaddr *)&sin, &len)) { + printf("Cannot determine client address"); + closesocket(session->pasv_sockfd); + session->pasv_sockfd = -1; + return -1; + } + + printf("Client PASV data connection from %s\n", inet_ntoa(sin.sin_addr)); + } + + return 0; +} + +static void close_data_connection(struct ftp_session* session) +{ + /* PASV server listening socket */ + if (session->pasv_listen_sockfd > 0) { + closesocket(session->pasv_listen_sockfd); + session->pasv_listen_sockfd = -1; + } + + /* PASV client socket */ + if (session->pasv_sockfd > 0) { + closesocket(session->pasv_sockfd); + session->pasv_sockfd = -1; + } +#if 0 + /* PORT */ + if (ctrl->data_address[0]) { + ctrl->data_address[0] = 0; + ctrl->data_port = 0; + } +#endif +} + + int ftp_get_filesize(char * filename) { int pos; @@ -179,6 +264,10 @@ void ftpd_thread_entry(void* parameter) session = ftp_new_session(); if (session != NULL) { + if (-1 == getsockname(com_socket, (struct sockaddr *)&session->server, &addr_len)) { + printf("Cannot determine our address, need it if client should connect to us\n"); + } + ipaddr_ntoa_r(&(session->server.sin_addr), session->serveraddr, sizeof(session->serveraddr)); strcpy(session->currentdir, FTP_SRV_ROOT); session->sockfd = com_socket; session->remote = remote; @@ -201,6 +290,7 @@ void ftpd_thread_entry(void* parameter) rt_kprintf("Client %s disconnected\n", inet_ntoa(session->remote.sin_addr)); FD_CLR(session->sockfd, &readfds); closesocket(session->sockfd); + session->sockfd = -1; ftp_close_session(session); } else @@ -210,6 +300,7 @@ void ftpd_thread_entry(void* parameter) { rt_kprintf("Client %s disconnected\r\n", inet_ntoa(session->remote.sin_addr)); closesocket(session->sockfd); + session->sockfd = -1; ftp_close_session(session); } } @@ -359,7 +450,7 @@ int ftp_process_request(struct ftp_session* session, char *buf) } else if (strcmp(parameter_ptr, FTP_USER) == 0) { - session->is_anonymous = RT_FALSE; + session->is_anonymous = RT_FALSE; rt_sprintf(sbuf, "331 Password required for %s\r\n", parameter_ptr); send(session->sockfd, sbuf, strlen(sbuf), 0); } @@ -380,25 +471,26 @@ int ftp_process_request(struct ftp_session* session, char *buf) session->is_anonymous == RT_TRUE) { // password correct - rt_sprintf(sbuf, "230 User logged in\r\n"); + rt_sprintf(sbuf, "230 User logged in.\r\n"); send(session->sockfd, sbuf, strlen(sbuf), 0); - rt_free(sbuf); + rt_free(sbuf); return 0; } // incorrect password rt_sprintf(sbuf, "530 Login or Password incorrect. Bye!\r\n"); send(session->sockfd, sbuf, strlen(sbuf), 0); - rt_free(sbuf); + rt_free(sbuf); return -1; } else if(str_begin_with(buf, "LIST")==0 ) { memset(sbuf,0,FTP_BUFFER_SIZE); + open_data_connection(session); rt_sprintf(sbuf, "150 Opening Binary mode connection for file list.\r\n"); send(session->sockfd, sbuf, strlen(sbuf), 0); do_list(session->currentdir, session->pasv_sockfd); - closesocket(session->pasv_sockfd); + close_data_connection(session); session->pasv_active = 0; rt_sprintf(sbuf, "226 Transfert Complete.\r\n"); send(session->sockfd, sbuf, strlen(sbuf), 0); @@ -408,8 +500,9 @@ int ftp_process_request(struct ftp_session* session, char *buf) memset(sbuf, 0, FTP_BUFFER_SIZE); rt_sprintf(sbuf, "150 Opening Binary mode connection for file list.\r\n"); send(session->sockfd, sbuf, strlen(sbuf), 0); + open_data_connection(session); do_simple_list(session->currentdir, session->pasv_sockfd); - closesocket(session->pasv_sockfd); + close_data_connection(session); session->pasv_active = 0; rt_sprintf(sbuf, "226 Transfert Complete.\r\n"); send(session->sockfd, sbuf, strlen(sbuf), 0); @@ -436,8 +529,20 @@ int ftp_process_request(struct ftp_session* session, char *buf) else if(str_begin_with(buf, "PASV")==0) { int dig1, dig2; - int sockfd; - char optval='1'; + //int sockfd; + int optval=1; + int port; + struct sockaddr_in data; + socklen_t len = sizeof(struct sockaddr); + char *msg, *p; + + if (session->pasv_sockfd > 0) { + closesocket(session->pasv_sockfd); + session->pasv_sockfd = -1; + } + + if (session->pasv_listen_sockfd > 0) + closesocket(session->pasv_listen_sockfd); session->pasv_port = 10000; session->pasv_active = 1; @@ -448,40 +553,63 @@ int ftp_process_request(struct ftp_session* session, char *buf) dig2 = session->pasv_port % 256; FD_ZERO(&readfds); - if((sockfd=socket(PF_INET, SOCK_STREAM, 0))==-1) + if((session->pasv_listen_sockfd=socket(PF_INET, SOCK_STREAM, 0))==-1) { - rt_sprintf(sbuf, "425 Can't open data connection.\r\n"); + rt_sprintf(sbuf, "425 Can't open data connection0.\r\n"); send(session->sockfd, sbuf, strlen(sbuf), 0); goto err1; } - if(setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval))==-1) + if(setsockopt(session->pasv_listen_sockfd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval))==-1) { - rt_sprintf(sbuf, "425 Can't open data connection.\r\n"); + rt_sprintf(sbuf, "425 Can't open data connection1.\r\n"); send(session->sockfd, sbuf, strlen(sbuf), 0); goto err1; } - if(bind(sockfd, (struct sockaddr *)&local, addr_len)==-1) + if(bind(session->pasv_listen_sockfd, (struct sockaddr *)&local, addr_len)==-1) { - rt_sprintf(sbuf, "425 Can't open data connection.\r\n"); + rt_sprintf(sbuf, "425 Can't open data connection2.\r\n"); send(session->sockfd, sbuf, strlen(sbuf), 0); goto err1; } - if(listen(sockfd, 1)==-1) + if(listen(session->pasv_listen_sockfd, 1)==-1) { - rt_sprintf(sbuf, "425 Can't open data connection.\r\n"); + rt_sprintf(sbuf, "425 Can't open data connection3.\r\n"); send(session->sockfd, sbuf, strlen(sbuf), 0); goto err1; } + if (-1 == getsockname(session->pasv_listen_sockfd, (struct sockaddr *)&data, &len)) { + rt_kprintf("Cannot determine our address, need it if client should connect to us\n"); + goto err1; + } + + port = ntohs(data.sin_port); + rt_kprintf("Port %d\n", port); + + /* Convert server IP address and port to comma separated list */ + msg = strdup(session->serveraddr); + if (!msg) { + rt_sprintf(sbuf, "426 Internal server error.\r\n"); + send(session->sockfd, sbuf, strlen(sbuf), 0); + goto err1; + } + p = msg; + while ((p = strchr(p, '.'))) + *p++ = ','; + rt_kprintf("Listening %d seconds @ port %d\n", tv.tv_sec, session->pasv_port); - rt_sprintf(sbuf, "227 Entering passive mode (%d,%d,%d,%d,%d,%d)\r\n", 127, 0, 0, 1, dig1, dig2); + rt_sprintf(sbuf, "227 Entering passive mode (%s,%d,%d)\r\n", msg, port / 256, port % 256); send(session->sockfd, sbuf, strlen(sbuf), 0); + rt_free(sbuf); + rt_free(msg); + return 0; + #if 0 FD_SET(sockfd, &readfds); select(0, &readfds, 0, 0, &tv); if(FD_ISSET(sockfd, &readfds)) { if((session->pasv_sockfd = accept(sockfd, (struct sockaddr*)&pasvremote, &addr_len))==-1) { - rt_sprintf(sbuf, "425 Can't open data connection.\r\n"); + rt_sprintf(sbuf, "425 Can't open data connection4.\r\n"); send(session->sockfd, sbuf, strlen(sbuf), 0); goto err1; } @@ -500,11 +628,21 @@ err1: rt_free(sbuf); return 0; } + #endif +err1: + close_data_connection(session); + session->pasv_active = 0; + rt_free(sbuf); + rt_free(msg); + return 0; + } else if (str_begin_with(buf, "RETR")==0) { int file_size; + open_data_connection(session); + strcpy(filename, buf + 5); build_full_path(session, parameter_ptr, filename, 256); @@ -514,13 +652,15 @@ err1: rt_sprintf(sbuf, "550 \"%s\" : not a regular file\r\n", filename); send(session->sockfd, sbuf, strlen(sbuf), 0); session->offset=0; - rt_free(sbuf); + close_data_connection(session); + rt_free(sbuf); return 0; } fd = open(filename, O_RDONLY, 0); if (fd < 0) { + close_data_connection(session); rt_free(sbuf); return 0; } @@ -543,14 +683,16 @@ err1: rt_sprintf(sbuf, "226 Finished.\r\n"); send(session->sockfd, sbuf, strlen(sbuf), 0); close(fd); - closesocket(session->pasv_sockfd); + close_data_connection(session); } else if (str_begin_with(buf, "STOR")==0) { + open_data_connection(session); if(session->is_anonymous == RT_TRUE) { rt_sprintf(sbuf, "550 Permission denied.\r\n"); send(session->sockfd, sbuf, strlen(sbuf), 0); + close_data_connection(session); rt_free(sbuf); return 0; } @@ -562,6 +704,7 @@ err1: { rt_sprintf(sbuf, "550 Cannot open \"%s\" for writing.\r\n", filename); send(session->sockfd, sbuf, strlen(sbuf), 0); + close_data_connection(session); rt_free(sbuf); return 0; } @@ -579,7 +722,7 @@ err1: else if(numbytes==0) { close(fd); - closesocket(session->pasv_sockfd); + close_data_connection(session); rt_sprintf(sbuf, "226 Finished.\r\n"); send(session->sockfd, sbuf, strlen(sbuf), 0); break; @@ -587,12 +730,12 @@ err1: else if(numbytes==-1) { close(fd); - closesocket(session->pasv_sockfd); + close_data_connection(session); rt_free(sbuf); return -1; } } - closesocket(session->pasv_sockfd); + close_data_connection(session); } else if(str_begin_with(buf, "SIZE")==0) { @@ -659,7 +802,7 @@ err1: send(session->sockfd, sbuf, strlen(sbuf), 0); closesocket(session->pasv_sockfd); session->pasv_active = 0; - rt_free(sbuf); + rt_free(sbuf); return 0; } pasvremote.sin_addr.s_addr=inet_addr(tmpip); @@ -674,7 +817,7 @@ err1: rt_sprintf(sbuf, "425 Can't open data connection.\r\n"); send(session->sockfd, sbuf, strlen(sbuf), 0); closesocket(session->pasv_sockfd); - rt_free(sbuf); + rt_free(sbuf); return 0; } } @@ -697,9 +840,9 @@ err1: { if (session->is_anonymous == RT_TRUE) { - rt_sprintf(sbuf, "550 Permission denied.\r\n"); + rt_sprintf(sbuf, "530 Permission denied.\r\n"); send(session->sockfd, sbuf, strlen(sbuf), 0); - rt_free(sbuf); + rt_free(sbuf); return 0; } @@ -720,7 +863,7 @@ err1: { if (session->is_anonymous == RT_TRUE) { - rt_sprintf(sbuf, "550 Permission denied.\r\n"); + rt_sprintf(sbuf, "530 Permission denied.\r\n"); send(session->sockfd, sbuf, strlen(sbuf), 0); rt_free(sbuf); return 0; @@ -740,9 +883,9 @@ err1: { if (session->is_anonymous == RT_TRUE) { - rt_sprintf(sbuf, "550 Permission denied.\r\n"); + rt_sprintf(sbuf, "530 Permission denied.\r\n"); send(session->sockfd, sbuf, strlen(sbuf), 0); - rt_free(sbuf); + rt_free(sbuf); return 0; } build_full_path(session, parameter_ptr, filename, 256); @@ -758,12 +901,53 @@ err1: send(session->sockfd, sbuf, strlen(sbuf), 0); } } - + else if(str_begin_with(buf, "RNFR")==0) + { + if (session->is_anonymous == RT_TRUE) + { + rt_sprintf(sbuf, "530 Permission denied.\r\n"); + send(session->sockfd, sbuf, strlen(sbuf), 0); + rt_free(sbuf); + return 0; + } + build_full_path(session, parameter_ptr, filename, 256); + + rt_sprintf(sbuf, "350 Successfully rececive old file \"%s\".\r\n", filename); + send(session->sockfd, sbuf, strlen(sbuf), 0); + } + else if(str_begin_with(buf, "RNTO")==0) + { + char new_filename[256]; + if (session->is_anonymous == RT_TRUE) + { + rt_sprintf(sbuf, "530 Permission denied.\r\n"); + send(session->sockfd, sbuf, strlen(sbuf), 0); + rt_free(sbuf); + return 0; + } + build_full_path(session, parameter_ptr, new_filename, 256); + + if(rename(filename, new_filename) == -1) + { + rt_sprintf(sbuf, "553 rename file \"%s\" error.\r\n", filename); + send(session->sockfd, sbuf, strlen(sbuf), 0); + } + else + { + rt_sprintf(sbuf, "250 Successfully rename to new file \"%s\".\r\n", filename); + send(session->sockfd, sbuf, strlen(sbuf), 0); + } + } + else if((str_begin_with(buf, "NOOP")==0) || str_begin_with(buf, "noop")==0) + { + rt_sprintf(sbuf, "200 noop!\r\n"); + send(session->sockfd, sbuf, strlen(sbuf), 0); + } else if(str_begin_with(buf, "QUIT")==0) { rt_sprintf(sbuf, "221 Bye!\r\n"); send(session->sockfd, sbuf, strlen(sbuf), 0); - rt_free(sbuf); + rt_free(sbuf); return -1; } else @@ -771,7 +955,7 @@ err1: rt_sprintf(sbuf, "502 Not Implemented.\r\n"); send(session->sockfd, sbuf, strlen(sbuf), 0); } - rt_free(sbuf); + rt_free(sbuf); return 0; } @@ -787,5 +971,15 @@ void ftpd_start() #ifdef RT_USING_FINSH #include -FINSH_FUNCTION_EXPORT(ftpd_start, start ftp server) +FINSH_FUNCTION_EXPORT(ftpd_start, start ftp server); + +#ifdef FINSH_USING_MSH +int cmd_ftpd_start(int argc, char** argv) +{ + ftpd_start(); + return 0; +} +FINSH_FUNCTION_EXPORT_ALIAS(cmd_ftpd_start, __cmd_ftpd_start, start ftp server.); +#endif + #endif