From 59f9a1a2868f3201abecbfeab241edb371948f31 Mon Sep 17 00:00:00 2001 From: Gregory Nutt Date: Sun, 25 Jan 2015 14:16:44 -0600 Subject: [PATCH] Networking: Add local Unix domain socket listen logic --- net/local/Make.defs | 3 +- net/local/local.h | 32 +++- net/local/local_bind.c | 113 ++++++++++++ net/local/local_conn.c | 380 -------------------------------------- net/local/local_connect.c | 247 +++++++++++++++++++++++++ net/local/local_listen.c | 146 +++++++++++++++ net/local/local_release.c | 183 ++++++++++++++++++ net/socket/Make.defs | 7 + net/socket/listen.c | 66 +++++-- 9 files changed, 779 insertions(+), 398 deletions(-) create mode 100644 net/local/local_bind.c create mode 100644 net/local/local_connect.c create mode 100644 net/local/local_listen.c create mode 100644 net/local/local_release.c diff --git a/net/local/Make.defs b/net/local/Make.defs index fc58f7211e6..4ddea967d4e 100644 --- a/net/local/Make.defs +++ b/net/local/Make.defs @@ -37,7 +37,8 @@ ifeq ($(CONFIG_NET_LOCAL),y) -NET_CSRCS += local_conn.c +NET_CSRCS += local_conn.c local_connect.c local_release.c local_bind.c +NET_CSRCS += local_listen.c # Include UDP build support diff --git a/net/local/local.h b/net/local/local.h index fa2936b41a9..674739562b8 100644 --- a/net/local/local.h +++ b/net/local/local.h @@ -44,6 +44,7 @@ #include #include +#include #include #include @@ -88,9 +89,15 @@ enum local_state_s struct local_conn_s { - /* Fields common to SOCK_STREAM and SOCK_DGRAM */ + /* lc_node supports a doubly linked list: Listening SOCK_STREAM servers + * will be linked into a list of listeners; SOCK_STREAM clients will be + * linked to the lc_waiters and lc_conn lists. + */ dq_entry_t lc_node; /* Supports a doubly linked list */ + + /* Fields common to SOCK_STREAM and SOCK_DGRAM */ + uint8_t lc_crefs; /* Reference counts on this instance */ uint8_t lc_family; /* SOCK_STREAM or SOCK_DGRAM */ uint8_t lc_type; /* See enum local_type_e */ @@ -138,6 +145,10 @@ extern "C" # define EXTERN extern #endif +/* A list of all SOCK_STREAM listener connections */ + +EXTERN dq_queue_t g_local_listeners; + /**************************************************************************** * Public Function Prototypes ****************************************************************************/ @@ -212,6 +223,25 @@ int local_bind(FAR struct local_conn_s *conn, int local_connect(FAR struct local_conn_s *client, FAR const struct sockaddr *addr); +/**************************************************************************** + * Name: local_listen + * + * Description: + * Listen for a new connection of a SOCK_STREAM Unix domain socket. + * + * This function is called as part of the implementation of listen(); + * + * Input Parameters: + * server - A reference to the server-side local connection structure + * backlog - Maximum number of pending connections. + * + * Returned Value: + * Zero (OK) on success; a negated errno value on failure. + * + ****************************************************************************/ + +int local_listen(FAR struct local_conn_s *server, int backlog); + /**************************************************************************** * Name: local_release * diff --git a/net/local/local_bind.c b/net/local/local_bind.c new file mode 100644 index 00000000000..5a469f78642 --- /dev/null +++ b/net/local/local_bind.c @@ -0,0 +1,113 @@ +/**************************************************************************** + * net/local/local_bind.c + * + * Copyright (C) 2015 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * 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 +#if defined(CONFIG_NET) && defined(CONFIG_NET_LOCAL) + +#include +#include +#include + +#include "local/local.h" + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: local_bind + * + * Description: + * This function implements the low-level parts of the standard local + * bind()operation. + * + ****************************************************************************/ + +int local_bind(FAR struct local_conn_s *conn, + FAR const struct sockaddr *addr, socklen_t addrlen) +{ + FAR const struct sockaddr_un *unaddr = + (FAR const struct sockaddr_un *)addr; + int namelen; + + DEBUGASSERT(conn && unaddr && unaddr->sun_family == AF_LOCAL && + addrlen >= sizeof(sa_family_t)); + + /* Save the address family */ + + conn->lc_family = unaddr->sun_family; + + /* No determine the type of the Unix domain socket by comparing the size + * of the address description. + */ + + if (addrlen == sizeof(sa_family_t)) + { + /* No sun_path... This is an un-named Unix domain socket */ + + conn->lc_type = LOCAL_TYPE_UNNAMED; + } + else + { + namelen = strnlen(unaddr->sun_path, UNIX_PATH_MAX-1); + if (namelen <= 0) + { + /* Zero-length sun_path... This is an abstract Unix domain socket */ + + conn->lc_type = LOCAL_TYPE_ABSTRACT; + conn->lc_path[0] = '\0'; + } + else + { + /* This is an normal, pathname Unix domain socket */ + + conn->lc_type = LOCAL_TYPE_PATHNAME; + + /* Copy the path into the connection structure */ + + (void)strncpy(conn->lc_path, unaddr->sun_path, UNIX_PATH_MAX-1); + conn->lc_path[UNIX_PATH_MAX-1] = '\0'; + } + } + + conn->lc_state = LOCAL_STATE_BOUND; + return OK; +} + +#endif /* CONFIG_NET && CONFIG_NET_LOCAL */ diff --git a/net/local/local_conn.c b/net/local/local_conn.c index 159cac99cde..e19a0c12c52 100644 --- a/net/local/local_conn.c +++ b/net/local/local_conn.c @@ -51,104 +51,6 @@ #include "local/local.h" -/**************************************************************************** - * Private Data - ****************************************************************************/ - -/* A list of all allocated packet socket connections */ - -static dq_queue_t g_local_listeners; - -/**************************************************************************** - * Private Functions - ****************************************************************************/ - -/**************************************************************************** - * Name: _local_semtake() and _local_semgive() - * - * Description: - * Take/give semaphore - * - ****************************************************************************/ - -static inline void _local_semtake(sem_t *sem) -{ - /* Take the semaphore (perhaps waiting) */ - - while (net_lockedwait(sem) != 0) - { - /* The only case that an error should occur here is if - * the wait was awakened by a signal. - */ - - ASSERT(*get_errno_ptr() == EINTR); - } -} - -#define _local_semgive(sem) sem_post(sem) - -/**************************************************************************** - * Name: local_stream_connect - * - * Description: - * Find a local connection structure that is the appropriate "server" - * connection to be used with the provided "client" connection. - * - * Returned Values: - * Zero (OK) returned on success; A negated errno value is returned on a - * failure. Possible failures include: - * - * Assumptions: - * The network is locked on entry, unlocked on return. This logic is - * an integral part of the lock_connect() implementation and was - * separated out only to improve readability. - * - ****************************************************************************/ - -int inline local_stream_connect(FAR struct local_conn_s *client, - FAR struct local_conn_s *server, - net_lock_t state) -{ - int ret; - - /* Has server backlog been reached? - * NOTE: The backlog will be zero if listen() has never been called by the - * server. - */ - - if (server->lc_state != LOCAL_STATE_LISTENING || - server->u.server.lc_pending >= server->u.server.lc_backlog) - { - net_unlock(state); - return -ECONNREFUSED; - } - - server->u.server.lc_pending++; - DEBUGASSERT(server->u.server.lc_pending != 0); - - /* Add ourself to the list of waiting connections and notify the server. */ - - dq_addlast(&client->lc_node, &server->u.server.lc_waiters); - client->lc_state = LOCAL_STATE_ACCEPT; - _local_semgive(&server->lc_waitsem); - net_unlock(state); - - /* Wait for the server to accept the connections */ - - client->u.client.lc_result = -EBUSY; - do - { - _local_semtake(&client->lc_waitsem); - ret = client->u.client.lc_result; - } - while (ret == -EBUSY); - - /* Was the connection successful? */ - - client->lc_state = (ret < 0 ? LOCAL_STATE_BOUND : LOCAL_STATE_CONNECTED); - return ret; -} - /**************************************************************************** * Public Functions ****************************************************************************/ @@ -220,286 +122,4 @@ void local_free(FAR struct local_conn_s *conn) kmm_free(conn); } -/**************************************************************************** - * Name: local_bind - * - * Description: - * This function implements the low-level parts of the standard local - * bind()operation. - * - ****************************************************************************/ - -int local_bind(FAR struct local_conn_s *conn, - FAR const struct sockaddr *addr, socklen_t addrlen) -{ - FAR const struct sockaddr_un *unaddr = - (FAR const struct sockaddr_un *)addr; - int namelen; - - DEBUGASSERT(conn && unaddr && unaddr->sun_family == AF_LOCAL && - addrlen >= sizeof(sa_family_t)); - - /* Save the address family */ - - conn->lc_family = unaddr->sun_family; - - /* No determine the type of the Unix domain socket by comparing the size - * of the address description. - */ - - if (addrlen == sizeof(sa_family_t)) - { - /* No sun_path... This is an un-named Unix domain socket */ - - conn->lc_type = LOCAL_TYPE_UNNAMED; - } - else - { - namelen = strnlen(unaddr->sun_path, UNIX_PATH_MAX-1); - if (namelen <= 0) - { - /* Zero-length sun_path... This is an abstract Unix domain socket */ - - conn->lc_type = LOCAL_TYPE_ABSTRACT; - conn->lc_path[0] = '\0'; - } - else - { - /* This is an normal, pathname Unix domain socket */ - - conn->lc_type = LOCAL_TYPE_PATHNAME; - - /* Copy the path into the connection structure */ - - (void)strncpy(conn->lc_path, unaddr->sun_path, UNIX_PATH_MAX-1); - conn->lc_path[UNIX_PATH_MAX-1] = '\0'; - } - } - - conn->lc_state = LOCAL_STATE_BOUND; - return OK; -} - -/**************************************************************************** - * Name: local_connect - * - * Description: - * Find a local connection structure that is the appropriate "server" - * connection to be used with the provided "client" connection. - * - * Returned Values: - * Zero (OK) returned on success; A negated errno value is returned on a - * failure. Possible failures include: - * - * EISCONN - The specified socket is connection-mode and is already - * connected. - * EADDRNOTAVAIL - The specified address is not available from the - * local machine. - * ECONNREFUSED - The target address was not listening for connections or - * refused the connection request because the connection backlog has - * been exceeded. - * - ****************************************************************************/ - -int local_connect(FAR struct local_conn_s *client, - FAR const struct sockaddr *addr) -{ - FAR struct local_conn_s *conn; - net_lock_t state; - - DEBUGASSERT(client); - - if (client->lc_state == LOCAL_STATE_ACCEPT || - client->lc_state == LOCAL_STATE_CONNECTED) - { - return -EISCONN; - } - - /* Find the matching server connection */ - - state = net_lock(); - for(conn = (FAR struct local_conn_s *)g_local_listeners.head; - conn; - conn = (FAR struct local_conn_s *)dq_next(&conn->lc_node)) - { - /* Skip over connections that that have not yet been bound, - * are or a different address family, or are of a different type. - */ - - if (conn->lc_state == LOCAL_STATE_UNBOUND || - conn->lc_state == LOCAL_STATE_CLOSED || - conn->lc_family != client->lc_family || - conn->lc_type != client->lc_type) - { - continue; - } - - /* Handle according to the connection type */ - - switch (client->lc_type) - { - case LOCAL_TYPE_UNNAMED: /* A Unix socket that is not bound to any name */ - case LOCAL_TYPE_ABSTRACT: /* lc_path is length zero */ - { -#warning Missing logic - net_unlock(state); - return OK; - } - break; - - case LOCAL_TYPE_PATHNAME: /* lc_path holds a null terminated string */ - { - if (strncmp(client->lc_path, conn->lc_path, UNIX_PATH_MAX-1) == 0) - { - /* We have to do more for the SOCK_STREAM family */ - - if (conn->lc_family == SOCK_STREAM) - { - return local_stream_connect(client, conn, state); - } - - net_unlock(state); - return OK; - } - } - break; - - default: /* Bad, memory must be corrupted */ - DEBUGPANIC(); /* PANIC if debug on, else fall through */ - - case LOCAL_TYPE_UNTYPED: /* Type is not determined until the socket is bound */ - { - net_unlock(state); - return -EINVAL; - } - } - } - - net_unlock(state); - return -EADDRNOTAVAIL; -} - -/**************************************************************************** - * Name: local_release - * - * Description: - * If the local, Unix domain socket is in the connected state, then - * disconnect it. Release the local connection structure in any event - * - * Input Parameters: - * conn - A reference to local connection structure - * - ****************************************************************************/ - -int local_release(FAR struct local_conn_s *conn) -{ - net_lock_t state; - - /* There should be no references on this structure */ - - DEBUGASSERT(conn->lc_crefs == 0); - state = net_lock(); - - /* We should not bet here with states LOCAL_STATE_CLOSED or with - * LOCAL_STATE_ACCEPT. Those are internal states that should be atomic - * with respect to socket operations. - */ - - DEBUGASSERT(conn->lc_state != LOCAL_STATE_CLOSED && - conn->lc_state != LOCAL_STATE_ACCEPT); - - /* If the socket is connected (SOCK_STREAM client), then disconnect it */ - - if (conn->lc_state == LOCAL_STATE_CONNECTED || - conn->lc_state == LOCAL_STATE_DISCONNECTED) - { - FAR struct local_conn_s *server; - - DEBUGASSERT(conn->lc_family == SOCK_STREAM); - - server = conn->u.client.lc_server; - DEBUGASSERT(server && - (server->lc_state == LOCAL_STATE_LISTENING || - server->lc_state == LOCAL_STATE_CLOSED) && - !dq_empty(&server->u.server.lc_conns)); - - /* Remove ourself from the list of connections */ - - dq_rem(&conn->lc_node, &server->u.server.lc_conns); - - /* Is the list of pending connections now empty? Was the connection - * already closed? - */ - - if (dq_empty(&server->u.server.lc_waiters) && - server->lc_state == LOCAL_STATE_CLOSED) - { - /* Yes, free the server connection as well */ - - local_free(server); - } - - /* Now we can free this connection structure */ - - local_free(conn); - } - - /* Is the socket is listening socket (SOCK_STREAM server) */ - - else if (conn->lc_state == LOCAL_STATE_LISTENING) - { - FAR struct local_conn_s *client; - FAR struct local_conn_s *next; - - DEBUGASSERT(conn->lc_family == SOCK_STREAM); - - /* Are there still clients waiting for a connection to the server? */ - - for (client = (FAR struct local_conn_s *)conn->u.server.lc_waiters.head; - client; - client = (FAR struct local_conn_s *)dq_next(&client->lc_node)) - { - client->u.client.lc_result = -ENETUNREACH; - _local_semgive(&client->lc_waitsem); - conn->lc_state = LOCAL_STATE_CLOSED; - } - - conn->u.server.lc_pending = 0; - - /* Disconnect any previous client connections */ - - for (client = (FAR struct local_conn_s *)conn->u.server.lc_conns.head; - client; - client = next) - { - next = (FAR struct local_conn_s *)dq_next(&client->lc_node); - dq_rem(&client->lc_node, &conn->u.server.lc_conns); - client->lc_state = LOCAL_STATE_DISCONNECTED; - } - - /* Can we free the connection structure now? We cannot - * if there are still pending connection requested to - * be resolved. - */ - - conn->u.server.lc_backlog = 0; - if (conn->lc_state == LOCAL_STATE_CLOSED) - { - local_free(conn); - } - } - - /* For the remaining states (LOCAL_STATE_UNBOUND and LOCAL_STATE_UNBOUND), - * we simply free the connection structure. - */ - - else - { - local_free(conn); - } - - net_unlock(state); - return OK; -} - #endif /* CONFIG_NET && CONFIG_NET_LOCAL */ diff --git a/net/local/local_connect.c b/net/local/local_connect.c new file mode 100644 index 00000000000..c1e4b9fdac9 --- /dev/null +++ b/net/local/local_connect.c @@ -0,0 +1,247 @@ +/**************************************************************************** + * net/local/local_connnect.c + * + * Copyright (C) 2015 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * 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 +#if defined(CONFIG_NET) && defined(CONFIG_NET_LOCAL) + +#include +#include +#include +#include +#include + +#include + +#include "utils/utils.h" +#include "local/local.h" + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: _local_semtake() and _local_semgive() + * + * Description: + * Take/give semaphore + * + ****************************************************************************/ + +static inline void _local_semtake(sem_t *sem) +{ + /* Take the semaphore (perhaps waiting) */ + + while (sem_wait(sem) != 0) + { + /* The only case that an error should occur here is if + * the wait was awakened by a signal. + */ + + ASSERT(*get_errno_ptr() == EINTR); + } +} + +#define _local_semgive(sem) sem_post(sem) + +/**************************************************************************** + * Name: local_stream_connect + * + * Description: + * Find a local connection structure that is the appropriate "server" + * connection to be used with the provided "client" connection. + * + * Returned Values: + * Zero (OK) returned on success; A negated errno value is returned on a + * failure. Possible failures include: + * + * Assumptions: + * The network is locked on entry, unlocked on return. This logic is + * an integral part of the lock_connect() implementation and was + * separated out only to improve readability. + * + ****************************************************************************/ + +int inline local_stream_connect(FAR struct local_conn_s *client, + FAR struct local_conn_s *server, + net_lock_t state) +{ + int ret; + + /* Has server backlog been reached? + * NOTE: The backlog will be zero if listen() has never been called by the + * server. + */ + + if (server->lc_state != LOCAL_STATE_LISTENING || + server->u.server.lc_pending >= server->u.server.lc_backlog) + { + net_unlock(state); + return -ECONNREFUSED; + } + + server->u.server.lc_pending++; + DEBUGASSERT(server->u.server.lc_pending != 0); + + /* Add ourself to the list of waiting connections and notify the server. */ + + dq_addlast(&client->lc_node, &server->u.server.lc_waiters); + client->lc_state = LOCAL_STATE_ACCEPT; + _local_semgive(&server->lc_waitsem); + net_unlock(state); + + /* Wait for the server to accept the connections */ + + client->u.client.lc_result = -EBUSY; + do + { + _local_semtake(&client->lc_waitsem); + ret = client->u.client.lc_result; + } + while (ret == -EBUSY); + + /* Was the connection successful? */ + + client->lc_state = (ret < 0 ? LOCAL_STATE_BOUND : LOCAL_STATE_CONNECTED); + return ret; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: local_connect + * + * Description: + * Find a local connection structure that is the appropriate "server" + * connection to be used with the provided "client" connection. + * + * Returned Values: + * Zero (OK) returned on success; A negated errno value is returned on a + * failure. Possible failures include: + * + * EISCONN - The specified socket is connection-mode and is already + * connected. + * EADDRNOTAVAIL - The specified address is not available from the + * local machine. + * ECONNREFUSED - The target address was not listening for connections or + * refused the connection request because the connection backlog has + * been exceeded. + * + ****************************************************************************/ + +int local_connect(FAR struct local_conn_s *client, + FAR const struct sockaddr *addr) +{ + FAR struct local_conn_s *conn; + net_lock_t state; + + DEBUGASSERT(client); + + if (client->lc_state == LOCAL_STATE_ACCEPT || + client->lc_state == LOCAL_STATE_CONNECTED) + { + return -EISCONN; + } + + /* Find the matching server connection */ + + state = net_lock(); + for(conn = (FAR struct local_conn_s *)g_local_listeners.head; + conn; + conn = (FAR struct local_conn_s *)dq_next(&conn->lc_node)) + { + /* Skip over connections that that have not yet been bound, + * are or a different address family, or are of a different type. + */ + + if (conn->lc_state == LOCAL_STATE_UNBOUND || + conn->lc_state == LOCAL_STATE_CLOSED || + conn->lc_family != client->lc_family || + conn->lc_type != client->lc_type) + { + continue; + } + + /* Handle according to the connection type */ + + switch (client->lc_type) + { + case LOCAL_TYPE_UNNAMED: /* A Unix socket that is not bound to any name */ + case LOCAL_TYPE_ABSTRACT: /* lc_path is length zero */ + { +#warning Missing logic + net_unlock(state); + return OK; + } + break; + + case LOCAL_TYPE_PATHNAME: /* lc_path holds a null terminated string */ + { + if (strncmp(client->lc_path, conn->lc_path, UNIX_PATH_MAX-1) == 0) + { + /* We have to do more for the SOCK_STREAM family */ + + if (conn->lc_family == SOCK_STREAM) + { + return local_stream_connect(client, conn, state); + } + + net_unlock(state); + return OK; + } + } + break; + + default: /* Bad, memory must be corrupted */ + DEBUGPANIC(); /* PANIC if debug on, else fall through */ + + case LOCAL_TYPE_UNTYPED: /* Type is not determined until the socket is bound */ + { + net_unlock(state); + return -EINVAL; + } + } + } + + net_unlock(state); + return -EADDRNOTAVAIL; +} + +#endif /* CONFIG_NET && CONFIG_NET_LOCAL */ diff --git a/net/local/local_listen.c b/net/local/local_listen.c new file mode 100644 index 00000000000..0f72b77ac04 --- /dev/null +++ b/net/local/local_listen.c @@ -0,0 +1,146 @@ +/**************************************************************************** + * net/local/local_listen.c + * + * Copyright (C) 2015 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * 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 +#if defined(CONFIG_NET) && defined(CONFIG_NET_LOCAL) + +#include +#include +#include + +#include + +#include "local/local.h" + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/* A list of all allocated packet socket connections */ + +dq_queue_t g_local_listeners; + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: local_listen + * + * Description: + * Listen for a new connection of a SOCK_STREAM Unix domain socket. + * + * This function is called as part of the implementation of listen(); + * + * Input Parameters: + * server - A reference to the server-side local connection structure + * backlog - Maximum number of pending connections. + * + * Returned Value: + * Zero (OK) on success; a negated errno value on failure. + * + ****************************************************************************/ + +int local_listen(FAR struct local_conn_s *server, int backlog) +{ + net_lock_t state; + int ret; + + /* Some sanity checks */ + + DEBUGASSERT(server); + + if (server->lc_family != SOCK_STREAM || + server->lc_state == LOCAL_STATE_UNBOUND || + server->lc_type != LOCAL_TYPE_PATHNAME) + { + return -EOPNOTSUPP; + } + + DEBUGASSERT(server->lc_state == LOCAL_STATE_BOUND || + server->lc_state == LOCAL_STATE_LISTENING); + + /* Set the backlog value */ + + DEBUGASSERT((unsigned)backlog < 256); + server->u.server.lc_backlog = backlog; + + /* Is this the first time since being bound to an address and that + * listen() was called? If so, the state should be LOCAL_STATE_BOUND. + */ + + if (server->lc_state == LOCAL_STATE_BOUND) + { + /* The connection should not reside in any other list */ + + DEBUGASSERT(server->lc_node.flink == NULL && + server->lc_node.flink == NULL); + + /* Add the connection structure to the list of listeners */ + + state = net_lock(); + dq_addlast(&server->lc_node, &g_local_listeners); + net_unlock(state); + + /* And change the server state to listing */ + + server->lc_state = LOCAL_STATE_LISTENING; + } + + /* Loop until a connection requested or we receive a signal */ + + while (dq_empty(&server->u.server.lc_waiters)) + { + /* No.. wait for a connection or a signal */ + + ret = sem_wait(&server->lc_waitsem); + if (ret < 0) + { + int errval = errno; + DEBUGASSERT(errval == EINTR); + return -errval; + } + } + + /* There is a client waiting for the connection */ + + return OK; +} + +#endif /* CONFIG_NET && CONFIG_NET_LOCAL */ diff --git a/net/local/local_release.c b/net/local/local_release.c new file mode 100644 index 00000000000..c2d6092ceec --- /dev/null +++ b/net/local/local_release.c @@ -0,0 +1,183 @@ +/**************************************************************************** + * net/local/local_release.c + * + * Copyright (C) 2015 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * 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 +#if defined(CONFIG_NET) && defined(CONFIG_NET_LOCAL) + +#include +#include +#include +#include + +#include + +#include "local/local.h" + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: local_release + * + * Description: + * If the local, Unix domain socket is in the connected state, then + * disconnect it. Release the local connection structure in any event + * + * Input Parameters: + * conn - A reference to local connection structure + * + ****************************************************************************/ + +int local_release(FAR struct local_conn_s *conn) +{ + net_lock_t state; + + /* There should be no references on this structure */ + + DEBUGASSERT(conn->lc_crefs == 0); + state = net_lock(); + + /* We should not bet here with states LOCAL_STATE_CLOSED or with + * LOCAL_STATE_ACCEPT. Those are internal states that should be atomic + * with respect to socket operations. + */ + + DEBUGASSERT(conn->lc_state != LOCAL_STATE_CLOSED && + conn->lc_state != LOCAL_STATE_ACCEPT); + + /* If the socket is connected (SOCK_STREAM client), then disconnect it */ + + if (conn->lc_state == LOCAL_STATE_CONNECTED || + conn->lc_state == LOCAL_STATE_DISCONNECTED) + { + FAR struct local_conn_s *server; + + DEBUGASSERT(conn->lc_family == SOCK_STREAM); + + server = conn->u.client.lc_server; + DEBUGASSERT(server && + (server->lc_state == LOCAL_STATE_LISTENING || + server->lc_state == LOCAL_STATE_CLOSED) && + !dq_empty(&server->u.server.lc_conns)); + + /* Remove ourself from the list of connections */ + + dq_rem(&conn->lc_node, &server->u.server.lc_conns); + + /* Is the list of pending connections now empty? Was the connection + * already closed? + */ + + if (dq_empty(&server->u.server.lc_waiters) && + server->lc_state == LOCAL_STATE_CLOSED) + { + /* Yes, free the server connection as well */ + + local_free(server); + } + + /* Now we can free this connection structure */ + + local_free(conn); + } + + /* Is the socket is listening socket (SOCK_STREAM server) */ + + else if (conn->lc_state == LOCAL_STATE_LISTENING) + { + FAR struct local_conn_s *client; + FAR struct local_conn_s *next; + + DEBUGASSERT(conn->lc_family == SOCK_STREAM); + + /* Are there still clients waiting for a connection to the server? */ + + for (client = (FAR struct local_conn_s *)conn->u.server.lc_waiters.head; + client; + client = (FAR struct local_conn_s *)dq_next(&client->lc_node)) + { + client->u.client.lc_result = -ENETUNREACH; + sem_post(&client->lc_waitsem); + conn->lc_state = LOCAL_STATE_CLOSED; + } + + conn->u.server.lc_pending = 0; + + /* Disconnect any previous client connections */ + + for (client = (FAR struct local_conn_s *)conn->u.server.lc_conns.head; + client; + client = next) + { + next = (FAR struct local_conn_s *)dq_next(&client->lc_node); + dq_rem(&client->lc_node, &conn->u.server.lc_conns); + client->lc_state = LOCAL_STATE_DISCONNECTED; + } + + /* Remove the server from the list of listeners */ + + dq_rem(&conn->lc_node, &g_local_listeners); + + /* Can we free the connection structure now? We cannot + * if there are still pending connection requested to + * be resolved. + */ + + conn->u.server.lc_backlog = 0; + if (conn->lc_state == LOCAL_STATE_CLOSED) + { + local_free(conn); + } + } + + /* For the remaining states (LOCAL_STATE_UNBOUND and LOCAL_STATE_UNBOUND), + * we simply free the connection structure. + */ + + else + { + local_free(conn); + } + + net_unlock(state); + return OK; +} + +#endif /* CONFIG_NET && CONFIG_NET_LOCAL */ diff --git a/net/socket/Make.defs b/net/socket/Make.defs index 44645261ad7..0859d28a874 100644 --- a/net/socket/Make.defs +++ b/net/socket/Make.defs @@ -43,6 +43,13 @@ SOCK_CSRCS += net_clone.c net_poll.c net_vfcntl.c ifeq ($(CONFIG_NET_TCP),y) SOCK_CSRCS += send.c listen.c accept.c net_monitor.c +else + +# Local Unix domain support + +ifeq ($(CONFIG_NET_LOCAL),y) +SOCK_CSRCS += send.c listen.c accept.c +endif endif # Socket options diff --git a/net/socket/listen.c b/net/socket/listen.c index b9b8d1d5b80..cdfc4d9d877 100644 --- a/net/socket/listen.c +++ b/net/socket/listen.c @@ -1,7 +1,7 @@ /**************************************************************************** * net/socket/listen.c * - * Copyright (C) 2007-2009 Gregory Nutt. All rights reserved. + * Copyright (C) 2007-2009, 2015 Gregory Nutt. All rights reserved. * Author: Gregory Nutt * * Redistribution and use in source and binary forms, with or without @@ -45,6 +45,7 @@ #include #include "tcp/tcp.h" +#include "local/local.h" #include "socket/socket.h" /**************************************************************************** @@ -89,7 +90,6 @@ int listen(int sockfd, int backlog) { FAR struct socket *psock = sockfd_socket(sockfd); - struct tcp_conn_s *conn; int err; /* Verify that the sockfd corresponds to valid, allocated socket */ @@ -111,39 +111,73 @@ int listen(int sockfd, int backlog) { err = EBADF; } + goto errout; } /* Verify that the sockfd corresponds to a connected SOCK_STREAM */ - conn = (FAR struct tcp_conn_s *)psock->s_conn; - if (psock->s_type != SOCK_STREAM || !psock->s_conn || conn->lport <= 0) + if (psock->s_type != SOCK_STREAM || !psock->s_conn) { err = EOPNOTSUPP; goto errout; } - /* Set up the backlog for this connection */ +#ifdef CONFIG_NET_LOCAL +#ifdef CONFIG_NET_TCP + if (psock->s_domain == PF_LOCAL) +#endif + { + FAR struct local_conn_s *conn = + (FAR struct local_conn_s *)psock->s_conn; + + err = local_listen(conn, backlog); + if (err < 0) + { + err = -err; + goto errout; + } + } +#endif /* CONFIG_NET_LOCAL */ + +#ifdef CONFIG_NET_TCP +#ifdef CONFIG_NET_LOCAL + else +#endif + { + FAR struct tcp_conn_s *conn = + (FAR struct tcp_conn_s *)psock->s_conn; + + if (conn->lport <= 0) + { + err = EOPNOTSUPP; + goto errout; + } + + /* Set up the backlog for this connection */ #ifdef CONFIG_NET_TCPBACKLOG - err = tcp_backlogcreate(conn, backlog); - if (err < 0) - { - err = -err; - goto errout; - } + err = tcp_backlogcreate(conn, backlog); + if (err < 0) + { + err = -err; + goto errout; + } #endif - /* Start listening to the bound port. This enables callbacks when accept() - * is called and enables poll()/select() logic. - */ + /* Start listening to the bound port. This enables callbacks when + * accept() is called and enables poll()/select() logic. + */ + + tcp_listen(conn); + } +#endif /* CONFIG_NET_TCP */ - tcp_listen(conn); psock->s_flags |= _SF_LISTENING; return OK; errout: - errno = err; + set_errno(err); return ERROR; }