Browse Source

[FTP server]
1.Fixed PASV mode issue;
2.Implement RNFR and RNTO command;
3.Change some command code according RFC959;
4.Add command for msh shell;
5.Implement "noop" command.

weety 10 years ago
parent
commit
707a657744
1 changed files with 229 additions and 35 deletions
  1. 229 35
      components/net/lwip/apps/ftpd.c

+ 229 - 35
components/net/lwip/apps/ftpd.c

@@ -11,17 +11,24 @@
 #define FTP_MAX_CONNECTION	2
 #define FTP_MAX_CONNECTION	2
 #define FTP_USER			"rtt"
 #define FTP_USER			"rtt"
 #define FTP_PASSWORD		"demo"
 #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 FTP_BUFFER_SIZE		1024
 
 
+#define INET_ADDRSTRLEN 16
+
+
 struct ftp_session
 struct ftp_session
 {
 {
 	rt_bool_t is_anonymous;
 	rt_bool_t is_anonymous;
 
 
 	int sockfd;
 	int sockfd;
 	struct sockaddr_in remote;
 	struct sockaddr_in remote;
+	struct sockaddr_in server;
+
+	char serveraddr[INET_ADDRSTRLEN];
 
 
 	/* pasv data */
 	/* pasv data */
+	int  pasv_listen_sockfd;
 	char pasv_active;
 	char pasv_active;
 	int  pasv_sockfd;
 	int  pasv_sockfd;
 
 
@@ -43,6 +50,7 @@ struct ftp_session* ftp_new_session()
 	struct ftp_session* session;
 	struct ftp_session* session;
 
 
 	session = (struct ftp_session*)rt_malloc(sizeof(struct ftp_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->next = session_list;
 	session_list = session;
 	session_list = session;
@@ -71,6 +79,83 @@ void ftp_close_session(struct ftp_session* session)
 	rt_free(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 ftp_get_filesize(char * filename)
 {
 {
 	int pos;
 	int pos;
@@ -179,6 +264,10 @@ void ftpd_thread_entry(void* parameter)
 				session = ftp_new_session();
 				session = ftp_new_session();
 				if (session != NULL)
 				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);
 					strcpy(session->currentdir, FTP_SRV_ROOT);
 					session->sockfd = com_socket;
 					session->sockfd = com_socket;
 					session->remote = remote;
 					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));
 						rt_kprintf("Client %s disconnected\n", inet_ntoa(session->remote.sin_addr));
 						FD_CLR(session->sockfd, &readfds);
 						FD_CLR(session->sockfd, &readfds);
 						closesocket(session->sockfd);
 						closesocket(session->sockfd);
+						session->sockfd = -1;
 						ftp_close_session(session);
 						ftp_close_session(session);
 					}
 					}
 					else
 					else
@@ -210,6 +300,7 @@ void ftpd_thread_entry(void* parameter)
 						{
 						{
 							rt_kprintf("Client %s disconnected\r\n", inet_ntoa(session->remote.sin_addr));
 							rt_kprintf("Client %s disconnected\r\n", inet_ntoa(session->remote.sin_addr));
 							closesocket(session->sockfd);
 							closesocket(session->sockfd);
+							session->sockfd = -1;
 							ftp_close_session(session);
 							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)
 		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);
 			rt_sprintf(sbuf, "331 Password required for %s\r\n", parameter_ptr);
 			send(session->sockfd, sbuf, strlen(sbuf), 0);
 			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)
 			session->is_anonymous == RT_TRUE)
 		{
 		{
 			// password correct
 			// 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);
 			send(session->sockfd, sbuf, strlen(sbuf), 0);
-			rt_free(sbuf);			
+			rt_free(sbuf);
 			return 0;
 			return 0;
 		}
 		}
 
 
 		// incorrect password
 		// incorrect password
 		rt_sprintf(sbuf, "530 Login or Password incorrect. Bye!\r\n");
 		rt_sprintf(sbuf, "530 Login or Password incorrect. Bye!\r\n");
 		send(session->sockfd, sbuf, strlen(sbuf), 0);
 		send(session->sockfd, sbuf, strlen(sbuf), 0);
