diff --git a/include/nuttx/net/net.h b/include/nuttx/net/net.h index 9302c6c4539..fb31d93158d 100644 --- a/include/nuttx/net/net.h +++ b/include/nuttx/net/net.h @@ -197,6 +197,7 @@ struct sock_intf_s CODE int (*si_close)(FAR struct socket *psock); CODE int (*si_ioctl)(FAR struct socket *psock, int cmd, FAR void *arg, size_t arglen); + CODE int (*si_socketpair)(FAR struct socket *psocks[2]); #ifdef CONFIG_NET_SENDFILE CODE ssize_t (*si_sendfile)(FAR struct socket *psock, FAR struct file *infile, FAR off_t *offset, @@ -1423,6 +1424,27 @@ int psock_vfcntl(FAR struct socket *psock, int cmd, va_list ap); int psock_fcntl(FAR struct socket *psock, int cmd, ...); +/**************************************************************************** + * Name: psock_socketpair + * + * Description: + * Create an unbound pair of connected sockets in a specified domain, of a + * specified type, under the protocol optionally specified by the protocol + * argument. The two sockets shall be identical. The file descriptors used + * in referencing the created sockets shall be returned in + * sv[0] and sv[1]. + * + * Input Parameters: + * domain (see sys/socket.h) + * type (see sys/socket.h) + * protocol (see sys/socket.h) + * psocks A pointer to a user allocated socket structure to be paired. + * + ****************************************************************************/ + +int psock_socketpair(int domain, int type, int protocol, + FAR struct socket *psocks[2]); + /**************************************************************************** * Name: netdev_register * diff --git a/libs/libc/net/Make.defs b/libs/libc/net/Make.defs index 1209791e66e..0c434f23759 100644 --- a/libs/libc/net/Make.defs +++ b/libs/libc/net/Make.defs @@ -26,7 +26,7 @@ CSRCS += lib_inetntop.c lib_inetpton.c CSRCS += lib_etherntoa.c lib_etheraton.c ifeq ($(CONFIG_NET),y) -CSRCS += lib_shutdown.c lib_socketpair.c +CSRCS += lib_shutdown.c endif ifeq ($(CONFIG_NET_LOOPBACK),y) diff --git a/libs/libc/net/lib_socketpair.c b/libs/libc/net/lib_socketpair.c deleted file mode 100644 index 3bae979f8ff..00000000000 --- a/libs/libc/net/lib_socketpair.c +++ /dev/null @@ -1,186 +0,0 @@ -/**************************************************************************** - * libs/libc/net/lib_socketpair.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 -#include -#include -#include -#include -#include -#include - -/**************************************************************************** - * Public Type Declarations - ****************************************************************************/ - -union sockaddr_u -{ - struct sockaddr addr; - struct sockaddr_in inaddr; - struct sockaddr_in6 in6addr; -}; - -/**************************************************************************** - * Private Functions - ****************************************************************************/ - -static inline void init_loop_addr(int domain, FAR union sockaddr_u *addr, - FAR socklen_t *len) -{ - if (domain == AF_INET6) - { - struct in6_addr init_sin6_addr = IN6ADDR_LOOPBACK_INIT; - - memset(&addr->in6addr, 0, sizeof(addr->in6addr)); - addr->in6addr.sin6_family = domain; - addr->in6addr.sin6_addr = init_sin6_addr; - *len = sizeof(addr->in6addr); - } - else - { - memset(&addr->inaddr, 0, sizeof(addr->inaddr)); - addr->inaddr.sin_family = domain; - addr->inaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); - *len = sizeof(addr->inaddr); - } -} - -static int create_socket(int domain, int type, int protocol, - FAR union sockaddr_u *addr, FAR socklen_t *len) -{ - int socketfd; - - socketfd = socket(domain, type, protocol); - if (socketfd < 0) - { - return socketfd; - } - - init_loop_addr(domain, addr, len); - if (bind(socketfd, &addr->addr, *len) == 0) - { - if (getsockname(socketfd, &addr->addr, len) == 0) - { - return socketfd; - } - } - - close(socketfd); - return -1; -} - -/**************************************************************************** - * Public Functions - ****************************************************************************/ - -/**************************************************************************** - * Name: socketpair - * - * Description: - * Create an unbound pair of connected sockets in a specified domain, of a - * specified type, under the protocol optionally specified by the protocol - * argument. The two sockets shall be identical. The file descriptors used - * in referencing the created sockets shall be returned in - * sv[0] and sv[1]. - * - ****************************************************************************/ - -int socketpair(int domain, int type, int protocol, int sv[2]) -{ - union sockaddr_u addr[2]; - socklen_t len; - - if (domain != AF_UNIX && domain != AF_INET && domain != AF_INET6) - { - set_errno(EAFNOSUPPORT); - return -1; - } - - if (sv == NULL) - { - set_errno(EINVAL); - return -1; - } - - if (domain == AF_UNIX) - { - domain = AF_INET; - } - - sv[0] = create_socket(domain, type, protocol, &addr[0], &len); - sv[1] = create_socket(domain, type, protocol, &addr[1], &len); - if (sv[0] < 0 || sv[1] < 0) - { - goto err; - } - - if (type == SOCK_DGRAM) - { - if (connect(sv[0], &addr[1].addr, len) < 0) - { - goto err; - } - - if (connect(sv[1], &addr[0].addr, len) < 0) - { - goto err; - } - } - else - { - int listener = sv[0]; - - if (listen(listener, 2) < 0) - { - goto err; - } - - if (connect(sv[1], &addr[0].addr, len) < 0) - { - goto err; - } - - sv[0] = accept(listener, &addr[0].addr, &len); - close(listener); - if (sv[0] < 0) - { - goto err; - } - } - - return 0; - -err: - if (sv[0] != -1) - { - close(sv[0]); - } - - if (sv[1] != -1) - { - close(sv[1]); - } - - return -1; -} diff --git a/net/inet/inet_sockif.c b/net/inet/inet_sockif.c index 7e5712d6915..3559e606624 100644 --- a/net/inet/inet_sockif.c +++ b/net/inet/inet_sockif.c @@ -32,6 +32,7 @@ #include #include +#include #include #include "tcp/tcp.h" @@ -44,6 +45,17 @@ #ifdef HAVE_INET_SOCKETS +/**************************************************************************** + * Private Type Definitions + ****************************************************************************/ + +union sockaddr_u +{ + struct sockaddr addr; + struct sockaddr_in inaddr; + struct sockaddr_in6 in6addr; +}; + /**************************************************************************** * Private Function Prototypes ****************************************************************************/ @@ -78,6 +90,7 @@ 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, @@ -103,7 +116,8 @@ static const struct sock_intf_s g_inet_sockif = inet_sendmsg, /* si_sendmsg */ inet_recvmsg, /* si_recvmsg */ inet_close, /* si_close */ - inet_ioctl /* si_ioctl */ + inet_ioctl, /* si_ioctl */ + inet_socketpair /* si_socketpair */ #ifdef CONFIG_NET_SENDFILE , inet_sendfile /* si_sendfile */ @@ -1328,6 +1342,141 @@ static int inet_ioctl(FAR struct socket *psock, int cmd, 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 * diff --git a/net/socket/Make.defs b/net/socket/Make.defs index 431868afd03..f71a42c3b5a 100644 --- a/net/socket/Make.defs +++ b/net/socket/Make.defs @@ -22,7 +22,7 @@ SOCK_CSRCS += bind.c connect.c getsockname.c getpeername.c SOCK_CSRCS += recv.c recvfrom.c send.c sendto.c -SOCK_CSRCS += socket.c net_close.c +SOCK_CSRCS += socket.c socketpair.c net_close.c SOCK_CSRCS += recvmsg.c sendmsg.c SOCK_CSRCS += net_dup2.c net_sockif.c net_poll.c net_vfcntl.c SOCK_CSRCS += net_fstat.c diff --git a/net/socket/socketpair.c b/net/socket/socketpair.c new file mode 100644 index 00000000000..8b1bf60adf6 --- /dev/null +++ b/net/socket/socketpair.c @@ -0,0 +1,187 @@ +/**************************************************************************** + * net/socket/socketpair.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 +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: psock_socketpair + * + * Description: + * Create an unbound pair of connected sockets in a specified domain, of a + * specified type, under the protocol optionally specified by the protocol + * argument. The two sockets shall be identical. The file descriptors used + * in referencing the created sockets shall be returned in + * sv[0] and sv[1]. + * + * Input Parameters: + * domain - (see sys/socket.h) + * type - (see sys/socket.h) + * protocol - (see sys/socket.h) + * psocks - The array to catch the pair descriptors + * + ****************************************************************************/ + +int psock_socketpair(int domain, int type, int protocol, + FAR struct socket *psocks[2]) +{ + int ret; + + /* Initialize the socket structure */ + + ret = psock_socket(domain, type, protocol, psocks[0]); + if (ret < 0) + { + return ret; + } + + if (psocks[0]->s_sockif->si_socketpair == NULL) + { + ret = -EAFNOSUPPORT; + goto errsock; + } + + ret = psock_socket(domain, type, protocol, psocks[1]); + if (ret < 0) + { + goto errsock; + } + + /* Perform socketpair process */ + + ret = psocks[0]->s_sockif->si_socketpair(psocks); + if (ret == 0) + { + return ret; + } + + psock_close(psocks[1]); +errsock: + psock_close(psocks[0]); + return ret; +} + +/**************************************************************************** + * Name: socketpair + * + * Description: + * Create an unbound pair of connected sockets in a specified domain, of a + * specified type, under the protocol optionally specified by the protocol + * argument. The two sockets shall be identical. The file descriptors used + * in referencing the created sockets shall be returned in + * sv[0] and sv[1]. + * + * Input Parameters: + * domain - (see sys/socket.h) + * type - (see sys/socket.h) + * protocol - (see sys/socket.h) + * sv[2] - The user provided array in which to catch the pair + * descriptors + * + ****************************************************************************/ + +int socketpair(int domain, int type, int protocol, int sv[2]) +{ + FAR struct socket *psocks[2]; + int oflags = O_RDWR; + int ret; + int i; + int j = 0; + int k; + + if (sv == NULL) + { + ret = -EINVAL; + goto errout; + } + + for (k = 0; k < 2; k++) + { + psocks[k] = kmm_zalloc(sizeof(*psocks[k])); + if (psocks[k] == NULL) + { + ret = -ENOMEM; + goto errout_with_alloc; + } + } + + ret = psock_socketpair(domain, type, protocol, psocks); + if (ret < 0) + { + goto errout_with_alloc; + } + + if (type & SOCK_CLOEXEC) + { + oflags |= O_CLOEXEC; + } + + /* Allocate a socket descriptor */ + + for (; j < 2; j++) + { + sv[j] = sockfd_allocate(psocks[j], oflags); + if (sv[j] < 0) + { + ret = sv[j]; + goto errout_with_psock; + } + } + + return OK; + +errout_with_psock: + for (i = 0; i < j; i++) + { + nx_close(sv[i]); + } + + for (i = j; i < k; i++) + { + psock_close(psocks[i]); + } + +errout_with_alloc: + for (i = j; i < k; i++) + { + kmm_free(psocks[i]); + } + +errout: + set_errno(-ret); + return ERROR; +}