nuttx-apps/netutils/ftpc/ftpc_connect.c
Anthony Merlino f489bcd6f9 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>
2017-11-06 23:41:30 +00:00

291 lines
8.3 KiB
C

/****************************************************************************
* apps/netutils/ftpc/ftpc_connect.c
*
* Copyright (C) 2011 Gregory Nutt. All rights reserved.
* Author: Gregory Nutt <gnutt@nuttx.org>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* 3. Neither the name NuttX nor the names of its contributors may be
* used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include "ftpc_config.h"
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <debug.h>
#include <arpa/inet.h>
#include "netutils/ftpc.h"
#include "ftpc_internal.h"
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: ftpc_connect
*
* Description:
* Create a session handle and connect to the server.
*
****************************************************************************/
SESSION ftpc_connect(FAR union ftpc_sockaddr_u *server)
{
FAR struct ftpc_session_s *session;
int ret;
/* Allocate a session structure */
session = (struct ftpc_session_s *)zalloc(sizeof(struct ftpc_session_s));
if (!session)
{
nerr("ERROR: Failed to allocate a session\n");
set_errno(ENOMEM);
goto errout;
}
/* Initialize the session structure with all non-zero and variable values */
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;
session->conntimeo = CONFIG_FTP_DEFTIMEO * CLOCKS_PER_SEC;
session->pid = getpid();
/* Use the default port if the user specified port number zero */
#ifdef CONFIG_NET_IPv6
if (session->server.sa.sa_family == AF_INET6)
{
if (!session->server.in6.sin6_port)
{
session->server.in6.sin6_port = HTONS(CONFIG_FTP_DEFPORT);
}
}
#endif
#ifdef CONFIG_NET_IPv4
if (session->server.sa.sa_family == AF_INET)
{
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
* we can change the current working directory without effecting any other
* logic that may be in same context.
*/
session->homeldir = strdup(ftpc_lpwd());
/* session->curldir = strdup(session->homeldir); */
/* Create up a timer to prevent hangs */
session->wdog = wd_create();
/* And (Re-)connect to the server */
ret = ftpc_reconnect(session);
if (ret != OK)
{
nerr("ERROR: ftpc_reconnect() failed: %d\n", errno);
goto errout_with_alloc;
}
return (SESSION)session;
errout_with_alloc:
if (session->homeldir != NULL)
{
free(session->homeldir);
}
free(session);
errout:
return NULL;
}
/****************************************************************************
* Name: ftpc_reconnect
*
* Description:
* re-connect to the server either initially, or after loss of connection.
*
****************************************************************************/
int ftpc_reconnect(FAR struct ftpc_session_s *session)
{
#ifdef CONFIG_DEBUG_NET_ERROR
char buffer[48];
#endif
int ret;
/* Re-initialize the session structure */
session->replytimeo = CONFIG_FTP_DEFTIMEO * CLOCKS_PER_SEC;
session->conntimeo = CONFIG_FTP_DEFTIMEO * CLOCKS_PER_SEC;
session->xfrmode = FTPC_XFRMODE_UNKNOWN;
/* Set up a timer to prevent hangs */
ret = wd_start(session->wdog, session->conntimeo, ftpc_timeout, 1, session);
if (ret != OK)
{
nerr("ERROR: wd_start() failed\n");
goto errout;
}
/* Initialize a socket */
ret = ftpc_sockinit(&session->cmd, session->server.sa.sa_family);
if (ret != OK)
{
nerr("ERROR: ftpc_sockinit() failed: %d\n", errno);
goto errout;
}
/* Connect the socket to the server */
#ifdef CONFIG_DEBUG_NET_ERROR
#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 */
ret = ftpc_sockconnect(&session->cmd, (FAR struct sockaddr *)&session->server);
if (ret != OK)
{
nerr("ERROR: ftpc_sockconnect() failed: %d\n", errno);
goto errout_with_socket;
}
/* Read startup message from server */
fptc_getreply(session);
/* Check for "120 Service ready in nnn minutes" */
if (session->code == 120)
{
fptc_getreply(session);
}
wd_cancel(session->wdog);
if (!ftpc_sockconnected(&session->cmd))
{
ftpc_reset(session);
goto errout;
}
/* Check for "220 Service ready for new user" */
if (session->code == 220)
{
FTPC_SET_CONNECTED(session);
}
if (!FTPC_IS_CONNECTED(session))
{
goto errout_with_socket;
}
#ifdef CONFIG_DEBUG_NET_ERROR
ninfo("Connected\n");
#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:
ftpc_sockclose(&session->cmd);
errout:
return ERROR;
}