-		rt_free(sbuf);		
+		rt_free(sbuf);
 		return -1;
 		return -1;
 	}
 	}
 	else if(str_begin_with(buf, "LIST")==0  )
 	else if(str_begin_with(buf, "LIST")==0  )
 	{
 	{
 		memset(sbuf,0,FTP_BUFFER_SIZE);
 		memset(sbuf,0,FTP_BUFFER_SIZE);
+		open_data_connection(session);
 		rt_sprintf(sbuf, "150 Opening Binary mode connection for file list.\r\n");
 		rt_sprintf(sbuf, "150 Opening Binary mode connection for file list.\r\n");
 		send(session->sockfd, sbuf, strlen(sbuf), 0);
 		send(session->sockfd, sbuf, strlen(sbuf), 0);
 		do_list(session->currentdir, session->pasv_sockfd);
 		do_list(session->currentdir, session->pasv_sockfd);
-		closesocket(session->pasv_sockfd);
+		close_data_connection(session);
 		session->pasv_active = 0;
 		session->pasv_active = 0;
 		rt_sprintf(sbuf, "226 Transfert Complete.\r\n");
 		rt_sprintf(sbuf, "226 Transfert Complete.\r\n");
 		send(session->sockfd, sbuf, strlen(sbuf), 0);
 		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);
 		memset(sbuf, 0, FTP_BUFFER_SIZE);
 		rt_sprintf(sbuf, "150 Opening Binary mode connection for file list.\r\n");
 		rt_sprintf(sbuf, "150 Opening Binary mode connection for file list.\r\n");
 		send(session->sockfd, sbuf, strlen(sbuf), 0);
 		send(session->sockfd, sbuf, strlen(sbuf), 0);
+		open_data_connection(session);
 		do_simple_list(session->currentdir, session->pasv_sockfd);
 		do_simple_list(session->currentdir, session->pasv_sockfd);
-		closesocket(session->pasv_sockfd);
+		close_data_connection(session);
 		session->pasv_active = 0;
 		session->pasv_active = 0;
 		rt_sprintf(sbuf, "226 Transfert Complete.\r\n");
 		rt_sprintf(sbuf, "226 Transfert Complete.\r\n");
 		send(session->sockfd, sbuf, strlen(sbuf), 0);
 		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)
 	else if(str_begin_with(buf, "PASV")==0)
 	{
 	{
 		int dig1, dig2;
 		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_port = 10000;
 		session->pasv_active = 1;
 		session->pasv_active = 1;
@@ -448,40 +553,63 @@ int ftp_process_request(struct ftp_session* session, char *buf)
 		dig2 = session->pasv_port % 256;
 		dig2 = session->pasv_port % 256;
 
 
 		FD_ZERO(&readfds);
 		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);
 			send(session->sockfd, sbuf, strlen(sbuf), 0);
 			goto err1;
 			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);
 			send(session->sockfd, sbuf, strlen(sbuf), 0);
 			goto err1;
 			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);
 			send(session->sockfd, sbuf, strlen(sbuf), 0);
 			goto err1;
 			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);
 			send(session->sockfd, sbuf, strlen(sbuf), 0);
 			goto err1;
 			goto err1;
 		}
 		}
+		p = msg;
+		while ((p = strchr(p, '.')))
+			*p++ = ',';
+	
 		rt_kprintf("Listening %d seconds @ port %d\n", tv.tv_sec, session->pasv_port);
 		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);
 		send(session->sockfd, sbuf, strlen(sbuf), 0);
+		rt_free(sbuf);
+		rt_free(msg);
+		return 0;
+	#if 0
 		FD_SET(sockfd, &readfds);
 		FD_SET(sockfd, &readfds);
 		select(0, &readfds, 0, 0, &tv);
 		select(0, &readfds, 0, 0, &tv);
 		if(FD_ISSET(sockfd, &readfds))
 		if(FD_ISSET(sockfd, &readfds))
 		{
 		{
 			if((session->pasv_sockfd = accept(sockfd, (struct sockaddr*)&pasvremote, &addr_len))==-1)
 			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);
 				send(session->sockfd, sbuf, strlen(sbuf), 0);
 				goto err1;
 				goto err1;
 			}
 			}
