0a6b1f55ab
Gregory Nutt is has submitted the SGA Sebastien Lorquet has submitted the ICLA as a result we can migrate the licenses to Apache. Signed-off-by: Alin Jerpelea <alin.jerpelea@sony.com>
407 lines
11 KiB
C
407 lines
11 KiB
C
/****************************************************************************
|
|
* apps/netutils/ftpc/ftpc_socket.c
|
|
*
|
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
|
* contributor license agreements. See the NOTICE file distributed with
|
|
* this work for additional information regarding copyright ownership. The
|
|
* ASF licenses this file to you under the Apache License, Version 2.0 (the
|
|
* "License"); you may not use this file except in compliance with the
|
|
* License. You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
* License for the specific language governing permissions and limitations
|
|
* under the License.
|
|
*
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Included Files
|
|
****************************************************************************/
|
|
|
|
#include "ftpc_config.h"
|
|
#include <sys/socket.h>
|
|
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
#include <string.h>
|
|
#include <assert.h>
|
|
#include <debug.h>
|
|
#include <errno.h>
|
|
|
|
#include "ftpc_internal.h"
|
|
|
|
/****************************************************************************
|
|
* Pre-processor Definitions
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Private Types
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Private Data
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Public Data
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Private Functions
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Public Functions
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Name: ftpc_sockinit
|
|
*
|
|
* Description:
|
|
* Initialize a socket. Create the socket and "wrap" it as C standard
|
|
* incoming and outgoing streams.
|
|
*
|
|
****************************************************************************/
|
|
|
|
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(family, SOCK_STREAM, IPPROTO_TCP);
|
|
if (sock->sd < 0)
|
|
{
|
|
nerr("ERROR: socket() failed: %d\n", errno);
|
|
goto errout;
|
|
}
|
|
|
|
/* Call fdopen to "wrap" the socket descriptor as an input stream using C
|
|
* buffered I/O.
|
|
*/
|
|
|
|
sock->instream = fdopen(sock->sd, "r");
|
|
if (!sock->instream)
|
|
{
|
|
nerr("ERROR: fdopen() failed: %d\n", errno);
|
|
goto errout_with_sd;
|
|
}
|
|
|
|
/* Call fdopen to "wrap" the socket descriptor as an output stream using C
|
|
* buffered I/O.
|
|
*/
|
|
|
|
sock->outstream = fdopen(sock->sd, "w");
|
|
if (!sock->outstream)
|
|
{
|
|
nerr("ERROR: fdopen() failed: %d\n", errno);
|
|
goto errout_with_instream;
|
|
}
|
|
|
|
return OK;
|
|
|
|
/* Close the instream. NOTE: Since the underlying socket descriptor is
|
|
* *not* dup'ed, the following close should fail harmlessly.
|
|
*/
|
|
|
|
errout_with_instream:
|
|
fclose(sock->instream);
|
|
sock->instream = NULL;
|
|
errout_with_sd:
|
|
close(sock->sd);
|
|
sock->sd = -1;
|
|
errout:
|
|
return ERROR;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: ftpc_sockclose
|
|
*
|
|
* Description:
|
|
* Close a socket
|
|
*
|
|
****************************************************************************/
|
|
|
|
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.
|
|
*/
|
|
|
|
fclose(sock->instream);
|
|
fclose(sock->outstream);
|
|
close(sock->sd);
|
|
memset(sock, 0, sizeof(struct ftpc_socket_s));
|
|
sock->sd = -1;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: ftpc_sockconnect
|
|
*
|
|
* Description:
|
|
* Connect the socket to the host. On a failure, the caller should call.
|
|
* ftpc_sockclose() to clean up.
|
|
*
|
|
****************************************************************************/
|
|
|
|
int ftpc_sockconnect(FAR struct ftpc_socket_s *sock,
|
|
FAR struct sockaddr *addr)
|
|
{
|
|
int ret;
|
|
|
|
/* Connect to the server */
|
|
|
|
#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);
|
|
return ERROR;
|
|
}
|
|
|
|
/* Get the local address of the socket */
|
|
|
|
ret = ftpc_sockgetsockname(sock, &sock->laddr);
|
|
if (ret < 0)
|
|
{
|
|
nerr("ERROR: ftpc_sockgetsockname() failed: %d\n", errno);
|
|
return ERROR;
|
|
}
|
|
|
|
sock->connected = true;
|
|
return OK;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: ftpc_sockcopy
|
|
*
|
|
* Description:
|
|
* Copy the socket state from one location to another.
|
|
*
|
|
****************************************************************************/
|
|
|
|
void ftpc_sockcopy(FAR struct ftpc_socket_s *dest,
|
|
FAR const struct ftpc_socket_s *src)
|
|
{
|
|
memcpy(&dest->laddr, &src->laddr, sizeof(dest->laddr));
|
|
dest->connected = ftpc_sockconnected(src);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: ftpc_sockaccept
|
|
*
|
|
* Description:
|
|
* 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 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.
|
|
*
|
|
****************************************************************************/
|
|
|
|
int ftpc_sockaccept(FAR struct ftpc_socket_s *acceptor,
|
|
FAR struct ftpc_socket_s *sock)
|
|
{
|
|
union ftpc_sockaddr_u addr;
|
|
socklen_t addrlen;
|
|
|
|
/* Any previous socket should have been uninitialized (0) or explicitly
|
|
* closed (-1). But the path to this function may include a call to
|
|
* ftpc_sockinit(). If so... close that socket and call accept to
|
|
* get a new one.
|
|
*/
|
|
|
|
if (sock->sd > 0)
|
|
{
|
|
ftpc_sockclose(sock);
|
|
}
|
|
|
|
addrlen = sizeof(addr);
|
|
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(union ftpc_sockaddr_u));
|
|
|
|
/* Create in/out C buffer I/O streams on the data channel. First,
|
|
* create the incoming buffered stream.
|
|
*/
|
|
|
|
sock->instream = fdopen(sock->sd, "r");
|
|
if (!sock->instream)
|
|
{
|
|
nerr("ERROR: fdopen() failed: %d\n", errno);
|
|
goto errout_with_sd;
|
|
}
|
|
|
|
/* Create the outgoing stream */
|
|
|
|
sock->outstream = fdopen(sock->sd, "w");
|
|
if (!sock->outstream)
|
|
{
|
|
nerr("ERROR: fdopen() failed: %d\n", errno);
|
|
goto errout_with_instream;
|
|
}
|
|
|
|
return OK;
|
|
|
|
/* Close the instream. NOTE: Since the underlying socket descriptor is
|
|
* *not* dup'ed, the following close should fail harmlessly.
|
|
*/
|
|
|
|
errout_with_instream:
|
|
fclose(sock->instream);
|
|
sock->instream = NULL;
|
|
errout_with_sd:
|
|
close(sock->sd);
|
|
sock->sd = -1;
|
|
return ERROR;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: ftpc_socklisten
|
|
*
|
|
* Description:
|
|
* Bind the socket to local address and wait for connection from server.
|
|
*
|
|
****************************************************************************/
|
|
|
|
int ftpc_socklisten(FAR struct ftpc_socket_s *sock)
|
|
{
|
|
int ret;
|
|
|
|
/* Bind the local socket to the local address */
|
|
|
|
#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);
|
|
return ERROR;
|
|
}
|
|
|
|
/* Wait for the connection to the server */
|
|
|
|
if (listen(sock->sd, 1) == -1)
|
|
{
|
|
return ERROR;
|
|
}
|
|
|
|
/* Then get the local address selected by NuttX */
|
|
|
|
ret = ftpc_sockgetsockname(sock, &sock->laddr);
|
|
return ret;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: ftpc_sockprintf
|
|
*
|
|
* Description:
|
|
* printf to a socket stream
|
|
*
|
|
****************************************************************************/
|
|
|
|
int ftpc_sockprintf(FAR struct ftpc_socket_s *sock, FAR const char *fmt, ...)
|
|
{
|
|
va_list ap;
|
|
int r;
|
|
|
|
va_start(ap, fmt);
|
|
r = vfprintf(sock->outstream, fmt, ap);
|
|
va_end(ap);
|
|
return r;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: ftpc_sockgetsockname
|
|
*
|
|
* Description:
|
|
* Get the address of the local socket
|
|
*
|
|
****************************************************************************/
|
|
|
|
int ftpc_sockgetsockname(FAR struct ftpc_socket_s *sock,
|
|
FAR union ftpc_sockaddr_u *addr)
|
|
{
|
|
socklen_t len;
|
|
int ret;
|
|
|
|
len = sizeof(union ftpc_sockaddr_u);
|
|
|
|
ret = getsockname(sock->sd, (FAR struct sockaddr *)addr, &len);
|
|
if (ret < 0)
|
|
{
|
|
nerr("ERROR: getsockname failed: %d\n", errno);
|
|
return ERROR;
|
|
}
|
|
|
|
return OK;
|
|
}
|