/** * Copyright (c) 2015 - 2019, Nordic Semiconductor ASA * * All rights reserved. * * 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, except as embedded into a Nordic * Semiconductor ASA integrated circuit in a product or a software update for * such product, 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 of Nordic Semiconductor ASA nor the names of its * contributors may be used to endorse or promote products derived from this * software without specific prior written permission. * * 4. This software, with or without modification, must only be used with a * Nordic Semiconductor ASA integrated circuit. * * 5. Any software provided in binary form under this license must not be reverse * engineered, decompiled, modified and/or disassembled. * * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA 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. * */ #include "nordic_common.h" #include "sdk_common.h" #include "sdk_config.h" #include "nrf_sdm.h" #include "app_scheduler.h" #include "app_timer.h" #include "iot_common.h" #include "app_error.h" #include "socket_api.h" #include "socket_common.h" #include "socket_trace.h" #include "sdk_os.h" #include "transport_if.h" #include "portdb.h" #include "errno.h" #include "mem_manager.h" #include "ipv6_parse.h" #include "netinet/in.h" #include "unistd.h" #include "sdk_os.h" #include "nrf_log_ctrl.h" #include "nrf_log_default_backends.h" #ifndef SOCKET_ENABLE_API_PARAM_CHECK #define SOCKET_ENABLE_API_PARAM_CHECK 0 #endif #include "socket_config.h" #if SOCKET_CONFIG_LOG_ENABLED == 1 NRF_LOG_MODULE_REGISTER(); #endif /** * @defgroup api_param_check API Parameters check macros. * * @details Macros that verify parameters passed to the module in the APIs. These macros * could be mapped to nothing in final versions of code to save execution and size. * SOCKET_ENABLE_API_PARAM_CHECK should be set to 0 to disable these checks. * * @{ */ #if SOCKET_ENABLE_API_PARAM_CHECK == 1 /**@brief Macro to check is module is initialized before requesting one of the module procedures. */ #define VERIFY_MODULE_IS_INITIALIZED() \ do { \ if (m_initialization_state == false) \ { \ return (SDK_ERR_MODULE_NOT_INITIALIZED | IOT_SOCKET_ERR_BASE);\ } \ } while (0) /** * @brief Verify NULL parameters are not passed to API by application. */ #define NULL_PARAM_CHECK(PARAM) \ do { \ if ((PARAM) == NULL) \ { \ set_errno(EFAULT); \ return -1; \ } \ } while (0) /** * @brief Verify socket id passed on the API by application is valid. */ #define VERIFY_SOCKET_ID(ID) \ do { \ if (((ID) < 0) || ((ID) >= NUM_SOCKETS)) \ { \ set_errno(EBADF); \ return -1; \ } \ } while (0) #else #define VERIFY_MODULE_IS_INITIALIZED() #define NULL_PARAM_CHECK(PARAM) #define VERIFY_SOCKET_ID(ID) #endif /** @} */ #define SOCKET_MUTEX_INIT() SDK_MUTEX_INIT(m_socket_mtx); #define SOCKET_MUTEX_LOCK() SDK_MUTEX_LOCK(m_socket_mtx) #define SOCKET_MUTEX_UNLOCK() SDK_MUTEX_UNLOCK(m_socket_mtx) // note: one extra for configuration socket #define NUM_SOCKETS SOCKET_MAX_SOCKET_COUNT + 1 SDK_MUTEX_DEFINE(m_socket_mtx) /**< Mutex for protecting m_socket_table (not individual entries). */ #define SCHED_QUEUE_SIZE 16 /**< Maximum number of events in the scheduler queue. */ #define SCHED_MAX_EVENT_DATA_SIZE 192 /**< Maximum size of scheduler events. */ static bool m_initialization_state = false; /**< Variable to maintain module initialization state. */ static volatile bool m_interface_up = false; /**< Interface state. */ static socket_t m_socket_table[NUM_SOCKETS]; /**< Socket table. */ const struct in6_addr in6addr_any = { {0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, /**< IPv6 anycast address. */ 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u} }; #if defined (NRF_LOG_ENABLED) && (NRF_LOG_ENABLED == 1) void log_init(void) { ret_code_t err_code = NRF_LOG_INIT(NULL); APP_ERROR_CHECK(err_code); NRF_LOG_DEFAULT_BACKENDS_INIT(); } #else // defined (NRF_LOG_ENABLED) && (NRF_LOG_ENABLED == 1) void log_init(void) { ; } #endif // defined (NRF_LOG_ENABLED) && (NRF_LOG_ENABLED == 1) uint32_t socket_init(void) { memset(m_socket_table, 0, sizeof(m_socket_table)); SOCKET_MUTEX_INIT(); log_init(); uint32_t err_code = nrf_mem_init(); APP_ERROR_CHECK(err_code); APP_SCHED_INIT(SCHED_MAX_EVENT_DATA_SIZE, SCHED_QUEUE_SIZE); err_code = app_timer_init(); APP_ERROR_CHECK(err_code); err_code = config_socket_init(); APP_ERROR_CHECK(err_code); #if SOCKET_TRANSPORT_ENABLE == 1 err_code = portdb_init(SOCKET_MAX_SOCKET_COUNT); APP_ERROR_CHECK(err_code); transport_handler_init(); #endif config_socket_start(); m_initialization_state = true; SOCKET_TRACE("Socket init complete"); return NRF_SUCCESS; } /** * Finds a free entry in the socket table, marks it as used and returns it. Returns -1 if no entry * was found. */ static int socket_allocate(socket_t ** pp_socket) { int ret_sock = -1; SOCKET_MUTEX_LOCK(); for (int sock = 0; sock < NUM_SOCKETS; sock++) { SOCKET_TRACE("Looking at socket %d with state %d", (int)sock, m_socket_table[sock].so_state); if (m_socket_table[sock].so_state == STATE_CLOSED) { m_socket_table[sock].so_state = STATE_OPEN; ret_sock = sock; *pp_socket = &m_socket_table[sock]; break; } } if (ret_sock < 0) { set_errno(EMFILE); } SOCKET_MUTEX_UNLOCK(); return ret_sock; } static socket_t * socket_find(int sock) { SOCKET_MUTEX_LOCK(); socket_t * p_socket = &m_socket_table[sock]; SOCKET_MUTEX_UNLOCK(); return p_socket; } static void socket_free(int sock) { SOCKET_TRACE("Freeing socket %d", (int)sock); SOCKET_MUTEX_LOCK(); memset(&m_socket_table[sock], 0, sizeof(m_socket_table[sock])); m_socket_table[sock].so_state = STATE_CLOSED; SOCKET_MUTEX_UNLOCK(); } #if SOCKET_TRANSPORT_ENABLE == 1 void transport_interface_up(void) { m_interface_up = true; } void transport_interface_down(void) { m_interface_up = false; for (int sock = 0; sock < NUM_SOCKETS; sock++) { (void) close(sock); } } #endif int fcntl(int fd, int cmd, int flags) { VERIFY_MODULE_IS_INITIALIZED(); VERIFY_SOCKET_ID(fd); if (!((cmd == F_SETFL) || (cmd == F_GETFL))) { set_errno(EINVAL); return -1; } socket_t * p_socket = socket_find(fd); if (cmd == F_SETFL) { p_socket->so_flags = flags; } else if (cmd == F_GETFL) { return p_socket->so_flags; } return 0; } static void socket_set_errno(uint32_t err_code) { switch (err_code) { case UDP_INTERFACE_NOT_READY: // fallthrough case SOCKET_INTERFACE_NOT_READY: set_errno(ENETDOWN); break; case SOCKET_WOULD_BLOCK: set_errno(EAGAIN); break; case SOCKET_NO_ROUTE: set_errno(ENETUNREACH); break; case NRF_ERROR_NO_MEM: // fallthrough case SOCKET_NO_MEM: set_errno(ENOMEM); break; case SOCKET_TIMEOUT: set_errno(ETIMEDOUT); break; case SOCKET_NO_AVAILABLE_PORTS: set_errno(EMFILE); break; case SOCKET_PORT_IN_USE: // fallthrough case SOCKET_ADDRESS_IN_USE: set_errno(EADDRINUSE); break; case SOCKET_INVALID_PARAM: set_errno(EINVAL); break; case SOCKET_UNSUPPORTED_PROTOCOL: set_errno(EPROTONOSUPPORT); break; case SOCKET_NOT_CONNECTED: set_errno(ENOTCONN); break; } } int socket(socket_family_t family, socket_type_t type, socket_protocol_t protocol) { if (m_initialization_state == false) { (void) socket_init(); } VERIFY_MODULE_IS_INITIALIZED(); int ret_sock = -1; socket_t * p_socket = NULL; int sock = socket_allocate(&p_socket); SOCKET_TRACE("Got value %d from allocate", (int)sock); if (sock >= 0) { p_socket->so_params.so_family = family; p_socket->so_params.so_protocol = protocol; p_socket->so_params.so_type = type; p_socket->so_transport = NULL; if (family == AF_INET6) { #if SOCKET_TRANSPORT_ENABLE == 1 p_socket->so_transport = &transport_impl; #else set_errno(EAFNOSUPPORT); #endif } else if (family == AF_NRF_CFG || family == AF_NRF_CFG_INTERNAL) { p_socket->so_transport = &config_socket_transport; } else { set_errno(EAFNOSUPPORT); } if (p_socket->so_transport != NULL) { uint32_t err_code = p_socket->so_transport->open(p_socket); socket_set_errno(err_code); ret_sock = (err_code == NRF_SUCCESS) ? sock : ret_sock; } if (ret_sock < 0) { socket_free(sock); } } SOCKET_TRACE("Returning socket value %d", (int)ret_sock); return ret_sock; } static uint32_t wait_interface_up(void) { SOCKET_TRACE("Waiting for interface to come up"); uint32_t err_code = NRF_SUCCESS; while (err_code == NRF_SUCCESS && m_interface_up == false) { err_code = socket_wait(); } if (m_interface_up == true) { SOCKET_TRACE("Interface is up!"); } return err_code; } static uint32_t socket_interface_up(bool is_blocking) { uint32_t err_code = NRF_SUCCESS; if (m_interface_up == false) { if (is_blocking) { (void) wait_interface_up(); } } if (m_interface_up == false) { err_code = SOCKET_INTERFACE_NOT_READY; } return err_code; } int connect(int sock, const void * p_addr, socklen_t addrlen) { VERIFY_MODULE_IS_INITIALIZED(); VERIFY_SOCKET_ID(sock); NULL_PARAM_CHECK(p_addr); socket_t * p_socket = socket_find(sock); bool is_blocking = ((p_socket->so_flags & O_NONBLOCK) == 0); int ret = -1; uint32_t err_code = socket_interface_up(is_blocking); if (err_code != NRF_SUCCESS) { socket_set_errno(err_code); } else if (p_socket->so_state == STATE_OPEN) { err_code = p_socket->so_transport->connect(p_socket, p_addr, addrlen); if (err_code == NRF_SUCCESS) { p_socket->so_state = STATE_CONNECTED; ret = 0; } socket_set_errno(err_code); } else if (p_socket->so_state == STATE_CONNECTED) { set_errno(EISCONN); } else if (p_socket->so_state == STATE_CLOSED) { set_errno(EBADF); } return ret; } ssize_t sendto(int sock, const void * p_buf, size_t buflen, int flags, const void * p_servaddr, socklen_t addrlen) { VERIFY_MODULE_IS_INITIALIZED(); VERIFY_SOCKET_ID(sock); NULL_PARAM_CHECK(p_buf); socket_t * p_socket = socket_find(sock); if ((p_socket->so_flags & O_NONBLOCK) != 0 && (flags & MSG_WAITALL) == 0) { flags |= MSG_DONTWAIT; } uint32_t err_code = socket_interface_up(((p_socket->so_flags & O_NONBLOCK) == 0) || ((flags & MSG_DONTWAIT) == 0)); ssize_t ret = -1; if (err_code == NRF_SUCCESS) { err_code = p_socket->so_transport->send(p_socket, p_buf, buflen, flags, p_servaddr, addrlen); if (err_code == NRF_SUCCESS) { ret = (ssize_t) buflen; } } socket_set_errno(err_code); return ret; } ssize_t send(int sock, const void * p_buf, size_t buflen, int flags) { return sendto(sock, p_buf, buflen, flags, NULL, 0); } ssize_t write(int sock, const void * p_buf, size_t buflen) { return send(sock, p_buf, buflen, 0); } ssize_t recvfrom(int sock, void * p_buf, size_t buf_size, int flags, void * p_cliaddr, socklen_t * p_addrlen) { VERIFY_MODULE_IS_INITIALIZED(); VERIFY_SOCKET_ID(sock); NULL_PARAM_CHECK(p_buf); socket_t * p_socket = socket_find(sock); ssize_t ret = -1; uint32_t recv_size = buf_size; uint32_t err_code = p_socket->so_transport->recv(p_socket, p_buf, &recv_size, flags, p_cliaddr, p_addrlen); if (err_code == NRF_SUCCESS) { ret = (ssize_t) recv_size; } socket_set_errno(err_code); return ret; } ssize_t recv(int sock, void * p_buf, size_t buf_size, int flags) { return recvfrom(sock, p_buf, buf_size, flags, NULL, NULL); } ssize_t read(int sock, void * p_buf, size_t buf_size) { return recv(sock, p_buf, buf_size, 0); } int setsockopt(int sock, socket_opt_lvl_t level, int optname, const void * p_optval, socklen_t optlen) { VERIFY_MODULE_IS_INITIALIZED(); VERIFY_SOCKET_ID(sock); socket_t * p_socket = socket_find(sock); uint32_t err_code = p_socket->so_transport->setsockopt(p_socket, level, optname, p_optval, optlen); socket_set_errno(err_code); return (err_code == NRF_SUCCESS ? 0 : -1); } int getsockopt(int sock, socket_opt_lvl_t level, int optname, void * p_optval, socklen_t * p_optlen) { VERIFY_MODULE_IS_INITIALIZED(); VERIFY_SOCKET_ID(sock); socket_t * p_socket = socket_find(sock); uint32_t err_code = p_socket->so_transport->getsockopt(p_socket, level, optname, p_optval, p_optlen); socket_set_errno(err_code); return (err_code == NRF_SUCCESS ? 0 : -1); } int bind(int sock, const void * p_addr, socklen_t addrlen) { VERIFY_MODULE_IS_INITIALIZED(); VERIFY_SOCKET_ID(sock); NULL_PARAM_CHECK(p_addr); socket_t * p_socket = socket_find(sock); bool is_blocking = ((p_socket->so_flags & O_NONBLOCK) == 0); int ret = -1; uint32_t err_code = socket_interface_up(is_blocking); if (err_code == NRF_SUCCESS) { err_code = p_socket->so_transport->bind(p_socket, p_addr, addrlen); } if (err_code == NRF_SUCCESS) { ret = 0; } socket_set_errno(err_code); return ret; } int listen(int sock, int backlog) { VERIFY_MODULE_IS_INITIALIZED(); VERIFY_SOCKET_ID(sock); socket_t * p_socket = socket_find(sock); uint32_t err_code = p_socket->so_transport->listen(p_socket, backlog); return (err_code == NRF_SUCCESS ? 0 : -1); } int accept(int sock, void * p_cliaddr, socklen_t * p_addrlen) { VERIFY_MODULE_IS_INITIALIZED(); VERIFY_SOCKET_ID(sock); NULL_PARAM_CHECK(p_cliaddr); NULL_PARAM_CHECK(p_addrlen); socket_t * p_socket = socket_find(sock); int ret = -1; if (p_socket->so_params.so_type != SOCK_STREAM) { set_errno(EOPNOTSUPP); } else { uint32_t err_code = NRF_SUCCESS; socket_t * p_client = NULL; int sock_cli = socket_allocate(&p_client); if (sock_cli >= 0) { p_client->so_params = p_socket->so_params; p_client->so_state = STATE_CONNECTED; p_client->so_transport = p_socket->so_transport; err_code = p_socket->so_transport->accept(p_socket, p_client, p_cliaddr, p_addrlen); } if (err_code == NRF_SUCCESS) { ret = sock_cli; } else { socket_set_errno(err_code); socket_free(sock_cli); } } return ret; } int close(int sock) { VERIFY_MODULE_IS_INITIALIZED(); VERIFY_SOCKET_ID(sock); socket_t * p_socket = socket_find(sock); int ret = 0; if (p_socket->so_state != STATE_CLOSED) { uint32_t err_code = p_socket->so_transport->close(p_socket); ret = (err_code == NRF_SUCCESS) ? 0 : -1; SOCKET_TRACE("Close socket %d: ret: %d", (int)sock, ret); socket_free(sock); } return ret; } int fd_set_cmp(fd_set * set_a, fd_set * set_b) { int ret = 0; if (set_a != NULL && set_b != NULL) { for (uint32_t i = 0; i < FD_SETSIZE; i++) { if (FD_ISSET(i, set_a) != FD_ISSET(i, set_b)) { ret = 1; break; } } } return ret; } int select(int nfds, fd_set * p_readset, fd_set * p_writeset, fd_set * p_exceptset, const struct timeval * p_timeout) { VERIFY_SOCKET_ID(nfds - 1); // Approximately 10 ms sleep between each iteration uint32_t timestep = 10000; uint32_t endtime = 0; if (p_timeout != NULL) { endtime = (p_timeout->tv_sec * 1000000) + p_timeout->tv_usec; } fd_set readset; FD_ZERO(&readset); fd_set writeset; FD_ZERO(&writeset); fd_set exceptset; FD_ZERO(&exceptset); #define SELECT_CHECK_SET(in_set, out_set, evt_var) \ if ((in_set) != NULL) \ { \ if (FD_ISSET(sock, (in_set)) && (evt_var) > 0) \ { \ FD_SET(sock, (out_set)); \ num_ready++; \ } \ else \ { \ FD_CLR(sock, (out_set)); \ } \ } int num_ready = 0; uint32_t err_code = NRF_SUCCESS; while (err_code == NRF_SUCCESS) { for (int sock = 0; sock < nfds; sock++) { socket_t * p_socket = socket_find(sock); SELECT_CHECK_SET(p_readset, &readset, p_socket->so_read_evt); SELECT_CHECK_SET(p_writeset, &writeset, p_socket->so_write_evt); SELECT_CHECK_SET(p_exceptset, &exceptset, p_socket->so_except_evt); } // TODO: Check out how app events queue up while we checked the socket if (fd_set_cmp(p_readset, &readset) == 0 && fd_set_cmp(p_writeset, &writeset) == 0 && fd_set_cmp(p_exceptset, &exceptset) == 0) { break; } else { if (p_timeout == NULL) { err_code = socket_wait(); } else if (endtime - timestep < endtime) { (void) usleep(timestep); endtime -= timestep; } else { break; } } } return num_ready; }