e749f6ca7e
1. remove the unnecessary interfaces tcp_close_monitor() socket flags(s_flags) is a global state for net connection remove the incorrect update for stop monitor 2. do not start the tcp monitor from duplicated psock the tcp monitor has already registered in connect callback ------------------------------------------------------------ This patch also fix the telnet issue reported by: https://github.com/apache/incubator-nuttx/pull/5434#issuecomment-1035600651 the orignal session fd is closed after dup, the connect state has incorrectly migrated to close: drivers/net/telnet.c: 977 static int telnet_session(FAR struct telnet_session_s *session) ... 1031 ret = psock_dup2(psock, &priv->td_psock); ... 1082 nx_close(session->ts_sd); Signed-off-by: chao.an <anchao@xiaomi.com>
1817 lines
50 KiB
C
1817 lines
50 KiB
C
/****************************************************************************
|
|
* net/inet/inet_sockif.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 <nuttx/config.h>
|
|
|
|
#include <sys/types.h>
|
|
#include <sys/socket.h>
|
|
#include <stdbool.h>
|
|
#include <assert.h>
|
|
#include <errno.h>
|
|
#include <debug.h>
|
|
|
|
#include <nuttx/net/net.h>
|
|
#include <nuttx/net/tcp.h>
|
|
#include <nuttx/kmalloc.h>
|
|
|
|
#include "tcp/tcp.h"
|
|
#include "udp/udp.h"
|
|
#include "icmp/icmp.h"
|
|
#include "icmpv6/icmpv6.h"
|
|
#include "sixlowpan/sixlowpan.h"
|
|
#include "socket/socket.h"
|
|
#include "inet/inet.h"
|
|
|
|
#ifdef HAVE_INET_SOCKETS
|
|
|
|
/****************************************************************************
|
|
* Private Type Definitions
|
|
****************************************************************************/
|
|
|
|
union sockaddr_u
|
|
{
|
|
struct sockaddr addr;
|
|
struct sockaddr_in inaddr;
|
|
struct sockaddr_in6 in6addr;
|
|
};
|
|
|
|
/****************************************************************************
|
|
* Private Function Prototypes
|
|
****************************************************************************/
|
|
|
|
#if defined(NET_UDP_HAVE_STACK) || defined(NET_TCP_HAVE_STACK)
|
|
|
|
static int inet_setup(FAR struct socket *psock, int protocol);
|
|
static sockcaps_t inet_sockcaps(FAR struct socket *psock);
|
|
static void inet_addref(FAR struct socket *psock);
|
|
static int inet_bind(FAR struct socket *psock,
|
|
FAR const struct sockaddr *addr, socklen_t addrlen);
|
|
static int inet_getsockname(FAR struct socket *psock,
|
|
FAR struct sockaddr *addr, FAR socklen_t *addrlen);
|
|
static int inet_getpeername(FAR struct socket *psock,
|
|
FAR struct sockaddr *addr, FAR socklen_t *addrlen);
|
|
static int inet_listen(FAR struct socket *psock, int backlog);
|
|
static int inet_connect(FAR struct socket *psock,
|
|
FAR const struct sockaddr *addr, socklen_t addrlen);
|
|
static int inet_accept(FAR struct socket *psock,
|
|
FAR struct sockaddr *addr, FAR socklen_t *addrlen,
|
|
FAR struct socket *newsock);
|
|
static int inet_poll(FAR struct socket *psock,
|
|
FAR struct pollfd *fds, bool setup);
|
|
static ssize_t inet_send(FAR struct socket *psock, FAR const void *buf,
|
|
size_t len, int flags);
|
|
static ssize_t inet_sendto(FAR struct socket *psock, FAR const void *buf,
|
|
size_t len, int flags, FAR const struct sockaddr *to,
|
|
socklen_t tolen);
|
|
static ssize_t inet_sendmsg(FAR struct socket *psock,
|
|
FAR struct msghdr *msg, int flags);
|
|
static ssize_t inet_recvmsg(FAR struct socket *psock,
|
|
FAR struct msghdr *msg, int flags);
|
|
static int inet_ioctl(FAR struct socket *psock, int cmd,
|
|
FAR void *arg, size_t arglen);
|
|
static int inet_socketpair(FAR struct socket *psocks[2]);
|
|
#ifdef CONFIG_NET_SENDFILE
|
|
static ssize_t inet_sendfile(FAR struct socket *psock,
|
|
FAR struct file *infile, FAR off_t *offset,
|
|
size_t count);
|
|
#endif
|
|
|
|
/****************************************************************************
|
|
* Private Data
|
|
****************************************************************************/
|
|
|
|
static const struct sock_intf_s g_inet_sockif =
|
|
{
|
|
inet_setup, /* si_setup */
|
|
inet_sockcaps, /* si_sockcaps */
|
|
inet_addref, /* si_addref */
|
|
inet_bind, /* si_bind */
|
|
inet_getsockname, /* si_getsockname */
|
|
inet_getpeername, /* si_getpeername */
|
|
inet_listen, /* si_listen */
|
|
inet_connect, /* si_connect */
|
|
inet_accept, /* si_accept */
|
|
inet_poll, /* si_poll */
|
|
inet_sendmsg, /* si_sendmsg */
|
|
inet_recvmsg, /* si_recvmsg */
|
|
inet_close, /* si_close */
|
|
inet_ioctl, /* si_ioctl */
|
|
inet_socketpair /* si_socketpair */
|
|
#ifdef CONFIG_NET_SENDFILE
|
|
,
|
|
inet_sendfile /* si_sendfile */
|
|
#endif
|
|
};
|
|
|
|
/****************************************************************************
|
|
* Private Functions
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Name: inet_tcp_alloc
|
|
*
|
|
* Description:
|
|
* Allocate and attach a TCP connection structure.
|
|
*
|
|
****************************************************************************/
|
|
|
|
#ifdef NET_TCP_HAVE_STACK
|
|
static int inet_tcp_alloc(FAR struct socket *psock)
|
|
{
|
|
/* Allocate the TCP connection structure */
|
|
|
|
FAR struct tcp_conn_s *conn = tcp_alloc(psock->s_domain);
|
|
if (conn == NULL)
|
|
{
|
|
/* Failed to reserve a connection structure */
|
|
|
|
nerr("ERROR: Failed to reserve TCP connection structure\n");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
/* Set the reference count on the connection structure. This reference
|
|
* count will be incremented only if the socket is dup'ed
|
|
*/
|
|
|
|
DEBUGASSERT(conn->crefs == 0);
|
|
conn->crefs = 1;
|
|
|
|
/* Save the pre-allocated connection in the socket structure */
|
|
|
|
psock->s_conn = conn;
|
|
return OK;
|
|
}
|
|
#endif /* NET_TCP_HAVE_STACK */
|
|
|
|
/****************************************************************************
|
|
* Name: inet_udp_alloc
|
|
*
|
|
* Description:
|
|
* Allocate and attach a UDP connection structure.
|
|
*
|
|
****************************************************************************/
|
|
|
|
#ifdef NET_UDP_HAVE_STACK
|
|
static int inet_udp_alloc(FAR struct socket *psock)
|
|
{
|
|
/* Allocate the UDP connection structure */
|
|
|
|
FAR struct udp_conn_s *conn = udp_alloc(psock->s_domain);
|
|
if (conn == NULL)
|
|
{
|
|
/* Failed to reserve a connection structure */
|
|
|
|
nerr("ERROR: Failed to reserve UDP connection structure\n");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
/* Set the reference count on the connection structure. This reference
|
|
* count will be incremented only if the socket is dup'ed
|
|
*/
|
|
|
|
DEBUGASSERT(conn->crefs == 0);
|
|
conn->crefs = 1;
|
|
|
|
/* Save the pre-allocated connection in the socket structure */
|
|
|
|
psock->s_conn = conn;
|
|
return OK;
|
|
}
|
|
#endif /* NET_UDP_HAVE_STACK */
|
|
|
|
/****************************************************************************
|
|
* Name: inet_setup
|
|
*
|
|
* Description:
|
|
* Called for socket() to verify that the provided socket type and
|
|
* protocol are usable by this address family. Perform any family-
|
|
* specific socket fields.
|
|
*
|
|
* NOTE: This is common logic for both the AF_INET and AF_INET6 address
|
|
* families.
|
|
*
|
|
* Input Parameters:
|
|
* psock A pointer to a user allocated socket structure to be
|
|
* initialized.
|
|
* protocol (see sys/socket.h)
|
|
*
|
|
* Returned Value:
|
|
* Zero (OK) is returned on success. Otherwise, a negated errno value is
|
|
* returned.
|
|
*
|
|
****************************************************************************/
|
|
|
|
static int inet_setup(FAR struct socket *psock, int protocol)
|
|
{
|
|
/* Allocate the appropriate connection structure. This reserves the
|
|
* the connection structure is is unallocated at this point. It will
|
|
* not actually be initialized until the socket is connected.
|
|
*
|
|
* REVISIT: Only SOCK_STREAM and SOCK_DGRAM are supported.
|
|
*/
|
|
|
|
switch (psock->s_type)
|
|
{
|
|
#ifdef CONFIG_NET_TCP
|
|
case SOCK_STREAM:
|
|
if (protocol != 0 && protocol != IPPROTO_TCP)
|
|
{
|
|
nerr("ERROR: Unsupported stream protocol: %d\n", protocol);
|
|
return -EPROTONOSUPPORT;
|
|
}
|
|
|
|
#ifdef NET_TCP_HAVE_STACK
|
|
/* Allocate and attach the TCP connection structure */
|
|
|
|
return inet_tcp_alloc(psock);
|
|
#else
|
|
nwarn("WARNING: SOCK_STREAM disabled\n");
|
|
return -ENETDOWN;
|
|
#endif
|
|
#endif /* CONFIG_NET_TCP */
|
|
|
|
#ifdef CONFIG_NET_UDP
|
|
case SOCK_DGRAM:
|
|
if (protocol != 0 && protocol != IPPROTO_UDP)
|
|
{
|
|
nerr("ERROR: Unsupported datagram protocol: %d\n", protocol);
|
|
return -EPROTONOSUPPORT;
|
|
}
|
|
|
|
#ifdef NET_UDP_HAVE_STACK
|
|
/* Allocate and attach the UDP connection structure */
|
|
|
|
return inet_udp_alloc(psock);
|
|
#else
|
|
nwarn("WARNING: SOCK_DGRAM disabled\n");
|
|
return -ENETDOWN;
|
|
#endif
|
|
#endif /* CONFIG_NET_UDP */
|
|
|
|
default:
|
|
nerr("ERROR: Unsupported type: %d\n", psock->s_type);
|
|
return -EPROTONOSUPPORT;
|
|
}
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: inet_sockcaps
|
|
*
|
|
* Description:
|
|
* Return the bit encoded capabilities of this socket.
|
|
*
|
|
* Input Parameters:
|
|
* psock - Socket structure of the socket whose capabilities are being
|
|
* queried.
|
|
*
|
|
* Returned Value:
|
|
* The non-negative set of socket cababilities is returned.
|
|
*
|
|
****************************************************************************/
|
|
|
|
static sockcaps_t inet_sockcaps(FAR struct socket *psock)
|
|
{
|
|
switch (psock->s_type)
|
|
{
|
|
#ifdef NET_TCP_HAVE_STACK
|
|
case SOCK_STREAM:
|
|
return SOCKCAP_NONBLOCKING;
|
|
#endif
|
|
|
|
#ifdef NET_UDP_HAVE_STACK
|
|
case SOCK_DGRAM:
|
|
return SOCKCAP_NONBLOCKING;
|
|
#endif
|
|
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: inet_addref
|
|
*
|
|
* Description:
|
|
* Increment the reference count on the underlying connection structure.
|
|
*
|
|
* Input Parameters:
|
|
* psock - Socket structure of the socket whose reference count will be
|
|
* incremented.
|
|
*
|
|
* Returned Value:
|
|
* None
|
|
*
|
|
****************************************************************************/
|
|
|
|
static void inet_addref(FAR struct socket *psock)
|
|
{
|
|
DEBUGASSERT(psock != NULL && psock->s_conn != NULL);
|
|
|
|
#ifdef NET_TCP_HAVE_STACK
|
|
if (psock->s_type == SOCK_STREAM)
|
|
{
|
|
FAR struct tcp_conn_s *conn = psock->s_conn;
|
|
DEBUGASSERT(conn->crefs > 0 && conn->crefs < 255);
|
|
conn->crefs++;
|
|
}
|
|
else
|
|
#endif
|
|
#ifdef NET_UDP_HAVE_STACK
|
|
if (psock->s_type == SOCK_DGRAM)
|
|
{
|
|
FAR struct udp_conn_s *conn = psock->s_conn;
|
|
DEBUGASSERT(conn->crefs > 0 && conn->crefs < 255);
|
|
conn->crefs++;
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
nerr("ERROR: Unsupported type: %d\n", psock->s_type);
|
|
}
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: inet_bind
|
|
*
|
|
* Description:
|
|
* inet_bind() gives the socket 'psock' the local address 'addr'. 'addr'
|
|
* is 'addrlen' bytes long. Traditionally, this is called "assigning a
|
|
* name to a socket." When a socket is created with socket(), it exists
|
|
* in a name space (address family) but has no name assigned.
|
|
*
|
|
* Input Parameters:
|
|
* psock Socket structure of the socket to bind
|
|
* addr Socket local address
|
|
* addrlen Length of 'addr'
|
|
*
|
|
* Returned Value:
|
|
* 0 on success; A negated errno value is returned on failure. See
|
|
* bind() for a list a appropriate error values.
|
|
*
|
|
****************************************************************************/
|
|
|
|
static int inet_bind(FAR struct socket *psock,
|
|
FAR const struct sockaddr *addr, socklen_t addrlen)
|
|
{
|
|
int minlen;
|
|
int ret;
|
|
|
|
/* Verify that a valid address has been provided */
|
|
|
|
switch (addr->sa_family)
|
|
{
|
|
#ifdef CONFIG_NET_IPv4
|
|
case AF_INET:
|
|
minlen = sizeof(struct sockaddr_in);
|
|
break;
|
|
#endif
|
|
|
|
#ifdef CONFIG_NET_IPv6
|
|
case AF_INET6:
|
|
minlen = sizeof(struct sockaddr_in6);
|
|
break;
|
|
#endif
|
|
|
|
default:
|
|
nerr("ERROR: Unrecognized address family: %d\n", addr->sa_family);
|
|
return -EAFNOSUPPORT;
|
|
}
|
|
|
|
if (addrlen < minlen)
|
|
{
|
|
nerr("ERROR: Invalid address length: %d < %d\n", addrlen, minlen);
|
|
return -EBADF;
|
|
}
|
|
|
|
/* Perform the binding depending on the protocol type */
|
|
|
|
switch (psock->s_type)
|
|
{
|
|
#ifdef CONFIG_NET_TCP
|
|
case SOCK_STREAM:
|
|
{
|
|
#ifdef NET_TCP_HAVE_STACK
|
|
/* Bind a TCP/IP stream socket. */
|
|
|
|
ret = tcp_bind(psock->s_conn, addr);
|
|
#else
|
|
nwarn("WARNING: TCP/IP stack is not available in this "
|
|
"configuration\n");
|
|
|
|
return -ENOSYS;
|
|
#endif
|
|
}
|
|
break;
|
|
#endif /* CONFIG_NET_TCP */
|
|
|
|
#ifdef CONFIG_NET_UDP
|
|
case SOCK_DGRAM:
|
|
{
|
|
#ifdef NET_UDP_HAVE_STACK
|
|
/* Bind a UDP/IP datagram socket */
|
|
|
|
ret = udp_bind(psock->s_conn, addr);
|
|
#else
|
|
nwarn("WARNING: UDP stack is not available in this "
|
|
"configuration\n");
|
|
ret = -ENOSYS;
|
|
#endif
|
|
}
|
|
break;
|
|
#endif /* CONFIG_NET_UDP */
|
|
|
|
default:
|
|
nerr("ERROR: Unsupported socket type: %d\n", psock->s_type);
|
|
ret = -EBADF;
|
|
break;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: inet_getsockname
|
|
*
|
|
* Description:
|
|
* The inet_getsockname() function retrieves the locally-bound name of
|
|
* the specified INET socket, stores this address in the sockaddr
|
|
* structure pointed to by the 'addr' argument, and stores the length of
|
|
* this address in the object pointed to by the 'addrlen' argument.
|
|
*
|
|
* If the actual length of the address is greater than the length of the
|
|
* supplied sockaddr structure, the stored address will be truncated.
|
|
*
|
|
* If the socket has not been bound to a local name, the value stored in
|
|
* the object pointed to by address is unspecified.
|
|
*
|
|
* Input Parameters:
|
|
* psock Socket structure of the socket to be queried
|
|
* addr sockaddr structure to receive data [out]
|
|
* addrlen Length of sockaddr structure [in/out]
|
|
*
|
|
* Returned Value:
|
|
* On success, 0 is returned, the 'addr' argument points to the address
|
|
* of the socket, and the 'addrlen' argument points to the length of the
|
|
* address. Otherwise, a negated errno value is returned. See
|
|
* getsockname() for the list of appropriate error numbers.
|
|
*
|
|
****************************************************************************/
|
|
|
|
static int inet_getsockname(FAR struct socket *psock,
|
|
FAR struct sockaddr *addr,
|
|
FAR socklen_t *addrlen)
|
|
{
|
|
/* Handle by address domain */
|
|
|
|
switch (psock->s_domain)
|
|
{
|
|
#ifdef CONFIG_NET_IPv4
|
|
case PF_INET:
|
|
return ipv4_getsockname(psock, addr, addrlen);
|
|
break;
|
|
#endif
|
|
|
|
#ifdef CONFIG_NET_IPv6
|
|
case PF_INET6:
|
|
return ipv6_getsockname(psock, addr, addrlen);
|
|
break;
|
|
#endif
|
|
|
|
default:
|
|
return -EAFNOSUPPORT;
|
|
}
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: inet_getpeername
|
|
*
|
|
* Description:
|
|
* The inet_getpeername() function retrieves the remote-connected name of
|
|
* the specified INET socket, stores this address in the sockaddr
|
|
* structure pointed to by the 'addr' argument, and stores the length of
|
|
* this address in the object pointed to by the 'addrlen' argument.
|
|
*
|
|
* If the actual length of the address is greater than the length of the
|
|
* supplied sockaddr structure, the stored address will be truncated.
|
|
*
|
|
* If the socket has not been bound to a local name, the value stored in
|
|
* the object pointed to by address is unspecified.
|
|
*
|
|
* Parameters:
|
|
* psock Socket structure of the socket to be queried
|
|
* addr sockaddr structure to receive data [out]
|
|
* addrlen Length of sockaddr structure [in/out]
|
|
*
|
|
* Returned Value:
|
|
* On success, 0 is returned, the 'addr' argument points to the address
|
|
* of the socket, and the 'addrlen' argument points to the length of the
|
|
* address. Otherwise, a negated errno value is returned. See
|
|
* getpeername() for the list of appropriate error numbers.
|
|
*
|
|
****************************************************************************/
|
|
|
|
static int inet_getpeername(FAR struct socket *psock,
|
|
FAR struct sockaddr *addr,
|
|
FAR socklen_t *addrlen)
|
|
{
|
|
/* Handle by address domain */
|
|
|
|
switch (psock->s_domain)
|
|
{
|
|
#ifdef CONFIG_NET_IPv4
|
|
case PF_INET:
|
|
return ipv4_getpeername(psock, addr, addrlen);
|
|
break;
|
|
#endif
|
|
|
|
#ifdef CONFIG_NET_IPv6
|
|
case PF_INET6:
|
|
return ipv6_getpeername(psock, addr, addrlen);
|
|
break;
|
|
#endif
|
|
|
|
default:
|
|
return -EAFNOSUPPORT;
|
|
}
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: inet_listen
|
|
*
|
|
* Description:
|
|
* To accept connections, a socket is first created with psock_socket(), a
|
|
* willingness to accept incoming connections and a queue limit for
|
|
* incoming connections are specified with psock_listen(), and then the
|
|
* connections are accepted with psock_accept(). For the case of AFINET
|
|
* and AFINET6 sockets, psock_listen() calls this function. The
|
|
* psock_listen() call applies only to sockets of type SOCK_STREAM or
|
|
* SOCK_SEQPACKET.
|
|
*
|
|
* Input Parameters:
|
|
* psock Reference to an internal, boound socket structure.
|
|
* backlog The maximum length the queue of pending connections may grow.
|
|
* If a connection request arrives with the queue full, the client
|
|
* may receive an error with an indication of ECONNREFUSED or,
|
|
* if the underlying protocol supports retransmission, the request
|
|
* may be ignored so that retries succeed.
|
|
*
|
|
* Returned Value:
|
|
* On success, zero is returned. On error, a negated errno value is
|
|
* returned. See listen() for the set of appropriate error values.
|
|
*
|
|
****************************************************************************/
|
|
|
|
int inet_listen(FAR struct socket *psock, int backlog)
|
|
{
|
|
#if defined(CONFIG_NET_TCP) && defined(NET_TCP_HAVE_STACK)
|
|
FAR struct tcp_conn_s *conn;
|
|
int ret;
|
|
#endif
|
|
|
|
/* Verify that the sockfd corresponds to a connected SOCK_STREAM */
|
|
|
|
if (psock->s_type != SOCK_STREAM)
|
|
{
|
|
nerr("ERROR: Unsupported socket type: %d\n",
|
|
psock->s_type);
|
|
return -EOPNOTSUPP;
|
|
}
|
|
|
|
#ifdef CONFIG_NET_TCP
|
|
#ifdef NET_TCP_HAVE_STACK
|
|
conn = (FAR struct tcp_conn_s *)psock->s_conn;
|
|
|
|
if (conn->lport <= 0)
|
|
{
|
|
return -EOPNOTSUPP;
|
|
}
|
|
|
|
#ifdef CONFIG_NET_TCPBACKLOG
|
|
/* Set up the backlog for this connection */
|
|
|
|
ret = tcp_backlogcreate(conn, backlog);
|
|
if (ret < 0)
|
|
{
|
|
nerr("ERROR: tcp_backlogcreate failed: %d\n", ret);
|
|
return ret;
|
|
}
|
|
#endif
|
|
|
|
/* Start listening to the bound port. This enables callbacks when
|
|
* accept() is called and enables poll()/select() logic.
|
|
*/
|
|
|
|
ret = tcp_listen(conn);
|
|
if (ret < 0)
|
|
{
|
|
nerr("ERROR: tcp_listen failed: %d\n", ret);
|
|
}
|
|
|
|
return ret;
|
|
#else
|
|
nwarn("WARNING: Stream socket support not available\n");
|
|
return -EOPNOTSUPP;
|
|
#endif /* NET_TCP_HAVE_STACK */
|
|
#else
|
|
nwarn("WARNING: Stream socket support not enabled\n");
|
|
return -EOPNOTSUPP;
|
|
#endif /* CONFIG_NET_TCP */
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: inet_connect
|
|
*
|
|
* Description:
|
|
* inet_connect() connects the local socket referred to by the structure
|
|
* 'psock' to the address specified by 'addr'. The addrlen argument
|
|
* specifies the size of 'addr'. The format of the address in 'addr' is
|
|
* determined by the address space of the socket 'psock'.
|
|
*
|
|
* If the socket 'psock' is of type SOCK_DGRAM then 'addr' is the address
|
|
* to which datagrams are sent by default, and the only address from which
|
|
* datagrams are received. If the socket is of type SOCK_STREAM or
|
|
* SOCK_SEQPACKET, this call attempts to make a connection to the socket
|
|
* that is bound to the address specified by 'addr'.
|
|
*
|
|
* Generally, connection-based protocol sockets may successfully
|
|
* inet_connect() only once; connectionless protocol sockets may use
|
|
* inet_connect() multiple times to change their association.
|
|
* Connectionless sockets may dissolve the association by connecting to
|
|
* an address with the sa_family member of sockaddr set to AF_UNSPEC.
|
|
*
|
|
* Input Parameters:
|
|
* psock - Pointer to a socket structure initialized by psock_socket()
|
|
* addr - Server address (form depends on type of socket). The upper
|
|
* socket layer has verified that this address is non-NULL.
|
|
* addrlen - Length of actual 'addr'
|
|
*
|
|
* Returned Value:
|
|
* 0 on success; a negated errno value on failure. See connect() for the
|
|
* list of appropriate errno values to be returned.
|
|
*
|
|
****************************************************************************/
|
|
|
|
static int inet_connect(FAR struct socket *psock,
|
|
FAR const struct sockaddr *addr, socklen_t addrlen)
|
|
{
|
|
FAR const struct sockaddr_in *inaddr =
|
|
(FAR const struct sockaddr_in *)addr;
|
|
|
|
/* Verify that a valid address has been provided */
|
|
|
|
switch (inaddr->sin_family)
|
|
{
|
|
#ifdef CONFIG_NET_IPv4
|
|
case AF_INET:
|
|
{
|
|
if (addrlen < sizeof(struct sockaddr_in))
|
|
{
|
|
return -EBADF;
|
|
}
|
|
}
|
|
break;
|
|
#endif
|
|
|
|
#ifdef CONFIG_NET_IPv6
|
|
case AF_INET6:
|
|
{
|
|
if (addrlen < sizeof(struct sockaddr_in6))
|
|
{
|
|
return -EBADF;
|
|
}
|
|
}
|
|
break;
|
|
#endif
|
|
|
|
default:
|
|
DEBUGPANIC();
|
|
return -EAFNOSUPPORT;
|
|
}
|
|
|
|
/* Perform the connection depending on the protocol type */
|
|
|
|
switch (psock->s_type)
|
|
{
|
|
#if defined(CONFIG_NET_TCP) && defined(NET_TCP_HAVE_STACK)
|
|
case SOCK_STREAM:
|
|
{
|
|
FAR struct socket_conn_s *conn = psock->s_conn;
|
|
|
|
/* Verify that the socket is not already connected */
|
|
|
|
if (_SS_ISCONNECTED(conn->s_flags))
|
|
{
|
|
return -EISCONN;
|
|
}
|
|
|
|
/* It's not ... Connect the TCP/IP socket */
|
|
|
|
return psock_tcp_connect(psock, addr);
|
|
}
|
|
#endif /* CONFIG_NET_TCP */
|
|
|
|
#if defined(CONFIG_NET_UDP) && defined(NET_UDP_HAVE_STACK)
|
|
case SOCK_DGRAM:
|
|
{
|
|
FAR struct udp_conn_s *conn;
|
|
int ret;
|
|
|
|
/* We will accept connecting to a addr == NULL for disconnection.
|
|
* However, the correct way is to disconnect is to provide an
|
|
* address with sa_family == AF_UNSPEC.
|
|
*/
|
|
|
|
if (addr != NULL && addr->sa_family == AF_UNSPEC)
|
|
{
|
|
addr = NULL;
|
|
}
|
|
|
|
/* Perform the connect/disconnect operation */
|
|
|
|
conn = (FAR struct udp_conn_s *)psock->s_conn;
|
|
ret = udp_connect(conn, addr);
|
|
if (ret < 0 || addr == NULL)
|
|
{
|
|
/* Failed to connect or explicitly disconnected */
|
|
|
|
conn->flags &= ~_UDP_FLAG_CONNECTMODE;
|
|
}
|
|
else
|
|
{
|
|
/* Successfully connected */
|
|
|
|
conn->flags |= _UDP_FLAG_CONNECTMODE;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
#endif /* CONFIG_NET_UDP */
|
|
|
|
default:
|
|
return -EBADF;
|
|
}
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: inet_accept
|
|
*
|
|
* Description:
|
|
* The inet_accept function is used with connection-based socket types
|
|
* (SOCK_STREAM, SOCK_SEQPACKET and SOCK_RDM). It extracts the first
|
|
* connection request on the queue of pending connections, creates a new
|
|
* connected socket with mostly the same properties as 'sockfd', and
|
|
* allocates a new socket descriptor for the socket, which is returned. The
|
|
* newly created socket is no longer in the listening state. The original
|
|
* socket 'sockfd' is unaffected by this call. Per file descriptor flags
|
|
* are not inherited across an inet_accept.
|
|
*
|
|
* The 'sockfd' argument is a socket descriptor that has been created with
|
|
* socket(), bound to a local address with bind(), and is listening for
|
|
* connections after a call to listen().
|
|
*
|
|
* On return, the 'addr' structure is filled in with the address of the
|
|
* connecting entity. The 'addrlen' argument initially contains the size
|
|
* of the structure pointed to by 'addr'; on return it will contain the
|
|
* actual length of the address returned.
|
|
*
|
|
* If no pending connections are present on the queue, and the socket is
|
|
* not marked as non-blocking, inet_accept blocks the caller until a
|
|
* connection is present. If the socket is marked non-blocking and no
|
|
* pending connections are present on the queue, inet_accept returns
|
|
* EAGAIN.
|
|
*
|
|
* Input Parameters:
|
|
* psock Reference to the listening socket structure
|
|
* addr Receives the address of the connecting client
|
|
* addrlen Input: allocated size of 'addr', Return: returned size of
|
|
* 'addr'
|
|
* newsock Location to return the accepted socket information.
|
|
*
|
|
* Returned Value:
|
|
* Returns 0 (OK) on success. On failure, it returns a negated errno
|
|
* value. See accept() for a description of the appropriate error value.
|
|
*
|
|
* Assumptions:
|
|
* The network is locked.
|
|
*
|
|
****************************************************************************/
|
|
|
|
static int inet_accept(FAR struct socket *psock, FAR struct sockaddr *addr,
|
|
FAR socklen_t *addrlen, FAR struct socket *newsock)
|
|
{
|
|
#if defined(CONFIG_NET_TCP) && defined(NET_TCP_HAVE_STACK)
|
|
int ret;
|
|
#endif
|
|
|
|
/* Is the socket a stream? */
|
|
|
|
if (psock->s_type != SOCK_STREAM)
|
|
{
|
|
nerr("ERROR: Inappropriate socket type: %d\n", psock->s_type);
|
|
return -EOPNOTSUPP;
|
|
}
|
|
|
|
/* Verify that a valid memory block has been provided to receive the
|
|
* address
|
|
*/
|
|
|
|
if (addr != NULL)
|
|
{
|
|
/* If an address is provided, then the length must also be provided. */
|
|
|
|
DEBUGASSERT(*addrlen > 0);
|
|
|
|
/* A valid length depends on the address domain */
|
|
|
|
switch (psock->s_domain)
|
|
{
|
|
#ifdef CONFIG_NET_IPv4
|
|
case PF_INET:
|
|
{
|
|
if (*addrlen < sizeof(struct sockaddr_in))
|
|
{
|
|
return -EBADF;
|
|
}
|
|
}
|
|
break;
|
|
#endif /* CONFIG_NET_IPv4 */
|
|
|
|
#ifdef CONFIG_NET_IPv6
|
|
case PF_INET6:
|
|
{
|
|
if (*addrlen < sizeof(struct sockaddr_in6))
|
|
{
|
|
return -EBADF;
|
|
}
|
|
}
|
|
break;
|
|
#endif /* CONFIG_NET_IPv6 */
|
|
|
|
default:
|
|
DEBUGPANIC();
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
|
|
/* Initialize the socket structure. */
|
|
|
|
newsock->s_domain = psock->s_domain;
|
|
newsock->s_type = SOCK_STREAM;
|
|
newsock->s_sockif = psock->s_sockif;
|
|
|
|
/* Perform the correct accept operation for this address domain */
|
|
|
|
#ifdef CONFIG_NET_TCP
|
|
#ifdef NET_TCP_HAVE_STACK
|
|
/* Perform the local accept operation (the network locked must be locked
|
|
* by the caller).
|
|
*/
|
|
|
|
ret = psock_tcp_accept(psock, addr, addrlen, &newsock->s_conn);
|
|
if (ret < 0)
|
|
{
|
|
nerr("ERROR: psock_tcp_accept failed: %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
/* Begin monitoring for TCP connection events on the newly connected
|
|
* socket
|
|
*/
|
|
|
|
ret = tcp_start_monitor(newsock);
|
|
if (ret < 0)
|
|
{
|
|
/* tcp_start_monitor() can only fail on certain race conditions where
|
|
* the connection was lost just before this function was called. Undo
|
|
* everything we have done and return a failure.
|
|
*/
|
|
|
|
psock_close(newsock);
|
|
return ret;
|
|
}
|
|
|
|
return OK;
|
|
|
|
#else
|
|
nwarn("WARNING: SOCK_STREAM not supported in this configuration\n");
|
|
return -EOPNOTSUPP;
|
|
#endif /* NET_TCP_HAVE_STACK */
|
|
|
|
#else
|
|
nwarn("WARNING: TCP/IP not supported in this configuration\n");
|
|
return -EOPNOTSUPP;
|
|
#endif /* CONFIG_NET_TCP */
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: inet_pollsetup
|
|
*
|
|
* Description:
|
|
* Setup to monitor events on one socket
|
|
*
|
|
* Input Parameters:
|
|
* psock - The socket of interest
|
|
* fds - The structure describing the events to be monitored, OR NULL if
|
|
* this is a request to stop monitoring events.
|
|
*
|
|
* Returned Value:
|
|
* 0: Success; Negated errno on failure
|
|
*
|
|
****************************************************************************/
|
|
|
|
#if defined(NET_TCP_HAVE_STACK) || defined(NET_UDP_HAVE_STACK)
|
|
static inline int inet_pollsetup(FAR struct socket *psock,
|
|
FAR struct pollfd *fds)
|
|
{
|
|
#ifdef NET_TCP_HAVE_STACK
|
|
if (psock->s_type == SOCK_STREAM)
|
|
{
|
|
return tcp_pollsetup(psock, fds);
|
|
}
|
|
else
|
|
#endif /* NET_TCP_HAVE_STACK */
|
|
#ifdef NET_UDP_HAVE_STACK
|
|
if (psock->s_type != SOCK_STREAM)
|
|
{
|
|
return udp_pollsetup(psock, fds);
|
|
}
|
|
else
|
|
#endif /* NET_UDP_HAVE_STACK */
|
|
{
|
|
return -ENOSYS;
|
|
}
|
|
}
|
|
#endif /* NET_TCP_HAVE_STACK || NET_UDP_HAVE_STACK */
|
|
|
|
/****************************************************************************
|
|
* Name: inet_pollteardown
|
|
*
|
|
* Description:
|
|
* Teardown monitoring of events on an socket
|
|
*
|
|
* Input Parameters:
|
|
* psock - The TCP/IP socket of interest
|
|
* fds - The structure describing the events to be monitored, OR NULL if
|
|
* this is a request to stop monitoring events.
|
|
*
|
|
* Returned Value:
|
|
* 0: Success; Negated errno on failure
|
|
*
|
|
****************************************************************************/
|
|
|
|
#if defined(NET_TCP_HAVE_STACK) || defined(NET_UDP_HAVE_STACK)
|
|
static inline int inet_pollteardown(FAR struct socket *psock,
|
|
FAR struct pollfd *fds)
|
|
{
|
|
#ifdef NET_TCP_HAVE_STACK
|
|
if (psock->s_type == SOCK_STREAM)
|
|
{
|
|
return tcp_pollteardown(psock, fds);
|
|
}
|
|
else
|
|
#endif /* NET_TCP_HAVE_STACK */
|
|
#ifdef NET_UDP_HAVE_STACK
|
|
if (psock->s_type == SOCK_DGRAM)
|
|
{
|
|
return udp_pollteardown(psock, fds);
|
|
}
|
|
else
|
|
#endif /* NET_UDP_HAVE_STACK */
|
|
{
|
|
return -ENOSYS;
|
|
}
|
|
}
|
|
#endif /* NET_TCP_HAVE_STACK || NET_UDP_HAVE_STACK */
|
|
|
|
/****************************************************************************
|
|
* Name: inet_poll
|
|
*
|
|
* Description:
|
|
* The standard poll() operation redirects operations on socket descriptors
|
|
* to net_poll which, indiectly, calls to function.
|
|
*
|
|
* Input Parameters:
|
|
* psock - An instance of the internal socket structure.
|
|
* fds - The structure describing the events to be monitored, OR NULL if
|
|
* this is a request to stop monitoring events.
|
|
* setup - true: Setup up the poll; false: Teardown the poll
|
|
*
|
|
* Returned Value:
|
|
* 0: Success; Negated errno on failure
|
|
*
|
|
****************************************************************************/
|
|
|
|
static int inet_poll(FAR struct socket *psock, FAR struct pollfd *fds,
|
|
bool setup)
|
|
{
|
|
#if defined(NET_TCP_HAVE_STACK) || defined(NET_UDP_HAVE_STACK)
|
|
|
|
/* Check if we are setting up or tearing down the poll */
|
|
|
|
if (setup)
|
|
{
|
|
/* Perform the TCP/IP poll() setup */
|
|
|
|
return inet_pollsetup(psock, fds);
|
|
}
|
|
else
|
|
{
|
|
/* Perform the TCP/IP poll() teardown */
|
|
|
|
return inet_pollteardown(psock, fds);
|
|
}
|
|
#else
|
|
{
|
|
return -ENOSYS;
|
|
}
|
|
#endif /* NET_TCP_HAVE_STACK || !NET_UDP_HAVE_STACK */
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: inet_send
|
|
*
|
|
* Description:
|
|
* The inet_send() call may be used only when the socket is in a connected
|
|
* state (so that the intended recipient is known).
|
|
*
|
|
* Input Parameters:
|
|
* psock An instance of the internal socket structure.
|
|
* buf Data to send
|
|
* len Length of data to send
|
|
* flags Send flags
|
|
*
|
|
* Returned Value:
|
|
* On success, returns the number of characters sent. On error, a negated
|
|
* errno value is returned (see send() for the list of appropriate error
|
|
* values.
|
|
*
|
|
****************************************************************************/
|
|
|
|
static ssize_t inet_send(FAR struct socket *psock, FAR const void *buf,
|
|
size_t len, int flags)
|
|
{
|
|
#ifdef NET_UDP_HAVE_STACK
|
|
FAR struct socket_conn_s *conn = psock->s_conn;
|
|
#endif
|
|
ssize_t ret;
|
|
|
|
switch (psock->s_type)
|
|
{
|
|
#ifdef CONFIG_NET_TCP
|
|
case SOCK_STREAM:
|
|
{
|
|
#ifdef CONFIG_NET_6LOWPAN
|
|
/* Try 6LoWPAN TCP packet send */
|
|
|
|
ret = psock_6lowpan_tcp_send(psock, buf, len);
|
|
|
|
#ifdef NET_TCP_HAVE_STACK
|
|
if (ret < 0)
|
|
{
|
|
/* TCP/IP packet send */
|
|
|
|
ret = psock_tcp_send(psock, buf, len, flags);
|
|
}
|
|
#endif /* NET_TCP_HAVE_STACK */
|
|
|
|
#elif defined(NET_TCP_HAVE_STACK)
|
|
ret = psock_tcp_send(psock, buf, len, flags);
|
|
#else
|
|
ret = -ENOSYS;
|
|
#endif /* CONFIG_NET_6LOWPAN */
|
|
}
|
|
break;
|
|
#endif /* CONFIG_NET_TCP */
|
|
|
|
#ifdef CONFIG_NET_UDP
|
|
case SOCK_DGRAM:
|
|
{
|
|
#if defined(CONFIG_NET_6LOWPAN)
|
|
/* Try 6LoWPAN UDP packet send */
|
|
|
|
ret = psock_6lowpan_udp_send(psock, buf, len);
|
|
|
|
#ifdef NET_UDP_HAVE_STACK
|
|
if (ret < 0)
|
|
{
|
|
/* UDP/IP packet send */
|
|
|
|
ret = _SS_ISCONNECTED(conn->s_flags) ?
|
|
psock_udp_sendto(psock, buf, len, 0, NULL, 0) : -ENOTCONN;
|
|
}
|
|
#endif /* NET_UDP_HAVE_STACK */
|
|
|
|
#elif defined(NET_UDP_HAVE_STACK)
|
|
/* Only UDP/IP packet send */
|
|
|
|
ret = _SS_ISCONNECTED(conn->s_flags) ?
|
|
psock_udp_sendto(psock, buf, len, 0, NULL, 0) : -ENOTCONN;
|
|
#else
|
|
ret = -ENOSYS;
|
|
#endif /* CONFIG_NET_6LOWPAN */
|
|
}
|
|
break;
|
|
#endif /* CONFIG_NET_UDP */
|
|
|
|
default:
|
|
{
|
|
/* EDESTADDRREQ. Signifies that the socket is not connection-mode
|
|
* and no peer address is set.
|
|
*/
|
|
|
|
nerr("ERROR: Bad socket type: %d\n", psock->s_type);
|
|
ret = -EDESTADDRREQ;
|
|
}
|
|
break;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: inet_sendto
|
|
*
|
|
* Description:
|
|
* Implements the sendto() operation for the case of the AF_INET and
|
|
* AF_INET6 sockets.
|
|
*
|
|
* Input Parameters:
|
|
* psock A pointer to a NuttX-specific, internal socket structure
|
|
* buf Data to send
|
|
* len Length of data to send
|
|
* flags Send flags
|
|
* to Address of recipient
|
|
* tolen The length of the address structure
|
|
*
|
|
* Returned Value:
|
|
* On success, returns the number of characters sent. On error, a negated
|
|
* errno value is returned (see send_to() for the list of appropriate error
|
|
* values.
|
|
*
|
|
****************************************************************************/
|
|
|
|
static ssize_t inet_sendto(FAR struct socket *psock, FAR const void *buf,
|
|
size_t len, int flags,
|
|
FAR const struct sockaddr *to, socklen_t tolen)
|
|
{
|
|
socklen_t minlen;
|
|
ssize_t nsent;
|
|
|
|
/* Verify that a valid address has been provided */
|
|
|
|
switch (to->sa_family)
|
|
{
|
|
#ifdef CONFIG_NET_IPv4
|
|
case AF_INET:
|
|
minlen = sizeof(struct sockaddr_in);
|
|
break;
|
|
#endif
|
|
|
|
#ifdef CONFIG_NET_IPv6
|
|
case AF_INET6:
|
|
minlen = sizeof(struct sockaddr_in6);
|
|
break;
|
|
#endif
|
|
|
|
default:
|
|
nerr("ERROR: Unrecognized address family: %d\n", to->sa_family);
|
|
return -EAFNOSUPPORT;
|
|
}
|
|
|
|
if (tolen < minlen)
|
|
{
|
|
nerr("ERROR: Invalid address length: %d < %d\n", tolen, minlen);
|
|
return -EBADF;
|
|
}
|
|
|
|
#ifdef CONFIG_NET_UDP
|
|
/* If this is a connected socket, then return EISCONN */
|
|
|
|
if (psock->s_type != SOCK_DGRAM)
|
|
{
|
|
nerr("ERROR: Connected socket\n");
|
|
return -EBADF;
|
|
}
|
|
|
|
/* Now handle the INET sendto() operation */
|
|
|
|
#if defined(CONFIG_NET_6LOWPAN)
|
|
/* Try 6LoWPAN UDP packet sendto() */
|
|
|
|
nsent = psock_6lowpan_udp_sendto(psock, buf, len, flags, to, minlen);
|
|
|
|
#ifdef NET_UDP_HAVE_STACK
|
|
if (nsent < 0)
|
|
{
|
|
/* UDP/IP packet sendto */
|
|
|
|
nsent = psock_udp_sendto(psock, buf, len, flags, to, tolen);
|
|
}
|
|
#endif /* NET_UDP_HAVE_STACK */
|
|
|
|
#elif defined(NET_UDP_HAVE_STACK)
|
|
nsent = psock_udp_sendto(psock, buf, len, flags, to, tolen);
|
|
#else
|
|
nwarn("WARNING: UDP not available in this configuiration\n");
|
|
nsent = -ENOSYS;
|
|
#endif /* CONFIG_NET_6LOWPAN */
|
|
#else
|
|
nwarn("WARNING: UDP not enabled in this configuiration\n");
|
|
nsent = -EISCONN;
|
|
#endif /* CONFIG_NET_UDP */
|
|
|
|
return nsent;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: inet_sendmsg
|
|
*
|
|
* Description:
|
|
* The inet_send() call may be used only when the socket is in a connected
|
|
* state (so that the intended recipient is known).
|
|
*
|
|
* Input Parameters:
|
|
* psock An instance of the internal socket structure.
|
|
* msg Message to send
|
|
* flags Send flags
|
|
*
|
|
* Returned Value:
|
|
* On success, returns the number of characters sent. On error, a negated
|
|
* errno value is returned (see sendmsg() for the list of appropriate error
|
|
* values.
|
|
*
|
|
****************************************************************************/
|
|
|
|
static ssize_t inet_sendmsg(FAR struct socket *psock,
|
|
FAR struct msghdr *msg, int flags)
|
|
{
|
|
FAR void *buf = msg->msg_iov->iov_base;
|
|
size_t len = msg->msg_iov->iov_len;
|
|
FAR const struct sockaddr *to = msg->msg_name;
|
|
socklen_t tolen = msg->msg_namelen;
|
|
FAR const struct iovec *iov;
|
|
FAR const struct iovec *end;
|
|
int ret;
|
|
|
|
if (msg->msg_iovlen == 1)
|
|
{
|
|
return to ? inet_sendto(psock, buf, len, flags, to, tolen) :
|
|
inet_send(psock, buf, len, flags);
|
|
}
|
|
|
|
end = &msg->msg_iov[msg->msg_iovlen];
|
|
for (len = 0, iov = msg->msg_iov; iov != end; iov++)
|
|
{
|
|
len += iov->iov_len;
|
|
}
|
|
|
|
buf = kmm_malloc(len);
|
|
if (buf == NULL)
|
|
{
|
|
return -ENOMEM;
|
|
}
|
|
|
|
for (len = 0, iov = msg->msg_iov; iov != end; iov++)
|
|
{
|
|
memcpy(((unsigned char *)buf) + len, iov->iov_base, iov->iov_len);
|
|
len += iov->iov_len;
|
|
}
|
|
|
|
ret = to ? inet_sendto(psock, buf, len, flags, to, tolen) :
|
|
inet_send(psock, buf, len, flags);
|
|
|
|
kmm_free(buf);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: inet_ioctl
|
|
*
|
|
* Description:
|
|
* This function performs network device specific operations.
|
|
*
|
|
* Parameters:
|
|
* psock A reference to the socket structure of the socket
|
|
* cmd The ioctl command
|
|
* arg The argument of the ioctl cmd
|
|
* arglen The length of 'arg'
|
|
*
|
|
****************************************************************************/
|
|
|
|
static int inet_ioctl(FAR struct socket *psock, int cmd,
|
|
FAR void *arg, size_t arglen)
|
|
{
|
|
/* Verify that the sockfd corresponds to valid, allocated socket */
|
|
|
|
if (psock == NULL || psock->s_conn == NULL)
|
|
{
|
|
return -EBADF;
|
|
}
|
|
|
|
#if defined(CONFIG_NET_TCP) && !defined(CONFIG_NET_TCP_NO_STACK)
|
|
if (psock->s_type == SOCK_STREAM)
|
|
{
|
|
return tcp_ioctl(psock->s_conn, cmd, arg, arglen);
|
|
}
|
|
#endif
|
|
|
|
#if defined(CONFIG_NET_UDP) && defined(NET_UDP_HAVE_STACK)
|
|
if (psock->s_type == SOCK_DGRAM)
|
|
{
|
|
return udp_ioctl(psock->s_conn, cmd, arg, arglen);
|
|
}
|
|
#endif
|
|
|
|
return -EINVAL;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: inet_socketpair
|
|
*
|
|
* Description:
|
|
* Create a pair of connected sockets between psocks[2]
|
|
*
|
|
* Parameters:
|
|
* psocks A reference to the socket structure of the socket pair
|
|
*
|
|
****************************************************************************/
|
|
|
|
static int inet_socketpair(FAR struct socket *psocks[2])
|
|
{
|
|
#if defined(CONFIG_NET_TCP) || defined(CONFIG_NET_UDP)
|
|
FAR struct socket *pserver = psocks[1];
|
|
#if defined(CONFIG_NET_TCP)
|
|
FAR struct socket server;
|
|
#endif
|
|
union sockaddr_u addr[2];
|
|
socklen_t len;
|
|
int ret;
|
|
|
|
/* Set the sock address to localhost */
|
|
|
|
#ifdef CONFIG_NET_IPv6
|
|
if (psocks[0]->s_domain == AF_INET6)
|
|
{
|
|
struct in6_addr init_sin6_addr = IN6ADDR_LOOPBACK_INIT;
|
|
|
|
len = sizeof(addr[0].in6addr);
|
|
memset(&addr[0], 0, len);
|
|
addr[0].in6addr.sin6_family = psocks[0]->s_domain;
|
|
addr[0].in6addr.sin6_addr = init_sin6_addr;
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
len = sizeof(addr[0].inaddr);
|
|
memset(&addr[0], 0, len);
|
|
addr[0].inaddr.sin_family = psocks[0]->s_domain;
|
|
addr[0].inaddr.sin_addr.s_addr = HTONL(INADDR_LOOPBACK);
|
|
}
|
|
|
|
memcpy(&addr[1], &addr[0], len);
|
|
|
|
ret = psock_bind(psocks[0], &addr[0].addr, len);
|
|
if (ret < 0)
|
|
{
|
|
return ret;
|
|
}
|
|
|
|
psock_getsockname(psocks[0], &addr[0].addr, &len);
|
|
|
|
/* For SOCK_STREAM, Use proxy service handle to make temporary
|
|
* pserver process, psocks[1] will be replaced with a new accept handle
|
|
*/
|
|
|
|
#if defined(CONFIG_NET_TCP)
|
|
if (psocks[0]->s_type == SOCK_STREAM)
|
|
{
|
|
ret = psock_socket(psocks[1]->s_domain, psocks[1]->s_type,
|
|
psocks[1]->s_proto, &server);
|
|
if (ret < 0)
|
|
{
|
|
return ret;
|
|
}
|
|
|
|
pserver = &server;
|
|
}
|
|
#endif /* CONFIG_NET_TCP */
|
|
|
|
ret = psock_bind(pserver, &addr[1].addr, len);
|
|
if (ret < 0)
|
|
{
|
|
goto errout;
|
|
}
|
|
|
|
psock_getsockname(pserver, &addr[1].addr, &len);
|
|
|
|
#if defined(CONFIG_NET_UDP)
|
|
if (psocks[0]->s_type == SOCK_DGRAM)
|
|
{
|
|
ret = psock_connect(psocks[0], &addr[1].addr, len);
|
|
if (ret < 0)
|
|
{
|
|
goto errout;
|
|
}
|
|
|
|
ret = psock_connect(pserver, &addr[0].addr, len);
|
|
if (ret < 0)
|
|
{
|
|
goto errout;
|
|
}
|
|
}
|
|
#endif /* CONFIG_NET_UDP */
|
|
|
|
#if defined(CONFIG_NET_TCP)
|
|
if (psocks[0]->s_type == SOCK_STREAM)
|
|
{
|
|
ret = psock_listen(pserver, 2);
|
|
if (ret < 0)
|
|
{
|
|
goto errout;
|
|
}
|
|
|
|
ret = psock_connect(psocks[0], &addr[1].addr, len);
|
|
if (ret < 0)
|
|
{
|
|
goto errout;
|
|
}
|
|
|
|
/* Release the resource of psocks[1], accept will replace
|
|
* this handle
|
|
*/
|
|
|
|
psock_close(psocks[1]);
|
|
|
|
ret = psock_accept(pserver, &addr[1].addr, &len, psocks[1]);
|
|
}
|
|
#endif /* CONFIG_NET_TCP */
|
|
|
|
errout:
|
|
#if defined(CONFIG_NET_TCP)
|
|
if (pserver->s_type == SOCK_STREAM)
|
|
{
|
|
psock_close(pserver);
|
|
}
|
|
#endif /* CONFIG_NET_TCP */
|
|
|
|
return ret;
|
|
#else
|
|
return -EOPNOTSUPP;
|
|
#endif /* CONFIG_NET_TCP || CONFIG_NET_UDP */
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: inet_sendfile
|
|
*
|
|
* Description:
|
|
* The inet_sendfile() call may be used only when the INET socket is in a
|
|
* connected state (so that the intended recipient is known).
|
|
*
|
|
* Input Parameters:
|
|
* psock An instance of the internal socket structure.
|
|
* buf Data to send
|
|
* len Length of data to send
|
|
* flags Send flags
|
|
*
|
|
* Returned Value:
|
|
* On success, returns the number of characters sent. On error,
|
|
* a negated errno value is returned. See sendfile() for a list
|
|
* appropriate error return values.
|
|
*
|
|
****************************************************************************/
|
|
|
|
#ifdef CONFIG_NET_SENDFILE
|
|
static ssize_t inet_sendfile(FAR struct socket *psock,
|
|
FAR struct file *infile, FAR off_t *offset,
|
|
size_t count)
|
|
{
|
|
#if defined(CONFIG_NET_TCP) && !defined(CONFIG_NET_TCP_NO_STACK)
|
|
if (psock->s_type == SOCK_STREAM)
|
|
{
|
|
return tcp_sendfile(psock, infile, offset, count);
|
|
}
|
|
#endif
|
|
|
|
return -ENOSYS;
|
|
}
|
|
#endif
|
|
|
|
/****************************************************************************
|
|
* Name: inet_recvmsg
|
|
*
|
|
* Description:
|
|
* Implements the socket recvfrom interface for the case of the AF_INET
|
|
* and AF_INET6 address families. inet_recvmsg() receives messages from
|
|
* a socket, and may be used to receive data on a socket whether or not it
|
|
* is connection-oriented.
|
|
*
|
|
* If msg_name is not NULL, and the underlying protocol provides the source
|
|
* address, this source address is filled in. The argument 'msg_namelen' is
|
|
* initialized to the size of the buffer associated with msg_name, and
|
|
* modified on return to indicate the actual size of the address stored
|
|
* there.
|
|
*
|
|
* Input Parameters:
|
|
* psock - A pointer to a NuttX-specific, internal socket structure
|
|
* msg - Buffer to receive the message
|
|
* flags - Receive flags
|
|
*
|
|
* Returned Value:
|
|
* On success, returns the number of characters received. If no data is
|
|
* available to be received and the peer has performed an orderly shutdown,
|
|
* recvmsg() will return 0. Otherwise, on errors, a negated errno value is
|
|
* returned (see recvmsg() for the list of appropriate error values).
|
|
*
|
|
****************************************************************************/
|
|
|
|
static ssize_t inet_recvmsg(FAR struct socket *psock,
|
|
FAR struct msghdr *msg, int flags)
|
|
{
|
|
FAR void *buf = msg->msg_iov->iov_base;
|
|
size_t len = msg->msg_iov->iov_len;
|
|
FAR struct sockaddr *from = msg->msg_name;
|
|
FAR socklen_t *fromlen = &msg->msg_namelen;
|
|
ssize_t ret;
|
|
|
|
/* If a 'from' address has been provided, verify that it is large
|
|
* enough to hold this address family.
|
|
*/
|
|
|
|
if (from)
|
|
{
|
|
socklen_t minlen;
|
|
|
|
/* Get the minimum socket length */
|
|
|
|
switch (psock->s_domain)
|
|
{
|
|
#ifdef CONFIG_NET_IPv4
|
|
case PF_INET:
|
|
{
|
|
minlen = sizeof(struct sockaddr_in);
|
|
}
|
|
break;
|
|
#endif
|
|
|
|
#ifdef CONFIG_NET_IPv6
|
|
case PF_INET6:
|
|
{
|
|
minlen = sizeof(struct sockaddr_in6);
|
|
}
|
|
break;
|
|
#endif
|
|
|
|
default:
|
|
DEBUGPANIC();
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (*fromlen < minlen)
|
|
{
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
|
|
/* Read from the network interface driver buffer.
|
|
* Or perform the TCP/IP or UDP recv() operation.
|
|
*/
|
|
|
|
switch (psock->s_type)
|
|
{
|
|
#ifdef CONFIG_NET_TCP
|
|
case SOCK_STREAM:
|
|
{
|
|
#ifdef NET_TCP_HAVE_STACK
|
|
ret = psock_tcp_recvfrom(psock, buf, len, flags, from, fromlen);
|
|
#else
|
|
ret = -ENOSYS;
|
|
#endif
|
|
}
|
|
break;
|
|
#endif /* CONFIG_NET_TCP */
|
|
|
|
#ifdef CONFIG_NET_UDP
|
|
case SOCK_DGRAM:
|
|
{
|
|
#ifdef NET_UDP_HAVE_STACK
|
|
ret = psock_udp_recvfrom(psock, buf, len, flags, from, fromlen);
|
|
#else
|
|
ret = -ENOSYS;
|
|
#endif
|
|
}
|
|
break;
|
|
#endif /* CONFIG_NET_UDP */
|
|
|
|
default:
|
|
{
|
|
nerr("ERROR: Unsupported socket type: %d\n", psock->s_type);
|
|
ret = -ENOSYS;
|
|
}
|
|
break;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
#endif /* NET_UDP_HAVE_STACK || NET_TCP_HAVE_STACK */
|
|
|
|
/****************************************************************************
|
|
* Public Functions
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Name: inet_close
|
|
*
|
|
* Description:
|
|
* Performs the close operation on an AF_INET or AF_INET6 socket instance
|
|
*
|
|
* Input Parameters:
|
|
* psock Socket instance
|
|
*
|
|
* Returned Value:
|
|
* 0 on success; -1 on error with errno set appropriately.
|
|
*
|
|
* Assumptions:
|
|
*
|
|
****************************************************************************/
|
|
|
|
int inet_close(FAR struct socket *psock)
|
|
{
|
|
/* Perform some pre-close operations for the AF_INET/AF_INET6 address
|
|
* types.
|
|
*/
|
|
|
|
switch (psock->s_type)
|
|
{
|
|
#ifdef CONFIG_NET_TCP
|
|
case SOCK_STREAM:
|
|
{
|
|
#ifdef NET_TCP_HAVE_STACK
|
|
FAR struct tcp_conn_s *conn = psock->s_conn;
|
|
int ret;
|
|
|
|
/* Is this the last reference to the connection structure (there
|
|
* could be more if the socket was dup'ed).
|
|
*/
|
|
|
|
if (conn->crefs <= 1)
|
|
{
|
|
/* Yes... Clost the socket */
|
|
|
|
ret = tcp_close(psock);
|
|
if (ret < 0)
|
|
{
|
|
/* This would normally occur only if there is a timeout
|
|
* from a lingering close.
|
|
*/
|
|
|
|
nerr("ERROR: tcp_close failed: %d\n", ret);
|
|
return ret;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* No.. Just decrement the reference count */
|
|
|
|
conn->crefs--;
|
|
}
|
|
#else
|
|
nwarn("WARNING: SOCK_STREAM support is not available in this "
|
|
"configuration\n");
|
|
return -EAFNOSUPPORT;
|
|
#endif /* NET_TCP_HAVE_STACK */
|
|
}
|
|
break;
|
|
#endif /* CONFIG_NET_TCP */
|
|
|
|
#ifdef CONFIG_NET_UDP
|
|
case SOCK_DGRAM:
|
|
{
|
|
#ifdef NET_UDP_HAVE_STACK
|
|
FAR struct udp_conn_s *conn = psock->s_conn;
|
|
int ret;
|
|
|
|
/* Is this the last reference to the connection structure (there
|
|
* could be more if the socket was dup'ed).
|
|
*/
|
|
|
|
if (conn->crefs <= 1)
|
|
{
|
|
/* Yes... Clost the socket */
|
|
|
|
ret = udp_close(psock);
|
|
if (ret < 0)
|
|
{
|
|
/* This would normally occur only if there is a timeout
|
|
* from a lingering close.
|
|
*/
|
|
|
|
nerr("ERROR: udp_close failed: %d\n", ret);
|
|
return ret;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* No.. Just decrement the reference count */
|
|
|
|
conn->crefs--;
|
|
}
|
|
#else
|
|
nwarn("WARNING: SOCK_DGRAM support is not available in this "
|
|
"configuration\n");
|
|
return -EAFNOSUPPORT;
|
|
#endif /* NET_UDP_HAVE_STACK */
|
|
}
|
|
break;
|
|
#endif /* CONFIG_NET_UDP */
|
|
|
|
default:
|
|
return -EBADF;
|
|
}
|
|
|
|
return OK;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: inet_sockif
|
|
*
|
|
* Description:
|
|
* Return the socket interface associated with the inet address family.
|
|
*
|
|
* Input Parameters:
|
|
* family - Socket address family
|
|
* type - Socket type
|
|
* protocol - Socket protocol
|
|
*
|
|
* Returned Value:
|
|
* On success, a non-NULL instance of struct sock_intf_s is returned. NULL
|
|
* is returned only if the address family is not supported.
|
|
*
|
|
****************************************************************************/
|
|
|
|
FAR const struct sock_intf_s *
|
|
inet_sockif(sa_family_t family, int type, int protocol)
|
|
{
|
|
DEBUGASSERT(family == PF_INET || family == PF_INET6);
|
|
|
|
#if defined(HAVE_PFINET_SOCKETS) && defined(CONFIG_NET_ICMP_SOCKET)
|
|
/* PF_INET, ICMP data gram sockets are a special case of raw sockets */
|
|
|
|
if (family == PF_INET && type == SOCK_DGRAM && protocol == IPPROTO_ICMP)
|
|
{
|
|
return &g_icmp_sockif;
|
|
}
|
|
else
|
|
#endif
|
|
#if defined(HAVE_PFINET6_SOCKETS) && defined(CONFIG_NET_ICMPv6_SOCKET)
|
|
/* PF_INET, ICMP data gram sockets are a special case of raw sockets */
|
|
|
|
if (family == PF_INET6 && type == SOCK_DGRAM && protocol == IPPROTO_ICMP6)
|
|
{
|
|
return &g_icmpv6_sockif;
|
|
}
|
|
else
|
|
#endif
|
|
#ifdef NET_UDP_HAVE_STACK
|
|
if (type == SOCK_DGRAM && (protocol == 0 || protocol == IPPROTO_UDP))
|
|
{
|
|
return &g_inet_sockif;
|
|
}
|
|
else
|
|
#endif
|
|
#ifdef NET_TCP_HAVE_STACK
|
|
if (type == SOCK_STREAM && (protocol == 0 || protocol == IPPROTO_TCP))
|
|
{
|
|
return &g_inet_sockif;
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
#endif /* HAVE_INET_SOCKETS */
|