@@ -500,11 +628,21 @@ err1:
 			rt_free(sbuf);
 			rt_free(sbuf);
 			return 0;
 			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)
 	else if (str_begin_with(buf, "RETR")==0)
 	{
 	{
 		int file_size;
 		int file_size;
 
 
+		open_data_connection(session);
+
 		strcpy(filename, buf + 5);
 		strcpy(filename, buf + 5);
 
 
 		build_full_path(session, parameter_ptr, filename, 256);
 		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);
 			rt_sprintf(sbuf, "550 \"%s\" : not a regular file\r\n", filename);
 			send(session->sockfd, sbuf, strlen(sbuf), 0);
 			send(session->sockfd, sbuf, strlen(sbuf), 0);
 			session->offset=0;
 			session->offset=0;
-			rt_free(sbuf);			
+			close_data_connection(session);
+			rt_free(sbuf);
 			return 0;
 			return 0;
 		}
 		}
 
 
 		fd = open(filename, O_RDONLY, 0);
 		fd = open(filename, O_RDONLY, 0);
 		if (fd < 0)
 		if (fd < 0)
 		{
 		{
+			close_data_connection(session);
 			rt_free(sbuf);
 			rt_free(sbuf);
 			return 0;
 			return 0;
 		}
 		}
@@ -543,14 +683,16 @@ err1:
 		rt_sprintf(sbuf, "226 Finished.\r\n");
 		rt_sprintf(sbuf, "226 Finished.\r\n");
 		send(session->sockfd, sbuf, strlen(sbuf), 0);
 		send(session->sockfd, sbuf, strlen(sbuf), 0);
 		close(fd);
 		close(fd);
-		closesocket(session->pasv_sockfd);
+		close_data_connection(session);
 	}
 	}
 	else if (str_begin_with(buf, "STOR")==0)
 	else if (str_begin_with(buf, "STOR")==0)
 	{
 	{
+		open_data_connection(session);
 		if(session->is_anonymous == RT_TRUE)
 		if(session->is_anonymous == RT_TRUE)
 		{
 		{
 			rt_sprintf(sbuf, "550 Permission denied.\r\n");
 			rt_sprintf(sbuf, "550 Permission denied.\r\n");
 			send(session->sockfd, sbuf, strlen(sbuf), 0);
 			send(session->sockfd, sbuf, strlen(sbuf), 0);
+			close_data_connection(session);
 			rt_free(sbuf);
 			rt_free(sbuf);
 			return 0;
 			return 0;
 		}
 		}
@@ -562,6 +704,7 @@ err1:
 		{
 		{
 			rt_sprintf(sbuf, "550 Cannot open \"%s\" for writing.\r\n", filename);
 			rt_sprintf(sbuf, "550 Cannot open \"%s\" for writing.\r\n", filename);
 			send(session->sockfd, sbuf, strlen(sbuf), 0);
 			send(session->sockfd, sbuf, strlen(sbuf), 0);
+			close_data_connection(session);
 			rt_free(sbuf);
 			rt_free(sbuf);
 			return 0;
 			return 0;
 		}
 		}
@@ -579,7 +722,7 @@ err1:
 			else if(numbytes==0)
 			else if(numbytes==0)
 			{
 			{
 				close(fd);
 				close(fd);
-				closesocket(session->pasv_sockfd);
+				close_data_connection(session);
 				rt_sprintf(sbuf, "226 Finished.\r\n");
 				rt_sprintf(sbuf, "226 Finished.\r\n");
 				send(session->sockfd, sbuf, strlen(sbuf), 0);
 				send(session->sockfd, sbuf, strlen(sbuf), 0);
 				break;
 				break;
@@ -587,12 +730,12 @@ err1:
 			else if(numbytes==-1)
 			else if(numbytes==-1)
 			{
 			{
 				close(fd);
 				close(fd);
-				closesocket(session->pasv_sockfd);
+				close_data_connection(session);
 				rt_free(sbuf);
 				rt_free(sbuf);
 				return -1;
 				return -1;
 			}
 			}
 		}
 		}
-		closesocket(session->pasv_sockfd);
+		close_data_connection(session);
 	}
 	}
 	else if(str_begin_with(buf, "SIZE")==0)
 	else if(str_begin_with(buf, "SIZE")==0)
 	{
 	{
@@ -659,7 +802,7 @@ err1:
 			send(session->sockfd, sbuf, strlen(sbuf), 0);
 			send(session->sockfd, sbuf, strlen(sbuf), 0);
 			closesocket(session->pasv_sockfd);
 			closesocket(session->pasv_sockfd);
 			session->pasv_active = 0;
 			session->pasv_active = 0;
-			rt_free(sbuf);	
+			rt_free(sbuf);
 			return 0;
 			return 0;
 		}
 		}
 		pasvremote.sin_addr.s_addr=inet_addr(tmpip);
 		pasvremote.sin_addr.s_addr=inet_addr(tmpip);
