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 <gnutt@nuttx.org>
This commit is contained in:
parent
efd1279025
commit
f489bcd6f9
@ -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
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -47,6 +47,7 @@
|
||||
#include <signal.h>
|
||||
#include <time.h>
|
||||
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
|
||||
/****************************************************************************
|
||||
@ -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 *************************************************************/
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
||||
/****************************************************************************
|
||||
|
@ -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:
|
||||
|
@ -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 */
|
||||
|
@ -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");
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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"
|
||||
|
@ -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");
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -49,8 +49,10 @@
|
||||
#include <errno.h>
|
||||
#include <assert.h>
|
||||
#include <debug.h>
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#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;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
Loading…
x
Reference in New Issue
Block a user