From f489bcd6f94d71cf36f30b422f6e8f15f16ccda1 Mon Sep 17 00:00:00 2001 From: Anthony Merlino Date: Mon, 6 Nov 2017 23:41:30 +0000 Subject: [PATCH] Merged in antmerlino/apps/ftp-ipv6 (pull request #122) FTP: Adds support for IPv6 and fixes various transfer issues * netutils/ftpd: Fix support for IPv6 FTP server * netutils/ftpc:Adds support for IPv6 sockets * ftpc: Must convert port received by EPSV reply to network order * ftpc: Data socket address for passive connection should be same as server address * ftpc: Must skip human readable string in EPSV response before scanning for port * ftpc: Don't send PASS command if USER command was sufficient. ie no password required * ftpc: Generically handle permanent negative completion in shard ftpc_cmd logic * ftpc: Minor addresssing fix * ftpc: Tweak logic for overriding network debugging output * FTP: Adds option for setting stack size of various threads in FTPD and FTPC * netutils/ftpd: Fixes build error setting IPv4 address. sin_addr => sin_addr.s_addr * netutils/ftpd: Protects against partial write returns by looping in that case Approved-by: Gregory Nutt --- examples/ftpc/Kconfig | 5 + examples/ftpc/Makefile | 4 +- examples/ftpc/ftpc_main.c | 55 ++++++- examples/ftpd/Kconfig | 5 + examples/ftpd/Makefile | 6 +- examples/ftpd/ftpd_main.c | 21 ++- include/netutils/ftpc.h | 20 ++- include/netutils/ftpd.h | 5 +- netutils/ftpc/Kconfig | 25 +++ netutils/ftpc/ftpc_cmd.c | 11 ++ netutils/ftpc/ftpc_config.h | 7 + netutils/ftpc/ftpc_connect.c | 98 +++++++++--- netutils/ftpc/ftpc_disconnect.c | 1 + netutils/ftpc/ftpc_getfile.c | 2 +- netutils/ftpc/ftpc_internal.h | 15 +- netutils/ftpc/ftpc_listdir.c | 2 +- netutils/ftpc/ftpc_login.c | 44 +++--- netutils/ftpc/ftpc_putfile.c | 2 +- netutils/ftpc/ftpc_socket.c | 81 +++++++--- netutils/ftpc/ftpc_transfer.c | 270 ++++++++++++++++++++++++++------ netutils/ftpd/Kconfig | 5 + netutils/ftpd/ftpd.c | 141 ++++++++++------- 22 files changed, 630 insertions(+), 195 deletions(-) diff --git a/examples/ftpc/Kconfig b/examples/ftpc/Kconfig index c8f74bf58..2c3b53faa 100644 --- a/examples/ftpc/Kconfig +++ b/examples/ftpc/Kconfig @@ -10,4 +10,9 @@ config EXAMPLES_FTPC Enable the FTP client example if EXAMPLES_FTPC + +config EXAMPLES_FTPC_STACKSIZE + int "FTPC stack size" + default 4096 + endif diff --git a/examples/ftpc/Makefile b/examples/ftpc/Makefile index a12654b98..683bcaf1f 100644 --- a/examples/ftpc/Makefile +++ b/examples/ftpc/Makefile @@ -37,9 +37,11 @@ # FTPC Client Application +CONFIG_EXAMPLES_FTPC_STACKSIZE ?= 4096 + APPNAME = ftpc PRIORITY = SCHED_PRIORITY_DEFAULT -STACKSIZE = 4096 +STACKSIZE = CONFIG_EXAMPLES_FTPC_STACKSIZE ASRCS = CSRCS = ftpc_cmds.c diff --git a/examples/ftpc/ftpc_main.c b/examples/ftpc/ftpc_main.c index 07d7aa7d2..2bd26fb3b 100644 --- a/examples/ftpc/ftpc_main.c +++ b/examples/ftpc/ftpc_main.c @@ -56,6 +56,16 @@ #define FTPC_MAX_ARGUMENTS 4 +/* If FTP is used and both IPv6 and IPv4 are enabled, then we need to + * pick one. + */ + +#ifdef CONFIG_NET_IPv6 +# define ADDR_FAMILY AF_INET6 +#else +# define ADDR_FAMILY AF_INET +#endif + /**************************************************************************** * Public Types ****************************************************************************/ @@ -361,39 +371,72 @@ int main(int argc, FAR char *argv[]) int ftpc_main(int argc, char **argv, char **envp) #endif { - struct ftpc_connect_s connect = {{0}, 0}; + union ftpc_sockaddr_u server; SESSION handle; +#if ADDR_FAMILY == AF_INET FAR char *ptr; +#endif #ifndef CONFIG_EXAMPLES_FTPC_FGETS int ret; #endif + memset(&server, 0, sizeof(union ftpc_sockaddr_u)); + if (argc != 2) { +#if ADDR_FAMILY == CONFIG_NET_IPv6 + printf("Usage:\n"); + printf(" %s xx:xx:xx:xx:xx:xx:xx:xx [pp]\n", argv[0]); + printf("Where\n"); + printf(" xx:xx:xx:xx:xx:xx:xx:xx is the IP address of the FTP server\n"); + printf(" pp is option port to use with the FTP server\n"); +#else printf("Usage:\n"); printf(" %s xx.xx.xx.xx[:pp]\n", argv[0]); printf("Where\n"); printf(" xx.xx.xx.xx is the IP address of the FTP server\n"); printf(" pp is option port to use with the FTP server\n"); +#endif exit(1); } + /* In any event, we can now extract the IP address from the comman-line */ + +#if ADDR_FAMILY == AF_INET6 + server.in6.sin6_family = AF_INET6; + ret = inet_pton(AF_INET6, argv[1], &server.in6.sin6_addr); + if (ret < 0) + { + printf("Invalid IPv6 address\n"); + exit(1); + } + + if (argc > 2) + { + server.in6.sin6_port = atoi(argv[2]); + } +#else /* Check if the argument includes a port number */ ptr = strchr(argv[1], ':'); if (ptr) { *ptr = '\0'; - connect.port = atoi(ptr+1); + server.in4.sin_port = atoi(ptr+1); } - /* In any event, we can now extract the IP address from the comman-line */ - - connect.addr.s_addr = inet_addr(argv[1]); + server.in4.sin_family = AF_INET; + ret = inet_pton(AF_INET, argv[1], &server.in4.sin_addr); + if (ret < 0) + { + printf("Invalid IP address\n"); + exit(1); + } +#endif /* Connect to the FTP server */ - handle = ftpc_connect(&connect); + handle = ftpc_connect(&server); if (!handle) { printf("Failed to connect to the server: %d\n", errno); diff --git a/examples/ftpd/Kconfig b/examples/ftpd/Kconfig index 86ac4af1d..77bfdd07a 100644 --- a/examples/ftpd/Kconfig +++ b/examples/ftpd/Kconfig @@ -10,4 +10,9 @@ config EXAMPLES_FTPD Enable the FTP server example if EXAMPLES_FTPD + +config EXAMPLES_FTPD_STACKSIZE + int "FTP Daemon Stack Size" + default 2048 + endif diff --git a/examples/ftpd/Makefile b/examples/ftpd/Makefile index 03da2970d..b141f0279 100644 --- a/examples/ftpd/Makefile +++ b/examples/ftpd/Makefile @@ -37,7 +37,7 @@ -include $(TOPDIR)/Make.defs include $(APPDIR)/Make.defs -# Hello, World! Example +CONFIG_EXAMPLES_FTPD_STACKSIZE ?= 2048 ASRCS = CSRCS = @@ -109,10 +109,10 @@ endif ifeq ($(CONFIG_NSH_BUILTIN_APPS),y) $(BUILTIN_REGISTRY)$(DELIM)ftpd_start.bdat: $(DEPCONFIG) Makefile - $(call REGISTER,ftpd_start,SCHED_PRIORITY_DEFAULT,2048,ftpd_main) + $(call REGISTER,ftpd_start,SCHED_PRIORITY_DEFAULT,$(CONFIG_EXAMPLES_FTPD_STACKSIZE),ftpd_main) $(BUILTIN_REGISTRY)$(DELIM)ftpd_stop.bdat: $(DEPCONFIG) Makefile - $(call REGISTER,ftpd_stop,SCHED_PRIORITY_DEFAULT,2048,ftpd_stop) + $(call REGISTER,ftpd_stop,SCHED_PRIORITY_DEFAULT,$(CONFIG_EXAMPLES_FTPD_STACKSIZE),ftpd_stop) context: $(BUILTIN_REGISTRY)$(DELIM)ftpd_start.bdat $(BUILTIN_REGISTRY)$(DELIM)ftpd_stop.bdat else diff --git a/examples/ftpd/ftpd_main.c b/examples/ftpd/ftpd_main.c index a60e36c88..e2b9acea4 100644 --- a/examples/ftpd/ftpd_main.c +++ b/examples/ftpd/ftpd_main.c @@ -51,6 +51,20 @@ #include "ftpd.h" +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* If FTP is used and both IPv6 and IPv4 are enabled, then we need to + * pick one. + */ + +#ifdef CONFIG_NET_IPv6 +# define ADDR_FAMILY AF_INET6 +#else +# define ADDR_FAMILY AF_INET +#endif + /**************************************************************************** * Private Data ****************************************************************************/ @@ -167,7 +181,12 @@ int ftpd_daemon(int s_argc, char **s_argv) /* Open FTPD */ - handle = ftpd_open(); +#if ADDR_FAMILY == AF_INET6 + handle = ftpd_open(AF_INET6); +#else + handle = ftpd_open(AF_INET); +#endif + if (!handle) { printf("FTP daemon [%d] failed to open FTPD\n", g_ftpdglob.pid); diff --git a/include/netutils/ftpc.h b/include/netutils/ftpc.h index 1f8836ca4..5954fca31 100644 --- a/include/netutils/ftpc.h +++ b/include/netutils/ftpc.h @@ -47,6 +47,7 @@ #include #include +#include #include /**************************************************************************** @@ -113,9 +114,9 @@ typedef FAR void *SESSION; -/* This structure provides information to connect to a host FTP server. +/* This union provides information to connect to a host FTP server. * - * addr - The IPv4 address of the FTP server (or the proxy) for the FTP + * addr - The IPv4 or IPv6 address of the FTP server (or the proxy) for the FTP * server. * port - The port number on the FTP server to connect to (in host byte * order). This is usually port 21 for FTP. You may set this @@ -123,10 +124,17 @@ typedef FAR void *SESSION; * you (it will use CONFIG_FTP_DEFPORT). */ -struct ftpc_connect_s +union ftpc_sockaddr_u { - struct in_addr addr; /* Server/proxy IP address */ - uint16_t port; /* Server/proxy port number (usually 21) in network order */ + uint8_t raw[sizeof(struct sockaddr_storage)]; + struct sockaddr_storage ss; + struct sockaddr sa; +#ifdef CONFIG_NET_IPv6 + struct sockaddr_in6 in6; +#endif +#ifdef CONFIG_NET_IPv4 + struct sockaddr_in in4; +#endif }; /* This structure provides FTP login information */ @@ -177,7 +185,7 @@ extern "C" ****************************************************************************/ /* Connection management ****************************************************/ -SESSION ftpc_connect(FAR struct ftpc_connect_s *server); +SESSION ftpc_connect(FAR union ftpc_sockaddr_u *server); void ftpc_disconnect(SESSION handle); /* FTP commands *************************************************************/ diff --git a/include/netutils/ftpd.h b/include/netutils/ftpd.h index 87567bc06..9912d024e 100644 --- a/include/netutils/ftpd.h +++ b/include/netutils/ftpd.h @@ -135,7 +135,8 @@ extern "C" * used to run the server. * * Input Parameters: - * None + * family - The type of INET family to use when opening the socket. AF_INET + * and AF_INET6 are supported. * * Returned Value: * On success, a non-NULL handle is returned that can be used to reference @@ -143,7 +144,7 @@ extern "C" * ****************************************************************************/ -FTPD_SESSION ftpd_open(void); +FTPD_SESSION ftpd_open(sa_family_t family); /**************************************************************************** * Name: ftpd_adduser diff --git a/netutils/ftpc/Kconfig b/netutils/ftpc/Kconfig index 938e8a86c..ff3f031cb 100644 --- a/netutils/ftpc/Kconfig +++ b/netutils/ftpc/Kconfig @@ -19,4 +19,29 @@ config DEBUG_FTPC Enable debug support for the FTP client. This option simple forces CONFIG_DEBUG_NET to be on, but only for the files within this directory. +config FTP_TMPDIR + string "TMP directory path for FTP" + default "/tmp" + ---help--- + The path to use for storing temporary files used in the transfer process + by the FTP client. + +config FTPC_DISABLE_EPRT + bool "Disable EPRT and use PORT instead" + default n + ---help--- + FTP uses PORT and EPRT when in active mode. EPRT replaced PORT to allow + for IPv6 support. EPRT is supported in most FTP implementations now. However, + if you need to use PORT instead, use this option to disable EPRT and + fallback to using PORT. + +config FTPC_DISABLE_EPSV + bool "Disable EPSV and use PASV instead" + default n + ---help--- + FTP uses EPSV or PASV when in passive mode. EPSV replaced PASV to allow + for IPv6 support. EPSV is supported in most FTP implementations now. However, + if you need to use PASV instead, use this option to disable EPSV and + fallback to using PASV. + endif diff --git a/netutils/ftpc/ftpc_cmd.c b/netutils/ftpc/ftpc_cmd.c index 157e1c786..fec0aa460 100644 --- a/netutils/ftpc/ftpc_cmd.c +++ b/netutils/ftpc/ftpc_cmd.c @@ -228,6 +228,17 @@ int ftpc_cmd(struct ftpc_session_s *session, const char *cmd, ...) } } + /* Error codes 5xx are Permanent Negative Completion. These can be + * handled generically for all commands. An example is error code 530 + * which means 'not logged in'. The reply should include a string to + * display to the user + */ + + else if (session->code > 500 && session->code < 600) + { + return ERROR; + } + /* The command was successfully sent */ return OK; diff --git a/netutils/ftpc/ftpc_config.h b/netutils/ftpc/ftpc_config.h index 59590a7b5..65d3bb727 100644 --- a/netutils/ftpc/ftpc_config.h +++ b/netutils/ftpc/ftpc_config.h @@ -58,7 +58,14 @@ */ #if !defined(CONFIG_DEBUG_NET) && defined(CONFIG_DEBUG_FTPC) +# undef CONFIG_DEBUG_NET # define CONFIG_DEBUG_NET 1 +# undef CONFIG_DEBUG_NET_ERROR +# define CONFIG_DEBUG_NET_ERROR 1 +# undef CONFIG_DEBUG_NET_WARN +# define CONFIG_DEBUG_NET_WARN 1 +# undef CONFIG_DEBUG_NET_INFO +# define CONFIG_DEBUG_NET_INFO 1 #endif /**************************************************************************** diff --git a/netutils/ftpc/ftpc_connect.c b/netutils/ftpc/ftpc_connect.c index fcb89423c..07d0d1584 100644 --- a/netutils/ftpc/ftpc_connect.c +++ b/netutils/ftpc/ftpc_connect.c @@ -63,7 +63,7 @@ * ****************************************************************************/ -SESSION ftpc_connect(FAR struct ftpc_connect_s *server) +SESSION ftpc_connect(FAR union ftpc_sockaddr_u *server) { FAR struct ftpc_session_s *session; int ret; @@ -80,7 +80,8 @@ SESSION ftpc_connect(FAR struct ftpc_connect_s *server) /* Initialize the session structure with all non-zero and variable values */ - session->addr.s_addr = server->addr.s_addr; + memcpy(&session->server, server, sizeof(union ftpc_sockaddr_u)); + session->flags &= ~FTPC_FLAGS_CLEAR; session->flags |= FTPC_FLAGS_SET; session->replytimeo = CONFIG_FTP_DEFTIMEO * CLOCKS_PER_SEC; @@ -89,14 +90,24 @@ SESSION ftpc_connect(FAR struct ftpc_connect_s *server) /* Use the default port if the user specified port number zero */ - if (!server->port) +#ifdef CONFIG_NET_IPv6 + if (session->server.sa.sa_family == AF_INET6) { - session->port = HTONS(CONFIG_FTP_DEFPORT); + if (!session->server.in6.sin6_port) + { + session->server.in6.sin6_port = HTONS(CONFIG_FTP_DEFPORT); + } } - else +#endif +#ifdef CONFIG_NET_IPv4 + if (session->server.sa.sa_family == AF_INET) { - session->port = htons(server->port); + if (!session->server.in4.sin_port) + { + session->server.in4.sin_port = HTONS(CONFIG_FTP_DEFPORT); + } } +#endif /* Get the local home directory, i.e., the value of the PWD environment * variable at the time of the connection. We keep a local copy so that @@ -143,9 +154,8 @@ errout: int ftpc_reconnect(FAR struct ftpc_session_s *session) { - struct sockaddr_in addr; #ifdef CONFIG_DEBUG_NET_ERROR - char *tmp; + char buffer[48]; #endif int ret; @@ -166,7 +176,7 @@ int ftpc_reconnect(FAR struct ftpc_session_s *session) /* Initialize a socket */ - ret = ftpc_sockinit(&session->cmd); + ret = ftpc_sockinit(&session->cmd, session->server.sa.sa_family); if (ret != OK) { nerr("ERROR: ftpc_sockinit() failed: %d\n", errno); @@ -176,16 +186,29 @@ int ftpc_reconnect(FAR struct ftpc_session_s *session) /* Connect the socket to the server */ #ifdef CONFIG_DEBUG_NET_ERROR - tmp = inet_ntoa(session->addr); - ninfo("Connecting to server address %s:%d\n", - tmp, ntohs(session->port)); -#endif +#ifdef CONFIG_NET_IPv6 + if (session->server.sa.sa_family == AF_INET6) + { + if (inet_ntop(AF_INET6, &session->server.in6.sin6_addr, buffer, 48) != NULL) + { + ninfo("Connecting to server address %s:%d\n", buffer, + ntohs(session->server.in6.sin6_port)); + } + } +#endif /* CONFIG_NET_IPv6 */ +#ifdef CONFIG_NET_IPv4 + if (session->server.sa.sa_family == AF_INET) + { + if (inet_ntop(AF_INET, &session->server.in4.sin_addr, buffer, 48) != NULL) + { + ninfo("Connecting to server address %s:%d\n", buffer, + ntohs(session->server.in4.sin_port)); + } + } +#endif /* CONFIG_NET_IPv4 */ +#endif /* CONFIG_DEBUG_NET_ERROR */ - addr.sin_family = AF_INET; - addr.sin_port = session->port; - addr.sin_addr.s_addr = session->addr.s_addr; - - ret = ftpc_sockconnect(&session->cmd, &addr); + ret = ftpc_sockconnect(&session->cmd, (FAR struct sockaddr *)&session->server); if (ret != OK) { nerr("ERROR: ftpc_sockconnect() failed: %d\n", errno); @@ -224,11 +247,40 @@ int ftpc_reconnect(FAR struct ftpc_session_s *session) #ifdef CONFIG_DEBUG_NET_ERROR ninfo("Connected\n"); - tmp = inet_ntoa(addr.sin_addr); - ninfo(" Remote address: %s:%d\n", tmp, ntohs(addr.sin_port)); - tmp = inet_ntoa(session->cmd.laddr.sin_addr); - ninfo(" Local address: %s:%d\n", tmp, ntohs(session->cmd.laddr.sin_port)); -#endif +#ifdef CONFIG_NET_IPv6 + if (session->server.sa.sa_family == AF_INET6) + { + if (inet_ntop(AF_INET6, &session->server.in6.sin6_addr, buffer, 48) != NULL) + { + ninfo(" Remote address: %s:%d\n", buffer, + ntohs(session->server.in6.sin6_port)); + } + + if (inet_ntop(AF_INET6, &session->cmd.laddr.in6.sin6_addr, buffer, 48) != NULL) + { + ninfo(" Local address: %s:%d\n", buffer, + ntohs(session->cmd.laddr.in6.sin6_port)); + } + } +#endif /* CONFIG_NET_IPv6 */ +#ifdef CONFIG_NET_IPv4 + if (session->server.sa.sa_family == AF_INET) + { + if (inet_ntop(AF_INET, &session->server.in4.sin_addr, buffer, 48) != NULL) + { + ninfo(" Remote address: %s:%d\n", buffer, + ntohs(session->server.in4.sin_port)); + } + + if (inet_ntop(AF_INET, &session->cmd.laddr.in4.sin_addr, buffer, 48) != NULL) + { + ninfo(" Local address: %s:%d\n", buffer, + ntohs(session->cmd.laddr.in4.sin_port)); + } + } +#endif /* CONFIG_NET_IPv4 */ +#endif /* CONFIG_DEBUG_NET_ERROR */ + return OK; errout_with_socket: diff --git a/netutils/ftpc/ftpc_disconnect.c b/netutils/ftpc/ftpc_disconnect.c index 745d54074..d842d0501 100644 --- a/netutils/ftpc/ftpc_disconnect.c +++ b/netutils/ftpc/ftpc_disconnect.c @@ -85,6 +85,7 @@ void ftpc_disconnect(SESSION handle) /* Release sockets */ ftpc_sockclose(&session->data); + ftpc_sockclose(&session->dacceptor); ftpc_sockclose(&session->cmd); /* Free strings */ diff --git a/netutils/ftpc/ftpc_getfile.c b/netutils/ftpc/ftpc_getfile.c index f6cf42c2e..f25afc543 100644 --- a/netutils/ftpc/ftpc_getfile.c +++ b/netutils/ftpc/ftpc_getfile.c @@ -154,7 +154,7 @@ static int ftpc_recvinit(struct ftpc_session_s *session, FAR const char *path, if (!FTPC_IS_PASSIVE(session)) { - ret = ftpc_sockaccept(&session->data); + ret = ftpc_sockaccept(&session->dacceptor, &session->data); if (ret != OK) { nerr("ERROR: Data connection not accepted\n"); diff --git a/netutils/ftpc/ftpc_internal.h b/netutils/ftpc/ftpc_internal.h index dcfdeeaa8..a4f3b2eaa 100644 --- a/netutils/ftpc/ftpc_internal.h +++ b/netutils/ftpc/ftpc_internal.h @@ -153,7 +153,7 @@ struct ftpc_socket_s int sd; /* Socket descriptor */ FILE *instream; /* Incoming stream */ FILE *outstream; /* Outgoing stream */ - struct sockaddr_in laddr; /* Local address */ + union ftpc_sockaddr_u laddr; /* Local Address */ bool connected; /* True: socket is connected */ }; @@ -161,9 +161,10 @@ struct ftpc_socket_s struct ftpc_session_s { - struct in_addr addr; /* Server/proxy IP address */ + union ftpc_sockaddr_u server; /* Server/proxy socket address */ struct ftpc_socket_s cmd; /* FTP command channel */ struct ftpc_socket_s data; /* FTP data channel */ + struct ftpc_socket_s dacceptor; /* FTP data listener (accepts data connection in active mode) */ WDOG_ID wdog; /* Timer */ FAR char *uname; /* Login uname */ FAR char *pwd; /* Login pwd */ @@ -173,7 +174,6 @@ struct ftpc_session_s FAR char *homeldir; /* Local home directory (PWD on startup) */ pid_t pid; /* Task ID of FTP client */ uint8_t xfrmode; /* Previous data transfer type (See FTPC_XFRMODE_* defines) */ - uint16_t port; /* Server/proxy port number (probably 21) */ uint16_t flags; /* Connection flags (see FTPC_FLAGS_* defines) */ uint16_t code; /* Last 3-digit reply code */ uint32_t replytimeo; /* Server reply timeout (ticks) */ @@ -250,13 +250,14 @@ EXTERN FAR char *ftpc_abslpath(FAR struct ftpc_session_s *session, /* Socket helpers */ -EXTERN int ftpc_sockinit(FAR struct ftpc_socket_s *sock); +EXTERN int ftpc_sockinit(FAR struct ftpc_socket_s *sock, sa_family_t family); EXTERN void ftpc_sockclose(FAR struct ftpc_socket_s *sock); EXTERN int ftpc_sockconnect(FAR struct ftpc_socket_s *sock, - FAR struct sockaddr_in *addr); + FAR struct sockaddr *addr); EXTERN int ftpc_sockgetsockname(FAR struct ftpc_socket_s *sock, - FAR struct sockaddr_in *sa); -EXTERN int ftpc_sockaccept(FAR struct ftpc_socket_s *sock); + FAR union ftpc_sockaddr_u *addr); +EXTERN int ftpc_sockaccept(FAR struct ftpc_socket_s *acceptor, + FAR struct ftpc_socket_s *sock); EXTERN int ftpc_socklisten(FAR struct ftpc_socket_s *sock); EXTERN void ftpc_sockcopy(FAR struct ftpc_socket_s *dest, FAR const struct ftpc_socket_s *src); diff --git a/netutils/ftpc/ftpc_listdir.c b/netutils/ftpc/ftpc_listdir.c index 4fb82eace..d9a52973a 100644 --- a/netutils/ftpc/ftpc_listdir.c +++ b/netutils/ftpc/ftpc_listdir.c @@ -206,7 +206,7 @@ static int ftpc_recvdir(FAR struct ftpc_session_s *session, if (!FTPC_IS_PASSIVE(session)) { - ret = ftpc_sockaccept(&session->data); + ret = ftpc_sockaccept(&session->dacceptor, &session->data); if (ret != OK) { nerr("ERROR: ftpc_sockaccept() failed: %d\n", errno); diff --git a/netutils/ftpc/ftpc_login.c b/netutils/ftpc/ftpc_login.c index 13ac4791e..3714c9afc 100644 --- a/netutils/ftpc/ftpc_login.c +++ b/netutils/ftpc/ftpc_login.c @@ -158,9 +158,6 @@ int ftpc_relogin(FAR struct ftpc_session_s *session) * Or the server may reject USER with: * * - "530 Not logged in" meaning that the username is unacceptable. - * - * In practice, the server does not check the username until after a PASS - * request */ FTPC_CLR_LOGGEDIN(session); @@ -171,26 +168,29 @@ int ftpc_relogin(FAR struct ftpc_session_s *session) return ERROR; } - /* Send the PASS command with the passed. The server may accept PASS with: - * - * - "230 User logged in, proceed" meaning that the client has permission to - * access files under that username - * - "202 Command not implemented, superfluous at this site" meaning that - * permission was already granted in response to USER - * - "332 Need account for login" meaning that permission might be granted - * after an ACCT request. - * - * The server may reject PASS with: - * - * - "503 Bad sequence of commands" if the previous request was not USER - * - "530 - Not logged in" if this username and password are unacceptable. - */ - - ret = ftpc_cmd(session, "PASS %s", session->pwd); - if (ret != OK) + if (session->code == 331) { - nerr("ERROR: PASS %s cmd failed: %d\n", session->pwd, errno); - return ret; + /* Send the PASS command with the passed. The server may accept PASS with: + * + * - "230 User logged in, proceed" meaning that the client has permission to + * access files under that username + * - "202 Command not implemented, superfluous at this site" meaning that + * permission was already granted in response to USER + * - "332 Need account for login" meaning that permission might be granted + * after an ACCT request. + * + * The server may reject PASS with: + * + * - "503 Bad sequence of commands" if the previous request was not USER + * - "530 - Not logged in" if this username and password are unacceptable. + */ + + ret = ftpc_cmd(session, "PASS %s", session->pwd); + if (ret != OK) + { + nerr("ERROR: PASS %s cmd failed: %d\n", session->pwd, errno); + return ret; + } } /* We are logged in.. the current working directory on login is our "home" diff --git a/netutils/ftpc/ftpc_putfile.c b/netutils/ftpc/ftpc_putfile.c index a83df63b9..4a688d6f9 100644 --- a/netutils/ftpc/ftpc_putfile.c +++ b/netutils/ftpc/ftpc_putfile.c @@ -331,7 +331,7 @@ static int ftpc_sendfile(struct ftpc_session_s *session, const char *path, if (!FTPC_IS_PASSIVE(session)) { - ret = ftpc_sockaccept(&session->data); + ret = ftpc_sockaccept(&session->dacceptor, &session->data); if (ret != OK) { nerr("ERROR: Data connection not accepted\n"); diff --git a/netutils/ftpc/ftpc_socket.c b/netutils/ftpc/ftpc_socket.c index efdbce6bb..d1f19d7a3 100644 --- a/netutils/ftpc/ftpc_socket.c +++ b/netutils/ftpc/ftpc_socket.c @@ -81,15 +81,19 @@ * ****************************************************************************/ -int ftpc_sockinit(FAR struct ftpc_socket_s *sock) +int ftpc_sockinit(FAR struct ftpc_socket_s *sock, sa_family_t family) { /* Initialize the socket structure */ memset(sock, 0, sizeof(struct ftpc_socket_s)); + DEBUGASSERT(family == AF_INET || family == AF_INET6); + + sock->laddr.sa.sa_family = family; + /* Create a socket descriptor */ - sock->sd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); + sock->sd = socket(family, SOCK_STREAM, IPPROTO_TCP); if (sock->sd < 0) { nerr("ERROR: socket() failed: %d\n", errno); @@ -142,7 +146,7 @@ errout: * ****************************************************************************/ -void ftpc_sockclose(struct ftpc_socket_s *sock) +void ftpc_sockclose(FAR struct ftpc_socket_s *sock) { /* Note that the same underlying socket descriptor is used for both streams. * There should be harmless failures on the second fclose and the close. @@ -164,13 +168,31 @@ void ftpc_sockclose(struct ftpc_socket_s *sock) * ****************************************************************************/ -int ftpc_sockconnect(struct ftpc_socket_s *sock, struct sockaddr_in *addr) +int ftpc_sockconnect(FAR struct ftpc_socket_s *sock, FAR struct sockaddr *addr) { int ret; /* Connect to the server */ - ret = connect(sock->sd, (struct sockaddr *)addr, sizeof(struct sockaddr)); +#ifdef CONFIG_NET_IPv6 + if (addr->sa_family == AF_INET6) + { + ret = connect(sock->sd, (struct sockaddr *)addr, sizeof(struct sockaddr_in6)); + } + else +#endif +#ifdef CONFIG_NET_IPv4 + if (addr->sa_family == AF_INET) + { + ret = connect(sock->sd, (struct sockaddr *)addr, sizeof(struct sockaddr_in)); + } + else +#endif + { + nerr("ERROR: Unsupported address family\n"); + return ERROR; + } + if (ret < 0) { nerr("ERROR: connect() failed: %d\n", errno); @@ -201,7 +223,7 @@ int ftpc_sockconnect(struct ftpc_socket_s *sock, struct sockaddr_in *addr) void ftpc_sockcopy(FAR struct ftpc_socket_s *dest, FAR const struct ftpc_socket_s *src) { - memcpy(&dest->laddr, &src->laddr, sizeof(struct sockaddr_in)); + memcpy(&dest->laddr, &src->laddr, sizeof(dest->laddr)); dest->connected = ftpc_sockconnected(src); } @@ -209,7 +231,7 @@ void ftpc_sockcopy(FAR struct ftpc_socket_s *dest, * Name: ftpc_sockaccept * * Description: - * Accept a connection on the data socket. This function is onlly used + * Accept a connection on the data socket. This function is only used * in active mode. * * In active mode FTP the client connects from a random port (N>1023) to the @@ -230,9 +252,10 @@ void ftpc_sockcopy(FAR struct ftpc_socket_s *dest, * ****************************************************************************/ -int ftpc_sockaccept(FAR struct ftpc_socket_s *sock) +int ftpc_sockaccept(FAR struct ftpc_socket_s *acceptor, + FAR struct ftpc_socket_s *sock) { - struct sockaddr addr; + union ftpc_sockaddr_u addr; socklen_t addrlen; /* Any previous socket should have been uninitialized (0) or explicitly @@ -247,14 +270,14 @@ int ftpc_sockaccept(FAR struct ftpc_socket_s *sock) } addrlen = sizeof(addr); - sock->sd = accept(sock->sd, &addr, &addrlen); + sock->sd = accept(acceptor->sd, (struct sockaddr *)&addr, &addrlen); if (sock->sd == -1) { nerr("ERROR: accept() failed: %d\n", errno); return ERROR; } - memcpy(&sock->laddr, &addr, sizeof(sock->laddr)); + memcpy(&sock->laddr, &addr, sizeof(union ftpc_sockaddr_u)); /* Create in/out C buffer I/O streams on the data channel. First, * create the incoming buffered stream. @@ -299,15 +322,35 @@ errout_with_sd: * ****************************************************************************/ -int ftpc_socklisten(struct ftpc_socket_s *sock) +int ftpc_socklisten(FAR struct ftpc_socket_s *sock) { - unsigned int addrlen = sizeof(sock->laddr); int ret; /* Bind the local socket to the local address */ - sock->laddr.sin_port = 0; - ret = bind(sock->sd, (struct sockaddr *)&sock->laddr, addrlen); +#ifdef CONFIG_NET_IPv6 + if (sock->laddr.sa.sa_family == AF_INET6) + { + sock->laddr.in6.sin6_port = 0; + ret = bind(sock->sd, (struct sockaddr *)&sock->laddr, + sizeof(struct sockaddr_in6)); + } + else +#endif +#ifdef CONFIG_NET_IPv4 + if (sock->laddr.sa.sa_family == AF_INET) + { + sock->laddr.in4.sin_port = 0; + ret = bind(sock->sd, (struct sockaddr *)&sock->laddr, + sizeof(struct sockaddr_in)); + } + else +#endif + { + nerr("ERROR: unsupported family\n"); + return ERROR; + } + if (ret < 0) { nerr("ERROR: bind() failed: %d\n", errno); @@ -335,7 +378,7 @@ int ftpc_socklisten(struct ftpc_socket_s *sock) * ****************************************************************************/ -int ftpc_sockprintf(struct ftpc_socket_s *sock, const char *fmt, ...) +int ftpc_sockprintf(FAR struct ftpc_socket_s *sock, FAR const char *fmt, ...) { va_list ap; int r; @@ -355,11 +398,13 @@ int ftpc_sockprintf(struct ftpc_socket_s *sock, const char *fmt, ...) ****************************************************************************/ int ftpc_sockgetsockname(FAR struct ftpc_socket_s *sock, - FAR struct sockaddr_in *addr) + FAR union ftpc_sockaddr_u *addr) { - socklen_t len = sizeof(struct sockaddr_in); + socklen_t len; int ret; + len = sizeof(union ftpc_sockaddr_u); + ret = getsockname(sock->sd, (FAR struct sockaddr *)addr, &len); if (ret < 0) { diff --git a/netutils/ftpc/ftpc_transfer.c b/netutils/ftpc/ftpc_transfer.c index 24db7e16e..033f1a768 100644 --- a/netutils/ftpc/ftpc_transfer.c +++ b/netutils/ftpc/ftpc_transfer.c @@ -49,8 +49,10 @@ #include #include #include +#include #include "netutils/ftpc.h" +#include "netutils/netlib.h" #include "ftpc_internal.h" @@ -75,10 +77,10 @@ ****************************************************************************/ /**************************************************************************** - * Name: ftp_pasvmode + * Name: ftp_cmd_epsv * * Description: - * Enter passive mode. + * Enter passive mode using EPSV command. * * In active mode FTP the client connects from a random port (N>1023) to the * FTP server's command port, port 21. Then, the client starts listening to @@ -98,22 +100,102 @@ * ****************************************************************************/ -static int ftp_pasvmode(struct ftpc_session_s *session, - uint8_t addrport[6]) +#ifndef CONFIG_FTPC_DISABLE_EPSV +static int ftp_cmd_epsv(FAR struct ftpc_session_s *session, + FAR union ftpc_sockaddr_u *addr) +{ + char *ptr; + int nscan; + int ret; + uint16_t tmp; + + /* Request passive mode. The server normally accepts EPSV with code 227. + * Its response is a single line showing the IP address of the server and + * the TCP port number where the server is accepting connections. + */ + + ret = ftpc_cmd(session, "EPSV"); + if (ret < 0 || !ftpc_connected(session)) + { + return ERROR; + } + + /* Skip over any leading stuff before important data begins */ + + ptr = session->reply + 4; + while (*ptr != '(') + { + ptr++; + + if (ptr > (session->reply + sizeof(session->reply) - 1)) + { + nwarn("WARNING: Error parsing EPSV reply: '%s'\n", session->reply); + return ERROR; + } + } + ptr++; + + /* The response is then just the port number. None of the other fields + * are supplied. + */ + + nscan = sscanf(ptr, "|||%u|", &tmp); + if (nscan != 1) + { + nwarn("WARNING: Error parsing EPSV reply: '%s'\n", session->reply); + return ERROR; + } + +#ifdef CONFIG_NET_IPv4 + if (addr->sa.sa_family == AF_INET) + { + addr->in4.sin_port = HTONS(tmp); + } +#endif + +#ifdef CONFIG_NET_IPv6 + if (addr->sa.sa_family == AF_INET6) + { + addr->in6.sin6_port = HTONS(tmp); + } +#endif + + return OK; +} +#endif + +/**************************************************************************** + * Name: ftp_cmd pasv + * + * Description: + * Enter passive mode using PASV command. + * + * In active mode FTP the client connects from a random port (N>1023) to the + * FTP server's command port, port 21. Then, the client starts listening to + * port N+1 and sends the FTP command PORT N+1 to the FTP server. The server + * will then connect back to the client's specified data port from its local + * data port, which is port 20. In passive mode FTP the client initiates + * both connections to the server, solving the problem of firewalls filtering + * the incoming data port connection to the client from the server. When + * opening an FTP connection, the client opens two random ports locally + * (N>1023 and N+1). The first port contacts the server on port 21, but + * instead of then issuing a PORT command and allowing the server to connect + * back to its data port, the client will issue the PASV command. The result + * of this is that the server then opens a random unprivileged port (P > + * 1023) and sends the PORT P command back to the client. The client then + * initiates the connection from port N+1 to port P on the server to transfer + * data. + * + ****************************************************************************/ + +#ifdef CONFIG_NET_IPv4 +static int ftp_cmd_pasv(FAR struct ftpc_session_s *session, + FAR union ftpc_sockaddr_u *addr) { int tmpap[6]; char *ptr; int nscan; int ret; - int i; - - /* Does this host support the PASV command */ - - if (!FTPC_HAS_PASV(session)) - { - nwarn("WARNING: Host doesn't support passive mode\n"); - return ERROR; - } /* Request passive mode. The server normally accepts PASV with code 227. * Its response is a single line showing the IP address of the server and @@ -126,7 +208,7 @@ static int ftp_pasvmode(struct ftpc_session_s *session, return ERROR; } - /* Skip over any leading stuff before address begins */ + /* Skip over any leading stuff before important data begins */ ptr = session->reply + 4; while (!isdigit((int)*ptr)) @@ -149,14 +231,12 @@ static int ftp_pasvmode(struct ftpc_session_s *session, /* Then copy the sscanf'ed values as bytes */ - - for (i = 0; i < 6; i++) - { - addrport[i] = (uint8_t)(tmpap[i] & 0xff); - } + memcpy(&addr->in4.sin_addr, tmpap, sizeof(addr->in4.sin_addr)); + memcpy(&addr->in4.sin_port, &tmpap[4], sizeof(addr->in4.sin_port)); return OK; } +#endif /**************************************************************************** * Name: ftpc_abspath @@ -245,11 +325,14 @@ static FAR char *ftpc_abspath(FAR struct ftpc_session_s *session, int ftpc_xfrinit(FAR struct ftpc_session_s *session) { - struct sockaddr_in addr; - uint8_t addrport[6]; + union ftpc_sockaddr_u addr; + int ret; +#ifdef CONFIG_FTPC_DISABLE_EPRT uint8_t *paddr; uint8_t *pport; - int ret; +#else + char ipstr[48]; +#endif /* We must be connected to initiate a transfer */ @@ -259,41 +342,55 @@ int ftpc_xfrinit(FAR struct ftpc_session_s *session) goto errout; } - /* Initialize the data channel */ - - ret = ftpc_sockinit(&session->data); - if (ret != OK) - { - nerr("ERROR: ftpc_sockinit() failed: %d\n", errno); - goto errout; - } - - /* Duplicate the address and connection information of the command channel */ - - ftpc_sockcopy(&session->data, &session->cmd); - /* Should we enter passive mode? */ if (FTPC_IS_PASSIVE(session)) { - /* Yes.. going passive. */ + /* Initialize the data channel */ - ret = ftp_pasvmode(session, addrport); + ret = ftpc_sockinit(&session->data, session->server.sa.sa_family); if (ret != OK) { - nerr("ERROR: ftp_pasvmode() failed: %d\n", errno); - goto errout_with_data; + nerr("ERROR: ftpc_sockinit() failed: %d\n", errno); + goto errout; } - /* Configure the data socket */ + /* Does this host support the PASV command */ - ftpc_sockgetsockname(&session->cmd, &addr); - memcpy(&addr.sin_addr, addrport, 4); - memcpy(&addr.sin_port, addrport+4, 2); + if (!FTPC_HAS_PASV(session)) + { + nerr("ERROR: Host doesn't support passive mode\n"); + goto errout_with_data; + } + + /* Configure the address to be the server address. If EPSV is used, the + * port will be populated by parsing the reply of the EPSV command. If the + * PASV command is used, the address and port will be overwritten. + */ + + memcpy(&addr, &session->server, sizeof(union ftpc_sockaddr_u)); + + /* Yes.. going passive. */ + +#ifdef CONFIG_FTPC_DISABLE_EPSV + ret = ftp_cmd_pasv(session, &addr); + if (ret < 0) + { + nerr("ERROR: ftp_cmd_pasv() failed: %d\n", errno); + goto errout_with_data; + } +#else + ret = ftp_cmd_epsv(session, &addr); + if (ret < 0) + { + nerr("ERROR: ftp_cmd_epsv() failed: %d\n", errno); + goto errout_with_data; + } +#endif /* Connect the data socket */ - ret = ftpc_sockconnect(&session->data, &addr); + ret = ftpc_sockconnect(&session->data, (FAR struct sockaddr *)&addr); if (ret < 0) { nerr("ERROR: ftpc_sockconnect() failed: %d\n", errno); @@ -302,18 +399,96 @@ int ftpc_xfrinit(FAR struct ftpc_session_s *session) } else { + /* Initialize the data listener socket that allows us to accept new + * data connections from the server + */ + + ret = ftpc_sockinit(&session->dacceptor, session->server.sa.sa_family); + if (ret != OK) + { + nerr("ERROR: ftpc_sockinit() failed: %d\n", errno); + goto errout; + } + /* Use the server IP address to find the network interface, and subsequent + * local IP address used to establish the active connection. We must send + * the IP and port to the server so that it knows how to connect. + */ + +#ifdef CONFIG_NET_IPv6 + if (session->server.sa.sa_family == AF_INET6) + { + ret = netlib_ipv6adaptor(&session->server.in6.sin6_addr, + &session->dacceptor.laddr.in6.sin6_addr); + if (ret < 0) + { + nerr("ERROR: netlib_ipv6adaptor() failed: %d\n", ret); + goto errout_with_data; + } + } + else +#endif +#ifdef CONFIG_NET_IPv4 + if (session->server.sa.sa_family == AF_INET) + { + ret = netlib_ipv4adaptor(&session->server.in4.sin_addr, + &session->dacceptor.laddr.in4.sin_addr); + if (ret < 0) + { + nerr("ERROR: netlib_ipv4adaptor() failed: %d\n", ret); + goto errout_with_data; + } + } + else +#endif + { + nerr("ERROR: unsupported address family\n"); + goto errout_with_data; + } + /* Wait for the connection to be established */ - ftpc_socklisten(&session->data); + ftpc_socklisten(&session->dacceptor); +#ifdef CONFIG_FTPC_DISABLE_EPRT /* Then send our local data channel address to the server */ - paddr = (uint8_t *)&session->data.laddr.sin_addr; - pport = (uint8_t *)&session->data.laddr.sin_port; + paddr = (uint8_t *)&session->dacceptor.laddr.in4.sin_addr; + pport = (uint8_t *)&session->dacceptor.laddr.in4.sin_port; ret = ftpc_cmd(session, "PORT %d,%d,%d,%d,%d,%d", paddr[0], paddr[1], paddr[2], paddr[3], pport[0], pport[1]); +#else +#ifdef CONFIG_NET_IPv6 + if (session->dacceptor.laddr.sa.sa_family == AF_INET6) + { + if (!inet_ntop(AF_INET6, &session->dacceptor.laddr.in6.sin6_addr, ipstr, 48)) + { + nerr("ERROR: inet_ntop failed: %d\n", errno); + goto errout_with_data; + } + + ret = ftpc_cmd(session, "EPRT |2|%s|%d|", ipstr, + session->dacceptor.laddr.in6.sin6_port); + } + else +#endif /* CONFIG_NET_IPv6 */ +#ifdef CONFIG_NET_IPv4 + if (session->dacceptor.laddr.sa.sa_family == AF_INET) + { + if (!inet_ntop(AF_INET, &session->dacceptor.laddr.in4.sin_addr, ipstr, 48)) + { + nerr("ERROR: inet_ntop failed: %d\n", errno); + goto errout_with_data; + } + + ret = ftpc_cmd(session, "EPRT |1|%s|%d|", ipstr, + session->dacceptor.laddr.in4.sin_port); + } + else +#endif /* CONFIG_NET_IPv4 */ +#endif /* CONFIG_FTPC_DISABLE_EPRT */ + if (ret < 0) { nerr("ERROR: ftpc_cmd() failed: %d\n", errno); @@ -324,6 +499,7 @@ int ftpc_xfrinit(FAR struct ftpc_session_s *session) errout_with_data: ftpc_sockclose(&session->data); + ftpc_sockclose(&session->dacceptor); errout: return ERROR; } diff --git a/netutils/ftpd/Kconfig b/netutils/ftpd/Kconfig index 43bf7ccc1..602aaf2e9 100644 --- a/netutils/ftpd/Kconfig +++ b/netutils/ftpd/Kconfig @@ -11,4 +11,9 @@ config NETUTILS_FTPD Enable support for the FTP server. if NETUTILS_FTPD + +config FTPD_WORKERSTACKSIZE + int "FTPD client thread stack size" + default 2048 + endif diff --git a/netutils/ftpd/ftpd.c b/netutils/ftpd/ftpd.c index e6c35fd83..6cb703e69 100644 --- a/netutils/ftpd/ftpd.c +++ b/netutils/ftpd/ftpd.c @@ -113,7 +113,7 @@ static ssize_t ftpd_response(int sd, int timeout, FAR const char *fmt, ...); static int ftpd_dataopen(FAR struct ftpd_session_s *session); static int ftpd_dataclose(FAR struct ftpd_session_s *session); -static FAR struct ftpd_server_s *ftpd_openserver(int port); +static FAR struct ftpd_server_s *ftpd_openserver(int port, sa_family_t family); /* Path helpers */ @@ -1103,10 +1103,9 @@ static int ftpd_dataclose(FAR struct ftpd_session_s *session) * Name: ftpd_openserver ****************************************************************************/ -static FAR struct ftpd_server_s *ftpd_openserver(int port) +static FAR struct ftpd_server_s *ftpd_openserver(int port, sa_family_t family) { FAR struct ftpd_server_s *server; - sa_family_t family; socklen_t addrlen; FAR const void *addr; #if defined(SOMAXCONN) @@ -1134,37 +1133,43 @@ static FAR struct ftpd_server_s *ftpd_openserver(int port) /* Create the server listen socket */ #ifdef CONFIG_NET_IPv6 - server->sd = socket(PF_INET6, SOCK_STREAM, IPPROTO_TCP); - if (server->sd < 0) + if (family == AF_INET6) { - ftpd_close((FTPD_SESSION)server); - return NULL; + server->addr.in6.sin6_family = family; + server->addr.in6.sin6_addr = in6addr_any; + server->addr.in6.sin6_port = htons(port); + + addrlen = (socklen_t)sizeof(server->addr.in6); + addr = (FAR void *)(&server->addr.in6); + + server->sd = socket(PF_INET6, SOCK_STREAM, IPPROTO_TCP); } - - family = AF_INET6; - addrlen = (socklen_t)sizeof(server->addr.in6); - addr = (FAR void *)(&server->addr.in6); - - server->addr.in6.sin6_family = family; - server->addr.in6.sin6_flowinfo = 0; - server->addr.in6.sin6_addr = in6addr_any; - server->addr.in6.sin6_port = htons(port); -#else - server->sd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); - if (server->sd < 0) - { - ftpd_close((FTPD_SESSION)server); - return NULL; - } - - family = AF_INET; - addrlen = (socklen_t)sizeof(server->addr.in4); - addr = (FAR void *)(&server->addr.in4); - - server->addr.in4.sin_family = family; - server->addr.in4.sin_addr.s_addr = htonl(INADDR_ANY); - server->addr.in4.sin_port = htons(port); + else #endif +#ifdef CONFIG_NET_IPv4 + if (family == AF_INET) + { + server->addr.in4.sin_family = family; + server->addr.in4.sin_addr.s_addr = htonl(INADDR_ANY); + server->addr.in4.sin_port = htons(port); + + addrlen = (socklen_t)sizeof(server->addr.in4); + addr = (FAR void *)(&server->addr.in4); + + server->sd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); + } + else +#endif + { + nerr("ERROR: Unsupported family\n"); + return NULL; + } + + if (server->sd < 0) + { + ftpd_close((FTPD_SESSION)server); + return NULL; + } #ifdef CONFIG_NET_HAVE_REUSEADDR { @@ -1976,14 +1981,31 @@ static int ftpd_stream(FAR struct ftpd_session_s *session, int cmdtype) } else { + int remaining; + int nwritten; + FAR char *next; + + remaining = buflen; + next = buffer; + /* Write to the file */ - wrbytes = write(session->fd, buffer, buflen); - if (wrbytes < 0) + do { - errval = errno; - nerr("ERROR: write() failed: %d\n", errval); + nwritten = write(session->fd, next, remaining); + if (nwritten < 0) + { + errval = errno; + nerr("ERROR: write() failed: %d\n", errval); + break; + } + + remaining -= nwritten; + next += nwritten; } + while (remaining > 0); + + wrbytes = next - buffer; } /* If the number of bytes returned by the write is not equal to the @@ -2646,6 +2668,7 @@ static int ftpd_command_noop(FAR struct ftpd_session_s *session) static int ftpd_command_port(FAR struct ftpd_session_s *session) { +#ifdef CONFIG_NET_IPv4 uint8_t value[6]; unsigned int utemp; int temp; @@ -2749,6 +2772,11 @@ static int ftpd_command_port(FAR struct ftpd_session_s *session) return ftpd_response(session->cmd.sd, session->txtimeout, g_respfmt1, 200, ' ', "PORT command successful"); +#else + return ftpd_response(session->cmd.sd, session->txtimeout, + g_respfmt1, 502, ' ', + "PORT command not implemented"); +#endif } /**************************************************************************** @@ -3074,6 +3102,7 @@ static int ftpd_command_dele(FAR struct ftpd_session_s *session) static int ftpd_command_pasv(FAR struct ftpd_session_s *session) { +#ifdef CONFIG_NET_IPv4 unsigned int value[6]; unsigned int temp; int ret; @@ -3118,22 +3147,7 @@ static int ftpd_command_pasv(FAR struct ftpd_session_s *session) session->data.addr.in4.sin_addr.s_addr = in4addr.s_addr; } } - else #endif -#ifndef CONFIG_NET_IPv6 - if (session->data.addr.ss.ss_family == AF_INET) - { - /* Fixed to ipv4 */ - - memset((FAR void *)(&session->data.addr), 0, sizeof(session->data.addr)); - session->data.addr.in4.sin_family = AF_INET; - session->data.addr.in4.sin_addr.s_addr = htonl(INADDR_ANY); - } - else -#endif - { - nerr("ERROR: Unsupported family\n"); - } session->data.addr.in4.sin_port = 0; ret = bind(session->data.sd, (FAR const struct sockaddr *)&session->data.addr, @@ -3191,6 +3205,11 @@ static int ftpd_command_pasv(FAR struct ftpd_session_s *session) } return ret; +#else + return ftpd_response(session->cmd.sd, session->txtimeout, + g_respfmt1, 502, ' ', + "PASV command not implemented"); +#endif } /**************************************************************************** @@ -3209,7 +3228,9 @@ static int ftpd_command_epsv(FAR struct ftpd_session_s *session) session->data.sd = socket(PF_INET6, SOCK_STREAM, IPPROTO_TCP); if (session->data.sd < 0) { +#ifdef CONFIG_NET_IPv4 session->data.sd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); +#endif } else { @@ -3245,13 +3266,21 @@ static int ftpd_command_epsv(FAR struct ftpd_session_s *session) { session->data.addr.in6.sin6_port = htons(0); } - else if (session->data.addr.ss.ss_family == AF_INET) + else +#endif +#ifdef CONFIG_NET_IPv4 + if (session->data.addr.ss.ss_family == AF_INET) { session->data.addr.in4.sin_port = htons(0); } -#else - session->data.addr.in4.sin_port = htons(0); + else #endif + { + ret = ftpd_response(session->cmd.sd, session->txtimeout, + g_respfmt1, 500, ' ', "EPSV family not supported!"); + (void)ftpd_dataclose(session); + return ret; + } ret = bind(session->data.sd, (FAR const struct sockaddr *)&session->data.addr, session->data.addrlen); @@ -4155,14 +4184,14 @@ static FAR void *ftpd_worker(FAR void *arg) * ****************************************************************************/ -FTPD_SESSION ftpd_open(void) +FTPD_SESSION ftpd_open(sa_family_t family) { FAR struct ftpd_server_s *server; - server = ftpd_openserver(21); + server = ftpd_openserver(21, family); if (!server) { - server = ftpd_openserver(2211); + server = ftpd_openserver(2211, family); } return (FTPD_SESSION)server;