@@ -674,7 +817,7 @@ err1:
 				rt_sprintf(sbuf, "425 Can't open data connection.\r\n");
 				rt_sprintf(sbuf, "425 Can't open data connection.\r\n");
 				send(session->sockfd, sbuf, strlen(sbuf), 0);
 				send(session->sockfd, sbuf, strlen(sbuf), 0);
 				closesocket(session->pasv_sockfd);
 				closesocket(session->pasv_sockfd);
-				rt_free(sbuf);				
+				rt_free(sbuf);
 				return 0;
 				return 0;
 			}
 			}
 		}
 		}
@@ -697,9 +840,9 @@ err1:
 	{
 	{
 		if (session->is_anonymous == RT_TRUE)
 		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);
 			send(session->sockfd, sbuf, strlen(sbuf), 0);
-			rt_free(sbuf);			
+			rt_free(sbuf);
 			return 0;
 			return 0;
 		}
 		}
 
 
@@ -720,7 +863,7 @@ err1:
 	{
 	{
 		if (session->is_anonymous == RT_TRUE)
 		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);
 			send(session->sockfd, sbuf, strlen(sbuf), 0);
 			rt_free(sbuf);
 			rt_free(sbuf);
 			return 0;
 			return 0;
@@ -740,9 +883,9 @@ err1:
 	{
 	{
 		if (session->is_anonymous == RT_TRUE)
 		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);
 			send(session->sockfd, sbuf, strlen(sbuf), 0);
-			rt_free(sbuf);			
+			rt_free(sbuf);
 			return 0;
 			return 0;
 		}
 		}
 		build_full_path(session, parameter_ptr, filename, 256);
 		build_full_path(session, parameter_ptr, filename, 256);
@@ -758,12 +901,53 @@ err1:
 			send(session->sockfd, sbuf, strlen(sbuf), 0);
 			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)
 	else if(str_begin_with(buf, "QUIT")==0)
 	{
 	{
 		rt_sprintf(sbuf, "221 Bye!\r\n");
 		rt_sprintf(sbuf, "221 Bye!\r\n");
 		send(session->sockfd, sbuf, strlen(sbuf), 0);
 		send(session->sockfd, sbuf, strlen(sbuf), 0);
-		rt_free(sbuf);		
+		rt_free(sbuf);
 		return -1;
 		return -1;
 	}
 	}
 	else
 	else
@@ -771,7 +955,7 @@ err1:
 		rt_sprintf(sbuf, "502 Not Implemented.\r\n");
 		rt_sprintf(sbuf, "502 Not Implemented.\r\n");
 		send(session->sockfd, sbuf, strlen(sbuf), 0);
 		send(session->sockfd, sbuf, strlen(sbuf), 0);
 	}
 	}
-	rt_free(sbuf);	
+	rt_free(sbuf);
 	return 0;
 	return 0;
 }
 }
 
 
@@ -787,5 +971,15 @@ void ftpd_start()
 
 
 #ifdef RT_USING_FINSH
 #ifdef RT_USING_FINSH
 #include <finsh.h>
 #include <finsh.h>
-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
 #endif