1*5d5fbe79SDavid van Moolenbroek /**
2*5d5fbe79SDavid van Moolenbroek * @file
3*5d5fbe79SDavid van Moolenbroek * Sockets BSD-Like API module
4*5d5fbe79SDavid van Moolenbroek *
5*5d5fbe79SDavid van Moolenbroek * @defgroup socket Socket API
6*5d5fbe79SDavid van Moolenbroek * @ingroup sequential_api
7*5d5fbe79SDavid van Moolenbroek * BSD-style socket API.\n
8*5d5fbe79SDavid van Moolenbroek * Thread-safe, to be called from non-TCPIP threads only.\n
9*5d5fbe79SDavid van Moolenbroek * Can be activated by defining @ref LWIP_SOCKET to 1.\n
10*5d5fbe79SDavid van Moolenbroek * Header is in posix/sys/socket.h\b
11*5d5fbe79SDavid van Moolenbroek */
12*5d5fbe79SDavid van Moolenbroek
13*5d5fbe79SDavid van Moolenbroek /*
14*5d5fbe79SDavid van Moolenbroek * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
15*5d5fbe79SDavid van Moolenbroek * All rights reserved.
16*5d5fbe79SDavid van Moolenbroek *
17*5d5fbe79SDavid van Moolenbroek * Redistribution and use in source and binary forms, with or without modification,
18*5d5fbe79SDavid van Moolenbroek * are permitted provided that the following conditions are met:
19*5d5fbe79SDavid van Moolenbroek *
20*5d5fbe79SDavid van Moolenbroek * 1. Redistributions of source code must retain the above copyright notice,
21*5d5fbe79SDavid van Moolenbroek * this list of conditions and the following disclaimer.
22*5d5fbe79SDavid van Moolenbroek * 2. Redistributions in binary form must reproduce the above copyright notice,
23*5d5fbe79SDavid van Moolenbroek * this list of conditions and the following disclaimer in the documentation
24*5d5fbe79SDavid van Moolenbroek * and/or other materials provided with the distribution.
25*5d5fbe79SDavid van Moolenbroek * 3. The name of the author may not be used to endorse or promote products
26*5d5fbe79SDavid van Moolenbroek * derived from this software without specific prior written permission.
27*5d5fbe79SDavid van Moolenbroek *
28*5d5fbe79SDavid van Moolenbroek * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
29*5d5fbe79SDavid van Moolenbroek * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
30*5d5fbe79SDavid van Moolenbroek * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
31*5d5fbe79SDavid van Moolenbroek * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
32*5d5fbe79SDavid van Moolenbroek * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
33*5d5fbe79SDavid van Moolenbroek * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
34*5d5fbe79SDavid van Moolenbroek * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
35*5d5fbe79SDavid van Moolenbroek * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
36*5d5fbe79SDavid van Moolenbroek * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
37*5d5fbe79SDavid van Moolenbroek * OF SUCH DAMAGE.
38*5d5fbe79SDavid van Moolenbroek *
39*5d5fbe79SDavid van Moolenbroek * This file is part of the lwIP TCP/IP stack.
40*5d5fbe79SDavid van Moolenbroek *
41*5d5fbe79SDavid van Moolenbroek * Author: Adam Dunkels <adam@sics.se>
42*5d5fbe79SDavid van Moolenbroek *
43*5d5fbe79SDavid van Moolenbroek * Improved by Marc Boucher <marc@mbsi.ca> and David Haas <dhaas@alum.rpi.edu>
44*5d5fbe79SDavid van Moolenbroek *
45*5d5fbe79SDavid van Moolenbroek */
46*5d5fbe79SDavid van Moolenbroek
47*5d5fbe79SDavid van Moolenbroek #include "lwip/opt.h"
48*5d5fbe79SDavid van Moolenbroek
49*5d5fbe79SDavid van Moolenbroek #if LWIP_SOCKET /* don't build if not configured for use in lwipopts.h */
50*5d5fbe79SDavid van Moolenbroek
51*5d5fbe79SDavid van Moolenbroek #include "lwip/sockets.h"
52*5d5fbe79SDavid van Moolenbroek #include "lwip/priv/sockets_priv.h"
53*5d5fbe79SDavid van Moolenbroek #include "lwip/api.h"
54*5d5fbe79SDavid van Moolenbroek #include "lwip/sys.h"
55*5d5fbe79SDavid van Moolenbroek #include "lwip/igmp.h"
56*5d5fbe79SDavid van Moolenbroek #include "lwip/inet.h"
57*5d5fbe79SDavid van Moolenbroek #include "lwip/tcp.h"
58*5d5fbe79SDavid van Moolenbroek #include "lwip/raw.h"
59*5d5fbe79SDavid van Moolenbroek #include "lwip/udp.h"
60*5d5fbe79SDavid van Moolenbroek #include "lwip/memp.h"
61*5d5fbe79SDavid van Moolenbroek #include "lwip/pbuf.h"
62*5d5fbe79SDavid van Moolenbroek #include "lwip/priv/tcpip_priv.h"
63*5d5fbe79SDavid van Moolenbroek #if LWIP_CHECKSUM_ON_COPY
64*5d5fbe79SDavid van Moolenbroek #include "lwip/inet_chksum.h"
65*5d5fbe79SDavid van Moolenbroek #endif
66*5d5fbe79SDavid van Moolenbroek
67*5d5fbe79SDavid van Moolenbroek #include <string.h>
68*5d5fbe79SDavid van Moolenbroek
69*5d5fbe79SDavid van Moolenbroek /* If the netconn API is not required publicly, then we include the necessary
70*5d5fbe79SDavid van Moolenbroek files here to get the implementation */
71*5d5fbe79SDavid van Moolenbroek #if !LWIP_NETCONN
72*5d5fbe79SDavid van Moolenbroek #undef LWIP_NETCONN
73*5d5fbe79SDavid van Moolenbroek #define LWIP_NETCONN 1
74*5d5fbe79SDavid van Moolenbroek #include "api_msg.c"
75*5d5fbe79SDavid van Moolenbroek #include "api_lib.c"
76*5d5fbe79SDavid van Moolenbroek #include "netbuf.c"
77*5d5fbe79SDavid van Moolenbroek #undef LWIP_NETCONN
78*5d5fbe79SDavid van Moolenbroek #define LWIP_NETCONN 0
79*5d5fbe79SDavid van Moolenbroek #endif
80*5d5fbe79SDavid van Moolenbroek
81*5d5fbe79SDavid van Moolenbroek #if LWIP_IPV4
82*5d5fbe79SDavid van Moolenbroek #define IP4ADDR_PORT_TO_SOCKADDR(sin, ipaddr, port) do { \
83*5d5fbe79SDavid van Moolenbroek (sin)->sin_len = sizeof(struct sockaddr_in); \
84*5d5fbe79SDavid van Moolenbroek (sin)->sin_family = AF_INET; \
85*5d5fbe79SDavid van Moolenbroek (sin)->sin_port = lwip_htons((port)); \
86*5d5fbe79SDavid van Moolenbroek inet_addr_from_ip4addr(&(sin)->sin_addr, ipaddr); \
87*5d5fbe79SDavid van Moolenbroek memset((sin)->sin_zero, 0, SIN_ZERO_LEN); }while(0)
88*5d5fbe79SDavid van Moolenbroek #define SOCKADDR4_TO_IP4ADDR_PORT(sin, ipaddr, port) do { \
89*5d5fbe79SDavid van Moolenbroek inet_addr_to_ip4addr(ip_2_ip4(ipaddr), &((sin)->sin_addr)); \
90*5d5fbe79SDavid van Moolenbroek (port) = lwip_ntohs((sin)->sin_port); }while(0)
91*5d5fbe79SDavid van Moolenbroek #endif /* LWIP_IPV4 */
92*5d5fbe79SDavid van Moolenbroek
93*5d5fbe79SDavid van Moolenbroek #if LWIP_IPV6
94*5d5fbe79SDavid van Moolenbroek #define IP6ADDR_PORT_TO_SOCKADDR(sin6, ipaddr, port) do { \
95*5d5fbe79SDavid van Moolenbroek (sin6)->sin6_len = sizeof(struct sockaddr_in6); \
96*5d5fbe79SDavid van Moolenbroek (sin6)->sin6_family = AF_INET6; \
97*5d5fbe79SDavid van Moolenbroek (sin6)->sin6_port = lwip_htons((port)); \
98*5d5fbe79SDavid van Moolenbroek (sin6)->sin6_flowinfo = 0; \
99*5d5fbe79SDavid van Moolenbroek inet6_addr_from_ip6addr(&(sin6)->sin6_addr, ipaddr); \
100*5d5fbe79SDavid van Moolenbroek (sin6)->sin6_scope_id = ip6_addr_zone(ipaddr); }while(0)
101*5d5fbe79SDavid van Moolenbroek #define SOCKADDR6_TO_IP6ADDR_PORT(sin6, ipaddr, port) do { \
102*5d5fbe79SDavid van Moolenbroek inet6_addr_to_ip6addr(ip_2_ip6(ipaddr), &((sin6)->sin6_addr)); \
103*5d5fbe79SDavid van Moolenbroek if (ip6_addr_has_scope(ip_2_ip6(ipaddr), IP6_UNKNOWN)) { \
104*5d5fbe79SDavid van Moolenbroek ip6_addr_set_zone(ip_2_ip6(ipaddr), (u8_t)((sin6)->sin6_scope_id)); \
105*5d5fbe79SDavid van Moolenbroek } \
106*5d5fbe79SDavid van Moolenbroek (port) = lwip_ntohs((sin6)->sin6_port); }while(0)
107*5d5fbe79SDavid van Moolenbroek #endif /* LWIP_IPV6 */
108*5d5fbe79SDavid van Moolenbroek
109*5d5fbe79SDavid van Moolenbroek #if LWIP_IPV4 && LWIP_IPV6
110*5d5fbe79SDavid van Moolenbroek static void sockaddr_to_ipaddr_port(const struct sockaddr* sockaddr, ip_addr_t* ipaddr, u16_t* port);
111*5d5fbe79SDavid van Moolenbroek
112*5d5fbe79SDavid van Moolenbroek #define IS_SOCK_ADDR_LEN_VALID(namelen) (((namelen) == sizeof(struct sockaddr_in)) || \
113*5d5fbe79SDavid van Moolenbroek ((namelen) == sizeof(struct sockaddr_in6)))
114*5d5fbe79SDavid van Moolenbroek #define IS_SOCK_ADDR_TYPE_VALID(name) (((name)->sa_family == AF_INET) || \
115*5d5fbe79SDavid van Moolenbroek ((name)->sa_family == AF_INET6))
116*5d5fbe79SDavid van Moolenbroek #define SOCK_ADDR_TYPE_MATCH(name, sock) \
117*5d5fbe79SDavid van Moolenbroek ((((name)->sa_family == AF_INET) && !(NETCONNTYPE_ISIPV6((sock)->conn->type))) || \
118*5d5fbe79SDavid van Moolenbroek (((name)->sa_family == AF_INET6) && (NETCONNTYPE_ISIPV6((sock)->conn->type))))
119*5d5fbe79SDavid van Moolenbroek #define IPADDR_PORT_TO_SOCKADDR(sockaddr, ipaddr, port) do { \
120*5d5fbe79SDavid van Moolenbroek if (IP_IS_V6(ipaddr)) { \
121*5d5fbe79SDavid van Moolenbroek IP6ADDR_PORT_TO_SOCKADDR((struct sockaddr_in6*)(void*)(sockaddr), ip_2_ip6(ipaddr), port); \
122*5d5fbe79SDavid van Moolenbroek } else { \
123*5d5fbe79SDavid van Moolenbroek IP4ADDR_PORT_TO_SOCKADDR((struct sockaddr_in*)(void*)(sockaddr), ip_2_ip4(ipaddr), port); \
124*5d5fbe79SDavid van Moolenbroek } } while(0)
125*5d5fbe79SDavid van Moolenbroek #define SOCKADDR_TO_IPADDR_PORT(sockaddr, ipaddr, port) sockaddr_to_ipaddr_port(sockaddr, ipaddr, &(port))
126*5d5fbe79SDavid van Moolenbroek #define DOMAIN_TO_NETCONN_TYPE(domain, type) (((domain) == AF_INET) ? \
127*5d5fbe79SDavid van Moolenbroek (type) : (enum netconn_type)((type) | NETCONN_TYPE_IPV6))
128*5d5fbe79SDavid van Moolenbroek #elif LWIP_IPV6 /* LWIP_IPV4 && LWIP_IPV6 */
129*5d5fbe79SDavid van Moolenbroek #define IS_SOCK_ADDR_LEN_VALID(namelen) ((namelen) == sizeof(struct sockaddr_in6))
130*5d5fbe79SDavid van Moolenbroek #define IS_SOCK_ADDR_TYPE_VALID(name) ((name)->sa_family == AF_INET6)
131*5d5fbe79SDavid van Moolenbroek #define SOCK_ADDR_TYPE_MATCH(name, sock) 1
132*5d5fbe79SDavid van Moolenbroek #define IPADDR_PORT_TO_SOCKADDR(sockaddr, ipaddr, port) \
133*5d5fbe79SDavid van Moolenbroek IP6ADDR_PORT_TO_SOCKADDR((struct sockaddr_in6*)(void*)(sockaddr), ip_2_ip6(ipaddr), port)
134*5d5fbe79SDavid van Moolenbroek #define SOCKADDR_TO_IPADDR_PORT(sockaddr, ipaddr, port) \
135*5d5fbe79SDavid van Moolenbroek SOCKADDR6_TO_IP6ADDR_PORT((const struct sockaddr_in6*)(const void*)(sockaddr), ipaddr, port)
136*5d5fbe79SDavid van Moolenbroek #define DOMAIN_TO_NETCONN_TYPE(domain, netconn_type) (netconn_type)
137*5d5fbe79SDavid van Moolenbroek #else /*-> LWIP_IPV4: LWIP_IPV4 && LWIP_IPV6 */
138*5d5fbe79SDavid van Moolenbroek #define IS_SOCK_ADDR_LEN_VALID(namelen) ((namelen) == sizeof(struct sockaddr_in))
139*5d5fbe79SDavid van Moolenbroek #define IS_SOCK_ADDR_TYPE_VALID(name) ((name)->sa_family == AF_INET)
140*5d5fbe79SDavid van Moolenbroek #define SOCK_ADDR_TYPE_MATCH(name, sock) 1
141*5d5fbe79SDavid van Moolenbroek #define IPADDR_PORT_TO_SOCKADDR(sockaddr, ipaddr, port) \
142*5d5fbe79SDavid van Moolenbroek IP4ADDR_PORT_TO_SOCKADDR((struct sockaddr_in*)(void*)(sockaddr), ip_2_ip4(ipaddr), port)
143*5d5fbe79SDavid van Moolenbroek #define SOCKADDR_TO_IPADDR_PORT(sockaddr, ipaddr, port) \
144*5d5fbe79SDavid van Moolenbroek SOCKADDR4_TO_IP4ADDR_PORT((const struct sockaddr_in*)(const void*)(sockaddr), ipaddr, port)
145*5d5fbe79SDavid van Moolenbroek #define DOMAIN_TO_NETCONN_TYPE(domain, netconn_type) (netconn_type)
146*5d5fbe79SDavid van Moolenbroek #endif /* LWIP_IPV6 */
147*5d5fbe79SDavid van Moolenbroek
148*5d5fbe79SDavid van Moolenbroek #define IS_SOCK_ADDR_TYPE_VALID_OR_UNSPEC(name) (((name)->sa_family == AF_UNSPEC) || \
149*5d5fbe79SDavid van Moolenbroek IS_SOCK_ADDR_TYPE_VALID(name))
150*5d5fbe79SDavid van Moolenbroek #define SOCK_ADDR_TYPE_MATCH_OR_UNSPEC(name, sock) (((name)->sa_family == AF_UNSPEC) || \
151*5d5fbe79SDavid van Moolenbroek SOCK_ADDR_TYPE_MATCH(name, sock))
152*5d5fbe79SDavid van Moolenbroek #define IS_SOCK_ADDR_ALIGNED(name) ((((mem_ptr_t)(name)) % 4) == 0)
153*5d5fbe79SDavid van Moolenbroek
154*5d5fbe79SDavid van Moolenbroek
155*5d5fbe79SDavid van Moolenbroek #define LWIP_SOCKOPT_CHECK_OPTLEN(optlen, opttype) do { if ((optlen) < sizeof(opttype)) { return EINVAL; }}while(0)
156*5d5fbe79SDavid van Moolenbroek #define LWIP_SOCKOPT_CHECK_OPTLEN_CONN(sock, optlen, opttype) do { \
157*5d5fbe79SDavid van Moolenbroek LWIP_SOCKOPT_CHECK_OPTLEN(optlen, opttype); \
158*5d5fbe79SDavid van Moolenbroek if ((sock)->conn == NULL) { return EINVAL; } }while(0)
159*5d5fbe79SDavid van Moolenbroek #define LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, optlen, opttype) do { \
160*5d5fbe79SDavid van Moolenbroek LWIP_SOCKOPT_CHECK_OPTLEN(optlen, opttype); \
161*5d5fbe79SDavid van Moolenbroek if (((sock)->conn == NULL) || ((sock)->conn->pcb.tcp == NULL)) { return EINVAL; } }while(0)
162*5d5fbe79SDavid van Moolenbroek #define LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, opttype, netconntype) do { \
163*5d5fbe79SDavid van Moolenbroek LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, optlen, opttype); \
164*5d5fbe79SDavid van Moolenbroek if (NETCONNTYPE_GROUP(netconn_type((sock)->conn)) != netconntype) { return ENOPROTOOPT; } }while(0)
165*5d5fbe79SDavid van Moolenbroek
166*5d5fbe79SDavid van Moolenbroek
167*5d5fbe79SDavid van Moolenbroek #define LWIP_SETGETSOCKOPT_DATA_VAR_REF(name) API_VAR_REF(name)
168*5d5fbe79SDavid van Moolenbroek #define LWIP_SETGETSOCKOPT_DATA_VAR_DECLARE(name) API_VAR_DECLARE(struct lwip_setgetsockopt_data, name)
169*5d5fbe79SDavid van Moolenbroek #define LWIP_SETGETSOCKOPT_DATA_VAR_FREE(name) API_VAR_FREE(MEMP_SOCKET_SETGETSOCKOPT_DATA, name)
170*5d5fbe79SDavid van Moolenbroek #if LWIP_MPU_COMPATIBLE
171*5d5fbe79SDavid van Moolenbroek #define LWIP_SETGETSOCKOPT_DATA_VAR_ALLOC(name, sock) do { \
172*5d5fbe79SDavid van Moolenbroek name = (struct lwip_setgetsockopt_data *)memp_malloc(MEMP_SOCKET_SETGETSOCKOPT_DATA); \
173*5d5fbe79SDavid van Moolenbroek if (name == NULL) { \
174*5d5fbe79SDavid van Moolenbroek sock_set_errno(sock, ENOMEM); \
175*5d5fbe79SDavid van Moolenbroek return -1; \
176*5d5fbe79SDavid van Moolenbroek } }while(0)
177*5d5fbe79SDavid van Moolenbroek #else /* LWIP_MPU_COMPATIBLE */
178*5d5fbe79SDavid van Moolenbroek #define LWIP_SETGETSOCKOPT_DATA_VAR_ALLOC(name, sock)
179*5d5fbe79SDavid van Moolenbroek #endif /* LWIP_MPU_COMPATIBLE */
180*5d5fbe79SDavid van Moolenbroek
181*5d5fbe79SDavid van Moolenbroek #if LWIP_SO_SNDRCVTIMEO_NONSTANDARD
182*5d5fbe79SDavid van Moolenbroek #define LWIP_SO_SNDRCVTIMEO_OPTTYPE int
183*5d5fbe79SDavid van Moolenbroek #define LWIP_SO_SNDRCVTIMEO_SET(optval, val) (*(int *)(optval) = (val))
184*5d5fbe79SDavid van Moolenbroek #define LWIP_SO_SNDRCVTIMEO_GET_MS(optval) ((s32_t)*(const int*)(optval))
185*5d5fbe79SDavid van Moolenbroek #else
186*5d5fbe79SDavid van Moolenbroek #define LWIP_SO_SNDRCVTIMEO_OPTTYPE struct timeval
187*5d5fbe79SDavid van Moolenbroek #define LWIP_SO_SNDRCVTIMEO_SET(optval, val) do { \
188*5d5fbe79SDavid van Moolenbroek s32_t loc = (val); \
189*5d5fbe79SDavid van Moolenbroek ((struct timeval *)(optval))->tv_sec = (loc) / 1000U; \
190*5d5fbe79SDavid van Moolenbroek ((struct timeval *)(optval))->tv_usec = ((loc) % 1000U) * 1000U; }while(0)
191*5d5fbe79SDavid van Moolenbroek #define LWIP_SO_SNDRCVTIMEO_GET_MS(optval) ((((const struct timeval *)(optval))->tv_sec * 1000U) + (((const struct timeval *)(optval))->tv_usec / 1000U))
192*5d5fbe79SDavid van Moolenbroek #endif
193*5d5fbe79SDavid van Moolenbroek
194*5d5fbe79SDavid van Moolenbroek #define NUM_SOCKETS MEMP_NUM_NETCONN
195*5d5fbe79SDavid van Moolenbroek
196*5d5fbe79SDavid van Moolenbroek /** This is overridable for the rare case where more than 255 threads
197*5d5fbe79SDavid van Moolenbroek * select on the same socket...
198*5d5fbe79SDavid van Moolenbroek */
199*5d5fbe79SDavid van Moolenbroek #ifndef SELWAIT_T
200*5d5fbe79SDavid van Moolenbroek #define SELWAIT_T u8_t
201*5d5fbe79SDavid van Moolenbroek #endif
202*5d5fbe79SDavid van Moolenbroek
203*5d5fbe79SDavid van Moolenbroek union lwip_sock_lastdata {
204*5d5fbe79SDavid van Moolenbroek struct netbuf *netbuf;
205*5d5fbe79SDavid van Moolenbroek struct pbuf *pbuf;
206*5d5fbe79SDavid van Moolenbroek };
207*5d5fbe79SDavid van Moolenbroek
208*5d5fbe79SDavid van Moolenbroek /** Contains all internal pointers and states used for a socket */
209*5d5fbe79SDavid van Moolenbroek struct lwip_sock {
210*5d5fbe79SDavid van Moolenbroek /** sockets currently are built on netconns, each socket has one netconn */
211*5d5fbe79SDavid van Moolenbroek struct netconn *conn;
212*5d5fbe79SDavid van Moolenbroek /** last error that occurred on this socket */
213*5d5fbe79SDavid van Moolenbroek int err;
214*5d5fbe79SDavid van Moolenbroek /** data that was left from the previous read */
215*5d5fbe79SDavid van Moolenbroek union lwip_sock_lastdata lastdata;
216*5d5fbe79SDavid van Moolenbroek #if LWIP_SOCKET_SELECT
217*5d5fbe79SDavid van Moolenbroek /** number of times data was received, set by event_callback(),
218*5d5fbe79SDavid van Moolenbroek tested by the receive and select functions */
219*5d5fbe79SDavid van Moolenbroek s16_t rcvevent;
220*5d5fbe79SDavid van Moolenbroek /** number of times data was ACKed (free send buffer), set by event_callback(),
221*5d5fbe79SDavid van Moolenbroek tested by select */
222*5d5fbe79SDavid van Moolenbroek u16_t sendevent;
223*5d5fbe79SDavid van Moolenbroek /** error happened for this socket, set by event_callback(), tested by select */
224*5d5fbe79SDavid van Moolenbroek u16_t errevent;
225*5d5fbe79SDavid van Moolenbroek /** counter of how many threads are waiting for this socket using select */
226*5d5fbe79SDavid van Moolenbroek SELWAIT_T select_waiting;
227*5d5fbe79SDavid van Moolenbroek #endif /* LWIP_SOCKET_SELECT */
228*5d5fbe79SDavid van Moolenbroek #if LWIP_NETCONN_FULLDUPLEX
229*5d5fbe79SDavid van Moolenbroek /* counter of how many threads are using a struct lwip_sock (not the 'int') */
230*5d5fbe79SDavid van Moolenbroek u8_t fd_used;
231*5d5fbe79SDavid van Moolenbroek /* status of pending close/delete actions */
232*5d5fbe79SDavid van Moolenbroek u8_t fd_free_pending;
233*5d5fbe79SDavid van Moolenbroek #define LWIP_SOCK_FD_FREE_TCP 1
234*5d5fbe79SDavid van Moolenbroek #define LWIP_SOCK_FD_FREE_FREE 2
235*5d5fbe79SDavid van Moolenbroek #endif
236*5d5fbe79SDavid van Moolenbroek };
237*5d5fbe79SDavid van Moolenbroek
238*5d5fbe79SDavid van Moolenbroek #if LWIP_NETCONN_SEM_PER_THREAD
239*5d5fbe79SDavid van Moolenbroek #define SELECT_SEM_T sys_sem_t*
240*5d5fbe79SDavid van Moolenbroek #define SELECT_SEM_PTR(sem) (sem)
241*5d5fbe79SDavid van Moolenbroek #else /* LWIP_NETCONN_SEM_PER_THREAD */
242*5d5fbe79SDavid van Moolenbroek #define SELECT_SEM_T sys_sem_t
243*5d5fbe79SDavid van Moolenbroek #define SELECT_SEM_PTR(sem) (&(sem))
244*5d5fbe79SDavid van Moolenbroek #endif /* LWIP_NETCONN_SEM_PER_THREAD */
245*5d5fbe79SDavid van Moolenbroek
246*5d5fbe79SDavid van Moolenbroek /** Description for a task waiting in select */
247*5d5fbe79SDavid van Moolenbroek struct lwip_select_cb {
248*5d5fbe79SDavid van Moolenbroek /** Pointer to the next waiting task */
249*5d5fbe79SDavid van Moolenbroek struct lwip_select_cb *next;
250*5d5fbe79SDavid van Moolenbroek /** Pointer to the previous waiting task */
251*5d5fbe79SDavid van Moolenbroek struct lwip_select_cb *prev;
252*5d5fbe79SDavid van Moolenbroek /** readset passed to select */
253*5d5fbe79SDavid van Moolenbroek fd_set *readset;
254*5d5fbe79SDavid van Moolenbroek /** writeset passed to select */
255*5d5fbe79SDavid van Moolenbroek fd_set *writeset;
256*5d5fbe79SDavid van Moolenbroek /** unimplemented: exceptset passed to select */
257*5d5fbe79SDavid van Moolenbroek fd_set *exceptset;
258*5d5fbe79SDavid van Moolenbroek /** don't signal the same semaphore twice: set to 1 when signalled */
259*5d5fbe79SDavid van Moolenbroek int sem_signalled;
260*5d5fbe79SDavid van Moolenbroek /** semaphore to wake up a task waiting for select */
261*5d5fbe79SDavid van Moolenbroek SELECT_SEM_T sem;
262*5d5fbe79SDavid van Moolenbroek };
263*5d5fbe79SDavid van Moolenbroek
264*5d5fbe79SDavid van Moolenbroek /** A struct sockaddr replacement that has the same alignment as sockaddr_in/
265*5d5fbe79SDavid van Moolenbroek * sockaddr_in6 if instantiated.
266*5d5fbe79SDavid van Moolenbroek */
267*5d5fbe79SDavid van Moolenbroek union sockaddr_aligned {
268*5d5fbe79SDavid van Moolenbroek struct sockaddr sa;
269*5d5fbe79SDavid van Moolenbroek #if LWIP_IPV6
270*5d5fbe79SDavid van Moolenbroek struct sockaddr_in6 sin6;
271*5d5fbe79SDavid van Moolenbroek #endif /* LWIP_IPV6 */
272*5d5fbe79SDavid van Moolenbroek #if LWIP_IPV4
273*5d5fbe79SDavid van Moolenbroek struct sockaddr_in sin;
274*5d5fbe79SDavid van Moolenbroek #endif /* LWIP_IPV4 */
275*5d5fbe79SDavid van Moolenbroek };
276*5d5fbe79SDavid van Moolenbroek
277*5d5fbe79SDavid van Moolenbroek #if LWIP_IGMP
278*5d5fbe79SDavid van Moolenbroek /* Define the number of IPv4 multicast memberships, default is one per socket */
279*5d5fbe79SDavid van Moolenbroek #ifndef LWIP_SOCKET_MAX_MEMBERSHIPS
280*5d5fbe79SDavid van Moolenbroek #define LWIP_SOCKET_MAX_MEMBERSHIPS NUM_SOCKETS
281*5d5fbe79SDavid van Moolenbroek #endif
282*5d5fbe79SDavid van Moolenbroek
283*5d5fbe79SDavid van Moolenbroek /* This is to keep track of IP_ADD_MEMBERSHIP calls to drop the membership when
284*5d5fbe79SDavid van Moolenbroek a socket is closed */
285*5d5fbe79SDavid van Moolenbroek struct lwip_socket_multicast_pair {
286*5d5fbe79SDavid van Moolenbroek /** the socket */
287*5d5fbe79SDavid van Moolenbroek struct lwip_sock* sock;
288*5d5fbe79SDavid van Moolenbroek /** the interface address */
289*5d5fbe79SDavid van Moolenbroek ip4_addr_t if_addr;
290*5d5fbe79SDavid van Moolenbroek /** the group address */
291*5d5fbe79SDavid van Moolenbroek ip4_addr_t multi_addr;
292*5d5fbe79SDavid van Moolenbroek };
293*5d5fbe79SDavid van Moolenbroek
294*5d5fbe79SDavid van Moolenbroek struct lwip_socket_multicast_pair socket_ipv4_multicast_memberships[LWIP_SOCKET_MAX_MEMBERSHIPS];
295*5d5fbe79SDavid van Moolenbroek
296*5d5fbe79SDavid van Moolenbroek static int lwip_socket_register_membership(int s, const ip4_addr_t *if_addr, const ip4_addr_t *multi_addr);
297*5d5fbe79SDavid van Moolenbroek static void lwip_socket_unregister_membership(int s, const ip4_addr_t *if_addr, const ip4_addr_t *multi_addr);
298*5d5fbe79SDavid van Moolenbroek static void lwip_socket_drop_registered_memberships(int s);
299*5d5fbe79SDavid van Moolenbroek #endif /* LWIP_IGMP */
300*5d5fbe79SDavid van Moolenbroek
301*5d5fbe79SDavid van Moolenbroek /** The global array of available sockets */
302*5d5fbe79SDavid van Moolenbroek static struct lwip_sock sockets[NUM_SOCKETS];
303*5d5fbe79SDavid van Moolenbroek /** The global list of tasks waiting for select */
304*5d5fbe79SDavid van Moolenbroek static struct lwip_select_cb *select_cb_list;
305*5d5fbe79SDavid van Moolenbroek /** This counter is increased from lwip_select when the list is changed
306*5d5fbe79SDavid van Moolenbroek and checked in event_callback to see if it has changed. */
307*5d5fbe79SDavid van Moolenbroek static volatile int select_cb_ctr;
308*5d5fbe79SDavid van Moolenbroek
309*5d5fbe79SDavid van Moolenbroek #define sock_set_errno(sk, e) do { \
310*5d5fbe79SDavid van Moolenbroek const int sockerr = (e); \
311*5d5fbe79SDavid van Moolenbroek sk->err = sockerr; \
312*5d5fbe79SDavid van Moolenbroek set_errno(sockerr); \
313*5d5fbe79SDavid van Moolenbroek } while (0)
314*5d5fbe79SDavid van Moolenbroek
315*5d5fbe79SDavid van Moolenbroek /* Forward declaration of some functions */
316*5d5fbe79SDavid van Moolenbroek #if LWIP_SOCKET_SELECT
317*5d5fbe79SDavid van Moolenbroek static void event_callback(struct netconn *conn, enum netconn_evt evt, u16_t len);
318*5d5fbe79SDavid van Moolenbroek #define DEFAULT_SOCKET_EVENTCB event_callback
319*5d5fbe79SDavid van Moolenbroek #else
320*5d5fbe79SDavid van Moolenbroek #define DEFAULT_SOCKET_EVENTCB NULL
321*5d5fbe79SDavid van Moolenbroek #endif
322*5d5fbe79SDavid van Moolenbroek #if !LWIP_TCPIP_CORE_LOCKING
323*5d5fbe79SDavid van Moolenbroek static void lwip_getsockopt_callback(void *arg);
324*5d5fbe79SDavid van Moolenbroek static void lwip_setsockopt_callback(void *arg);
325*5d5fbe79SDavid van Moolenbroek #endif
326*5d5fbe79SDavid van Moolenbroek static int lwip_getsockopt_impl(int s, int level, int optname, void *optval, socklen_t *optlen);
327*5d5fbe79SDavid van Moolenbroek static int lwip_setsockopt_impl(int s, int level, int optname, const void *optval, socklen_t optlen);
328*5d5fbe79SDavid van Moolenbroek static void free_socket(struct lwip_sock *sock, int is_tcp);
329*5d5fbe79SDavid van Moolenbroek
330*5d5fbe79SDavid van Moolenbroek #if LWIP_IPV4 && LWIP_IPV6
331*5d5fbe79SDavid van Moolenbroek static void
sockaddr_to_ipaddr_port(const struct sockaddr * sockaddr,ip_addr_t * ipaddr,u16_t * port)332*5d5fbe79SDavid van Moolenbroek sockaddr_to_ipaddr_port(const struct sockaddr* sockaddr, ip_addr_t* ipaddr, u16_t* port)
333*5d5fbe79SDavid van Moolenbroek {
334*5d5fbe79SDavid van Moolenbroek if ((sockaddr->sa_family) == AF_INET6) {
335*5d5fbe79SDavid van Moolenbroek SOCKADDR6_TO_IP6ADDR_PORT((const struct sockaddr_in6*)(const void*)(sockaddr), ipaddr, *port);
336*5d5fbe79SDavid van Moolenbroek ipaddr->type = IPADDR_TYPE_V6;
337*5d5fbe79SDavid van Moolenbroek } else {
338*5d5fbe79SDavid van Moolenbroek SOCKADDR4_TO_IP4ADDR_PORT((const struct sockaddr_in*)(const void*)(sockaddr), ipaddr, *port);
339*5d5fbe79SDavid van Moolenbroek ipaddr->type = IPADDR_TYPE_V4;
340*5d5fbe79SDavid van Moolenbroek }
341*5d5fbe79SDavid van Moolenbroek }
342*5d5fbe79SDavid van Moolenbroek #endif /* LWIP_IPV4 && LWIP_IPV6 */
343*5d5fbe79SDavid van Moolenbroek
344*5d5fbe79SDavid van Moolenbroek /** LWIP_NETCONN_SEM_PER_THREAD==1: initialize thread-local semaphore */
345*5d5fbe79SDavid van Moolenbroek void
lwip_socket_thread_init(void)346*5d5fbe79SDavid van Moolenbroek lwip_socket_thread_init(void)
347*5d5fbe79SDavid van Moolenbroek {
348*5d5fbe79SDavid van Moolenbroek netconn_thread_init();
349*5d5fbe79SDavid van Moolenbroek }
350*5d5fbe79SDavid van Moolenbroek
351*5d5fbe79SDavid van Moolenbroek /** LWIP_NETCONN_SEM_PER_THREAD==1: destroy thread-local semaphore */
352*5d5fbe79SDavid van Moolenbroek void
lwip_socket_thread_cleanup(void)353*5d5fbe79SDavid van Moolenbroek lwip_socket_thread_cleanup(void)
354*5d5fbe79SDavid van Moolenbroek {
355*5d5fbe79SDavid van Moolenbroek netconn_thread_cleanup();
356*5d5fbe79SDavid van Moolenbroek }
357*5d5fbe79SDavid van Moolenbroek
358*5d5fbe79SDavid van Moolenbroek #if LWIP_NETCONN_FULLDUPLEX
359*5d5fbe79SDavid van Moolenbroek /* Thread-safe increment of sock->fd_used, with overflow check */
360*5d5fbe79SDavid van Moolenbroek static void
sock_inc_used(struct lwip_sock * sock)361*5d5fbe79SDavid van Moolenbroek sock_inc_used(struct lwip_sock *sock)
362*5d5fbe79SDavid van Moolenbroek {
363*5d5fbe79SDavid van Moolenbroek LWIP_ASSERT("sock != NULL", sock != NULL);
364*5d5fbe79SDavid van Moolenbroek SYS_ARCH_INC(sock->fd_used, 1);
365*5d5fbe79SDavid van Moolenbroek LWIP_ASSERT("sock->fd_used != 0", sock->fd_used != 0);
366*5d5fbe79SDavid van Moolenbroek }
367*5d5fbe79SDavid van Moolenbroek
368*5d5fbe79SDavid van Moolenbroek /* In full-duplex mode,sock->fd_used != 0 prevents a socket descriptor from being
369*5d5fbe79SDavid van Moolenbroek * released (and possibly reused) when used from more than one thread
370*5d5fbe79SDavid van Moolenbroek * (e.g. read-while-write or close-while-write, etc)
371*5d5fbe79SDavid van Moolenbroek * This function is called at the end of functions using (try)get_socket*().
372*5d5fbe79SDavid van Moolenbroek */
373*5d5fbe79SDavid van Moolenbroek static void
done_socket(struct lwip_sock * sock)374*5d5fbe79SDavid van Moolenbroek done_socket(struct lwip_sock *sock)
375*5d5fbe79SDavid van Moolenbroek {
376*5d5fbe79SDavid van Moolenbroek SYS_ARCH_DECL_PROTECT(lev);
377*5d5fbe79SDavid van Moolenbroek
378*5d5fbe79SDavid van Moolenbroek LWIP_ASSERT("sock != NULL", sock != NULL);
379*5d5fbe79SDavid van Moolenbroek
380*5d5fbe79SDavid van Moolenbroek SYS_ARCH_PROTECT(lev);
381*5d5fbe79SDavid van Moolenbroek LWIP_ASSERT("sock->fd_used > 0", sock->fd_used > 0);
382*5d5fbe79SDavid van Moolenbroek if (--sock->fd_used == 0) {
383*5d5fbe79SDavid van Moolenbroek if (sock->fd_free_pending) {
384*5d5fbe79SDavid van Moolenbroek /* free the socket */
385*5d5fbe79SDavid van Moolenbroek sock->fd_used = 1;
386*5d5fbe79SDavid van Moolenbroek free_socket(sock, sock->fd_free_pending & LWIP_SOCK_FD_FREE_TCP);
387*5d5fbe79SDavid van Moolenbroek }
388*5d5fbe79SDavid van Moolenbroek }
389*5d5fbe79SDavid van Moolenbroek SYS_ARCH_UNPROTECT(lev);
390*5d5fbe79SDavid van Moolenbroek }
391*5d5fbe79SDavid van Moolenbroek #else /* LWIP_NETCONN_FULLDUPLEX */
392*5d5fbe79SDavid van Moolenbroek #define sock_inc_used(sock)
393*5d5fbe79SDavid van Moolenbroek #define done_socket(sock)
394*5d5fbe79SDavid van Moolenbroek #endif /* LWIP_NETCONN_FULLDUPLEX */
395*5d5fbe79SDavid van Moolenbroek
396*5d5fbe79SDavid van Moolenbroek /* Translate a socket 'int' into a pointer (only fails if the index is invalid) */
397*5d5fbe79SDavid van Moolenbroek static struct lwip_sock *
tryget_socket_unconn(int fd)398*5d5fbe79SDavid van Moolenbroek tryget_socket_unconn(int fd)
399*5d5fbe79SDavid van Moolenbroek {
400*5d5fbe79SDavid van Moolenbroek int s = fd - LWIP_SOCKET_OFFSET;
401*5d5fbe79SDavid van Moolenbroek if ((s < 0) || (s >= NUM_SOCKETS)) {
402*5d5fbe79SDavid van Moolenbroek LWIP_DEBUGF(SOCKETS_DEBUG, ("tryget_socket_unconn(%d): invalid\n", fd));
403*5d5fbe79SDavid van Moolenbroek return NULL;
404*5d5fbe79SDavid van Moolenbroek }
405*5d5fbe79SDavid van Moolenbroek sock_inc_used(&sockets[s]);
406*5d5fbe79SDavid van Moolenbroek return &sockets[s];
407*5d5fbe79SDavid van Moolenbroek }
408*5d5fbe79SDavid van Moolenbroek
409*5d5fbe79SDavid van Moolenbroek /**
410*5d5fbe79SDavid van Moolenbroek * Same as get_socket but doesn't set errno
411*5d5fbe79SDavid van Moolenbroek *
412*5d5fbe79SDavid van Moolenbroek * @param fd externally used socket index
413*5d5fbe79SDavid van Moolenbroek * @return struct lwip_sock for the socket or NULL if not found
414*5d5fbe79SDavid van Moolenbroek */
415*5d5fbe79SDavid van Moolenbroek static struct lwip_sock *
tryget_socket(int fd)416*5d5fbe79SDavid van Moolenbroek tryget_socket(int fd)
417*5d5fbe79SDavid van Moolenbroek {
418*5d5fbe79SDavid van Moolenbroek struct lwip_sock *sock = tryget_socket_unconn(fd);
419*5d5fbe79SDavid van Moolenbroek if (sock != NULL) {
420*5d5fbe79SDavid van Moolenbroek if (sock->conn) {
421*5d5fbe79SDavid van Moolenbroek return sock;
422*5d5fbe79SDavid van Moolenbroek }
423*5d5fbe79SDavid van Moolenbroek done_socket(sock);
424*5d5fbe79SDavid van Moolenbroek }
425*5d5fbe79SDavid van Moolenbroek return NULL;
426*5d5fbe79SDavid van Moolenbroek }
427*5d5fbe79SDavid van Moolenbroek
428*5d5fbe79SDavid van Moolenbroek /**
429*5d5fbe79SDavid van Moolenbroek * Map a externally used socket index to the internal socket representation.
430*5d5fbe79SDavid van Moolenbroek *
431*5d5fbe79SDavid van Moolenbroek * @param fd externally used socket index
432*5d5fbe79SDavid van Moolenbroek * @return struct lwip_sock for the socket or NULL if not found
433*5d5fbe79SDavid van Moolenbroek */
434*5d5fbe79SDavid van Moolenbroek static struct lwip_sock *
get_socket(int fd)435*5d5fbe79SDavid van Moolenbroek get_socket(int fd)
436*5d5fbe79SDavid van Moolenbroek {
437*5d5fbe79SDavid van Moolenbroek struct lwip_sock *sock = tryget_socket(fd);
438*5d5fbe79SDavid van Moolenbroek if (!sock) {
439*5d5fbe79SDavid van Moolenbroek if ((fd < LWIP_SOCKET_OFFSET) || (fd >= (LWIP_SOCKET_OFFSET + NUM_SOCKETS))) {
440*5d5fbe79SDavid van Moolenbroek LWIP_DEBUGF(SOCKETS_DEBUG, ("get_socket(%d): invalid\n", fd));
441*5d5fbe79SDavid van Moolenbroek }
442*5d5fbe79SDavid van Moolenbroek set_errno(EBADF);
443*5d5fbe79SDavid van Moolenbroek return NULL;
444*5d5fbe79SDavid van Moolenbroek }
445*5d5fbe79SDavid van Moolenbroek return sock;
446*5d5fbe79SDavid van Moolenbroek }
447*5d5fbe79SDavid van Moolenbroek
448*5d5fbe79SDavid van Moolenbroek /**
449*5d5fbe79SDavid van Moolenbroek * Allocate a new socket for a given netconn.
450*5d5fbe79SDavid van Moolenbroek *
451*5d5fbe79SDavid van Moolenbroek * @param newconn the netconn for which to allocate a socket
452*5d5fbe79SDavid van Moolenbroek * @param accepted 1 if socket has been created by accept(),
453*5d5fbe79SDavid van Moolenbroek * 0 if socket has been created by socket()
454*5d5fbe79SDavid van Moolenbroek * @return the index of the new socket; -1 on error
455*5d5fbe79SDavid van Moolenbroek */
456*5d5fbe79SDavid van Moolenbroek static int
alloc_socket(struct netconn * newconn,int accepted)457*5d5fbe79SDavid van Moolenbroek alloc_socket(struct netconn *newconn, int accepted)
458*5d5fbe79SDavid van Moolenbroek {
459*5d5fbe79SDavid van Moolenbroek int i;
460*5d5fbe79SDavid van Moolenbroek SYS_ARCH_DECL_PROTECT(lev);
461*5d5fbe79SDavid van Moolenbroek LWIP_UNUSED_ARG(accepted);
462*5d5fbe79SDavid van Moolenbroek
463*5d5fbe79SDavid van Moolenbroek /* allocate a new socket identifier */
464*5d5fbe79SDavid van Moolenbroek for (i = 0; i < NUM_SOCKETS; ++i) {
465*5d5fbe79SDavid van Moolenbroek /* Protect socket array */
466*5d5fbe79SDavid van Moolenbroek SYS_ARCH_PROTECT(lev);
467*5d5fbe79SDavid van Moolenbroek if (!sockets[i].conn) {
468*5d5fbe79SDavid van Moolenbroek #if LWIP_NETCONN_FULLDUPLEX
469*5d5fbe79SDavid van Moolenbroek if (sockets[i].fd_used) {
470*5d5fbe79SDavid van Moolenbroek continue;
471*5d5fbe79SDavid van Moolenbroek }
472*5d5fbe79SDavid van Moolenbroek sockets[i].fd_used = 1;
473*5d5fbe79SDavid van Moolenbroek sockets[i].fd_free_pending = 0;
474*5d5fbe79SDavid van Moolenbroek #endif
475*5d5fbe79SDavid van Moolenbroek sockets[i].conn = newconn;
476*5d5fbe79SDavid van Moolenbroek /* The socket is not yet known to anyone, so no need to protect
477*5d5fbe79SDavid van Moolenbroek after having marked it as used. */
478*5d5fbe79SDavid van Moolenbroek SYS_ARCH_UNPROTECT(lev);
479*5d5fbe79SDavid van Moolenbroek sockets[i].lastdata.pbuf = NULL;
480*5d5fbe79SDavid van Moolenbroek #if LWIP_SOCKET_SELECT
481*5d5fbe79SDavid van Moolenbroek LWIP_ASSERT("sockets[i].select_waiting == 0", sockets[i].select_waiting == 0);
482*5d5fbe79SDavid van Moolenbroek sockets[i].rcvevent = 0;
483*5d5fbe79SDavid van Moolenbroek /* TCP sendbuf is empty, but the socket is not yet writable until connected
484*5d5fbe79SDavid van Moolenbroek * (unless it has been created by accept()). */
485*5d5fbe79SDavid van Moolenbroek sockets[i].sendevent = (NETCONNTYPE_GROUP(newconn->type) == NETCONN_TCP ? (accepted != 0) : 1);
486*5d5fbe79SDavid van Moolenbroek sockets[i].errevent = 0;
487*5d5fbe79SDavid van Moolenbroek #endif /* LWIP_SOCKET_SELECT */
488*5d5fbe79SDavid van Moolenbroek sockets[i].err = 0;
489*5d5fbe79SDavid van Moolenbroek return i + LWIP_SOCKET_OFFSET;
490*5d5fbe79SDavid van Moolenbroek }
491*5d5fbe79SDavid van Moolenbroek SYS_ARCH_UNPROTECT(lev);
492*5d5fbe79SDavid van Moolenbroek }
493*5d5fbe79SDavid van Moolenbroek return -1;
494*5d5fbe79SDavid van Moolenbroek }
495*5d5fbe79SDavid van Moolenbroek
496*5d5fbe79SDavid van Moolenbroek /** Free a socket. The socket's netconn must have been
497*5d5fbe79SDavid van Moolenbroek * delete before!
498*5d5fbe79SDavid van Moolenbroek *
499*5d5fbe79SDavid van Moolenbroek * @param sock the socket to free
500*5d5fbe79SDavid van Moolenbroek * @param is_tcp != 0 for TCP sockets, used to free lastdata
501*5d5fbe79SDavid van Moolenbroek */
502*5d5fbe79SDavid van Moolenbroek static void
free_socket(struct lwip_sock * sock,int is_tcp)503*5d5fbe79SDavid van Moolenbroek free_socket(struct lwip_sock *sock, int is_tcp)
504*5d5fbe79SDavid van Moolenbroek {
505*5d5fbe79SDavid van Moolenbroek union lwip_sock_lastdata lastdata;
506*5d5fbe79SDavid van Moolenbroek SYS_ARCH_DECL_PROTECT(lev);
507*5d5fbe79SDavid van Moolenbroek
508*5d5fbe79SDavid van Moolenbroek /* Protect socket array */
509*5d5fbe79SDavid van Moolenbroek SYS_ARCH_PROTECT(lev);
510*5d5fbe79SDavid van Moolenbroek
511*5d5fbe79SDavid van Moolenbroek #if LWIP_NETCONN_FULLDUPLEX
512*5d5fbe79SDavid van Moolenbroek LWIP_ASSERT("sock->fd_used > 0", sock->fd_used > 0);
513*5d5fbe79SDavid van Moolenbroek if (--sock->fd_used > 0) {
514*5d5fbe79SDavid van Moolenbroek sock->fd_free_pending = LWIP_SOCK_FD_FREE_FREE | is_tcp ? LWIP_SOCK_FD_FREE_TCP : 0;
515*5d5fbe79SDavid van Moolenbroek SYS_ARCH_UNPROTECT(lev);
516*5d5fbe79SDavid van Moolenbroek return;
517*5d5fbe79SDavid van Moolenbroek }
518*5d5fbe79SDavid van Moolenbroek #endif
519*5d5fbe79SDavid van Moolenbroek
520*5d5fbe79SDavid van Moolenbroek lastdata = sock->lastdata;
521*5d5fbe79SDavid van Moolenbroek sock->lastdata.pbuf = NULL;
522*5d5fbe79SDavid van Moolenbroek sock->err = 0;
523*5d5fbe79SDavid van Moolenbroek sock->conn = NULL;
524*5d5fbe79SDavid van Moolenbroek SYS_ARCH_UNPROTECT(lev);
525*5d5fbe79SDavid van Moolenbroek /* don't use 'sock' after this line, as another task might have allocated it */
526*5d5fbe79SDavid van Moolenbroek
527*5d5fbe79SDavid van Moolenbroek if (lastdata.pbuf != NULL) {
528*5d5fbe79SDavid van Moolenbroek if (is_tcp) {
529*5d5fbe79SDavid van Moolenbroek pbuf_free(lastdata.pbuf);
530*5d5fbe79SDavid van Moolenbroek } else {
531*5d5fbe79SDavid van Moolenbroek netbuf_delete(lastdata.netbuf);
532*5d5fbe79SDavid van Moolenbroek }
533*5d5fbe79SDavid van Moolenbroek }
534*5d5fbe79SDavid van Moolenbroek }
535*5d5fbe79SDavid van Moolenbroek
536*5d5fbe79SDavid van Moolenbroek /* Below this, the well-known socket functions are implemented.
537*5d5fbe79SDavid van Moolenbroek * Use google.com or opengroup.org to get a good description :-)
538*5d5fbe79SDavid van Moolenbroek *
539*5d5fbe79SDavid van Moolenbroek * Exceptions are documented!
540*5d5fbe79SDavid van Moolenbroek */
541*5d5fbe79SDavid van Moolenbroek
542*5d5fbe79SDavid van Moolenbroek int
lwip_accept(int s,struct sockaddr * addr,socklen_t * addrlen)543*5d5fbe79SDavid van Moolenbroek lwip_accept(int s, struct sockaddr *addr, socklen_t *addrlen)
544*5d5fbe79SDavid van Moolenbroek {
545*5d5fbe79SDavid van Moolenbroek struct lwip_sock *sock, *nsock;
546*5d5fbe79SDavid van Moolenbroek struct netconn *newconn;
547*5d5fbe79SDavid van Moolenbroek ip_addr_t naddr;
548*5d5fbe79SDavid van Moolenbroek u16_t port = 0;
549*5d5fbe79SDavid van Moolenbroek int newsock;
550*5d5fbe79SDavid van Moolenbroek err_t err;
551*5d5fbe79SDavid van Moolenbroek int recvevent;
552*5d5fbe79SDavid van Moolenbroek SYS_ARCH_DECL_PROTECT(lev);
553*5d5fbe79SDavid van Moolenbroek
554*5d5fbe79SDavid van Moolenbroek LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_accept(%d)...\n", s));
555*5d5fbe79SDavid van Moolenbroek sock = get_socket(s);
556*5d5fbe79SDavid van Moolenbroek if (!sock) {
557*5d5fbe79SDavid van Moolenbroek return -1;
558*5d5fbe79SDavid van Moolenbroek }
559*5d5fbe79SDavid van Moolenbroek
560*5d5fbe79SDavid van Moolenbroek /* wait for a new connection */
561*5d5fbe79SDavid van Moolenbroek err = netconn_accept(sock->conn, &newconn);
562*5d5fbe79SDavid van Moolenbroek if (err != ERR_OK) {
563*5d5fbe79SDavid van Moolenbroek LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_accept(%d): netconn_acept failed, err=%d\n", s, err));
564*5d5fbe79SDavid van Moolenbroek if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_TCP) {
565*5d5fbe79SDavid van Moolenbroek sock_set_errno(sock, EOPNOTSUPP);
566*5d5fbe79SDavid van Moolenbroek } else if (err == ERR_CLSD) {
567*5d5fbe79SDavid van Moolenbroek sock_set_errno(sock, EINVAL);
568*5d5fbe79SDavid van Moolenbroek } else {
569*5d5fbe79SDavid van Moolenbroek sock_set_errno(sock, err_to_errno(err));
570*5d5fbe79SDavid van Moolenbroek }
571*5d5fbe79SDavid van Moolenbroek done_socket(sock);
572*5d5fbe79SDavid van Moolenbroek return -1;
573*5d5fbe79SDavid van Moolenbroek }
574*5d5fbe79SDavid van Moolenbroek LWIP_ASSERT("newconn != NULL", newconn != NULL);
575*5d5fbe79SDavid van Moolenbroek
576*5d5fbe79SDavid van Moolenbroek newsock = alloc_socket(newconn, 1);
577*5d5fbe79SDavid van Moolenbroek if (newsock == -1) {
578*5d5fbe79SDavid van Moolenbroek netconn_delete(newconn);
579*5d5fbe79SDavid van Moolenbroek sock_set_errno(sock, ENFILE);
580*5d5fbe79SDavid van Moolenbroek done_socket(sock);
581*5d5fbe79SDavid van Moolenbroek return -1;
582*5d5fbe79SDavid van Moolenbroek }
583*5d5fbe79SDavid van Moolenbroek LWIP_ASSERT("invalid socket index", (newsock >= LWIP_SOCKET_OFFSET) && (newsock < NUM_SOCKETS + LWIP_SOCKET_OFFSET));
584*5d5fbe79SDavid van Moolenbroek nsock = &sockets[newsock - LWIP_SOCKET_OFFSET];
585*5d5fbe79SDavid van Moolenbroek
586*5d5fbe79SDavid van Moolenbroek /* See event_callback: If data comes in right away after an accept, even
587*5d5fbe79SDavid van Moolenbroek * though the server task might not have created a new socket yet.
588*5d5fbe79SDavid van Moolenbroek * In that case, newconn->socket is counted down (newconn->socket--),
589*5d5fbe79SDavid van Moolenbroek * so nsock->rcvevent is >= 1 here!
590*5d5fbe79SDavid van Moolenbroek */
591*5d5fbe79SDavid van Moolenbroek SYS_ARCH_PROTECT(lev);
592*5d5fbe79SDavid van Moolenbroek recvevent = (s16_t)(-1 - newconn->socket);
593*5d5fbe79SDavid van Moolenbroek newconn->socket = newsock;
594*5d5fbe79SDavid van Moolenbroek SYS_ARCH_UNPROTECT(lev);
595*5d5fbe79SDavid van Moolenbroek
596*5d5fbe79SDavid van Moolenbroek if (newconn->callback) {
597*5d5fbe79SDavid van Moolenbroek while(recvevent > 0) {
598*5d5fbe79SDavid van Moolenbroek recvevent--;
599*5d5fbe79SDavid van Moolenbroek newconn->callback(newconn, NETCONN_EVT_RCVPLUS, 0);
600*5d5fbe79SDavid van Moolenbroek }
601*5d5fbe79SDavid van Moolenbroek }
602*5d5fbe79SDavid van Moolenbroek
603*5d5fbe79SDavid van Moolenbroek /* Note that POSIX only requires us to check addr is non-NULL. addrlen must
604*5d5fbe79SDavid van Moolenbroek * not be NULL if addr is valid.
605*5d5fbe79SDavid van Moolenbroek */
606*5d5fbe79SDavid van Moolenbroek if ((addr != NULL) && (addrlen != NULL)) {
607*5d5fbe79SDavid van Moolenbroek union sockaddr_aligned tempaddr;
608*5d5fbe79SDavid van Moolenbroek /* get the IP address and port of the remote host */
609*5d5fbe79SDavid van Moolenbroek err = netconn_peer(newconn, &naddr, &port);
610*5d5fbe79SDavid van Moolenbroek if (err != ERR_OK) {
611*5d5fbe79SDavid van Moolenbroek LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_accept(%d): netconn_peer failed, err=%d\n", s, err));
612*5d5fbe79SDavid van Moolenbroek netconn_delete(newconn);
613*5d5fbe79SDavid van Moolenbroek free_socket(nsock, 1);
614*5d5fbe79SDavid van Moolenbroek sock_set_errno(sock, err_to_errno(err));
615*5d5fbe79SDavid van Moolenbroek done_socket(sock);
616*5d5fbe79SDavid van Moolenbroek return -1;
617*5d5fbe79SDavid van Moolenbroek }
618*5d5fbe79SDavid van Moolenbroek
619*5d5fbe79SDavid van Moolenbroek IPADDR_PORT_TO_SOCKADDR(&tempaddr, &naddr, port);
620*5d5fbe79SDavid van Moolenbroek if (*addrlen > tempaddr.sa.sa_len) {
621*5d5fbe79SDavid van Moolenbroek *addrlen = tempaddr.sa.sa_len;
622*5d5fbe79SDavid van Moolenbroek }
623*5d5fbe79SDavid van Moolenbroek MEMCPY(addr, &tempaddr, *addrlen);
624*5d5fbe79SDavid van Moolenbroek
625*5d5fbe79SDavid van Moolenbroek LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_accept(%d) returning new sock=%d addr=", s, newsock));
626*5d5fbe79SDavid van Moolenbroek ip_addr_debug_print_val(SOCKETS_DEBUG, naddr);
627*5d5fbe79SDavid van Moolenbroek LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F"\n", port));
628*5d5fbe79SDavid van Moolenbroek } else {
629*5d5fbe79SDavid van Moolenbroek LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_accept(%d) returning new sock=%d", s, newsock));
630*5d5fbe79SDavid van Moolenbroek }
631*5d5fbe79SDavid van Moolenbroek
632*5d5fbe79SDavid van Moolenbroek sock_set_errno(sock, 0);
633*5d5fbe79SDavid van Moolenbroek done_socket(sock);
634*5d5fbe79SDavid van Moolenbroek done_socket(nsock);
635*5d5fbe79SDavid van Moolenbroek return newsock;
636*5d5fbe79SDavid van Moolenbroek }
637*5d5fbe79SDavid van Moolenbroek
638*5d5fbe79SDavid van Moolenbroek int
lwip_bind(int s,const struct sockaddr * name,socklen_t namelen)639*5d5fbe79SDavid van Moolenbroek lwip_bind(int s, const struct sockaddr *name, socklen_t namelen)
640*5d5fbe79SDavid van Moolenbroek {
641*5d5fbe79SDavid van Moolenbroek struct lwip_sock *sock;
642*5d5fbe79SDavid van Moolenbroek ip_addr_t local_addr;
643*5d5fbe79SDavid van Moolenbroek u16_t local_port;
644*5d5fbe79SDavid van Moolenbroek err_t err;
645*5d5fbe79SDavid van Moolenbroek
646*5d5fbe79SDavid van Moolenbroek sock = get_socket(s);
647*5d5fbe79SDavid van Moolenbroek if (!sock) {
648*5d5fbe79SDavid van Moolenbroek return -1;
649*5d5fbe79SDavid van Moolenbroek }
650*5d5fbe79SDavid van Moolenbroek
651*5d5fbe79SDavid van Moolenbroek if (!SOCK_ADDR_TYPE_MATCH(name, sock)) {
652*5d5fbe79SDavid van Moolenbroek /* sockaddr does not match socket type (IPv4/IPv6) */
653*5d5fbe79SDavid van Moolenbroek sock_set_errno(sock, err_to_errno(ERR_VAL));
654*5d5fbe79SDavid van Moolenbroek done_socket(sock);
655*5d5fbe79SDavid van Moolenbroek return -1;
656*5d5fbe79SDavid van Moolenbroek }
657*5d5fbe79SDavid van Moolenbroek
658*5d5fbe79SDavid van Moolenbroek /* check size, family and alignment of 'name' */
659*5d5fbe79SDavid van Moolenbroek LWIP_ERROR("lwip_bind: invalid address", (IS_SOCK_ADDR_LEN_VALID(namelen) &&
660*5d5fbe79SDavid van Moolenbroek IS_SOCK_ADDR_TYPE_VALID(name) && IS_SOCK_ADDR_ALIGNED(name)),
661*5d5fbe79SDavid van Moolenbroek sock_set_errno(sock, err_to_errno(ERR_ARG)); return -1;);
662*5d5fbe79SDavid van Moolenbroek LWIP_UNUSED_ARG(namelen);
663*5d5fbe79SDavid van Moolenbroek
664*5d5fbe79SDavid van Moolenbroek SOCKADDR_TO_IPADDR_PORT(name, &local_addr, local_port);
665*5d5fbe79SDavid van Moolenbroek LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_bind(%d, addr=", s));
666*5d5fbe79SDavid van Moolenbroek ip_addr_debug_print_val(SOCKETS_DEBUG, local_addr);
667*5d5fbe79SDavid van Moolenbroek LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F")\n", local_port));
668*5d5fbe79SDavid van Moolenbroek
669*5d5fbe79SDavid van Moolenbroek #if LWIP_IPV4 && LWIP_IPV6
670*5d5fbe79SDavid van Moolenbroek /* Dual-stack: Unmap IPv4 mapped IPv6 addresses */
671*5d5fbe79SDavid van Moolenbroek if (IP_IS_V6_VAL(local_addr) && ip6_addr_isipv4mappedipv6(ip_2_ip6(&local_addr))) {
672*5d5fbe79SDavid van Moolenbroek unmap_ipv4_mapped_ipv6(ip_2_ip4(&local_addr), ip_2_ip6(&local_addr));
673*5d5fbe79SDavid van Moolenbroek IP_SET_TYPE_VAL(local_addr, IPADDR_TYPE_V4);
674*5d5fbe79SDavid van Moolenbroek }
675*5d5fbe79SDavid van Moolenbroek #endif /* LWIP_IPV4 && LWIP_IPV6 */
676*5d5fbe79SDavid van Moolenbroek
677*5d5fbe79SDavid van Moolenbroek err = netconn_bind(sock->conn, &local_addr, local_port);
678*5d5fbe79SDavid van Moolenbroek
679*5d5fbe79SDavid van Moolenbroek if (err != ERR_OK) {
680*5d5fbe79SDavid van Moolenbroek LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_bind(%d) failed, err=%d\n", s, err));
681*5d5fbe79SDavid van Moolenbroek sock_set_errno(sock, err_to_errno(err));
682*5d5fbe79SDavid van Moolenbroek done_socket(sock);
683*5d5fbe79SDavid van Moolenbroek return -1;
684*5d5fbe79SDavid van Moolenbroek }
685*5d5fbe79SDavid van Moolenbroek
686*5d5fbe79SDavid van Moolenbroek LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_bind(%d) succeeded\n", s));
687*5d5fbe79SDavid van Moolenbroek sock_set_errno(sock, 0);
688*5d5fbe79SDavid van Moolenbroek done_socket(sock);
689*5d5fbe79SDavid van Moolenbroek return 0;
690*5d5fbe79SDavid van Moolenbroek }
691*5d5fbe79SDavid van Moolenbroek
692*5d5fbe79SDavid van Moolenbroek int
lwip_close(int s)693*5d5fbe79SDavid van Moolenbroek lwip_close(int s)
694*5d5fbe79SDavid van Moolenbroek {
695*5d5fbe79SDavid van Moolenbroek struct lwip_sock *sock;
696*5d5fbe79SDavid van Moolenbroek int is_tcp = 0;
697*5d5fbe79SDavid van Moolenbroek err_t err;
698*5d5fbe79SDavid van Moolenbroek
699*5d5fbe79SDavid van Moolenbroek LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_close(%d)\n", s));
700*5d5fbe79SDavid van Moolenbroek
701*5d5fbe79SDavid van Moolenbroek sock = get_socket(s);
702*5d5fbe79SDavid van Moolenbroek if (!sock) {
703*5d5fbe79SDavid van Moolenbroek return -1;
704*5d5fbe79SDavid van Moolenbroek }
705*5d5fbe79SDavid van Moolenbroek
706*5d5fbe79SDavid van Moolenbroek if (sock->conn != NULL) {
707*5d5fbe79SDavid van Moolenbroek is_tcp = NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_TCP;
708*5d5fbe79SDavid van Moolenbroek } else {
709*5d5fbe79SDavid van Moolenbroek LWIP_ASSERT("sock->lastdata == NULL", sock->lastdata.pbuf == NULL);
710*5d5fbe79SDavid van Moolenbroek }
711*5d5fbe79SDavid van Moolenbroek
712*5d5fbe79SDavid van Moolenbroek #if LWIP_IGMP
713*5d5fbe79SDavid van Moolenbroek /* drop all possibly joined IGMP memberships */
714*5d5fbe79SDavid van Moolenbroek lwip_socket_drop_registered_memberships(s);
715*5d5fbe79SDavid van Moolenbroek #endif /* LWIP_IGMP */
716*5d5fbe79SDavid van Moolenbroek
717*5d5fbe79SDavid van Moolenbroek err = netconn_delete(sock->conn);
718*5d5fbe79SDavid van Moolenbroek if (err != ERR_OK) {
719*5d5fbe79SDavid van Moolenbroek sock_set_errno(sock, err_to_errno(err));
720*5d5fbe79SDavid van Moolenbroek done_socket(sock);
721*5d5fbe79SDavid van Moolenbroek return -1;
722*5d5fbe79SDavid van Moolenbroek }
723*5d5fbe79SDavid van Moolenbroek
724*5d5fbe79SDavid van Moolenbroek free_socket(sock, is_tcp);
725*5d5fbe79SDavid van Moolenbroek set_errno(0);
726*5d5fbe79SDavid van Moolenbroek return 0;
727*5d5fbe79SDavid van Moolenbroek }
728*5d5fbe79SDavid van Moolenbroek
729*5d5fbe79SDavid van Moolenbroek int
lwip_connect(int s,const struct sockaddr * name,socklen_t namelen)730*5d5fbe79SDavid van Moolenbroek lwip_connect(int s, const struct sockaddr *name, socklen_t namelen)
731*5d5fbe79SDavid van Moolenbroek {
732*5d5fbe79SDavid van Moolenbroek struct lwip_sock *sock;
733*5d5fbe79SDavid van Moolenbroek err_t err;
734*5d5fbe79SDavid van Moolenbroek
735*5d5fbe79SDavid van Moolenbroek sock = get_socket(s);
736*5d5fbe79SDavid van Moolenbroek if (!sock) {
737*5d5fbe79SDavid van Moolenbroek return -1;
738*5d5fbe79SDavid van Moolenbroek }
739*5d5fbe79SDavid van Moolenbroek
740*5d5fbe79SDavid van Moolenbroek if (!SOCK_ADDR_TYPE_MATCH_OR_UNSPEC(name, sock)) {
741*5d5fbe79SDavid van Moolenbroek /* sockaddr does not match socket type (IPv4/IPv6) */
742*5d5fbe79SDavid van Moolenbroek sock_set_errno(sock, err_to_errno(ERR_VAL));
743*5d5fbe79SDavid van Moolenbroek done_socket(sock);
744*5d5fbe79SDavid van Moolenbroek return -1;
745*5d5fbe79SDavid van Moolenbroek }
746*5d5fbe79SDavid van Moolenbroek
747*5d5fbe79SDavid van Moolenbroek LWIP_UNUSED_ARG(namelen);
748*5d5fbe79SDavid van Moolenbroek if (name->sa_family == AF_UNSPEC) {
749*5d5fbe79SDavid van Moolenbroek LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_connect(%d, AF_UNSPEC)\n", s));
750*5d5fbe79SDavid van Moolenbroek err = netconn_disconnect(sock->conn);
751*5d5fbe79SDavid van Moolenbroek } else {
752*5d5fbe79SDavid van Moolenbroek ip_addr_t remote_addr;
753*5d5fbe79SDavid van Moolenbroek u16_t remote_port;
754*5d5fbe79SDavid van Moolenbroek
755*5d5fbe79SDavid van Moolenbroek /* check size, family and alignment of 'name' */
756*5d5fbe79SDavid van Moolenbroek LWIP_ERROR("lwip_connect: invalid address", IS_SOCK_ADDR_LEN_VALID(namelen) &&
757*5d5fbe79SDavid van Moolenbroek IS_SOCK_ADDR_TYPE_VALID_OR_UNSPEC(name) && IS_SOCK_ADDR_ALIGNED(name),
758*5d5fbe79SDavid van Moolenbroek sock_set_errno(sock, err_to_errno(ERR_ARG)); return -1;);
759*5d5fbe79SDavid van Moolenbroek
760*5d5fbe79SDavid van Moolenbroek SOCKADDR_TO_IPADDR_PORT(name, &remote_addr, remote_port);
761*5d5fbe79SDavid van Moolenbroek LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_connect(%d, addr=", s));
762*5d5fbe79SDavid van Moolenbroek ip_addr_debug_print_val(SOCKETS_DEBUG, remote_addr);
763*5d5fbe79SDavid van Moolenbroek LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F")\n", remote_port));
764*5d5fbe79SDavid van Moolenbroek
765*5d5fbe79SDavid van Moolenbroek #if LWIP_IPV4 && LWIP_IPV6
766*5d5fbe79SDavid van Moolenbroek /* Dual-stack: Unmap IPv4 mapped IPv6 addresses */
767*5d5fbe79SDavid van Moolenbroek if (IP_IS_V6_VAL(remote_addr) && ip6_addr_isipv4mappedipv6(ip_2_ip6(&remote_addr))) {
768*5d5fbe79SDavid van Moolenbroek unmap_ipv4_mapped_ipv6(ip_2_ip4(&remote_addr), ip_2_ip6(&remote_addr));
769*5d5fbe79SDavid van Moolenbroek IP_SET_TYPE_VAL(remote_addr, IPADDR_TYPE_V4);
770*5d5fbe79SDavid van Moolenbroek }
771*5d5fbe79SDavid van Moolenbroek #endif /* LWIP_IPV4 && LWIP_IPV6 */
772*5d5fbe79SDavid van Moolenbroek
773*5d5fbe79SDavid van Moolenbroek err = netconn_connect(sock->conn, &remote_addr, remote_port);
774*5d5fbe79SDavid van Moolenbroek }
775*5d5fbe79SDavid van Moolenbroek
776*5d5fbe79SDavid van Moolenbroek if (err != ERR_OK) {
777*5d5fbe79SDavid van Moolenbroek LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_connect(%d) failed, err=%d\n", s, err));
778*5d5fbe79SDavid van Moolenbroek sock_set_errno(sock, err_to_errno(err));
779*5d5fbe79SDavid van Moolenbroek done_socket(sock);
780*5d5fbe79SDavid van Moolenbroek return -1;
781*5d5fbe79SDavid van Moolenbroek }
782*5d5fbe79SDavid van Moolenbroek
783*5d5fbe79SDavid van Moolenbroek LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_connect(%d) succeeded\n", s));
784*5d5fbe79SDavid van Moolenbroek sock_set_errno(sock, 0);
785*5d5fbe79SDavid van Moolenbroek done_socket(sock);
786*5d5fbe79SDavid van Moolenbroek return 0;
787*5d5fbe79SDavid van Moolenbroek }
788*5d5fbe79SDavid van Moolenbroek
789*5d5fbe79SDavid van Moolenbroek /**
790*5d5fbe79SDavid van Moolenbroek * Set a socket into listen mode.
791*5d5fbe79SDavid van Moolenbroek * The socket may not have been used for another connection previously.
792*5d5fbe79SDavid van Moolenbroek *
793*5d5fbe79SDavid van Moolenbroek * @param s the socket to set to listening mode
794*5d5fbe79SDavid van Moolenbroek * @param backlog (ATTENTION: needs TCP_LISTEN_BACKLOG=1)
795*5d5fbe79SDavid van Moolenbroek * @return 0 on success, non-zero on failure
796*5d5fbe79SDavid van Moolenbroek */
797*5d5fbe79SDavid van Moolenbroek int
lwip_listen(int s,int backlog)798*5d5fbe79SDavid van Moolenbroek lwip_listen(int s, int backlog)
799*5d5fbe79SDavid van Moolenbroek {
800*5d5fbe79SDavid van Moolenbroek struct lwip_sock *sock;
801*5d5fbe79SDavid van Moolenbroek err_t err;
802*5d5fbe79SDavid van Moolenbroek
803*5d5fbe79SDavid van Moolenbroek LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_listen(%d, backlog=%d)\n", s, backlog));
804*5d5fbe79SDavid van Moolenbroek
805*5d5fbe79SDavid van Moolenbroek sock = get_socket(s);
806*5d5fbe79SDavid van Moolenbroek if (!sock) {
807*5d5fbe79SDavid van Moolenbroek return -1;
808*5d5fbe79SDavid van Moolenbroek }
809*5d5fbe79SDavid van Moolenbroek
810*5d5fbe79SDavid van Moolenbroek /* limit the "backlog" parameter to fit in an u8_t */
811*5d5fbe79SDavid van Moolenbroek backlog = LWIP_MIN(LWIP_MAX(backlog, 0), 0xff);
812*5d5fbe79SDavid van Moolenbroek
813*5d5fbe79SDavid van Moolenbroek err = netconn_listen_with_backlog(sock->conn, (u8_t)backlog);
814*5d5fbe79SDavid van Moolenbroek
815*5d5fbe79SDavid van Moolenbroek if (err != ERR_OK) {
816*5d5fbe79SDavid van Moolenbroek LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_listen(%d) failed, err=%d\n", s, err));
817*5d5fbe79SDavid van Moolenbroek if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_TCP) {
818*5d5fbe79SDavid van Moolenbroek sock_set_errno(sock, EOPNOTSUPP);
819*5d5fbe79SDavid van Moolenbroek } else {
820*5d5fbe79SDavid van Moolenbroek sock_set_errno(sock, err_to_errno(err));
821*5d5fbe79SDavid van Moolenbroek }
822*5d5fbe79SDavid van Moolenbroek done_socket(sock);
823*5d5fbe79SDavid van Moolenbroek return -1;
824*5d5fbe79SDavid van Moolenbroek }
825*5d5fbe79SDavid van Moolenbroek
826*5d5fbe79SDavid van Moolenbroek sock_set_errno(sock, 0);
827*5d5fbe79SDavid van Moolenbroek done_socket(sock);
828*5d5fbe79SDavid van Moolenbroek return 0;
829*5d5fbe79SDavid van Moolenbroek }
830*5d5fbe79SDavid van Moolenbroek
831*5d5fbe79SDavid van Moolenbroek /* Helper function to loop over receiving pbufs from netconn
832*5d5fbe79SDavid van Moolenbroek * until "len" bytes are received or we're otherwise done.
833*5d5fbe79SDavid van Moolenbroek */
834*5d5fbe79SDavid van Moolenbroek static int
lwip_recv_tcp(struct lwip_sock * sock,void * mem,size_t len,int flags)835*5d5fbe79SDavid van Moolenbroek lwip_recv_tcp(struct lwip_sock *sock, void *mem, size_t len, int flags)
836*5d5fbe79SDavid van Moolenbroek {
837*5d5fbe79SDavid van Moolenbroek u8_t apiflags = NETCONN_NOAUTORCVD;
838*5d5fbe79SDavid van Moolenbroek int recvd = 0;
839*5d5fbe79SDavid van Moolenbroek int recv_left = len;
840*5d5fbe79SDavid van Moolenbroek
841*5d5fbe79SDavid van Moolenbroek LWIP_ASSERT("no socket given", sock != NULL);
842*5d5fbe79SDavid van Moolenbroek LWIP_ASSERT("this should be checked internally", NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_TCP);
843*5d5fbe79SDavid van Moolenbroek
844*5d5fbe79SDavid van Moolenbroek if (flags & MSG_DONTWAIT) {
845*5d5fbe79SDavid van Moolenbroek apiflags = NETCONN_DONTBLOCK;
846*5d5fbe79SDavid van Moolenbroek } else {
847*5d5fbe79SDavid van Moolenbroek apiflags = 0;
848*5d5fbe79SDavid van Moolenbroek }
849*5d5fbe79SDavid van Moolenbroek
850*5d5fbe79SDavid van Moolenbroek do {
851*5d5fbe79SDavid van Moolenbroek struct pbuf *p;
852*5d5fbe79SDavid van Moolenbroek err_t err;
853*5d5fbe79SDavid van Moolenbroek u16_t copylen;
854*5d5fbe79SDavid van Moolenbroek
855*5d5fbe79SDavid van Moolenbroek LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recv_tcp: top while sock->lastdata=%p\n", (void*)sock->lastdata.pbuf));
856*5d5fbe79SDavid van Moolenbroek /* Check if there is data left from the last recv operation. */
857*5d5fbe79SDavid van Moolenbroek if (sock->lastdata.pbuf) {
858*5d5fbe79SDavid van Moolenbroek p = sock->lastdata.pbuf;
859*5d5fbe79SDavid van Moolenbroek } else {
860*5d5fbe79SDavid van Moolenbroek /* No data was left from the previous operation, so we try to get
861*5d5fbe79SDavid van Moolenbroek some from the network. */
862*5d5fbe79SDavid van Moolenbroek err = netconn_recv_tcp_pbuf_flags(sock->conn, &p, apiflags);
863*5d5fbe79SDavid van Moolenbroek LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recv_tcp: netconn_recv err=%d, pbuf=%p\n",
864*5d5fbe79SDavid van Moolenbroek err, (void*)p));
865*5d5fbe79SDavid van Moolenbroek
866*5d5fbe79SDavid van Moolenbroek if (err != ERR_OK) {
867*5d5fbe79SDavid van Moolenbroek if (recvd > 0) {
868*5d5fbe79SDavid van Moolenbroek /* already received data, return that (this trusts in getting the same error from
869*5d5fbe79SDavid van Moolenbroek netconn layer again next time netconn_recv is called) */
870*5d5fbe79SDavid van Moolenbroek if (err == ERR_CLSD) {
871*5d5fbe79SDavid van Moolenbroek /* closed but already received data, ensure select gets the FIN, too */
872*5d5fbe79SDavid van Moolenbroek if (sock->conn->callback != NULL) {
873*5d5fbe79SDavid van Moolenbroek sock->conn->callback(sock->conn, NETCONN_EVT_RCVPLUS, 0);
874*5d5fbe79SDavid van Moolenbroek }
875*5d5fbe79SDavid van Moolenbroek }
876*5d5fbe79SDavid van Moolenbroek goto lwip_recv_tcp_done;
877*5d5fbe79SDavid van Moolenbroek }
878*5d5fbe79SDavid van Moolenbroek /* We should really do some error checking here. */
879*5d5fbe79SDavid van Moolenbroek LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recv_tcp: p == NULL, error is \"%s\"!\n",
880*5d5fbe79SDavid van Moolenbroek lwip_strerr(err)));
881*5d5fbe79SDavid van Moolenbroek sock_set_errno(sock, err_to_errno(err));
882*5d5fbe79SDavid van Moolenbroek if (err == ERR_CLSD) {
883*5d5fbe79SDavid van Moolenbroek return 0;
884*5d5fbe79SDavid van Moolenbroek } else {
885*5d5fbe79SDavid van Moolenbroek return -1;
886*5d5fbe79SDavid van Moolenbroek }
887*5d5fbe79SDavid van Moolenbroek }
888*5d5fbe79SDavid van Moolenbroek LWIP_ASSERT("p != NULL", p != NULL);
889*5d5fbe79SDavid van Moolenbroek sock->lastdata.pbuf = p;
890*5d5fbe79SDavid van Moolenbroek }
891*5d5fbe79SDavid van Moolenbroek
892*5d5fbe79SDavid van Moolenbroek LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recv_tcp: buflen=%"U16_F" recv_left=%d off=%d\n",
893*5d5fbe79SDavid van Moolenbroek p->tot_len, recv_left, recvd));
894*5d5fbe79SDavid van Moolenbroek
895*5d5fbe79SDavid van Moolenbroek if (recv_left > p->tot_len) {
896*5d5fbe79SDavid van Moolenbroek copylen = p->tot_len;
897*5d5fbe79SDavid van Moolenbroek } else {
898*5d5fbe79SDavid van Moolenbroek copylen = (u16_t)recv_left;
899*5d5fbe79SDavid van Moolenbroek }
900*5d5fbe79SDavid van Moolenbroek
901*5d5fbe79SDavid van Moolenbroek /* copy the contents of the received buffer into
902*5d5fbe79SDavid van Moolenbroek the supplied memory pointer mem */
903*5d5fbe79SDavid van Moolenbroek pbuf_copy_partial(p, (u8_t*)mem + recvd, copylen, 0);
904*5d5fbe79SDavid van Moolenbroek
905*5d5fbe79SDavid van Moolenbroek recvd += copylen;
906*5d5fbe79SDavid van Moolenbroek
907*5d5fbe79SDavid van Moolenbroek /* TCP combines multiple pbufs for one recv */
908*5d5fbe79SDavid van Moolenbroek LWIP_ASSERT("invalid copylen, len would underflow", recv_left >= copylen);
909*5d5fbe79SDavid van Moolenbroek recv_left -= copylen;
910*5d5fbe79SDavid van Moolenbroek
911*5d5fbe79SDavid van Moolenbroek /* Unless we peek the incoming message... */
912*5d5fbe79SDavid van Moolenbroek if ((flags & MSG_PEEK) == 0) {
913*5d5fbe79SDavid van Moolenbroek /* ... check if there is data left in the pbuf */
914*5d5fbe79SDavid van Moolenbroek LWIP_ASSERT("invalid copylen", p->tot_len >= copylen);
915*5d5fbe79SDavid van Moolenbroek if (p->tot_len - copylen > 0) {
916*5d5fbe79SDavid van Moolenbroek /* If so, it should be saved in the sock structure for the next recv call.
917*5d5fbe79SDavid van Moolenbroek We store the pbuf but hide/free the consumed data: */
918*5d5fbe79SDavid van Moolenbroek sock->lastdata.pbuf = pbuf_free_header(p, copylen);
919*5d5fbe79SDavid van Moolenbroek LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recv_tcp: lastdata now pbuf=%p\n", (void*)sock->lastdata.pbuf));
920*5d5fbe79SDavid van Moolenbroek } else {
921*5d5fbe79SDavid van Moolenbroek sock->lastdata.pbuf = NULL;
922*5d5fbe79SDavid van Moolenbroek LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recv_tcp: deleting pbuf=%p\n", (void*)p));
923*5d5fbe79SDavid van Moolenbroek pbuf_free(p);
924*5d5fbe79SDavid van Moolenbroek }
925*5d5fbe79SDavid van Moolenbroek }
926*5d5fbe79SDavid van Moolenbroek /* once we have some data to return, only add more if we don't need to wait */
927*5d5fbe79SDavid van Moolenbroek apiflags |= NETCONN_DONTBLOCK;
928*5d5fbe79SDavid van Moolenbroek /* @todo: do we need to support peeking more than one pbuf? */
929*5d5fbe79SDavid van Moolenbroek } while ((recv_left > 0) || (flags & MSG_PEEK));
930*5d5fbe79SDavid van Moolenbroek lwip_recv_tcp_done:
931*5d5fbe79SDavid van Moolenbroek if (recvd > 0) {
932*5d5fbe79SDavid van Moolenbroek /* ensure window update after copying all data */
933*5d5fbe79SDavid van Moolenbroek netconn_tcp_recvd(sock->conn, recvd);
934*5d5fbe79SDavid van Moolenbroek }
935*5d5fbe79SDavid van Moolenbroek sock_set_errno(sock, 0);
936*5d5fbe79SDavid van Moolenbroek return recvd;
937*5d5fbe79SDavid van Moolenbroek }
938*5d5fbe79SDavid van Moolenbroek
939*5d5fbe79SDavid van Moolenbroek /* Convert a netbuf's address data to struct sockaddr */
940*5d5fbe79SDavid van Moolenbroek static void
lwip_sock_make_addr(struct netconn * conn,ip_addr_t * fromaddr,u16_t port,struct sockaddr * from,socklen_t * fromlen)941*5d5fbe79SDavid van Moolenbroek lwip_sock_make_addr(struct netconn *conn, ip_addr_t *fromaddr, u16_t port,
942*5d5fbe79SDavid van Moolenbroek struct sockaddr *from, socklen_t *fromlen)
943*5d5fbe79SDavid van Moolenbroek {
944*5d5fbe79SDavid van Moolenbroek union sockaddr_aligned saddr;
945*5d5fbe79SDavid van Moolenbroek
946*5d5fbe79SDavid van Moolenbroek LWIP_UNUSED_ARG(conn);
947*5d5fbe79SDavid van Moolenbroek
948*5d5fbe79SDavid van Moolenbroek #if LWIP_IPV4 && LWIP_IPV6
949*5d5fbe79SDavid van Moolenbroek /* Dual-stack: Map IPv4 addresses to IPv4 mapped IPv6 */
950*5d5fbe79SDavid van Moolenbroek if (NETCONNTYPE_ISIPV6(netconn_type(conn)) && IP_IS_V4(fromaddr)) {
951*5d5fbe79SDavid van Moolenbroek ip4_2_ipv4_mapped_ipv6(ip_2_ip6(fromaddr), ip_2_ip4(fromaddr));
952*5d5fbe79SDavid van Moolenbroek IP_SET_TYPE(fromaddr, IPADDR_TYPE_V6);
953*5d5fbe79SDavid van Moolenbroek }
954*5d5fbe79SDavid van Moolenbroek #endif /* LWIP_IPV4 && LWIP_IPV6 */
955*5d5fbe79SDavid van Moolenbroek
956*5d5fbe79SDavid van Moolenbroek LWIP_DEBUGF(SOCKETS_DEBUG, (" addr="));
957*5d5fbe79SDavid van Moolenbroek IPADDR_PORT_TO_SOCKADDR(&saddr, fromaddr, port);
958*5d5fbe79SDavid van Moolenbroek ip_addr_debug_print(SOCKETS_DEBUG, fromaddr);
959*5d5fbe79SDavid van Moolenbroek LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F"", port));
960*5d5fbe79SDavid van Moolenbroek if (from && fromlen)
961*5d5fbe79SDavid van Moolenbroek {
962*5d5fbe79SDavid van Moolenbroek if (*fromlen > saddr.sa.sa_len) {
963*5d5fbe79SDavid van Moolenbroek *fromlen = saddr.sa.sa_len;
964*5d5fbe79SDavid van Moolenbroek }
965*5d5fbe79SDavid van Moolenbroek MEMCPY(from, &saddr, *fromlen);
966*5d5fbe79SDavid van Moolenbroek }
967*5d5fbe79SDavid van Moolenbroek }
968*5d5fbe79SDavid van Moolenbroek
969*5d5fbe79SDavid van Moolenbroek int
lwip_recvfrom(int s,void * mem,size_t len,int flags,struct sockaddr * from,socklen_t * fromlen)970*5d5fbe79SDavid van Moolenbroek lwip_recvfrom(int s, void *mem, size_t len, int flags,
971*5d5fbe79SDavid van Moolenbroek struct sockaddr *from, socklen_t *fromlen)
972*5d5fbe79SDavid van Moolenbroek {
973*5d5fbe79SDavid van Moolenbroek struct lwip_sock *sock;
974*5d5fbe79SDavid van Moolenbroek int ret;
975*5d5fbe79SDavid van Moolenbroek
976*5d5fbe79SDavid van Moolenbroek LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom(%d, %p, %"SZT_F", 0x%x, ..)\n", s, mem, len, flags));
977*5d5fbe79SDavid van Moolenbroek sock = get_socket(s);
978*5d5fbe79SDavid van Moolenbroek if (!sock) {
979*5d5fbe79SDavid van Moolenbroek return -1;
980*5d5fbe79SDavid van Moolenbroek }
981*5d5fbe79SDavid van Moolenbroek if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_TCP) {
982*5d5fbe79SDavid van Moolenbroek ret = lwip_recv_tcp(sock, mem, len, flags);
983*5d5fbe79SDavid van Moolenbroek #if !SOCKETS_DEBUG
984*5d5fbe79SDavid van Moolenbroek if (from && fromlen)
985*5d5fbe79SDavid van Moolenbroek #endif /* !SOCKETS_DEBUG */
986*5d5fbe79SDavid van Moolenbroek {
987*5d5fbe79SDavid van Moolenbroek /* get remote addr/port from tcp_pcb */
988*5d5fbe79SDavid van Moolenbroek u16_t port;
989*5d5fbe79SDavid van Moolenbroek ip_addr_t tmpaddr;
990*5d5fbe79SDavid van Moolenbroek netconn_getaddr(sock->conn, &tmpaddr, &port, 0);
991*5d5fbe79SDavid van Moolenbroek LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom(%d):", s));
992*5d5fbe79SDavid van Moolenbroek lwip_sock_make_addr(sock->conn, &tmpaddr, port, from, fromlen);
993*5d5fbe79SDavid van Moolenbroek LWIP_DEBUGF(SOCKETS_DEBUG, (" len=%d\n", ret));
994*5d5fbe79SDavid van Moolenbroek }
995*5d5fbe79SDavid van Moolenbroek done_socket(sock);
996*5d5fbe79SDavid van Moolenbroek return ret;
997*5d5fbe79SDavid van Moolenbroek } else {
998*5d5fbe79SDavid van Moolenbroek struct netbuf *buf;
999*5d5fbe79SDavid van Moolenbroek u16_t buflen, copylen;
1000*5d5fbe79SDavid van Moolenbroek err_t err;
1001*5d5fbe79SDavid van Moolenbroek u8_t apiflags;
1002*5d5fbe79SDavid van Moolenbroek /* UDP / RAW */
1003*5d5fbe79SDavid van Moolenbroek if (flags & MSG_DONTWAIT) {
1004*5d5fbe79SDavid van Moolenbroek apiflags = NETCONN_DONTBLOCK;
1005*5d5fbe79SDavid van Moolenbroek } else {
1006*5d5fbe79SDavid van Moolenbroek apiflags = 0;
1007*5d5fbe79SDavid van Moolenbroek }
1008*5d5fbe79SDavid van Moolenbroek
1009*5d5fbe79SDavid van Moolenbroek LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom[UDP/RAW]: top sock->lastdata=%p\n", (void*)sock->lastdata.netbuf));
1010*5d5fbe79SDavid van Moolenbroek /* Check if there is data left from the last recv operation. */
1011*5d5fbe79SDavid van Moolenbroek buf = sock->lastdata.netbuf;
1012*5d5fbe79SDavid van Moolenbroek if (buf == NULL) {
1013*5d5fbe79SDavid van Moolenbroek /* No data was left from the previous operation, so we try to get
1014*5d5fbe79SDavid van Moolenbroek some from the network. */
1015*5d5fbe79SDavid van Moolenbroek err = netconn_recv_udp_raw_netbuf_flags(sock->conn, &buf, apiflags);
1016*5d5fbe79SDavid van Moolenbroek LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom[UDP/RAW]: netconn_recv err=%d, netbuf=%p\n",
1017*5d5fbe79SDavid van Moolenbroek err, (void*)buf));
1018*5d5fbe79SDavid van Moolenbroek
1019*5d5fbe79SDavid van Moolenbroek if (err != ERR_OK) {
1020*5d5fbe79SDavid van Moolenbroek LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom[UDP/RAW](%d): buf == NULL, error is \"%s\"!\n",
1021*5d5fbe79SDavid van Moolenbroek s, lwip_strerr(err)));
1022*5d5fbe79SDavid van Moolenbroek sock_set_errno(sock, err_to_errno(err));
1023*5d5fbe79SDavid van Moolenbroek done_socket(sock);
1024*5d5fbe79SDavid van Moolenbroek return -1;
1025*5d5fbe79SDavid van Moolenbroek }
1026*5d5fbe79SDavid van Moolenbroek LWIP_ASSERT("buf != NULL", buf != NULL);
1027*5d5fbe79SDavid van Moolenbroek sock->lastdata.netbuf = buf;
1028*5d5fbe79SDavid van Moolenbroek }
1029*5d5fbe79SDavid van Moolenbroek
1030*5d5fbe79SDavid van Moolenbroek buflen = buf->p->tot_len;
1031*5d5fbe79SDavid van Moolenbroek LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom: buflen=%"U16_F" len=%"SZT_F"\n",
1032*5d5fbe79SDavid van Moolenbroek buflen, len));
1033*5d5fbe79SDavid van Moolenbroek
1034*5d5fbe79SDavid van Moolenbroek if (len > buflen) {
1035*5d5fbe79SDavid van Moolenbroek copylen = buflen;
1036*5d5fbe79SDavid van Moolenbroek } else {
1037*5d5fbe79SDavid van Moolenbroek copylen = (u16_t)len;
1038*5d5fbe79SDavid van Moolenbroek }
1039*5d5fbe79SDavid van Moolenbroek
1040*5d5fbe79SDavid van Moolenbroek /* copy the contents of the received buffer into
1041*5d5fbe79SDavid van Moolenbroek the supplied memory pointer mem */
1042*5d5fbe79SDavid van Moolenbroek pbuf_copy_partial(buf->p, (u8_t*)mem, copylen, 0);
1043*5d5fbe79SDavid van Moolenbroek ret = copylen;
1044*5d5fbe79SDavid van Moolenbroek
1045*5d5fbe79SDavid van Moolenbroek /* Check to see from where the data was.*/
1046*5d5fbe79SDavid van Moolenbroek #if !SOCKETS_DEBUG
1047*5d5fbe79SDavid van Moolenbroek if (from && fromlen)
1048*5d5fbe79SDavid van Moolenbroek #endif /* !SOCKETS_DEBUG */
1049*5d5fbe79SDavid van Moolenbroek {
1050*5d5fbe79SDavid van Moolenbroek LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom(%d):", s));
1051*5d5fbe79SDavid van Moolenbroek lwip_sock_make_addr(sock->conn, netbuf_fromaddr(buf), netbuf_fromport(buf), from, fromlen);
1052*5d5fbe79SDavid van Moolenbroek LWIP_DEBUGF(SOCKETS_DEBUG, (" len=%d\n", ret));
1053*5d5fbe79SDavid van Moolenbroek }
1054*5d5fbe79SDavid van Moolenbroek
1055*5d5fbe79SDavid van Moolenbroek /* If we don't peek the incoming message: zero lastdata pointer and free the netbuf */
1056*5d5fbe79SDavid van Moolenbroek if ((flags & MSG_PEEK) == 0) {
1057*5d5fbe79SDavid van Moolenbroek sock->lastdata.netbuf = NULL;
1058*5d5fbe79SDavid van Moolenbroek netbuf_delete(buf);
1059*5d5fbe79SDavid van Moolenbroek }
1060*5d5fbe79SDavid van Moolenbroek }
1061*5d5fbe79SDavid van Moolenbroek
1062*5d5fbe79SDavid van Moolenbroek sock_set_errno(sock, 0);
1063*5d5fbe79SDavid van Moolenbroek done_socket(sock);
1064*5d5fbe79SDavid van Moolenbroek return ret;
1065*5d5fbe79SDavid van Moolenbroek }
1066*5d5fbe79SDavid van Moolenbroek
1067*5d5fbe79SDavid van Moolenbroek int
lwip_read(int s,void * mem,size_t len)1068*5d5fbe79SDavid van Moolenbroek lwip_read(int s, void *mem, size_t len)
1069*5d5fbe79SDavid van Moolenbroek {
1070*5d5fbe79SDavid van Moolenbroek return lwip_recvfrom(s, mem, len, 0, NULL, NULL);
1071*5d5fbe79SDavid van Moolenbroek }
1072*5d5fbe79SDavid van Moolenbroek
1073*5d5fbe79SDavid van Moolenbroek int
lwip_recv(int s,void * mem,size_t len,int flags)1074*5d5fbe79SDavid van Moolenbroek lwip_recv(int s, void *mem, size_t len, int flags)
1075*5d5fbe79SDavid van Moolenbroek {
1076*5d5fbe79SDavid van Moolenbroek return lwip_recvfrom(s, mem, len, flags, NULL, NULL);
1077*5d5fbe79SDavid van Moolenbroek }
1078*5d5fbe79SDavid van Moolenbroek
1079*5d5fbe79SDavid van Moolenbroek int
lwip_send(int s,const void * data,size_t size,int flags)1080*5d5fbe79SDavid van Moolenbroek lwip_send(int s, const void *data, size_t size, int flags)
1081*5d5fbe79SDavid van Moolenbroek {
1082*5d5fbe79SDavid van Moolenbroek struct lwip_sock *sock;
1083*5d5fbe79SDavid van Moolenbroek err_t err;
1084*5d5fbe79SDavid van Moolenbroek u8_t write_flags;
1085*5d5fbe79SDavid van Moolenbroek size_t written;
1086*5d5fbe79SDavid van Moolenbroek
1087*5d5fbe79SDavid van Moolenbroek LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_send(%d, data=%p, size=%"SZT_F", flags=0x%x)\n",
1088*5d5fbe79SDavid van Moolenbroek s, data, size, flags));
1089*5d5fbe79SDavid van Moolenbroek
1090*5d5fbe79SDavid van Moolenbroek sock = get_socket(s);
1091*5d5fbe79SDavid van Moolenbroek if (!sock) {
1092*5d5fbe79SDavid van Moolenbroek return -1;
1093*5d5fbe79SDavid van Moolenbroek }
1094*5d5fbe79SDavid van Moolenbroek
1095*5d5fbe79SDavid van Moolenbroek if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_TCP) {
1096*5d5fbe79SDavid van Moolenbroek #if (LWIP_UDP || LWIP_RAW)
1097*5d5fbe79SDavid van Moolenbroek done_socket(sock);
1098*5d5fbe79SDavid van Moolenbroek return lwip_sendto(s, data, size, flags, NULL, 0);
1099*5d5fbe79SDavid van Moolenbroek #else /* (LWIP_UDP || LWIP_RAW) */
1100*5d5fbe79SDavid van Moolenbroek sock_set_errno(sock, err_to_errno(ERR_ARG));
1101*5d5fbe79SDavid van Moolenbroek done_socket(sock);
1102*5d5fbe79SDavid van Moolenbroek return -1;
1103*5d5fbe79SDavid van Moolenbroek #endif /* (LWIP_UDP || LWIP_RAW) */
1104*5d5fbe79SDavid van Moolenbroek }
1105*5d5fbe79SDavid van Moolenbroek
1106*5d5fbe79SDavid van Moolenbroek write_flags = NETCONN_COPY |
1107*5d5fbe79SDavid van Moolenbroek ((flags & MSG_MORE) ? NETCONN_MORE : 0) |
1108*5d5fbe79SDavid van Moolenbroek ((flags & MSG_DONTWAIT) ? NETCONN_DONTBLOCK : 0);
1109*5d5fbe79SDavid van Moolenbroek written = 0;
1110*5d5fbe79SDavid van Moolenbroek err = netconn_write_partly(sock->conn, data, size, write_flags, &written);
1111*5d5fbe79SDavid van Moolenbroek
1112*5d5fbe79SDavid van Moolenbroek LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_send(%d) err=%d written=%"SZT_F"\n", s, err, written));
1113*5d5fbe79SDavid van Moolenbroek sock_set_errno(sock, err_to_errno(err));
1114*5d5fbe79SDavid van Moolenbroek done_socket(sock);
1115*5d5fbe79SDavid van Moolenbroek return (err == ERR_OK ? (int)written : -1);
1116*5d5fbe79SDavid van Moolenbroek }
1117*5d5fbe79SDavid van Moolenbroek
1118*5d5fbe79SDavid van Moolenbroek int
lwip_sendmsg(int s,const struct msghdr * msg,int flags)1119*5d5fbe79SDavid van Moolenbroek lwip_sendmsg(int s, const struct msghdr *msg, int flags)
1120*5d5fbe79SDavid van Moolenbroek {
1121*5d5fbe79SDavid van Moolenbroek struct lwip_sock *sock;
1122*5d5fbe79SDavid van Moolenbroek int i;
1123*5d5fbe79SDavid van Moolenbroek #if LWIP_TCP
1124*5d5fbe79SDavid van Moolenbroek u8_t write_flags;
1125*5d5fbe79SDavid van Moolenbroek size_t written;
1126*5d5fbe79SDavid van Moolenbroek #endif
1127*5d5fbe79SDavid van Moolenbroek int size = 0;
1128*5d5fbe79SDavid van Moolenbroek err_t err = ERR_OK;
1129*5d5fbe79SDavid van Moolenbroek
1130*5d5fbe79SDavid van Moolenbroek sock = get_socket(s);
1131*5d5fbe79SDavid van Moolenbroek if (!sock) {
1132*5d5fbe79SDavid van Moolenbroek return -1;
1133*5d5fbe79SDavid van Moolenbroek }
1134*5d5fbe79SDavid van Moolenbroek
1135*5d5fbe79SDavid van Moolenbroek LWIP_ERROR("lwip_sendmsg: invalid msghdr", msg != NULL,
1136*5d5fbe79SDavid van Moolenbroek sock_set_errno(sock, err_to_errno(ERR_ARG)); return -1;);
1137*5d5fbe79SDavid van Moolenbroek LWIP_ERROR("lwip_sendmsg: invalid msghdr iov", msg->msg_iov != NULL,
1138*5d5fbe79SDavid van Moolenbroek sock_set_errno(sock, err_to_errno(ERR_ARG)); return -1;);
1139*5d5fbe79SDavid van Moolenbroek LWIP_ERROR("lwip_sendmsg: maximum iovs exceeded", (msg->msg_iovlen > 0) && (msg->msg_iovlen <= IOV_MAX),
1140*5d5fbe79SDavid van Moolenbroek sock_set_errno(sock, EMSGSIZE); return -1;);
1141*5d5fbe79SDavid van Moolenbroek LWIP_ERROR("lwip_sendmsg: unsupported flags", ((flags == 0) || (flags == MSG_NOSIGNAL)),
1142*5d5fbe79SDavid van Moolenbroek sock_set_errno(sock, EOPNOTSUPP); return -1;);
1143*5d5fbe79SDavid van Moolenbroek
1144*5d5fbe79SDavid van Moolenbroek LWIP_UNUSED_ARG(msg->msg_control);
1145*5d5fbe79SDavid van Moolenbroek LWIP_UNUSED_ARG(msg->msg_controllen);
1146*5d5fbe79SDavid van Moolenbroek LWIP_UNUSED_ARG(msg->msg_flags);
1147*5d5fbe79SDavid van Moolenbroek
1148*5d5fbe79SDavid van Moolenbroek if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_TCP) {
1149*5d5fbe79SDavid van Moolenbroek #if LWIP_TCP
1150*5d5fbe79SDavid van Moolenbroek write_flags = NETCONN_COPY |
1151*5d5fbe79SDavid van Moolenbroek ((flags & MSG_MORE) ? NETCONN_MORE : 0) |
1152*5d5fbe79SDavid van Moolenbroek ((flags & MSG_DONTWAIT) ? NETCONN_DONTBLOCK : 0);
1153*5d5fbe79SDavid van Moolenbroek
1154*5d5fbe79SDavid van Moolenbroek written = 0;
1155*5d5fbe79SDavid van Moolenbroek err = netconn_write_vectors_partly(sock->conn, (struct netvector *)msg->msg_iov, (u16_t)msg->msg_iovlen, write_flags, &written);
1156*5d5fbe79SDavid van Moolenbroek sock_set_errno(sock, err_to_errno(err));
1157*5d5fbe79SDavid van Moolenbroek done_socket(sock);
1158*5d5fbe79SDavid van Moolenbroek return (err == ERR_OK ? (int)written : -1);
1159*5d5fbe79SDavid van Moolenbroek #else /* LWIP_TCP */
1160*5d5fbe79SDavid van Moolenbroek sock_set_errno(sock, err_to_errno(ERR_ARG));
1161*5d5fbe79SDavid van Moolenbroek done_socket(sock);
1162*5d5fbe79SDavid van Moolenbroek return -1;
1163*5d5fbe79SDavid van Moolenbroek #endif /* LWIP_TCP */
1164*5d5fbe79SDavid van Moolenbroek }
1165*5d5fbe79SDavid van Moolenbroek /* else, UDP and RAW NETCONNs */
1166*5d5fbe79SDavid van Moolenbroek #if LWIP_UDP || LWIP_RAW
1167*5d5fbe79SDavid van Moolenbroek {
1168*5d5fbe79SDavid van Moolenbroek struct netbuf chain_buf;
1169*5d5fbe79SDavid van Moolenbroek
1170*5d5fbe79SDavid van Moolenbroek LWIP_UNUSED_ARG(flags);
1171*5d5fbe79SDavid van Moolenbroek LWIP_ERROR("lwip_sendmsg: invalid msghdr name", (((msg->msg_name == NULL) && (msg->msg_namelen == 0)) ||
1172*5d5fbe79SDavid van Moolenbroek IS_SOCK_ADDR_LEN_VALID(msg->msg_namelen)) ,
1173*5d5fbe79SDavid van Moolenbroek sock_set_errno(sock, err_to_errno(ERR_ARG)); return -1;);
1174*5d5fbe79SDavid van Moolenbroek
1175*5d5fbe79SDavid van Moolenbroek /* initialize chain buffer with destination */
1176*5d5fbe79SDavid van Moolenbroek memset(&chain_buf, 0, sizeof(struct netbuf));
1177*5d5fbe79SDavid van Moolenbroek if (msg->msg_name) {
1178*5d5fbe79SDavid van Moolenbroek u16_t remote_port;
1179*5d5fbe79SDavid van Moolenbroek SOCKADDR_TO_IPADDR_PORT((const struct sockaddr *)msg->msg_name, &chain_buf.addr, remote_port);
1180*5d5fbe79SDavid van Moolenbroek netbuf_fromport(&chain_buf) = remote_port;
1181*5d5fbe79SDavid van Moolenbroek }
1182*5d5fbe79SDavid van Moolenbroek #if LWIP_NETIF_TX_SINGLE_PBUF
1183*5d5fbe79SDavid van Moolenbroek for (i = 0; i < msg->msg_iovlen; i++) {
1184*5d5fbe79SDavid van Moolenbroek size += msg->msg_iov[i].iov_len;
1185*5d5fbe79SDavid van Moolenbroek if ((msg->msg_iov[i].iov_len > INT_MAX) || (size < (int)msg->msg_iov[i].iov_len)) {
1186*5d5fbe79SDavid van Moolenbroek /* overflow */
1187*5d5fbe79SDavid van Moolenbroek goto sendmsg_emsgsize;
1188*5d5fbe79SDavid van Moolenbroek }
1189*5d5fbe79SDavid van Moolenbroek }
1190*5d5fbe79SDavid van Moolenbroek if (size > 0xFFFF) {
1191*5d5fbe79SDavid van Moolenbroek /* overflow */
1192*5d5fbe79SDavid van Moolenbroek goto sendmsg_emsgsize;
1193*5d5fbe79SDavid van Moolenbroek }
1194*5d5fbe79SDavid van Moolenbroek /* Allocate a new netbuf and copy the data into it. */
1195*5d5fbe79SDavid van Moolenbroek if (netbuf_alloc(&chain_buf, (u16_t)size) == NULL) {
1196*5d5fbe79SDavid van Moolenbroek err = ERR_MEM;
1197*5d5fbe79SDavid van Moolenbroek } else {
1198*5d5fbe79SDavid van Moolenbroek /* flatten the IO vectors */
1199*5d5fbe79SDavid van Moolenbroek size_t offset = 0;
1200*5d5fbe79SDavid van Moolenbroek for (i = 0; i < msg->msg_iovlen; i++) {
1201*5d5fbe79SDavid van Moolenbroek MEMCPY(&((u8_t*)chain_buf.p->payload)[offset], msg->msg_iov[i].iov_base, msg->msg_iov[i].iov_len);
1202*5d5fbe79SDavid van Moolenbroek offset += msg->msg_iov[i].iov_len;
1203*5d5fbe79SDavid van Moolenbroek }
1204*5d5fbe79SDavid van Moolenbroek #if LWIP_CHECKSUM_ON_COPY
1205*5d5fbe79SDavid van Moolenbroek {
1206*5d5fbe79SDavid van Moolenbroek /* This can be improved by using LWIP_CHKSUM_COPY() and aggregating the checksum for each IO vector */
1207*5d5fbe79SDavid van Moolenbroek u16_t chksum = ~inet_chksum_pbuf(chain_buf.p);
1208*5d5fbe79SDavid van Moolenbroek netbuf_set_chksum(&chain_buf, chksum);
1209*5d5fbe79SDavid van Moolenbroek }
1210*5d5fbe79SDavid van Moolenbroek #endif /* LWIP_CHECKSUM_ON_COPY */
1211*5d5fbe79SDavid van Moolenbroek err = ERR_OK;
1212*5d5fbe79SDavid van Moolenbroek }
1213*5d5fbe79SDavid van Moolenbroek #else /* LWIP_NETIF_TX_SINGLE_PBUF */
1214*5d5fbe79SDavid van Moolenbroek /* create a chained netbuf from the IO vectors. NOTE: we assemble a pbuf chain
1215*5d5fbe79SDavid van Moolenbroek manually to avoid having to allocate, chain, and delete a netbuf for each iov */
1216*5d5fbe79SDavid van Moolenbroek for (i = 0; i < msg->msg_iovlen; i++) {
1217*5d5fbe79SDavid van Moolenbroek struct pbuf *p;
1218*5d5fbe79SDavid van Moolenbroek if (msg->msg_iov[i].iov_len > 0xFFFF) {
1219*5d5fbe79SDavid van Moolenbroek /* overflow */
1220*5d5fbe79SDavid van Moolenbroek goto sendmsg_emsgsize;
1221*5d5fbe79SDavid van Moolenbroek }
1222*5d5fbe79SDavid van Moolenbroek p = pbuf_alloc(PBUF_TRANSPORT, 0, PBUF_REF);
1223*5d5fbe79SDavid van Moolenbroek if (p == NULL) {
1224*5d5fbe79SDavid van Moolenbroek err = ERR_MEM; /* let netbuf_delete() cleanup chain_buf */
1225*5d5fbe79SDavid van Moolenbroek break;
1226*5d5fbe79SDavid van Moolenbroek }
1227*5d5fbe79SDavid van Moolenbroek p->payload = msg->msg_iov[i].iov_base;
1228*5d5fbe79SDavid van Moolenbroek p->len = p->tot_len = (u16_t)msg->msg_iov[i].iov_len;
1229*5d5fbe79SDavid van Moolenbroek /* netbuf empty, add new pbuf */
1230*5d5fbe79SDavid van Moolenbroek if (chain_buf.p == NULL) {
1231*5d5fbe79SDavid van Moolenbroek chain_buf.p = chain_buf.ptr = p;
1232*5d5fbe79SDavid van Moolenbroek /* add pbuf to existing pbuf chain */
1233*5d5fbe79SDavid van Moolenbroek } else {
1234*5d5fbe79SDavid van Moolenbroek if (chain_buf.p->tot_len + p->len > 0xffff) {
1235*5d5fbe79SDavid van Moolenbroek /* overflow */
1236*5d5fbe79SDavid van Moolenbroek pbuf_free(p);
1237*5d5fbe79SDavid van Moolenbroek goto sendmsg_emsgsize;
1238*5d5fbe79SDavid van Moolenbroek }
1239*5d5fbe79SDavid van Moolenbroek pbuf_cat(chain_buf.p, p);
1240*5d5fbe79SDavid van Moolenbroek }
1241*5d5fbe79SDavid van Moolenbroek }
1242*5d5fbe79SDavid van Moolenbroek /* save size of total chain */
1243*5d5fbe79SDavid van Moolenbroek if (err == ERR_OK) {
1244*5d5fbe79SDavid van Moolenbroek size = netbuf_len(&chain_buf);
1245*5d5fbe79SDavid van Moolenbroek }
1246*5d5fbe79SDavid van Moolenbroek #endif /* LWIP_NETIF_TX_SINGLE_PBUF */
1247*5d5fbe79SDavid van Moolenbroek
1248*5d5fbe79SDavid van Moolenbroek if (err == ERR_OK) {
1249*5d5fbe79SDavid van Moolenbroek #if LWIP_IPV4 && LWIP_IPV6
1250*5d5fbe79SDavid van Moolenbroek /* Dual-stack: Unmap IPv4 mapped IPv6 addresses */
1251*5d5fbe79SDavid van Moolenbroek if (IP_IS_V6_VAL(chain_buf.addr) && ip6_addr_isipv4mappedipv6(ip_2_ip6(&chain_buf.addr))) {
1252*5d5fbe79SDavid van Moolenbroek unmap_ipv4_mapped_ipv6(ip_2_ip4(&chain_buf.addr), ip_2_ip6(&chain_buf.addr));
1253*5d5fbe79SDavid van Moolenbroek IP_SET_TYPE_VAL(chain_buf.addr, IPADDR_TYPE_V4);
1254*5d5fbe79SDavid van Moolenbroek }
1255*5d5fbe79SDavid van Moolenbroek #endif /* LWIP_IPV4 && LWIP_IPV6 */
1256*5d5fbe79SDavid van Moolenbroek
1257*5d5fbe79SDavid van Moolenbroek /* send the data */
1258*5d5fbe79SDavid van Moolenbroek err = netconn_send(sock->conn, &chain_buf);
1259*5d5fbe79SDavid van Moolenbroek }
1260*5d5fbe79SDavid van Moolenbroek
1261*5d5fbe79SDavid van Moolenbroek /* deallocated the buffer */
1262*5d5fbe79SDavid van Moolenbroek netbuf_free(&chain_buf);
1263*5d5fbe79SDavid van Moolenbroek
1264*5d5fbe79SDavid van Moolenbroek sock_set_errno(sock, err_to_errno(err));
1265*5d5fbe79SDavid van Moolenbroek done_socket(sock);
1266*5d5fbe79SDavid van Moolenbroek return (err == ERR_OK ? size : -1);
1267*5d5fbe79SDavid van Moolenbroek sendmsg_emsgsize:
1268*5d5fbe79SDavid van Moolenbroek sock_set_errno(sock, EMSGSIZE);
1269*5d5fbe79SDavid van Moolenbroek netbuf_free(&chain_buf);
1270*5d5fbe79SDavid van Moolenbroek done_socket(sock);
1271*5d5fbe79SDavid van Moolenbroek return -1;
1272*5d5fbe79SDavid van Moolenbroek }
1273*5d5fbe79SDavid van Moolenbroek #else /* LWIP_UDP || LWIP_RAW */
1274*5d5fbe79SDavid van Moolenbroek sock_set_errno(sock, err_to_errno(ERR_ARG));
1275*5d5fbe79SDavid van Moolenbroek done_socket(sock);
1276*5d5fbe79SDavid van Moolenbroek return -1;
1277*5d5fbe79SDavid van Moolenbroek #endif /* LWIP_UDP || LWIP_RAW */
1278*5d5fbe79SDavid van Moolenbroek }
1279*5d5fbe79SDavid van Moolenbroek
1280*5d5fbe79SDavid van Moolenbroek int
lwip_sendto(int s,const void * data,size_t size,int flags,const struct sockaddr * to,socklen_t tolen)1281*5d5fbe79SDavid van Moolenbroek lwip_sendto(int s, const void *data, size_t size, int flags,
1282*5d5fbe79SDavid van Moolenbroek const struct sockaddr *to, socklen_t tolen)
1283*5d5fbe79SDavid van Moolenbroek {
1284*5d5fbe79SDavid van Moolenbroek struct lwip_sock *sock;
1285*5d5fbe79SDavid van Moolenbroek err_t err;
1286*5d5fbe79SDavid van Moolenbroek u16_t short_size;
1287*5d5fbe79SDavid van Moolenbroek u16_t remote_port;
1288*5d5fbe79SDavid van Moolenbroek struct netbuf buf;
1289*5d5fbe79SDavid van Moolenbroek
1290*5d5fbe79SDavid van Moolenbroek sock = get_socket(s);
1291*5d5fbe79SDavid van Moolenbroek if (!sock) {
1292*5d5fbe79SDavid van Moolenbroek return -1;
1293*5d5fbe79SDavid van Moolenbroek }
1294*5d5fbe79SDavid van Moolenbroek
1295*5d5fbe79SDavid van Moolenbroek if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_TCP) {
1296*5d5fbe79SDavid van Moolenbroek #if LWIP_TCP
1297*5d5fbe79SDavid van Moolenbroek done_socket(sock);
1298*5d5fbe79SDavid van Moolenbroek return lwip_send(s, data, size, flags);
1299*5d5fbe79SDavid van Moolenbroek #else /* LWIP_TCP */
1300*5d5fbe79SDavid van Moolenbroek LWIP_UNUSED_ARG(flags);
1301*5d5fbe79SDavid van Moolenbroek sock_set_errno(sock, err_to_errno(ERR_ARG));
1302*5d5fbe79SDavid van Moolenbroek done_socket(sock);
1303*5d5fbe79SDavid van Moolenbroek return -1;
1304*5d5fbe79SDavid van Moolenbroek #endif /* LWIP_TCP */
1305*5d5fbe79SDavid van Moolenbroek }
1306*5d5fbe79SDavid van Moolenbroek
1307*5d5fbe79SDavid van Moolenbroek if (size > 0xFFFF) {
1308*5d5fbe79SDavid van Moolenbroek /* cannot fit into one datagram (at least for us) */
1309*5d5fbe79SDavid van Moolenbroek sock_set_errno(sock, EMSGSIZE);
1310*5d5fbe79SDavid van Moolenbroek done_socket(sock);
1311*5d5fbe79SDavid van Moolenbroek return -1;
1312*5d5fbe79SDavid van Moolenbroek }
1313*5d5fbe79SDavid van Moolenbroek short_size = (u16_t)size;
1314*5d5fbe79SDavid van Moolenbroek LWIP_ERROR("lwip_sendto: invalid address", (((to == NULL) && (tolen == 0)) ||
1315*5d5fbe79SDavid van Moolenbroek (IS_SOCK_ADDR_LEN_VALID(tolen) &&
1316*5d5fbe79SDavid van Moolenbroek IS_SOCK_ADDR_TYPE_VALID(to) && IS_SOCK_ADDR_ALIGNED(to))),
1317*5d5fbe79SDavid van Moolenbroek sock_set_errno(sock, err_to_errno(ERR_ARG)); return -1;);
1318*5d5fbe79SDavid van Moolenbroek LWIP_UNUSED_ARG(tolen);
1319*5d5fbe79SDavid van Moolenbroek
1320*5d5fbe79SDavid van Moolenbroek /* initialize a buffer */
1321*5d5fbe79SDavid van Moolenbroek buf.p = buf.ptr = NULL;
1322*5d5fbe79SDavid van Moolenbroek #if LWIP_CHECKSUM_ON_COPY
1323*5d5fbe79SDavid van Moolenbroek buf.flags = 0;
1324*5d5fbe79SDavid van Moolenbroek #endif /* LWIP_CHECKSUM_ON_COPY */
1325*5d5fbe79SDavid van Moolenbroek if (to) {
1326*5d5fbe79SDavid van Moolenbroek SOCKADDR_TO_IPADDR_PORT(to, &buf.addr, remote_port);
1327*5d5fbe79SDavid van Moolenbroek } else {
1328*5d5fbe79SDavid van Moolenbroek remote_port = 0;
1329*5d5fbe79SDavid van Moolenbroek ip_addr_set_any(NETCONNTYPE_ISIPV6(netconn_type(sock->conn)), &buf.addr);
1330*5d5fbe79SDavid van Moolenbroek }
1331*5d5fbe79SDavid van Moolenbroek netbuf_fromport(&buf) = remote_port;
1332*5d5fbe79SDavid van Moolenbroek
1333*5d5fbe79SDavid van Moolenbroek
1334*5d5fbe79SDavid van Moolenbroek LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_sendto(%d, data=%p, short_size=%"U16_F", flags=0x%x to=",
1335*5d5fbe79SDavid van Moolenbroek s, data, short_size, flags));
1336*5d5fbe79SDavid van Moolenbroek ip_addr_debug_print(SOCKETS_DEBUG, &buf.addr);
1337*5d5fbe79SDavid van Moolenbroek LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F"\n", remote_port));
1338*5d5fbe79SDavid van Moolenbroek
1339*5d5fbe79SDavid van Moolenbroek /* make the buffer point to the data that should be sent */
1340*5d5fbe79SDavid van Moolenbroek #if LWIP_NETIF_TX_SINGLE_PBUF
1341*5d5fbe79SDavid van Moolenbroek /* Allocate a new netbuf and copy the data into it. */
1342*5d5fbe79SDavid van Moolenbroek if (netbuf_alloc(&buf, short_size) == NULL) {
1343*5d5fbe79SDavid van Moolenbroek err = ERR_MEM;
1344*5d5fbe79SDavid van Moolenbroek } else {
1345*5d5fbe79SDavid van Moolenbroek #if LWIP_CHECKSUM_ON_COPY
1346*5d5fbe79SDavid van Moolenbroek if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_RAW) {
1347*5d5fbe79SDavid van Moolenbroek u16_t chksum = LWIP_CHKSUM_COPY(buf.p->payload, data, short_size);
1348*5d5fbe79SDavid van Moolenbroek netbuf_set_chksum(&buf, chksum);
1349*5d5fbe79SDavid van Moolenbroek } else
1350*5d5fbe79SDavid van Moolenbroek #endif /* LWIP_CHECKSUM_ON_COPY */
1351*5d5fbe79SDavid van Moolenbroek {
1352*5d5fbe79SDavid van Moolenbroek MEMCPY(buf.p->payload, data, short_size);
1353*5d5fbe79SDavid van Moolenbroek }
1354*5d5fbe79SDavid van Moolenbroek err = ERR_OK;
1355*5d5fbe79SDavid van Moolenbroek }
1356*5d5fbe79SDavid van Moolenbroek #else /* LWIP_NETIF_TX_SINGLE_PBUF */
1357*5d5fbe79SDavid van Moolenbroek err = netbuf_ref(&buf, data, short_size);
1358*5d5fbe79SDavid van Moolenbroek #endif /* LWIP_NETIF_TX_SINGLE_PBUF */
1359*5d5fbe79SDavid van Moolenbroek if (err == ERR_OK) {
1360*5d5fbe79SDavid van Moolenbroek #if LWIP_IPV4 && LWIP_IPV6
1361*5d5fbe79SDavid van Moolenbroek /* Dual-stack: Unmap IPv4 mapped IPv6 addresses */
1362*5d5fbe79SDavid van Moolenbroek if (IP_IS_V6_VAL(buf.addr) && ip6_addr_isipv4mappedipv6(ip_2_ip6(&buf.addr))) {
1363*5d5fbe79SDavid van Moolenbroek unmap_ipv4_mapped_ipv6(ip_2_ip4(&buf.addr), ip_2_ip6(&buf.addr));
1364*5d5fbe79SDavid van Moolenbroek IP_SET_TYPE_VAL(buf.addr, IPADDR_TYPE_V4);
1365*5d5fbe79SDavid van Moolenbroek }
1366*5d5fbe79SDavid van Moolenbroek #endif /* LWIP_IPV4 && LWIP_IPV6 */
1367*5d5fbe79SDavid van Moolenbroek
1368*5d5fbe79SDavid van Moolenbroek /* send the data */
1369*5d5fbe79SDavid van Moolenbroek err = netconn_send(sock->conn, &buf);
1370*5d5fbe79SDavid van Moolenbroek }
1371*5d5fbe79SDavid van Moolenbroek
1372*5d5fbe79SDavid van Moolenbroek /* deallocated the buffer */
1373*5d5fbe79SDavid van Moolenbroek netbuf_free(&buf);
1374*5d5fbe79SDavid van Moolenbroek
1375*5d5fbe79SDavid van Moolenbroek sock_set_errno(sock, err_to_errno(err));
1376*5d5fbe79SDavid van Moolenbroek done_socket(sock);
1377*5d5fbe79SDavid van Moolenbroek return (err == ERR_OK ? short_size : -1);
1378*5d5fbe79SDavid van Moolenbroek }
1379*5d5fbe79SDavid van Moolenbroek
1380*5d5fbe79SDavid van Moolenbroek int
lwip_socket(int domain,int type,int protocol)1381*5d5fbe79SDavid van Moolenbroek lwip_socket(int domain, int type, int protocol)
1382*5d5fbe79SDavid van Moolenbroek {
1383*5d5fbe79SDavid van Moolenbroek struct netconn *conn;
1384*5d5fbe79SDavid van Moolenbroek int i;
1385*5d5fbe79SDavid van Moolenbroek
1386*5d5fbe79SDavid van Moolenbroek LWIP_UNUSED_ARG(domain); /* @todo: check this */
1387*5d5fbe79SDavid van Moolenbroek
1388*5d5fbe79SDavid van Moolenbroek /* create a netconn */
1389*5d5fbe79SDavid van Moolenbroek switch (type) {
1390*5d5fbe79SDavid van Moolenbroek case SOCK_RAW:
1391*5d5fbe79SDavid van Moolenbroek conn = netconn_new_with_proto_and_callback(DOMAIN_TO_NETCONN_TYPE(domain, NETCONN_RAW),
1392*5d5fbe79SDavid van Moolenbroek (u8_t)protocol, DEFAULT_SOCKET_EVENTCB);
1393*5d5fbe79SDavid van Moolenbroek LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_socket(%s, SOCK_RAW, %d) = ",
1394*5d5fbe79SDavid van Moolenbroek domain == PF_INET ? "PF_INET" : "UNKNOWN", protocol));
1395*5d5fbe79SDavid van Moolenbroek break;
1396*5d5fbe79SDavid van Moolenbroek case SOCK_DGRAM:
1397*5d5fbe79SDavid van Moolenbroek conn = netconn_new_with_callback(DOMAIN_TO_NETCONN_TYPE(domain,
1398*5d5fbe79SDavid van Moolenbroek ((protocol == IPPROTO_UDPLITE) ? NETCONN_UDPLITE : NETCONN_UDP)) ,
1399*5d5fbe79SDavid van Moolenbroek DEFAULT_SOCKET_EVENTCB);
1400*5d5fbe79SDavid van Moolenbroek LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_socket(%s, SOCK_DGRAM, %d) = ",
1401*5d5fbe79SDavid van Moolenbroek domain == PF_INET ? "PF_INET" : "UNKNOWN", protocol));
1402*5d5fbe79SDavid van Moolenbroek break;
1403*5d5fbe79SDavid van Moolenbroek case SOCK_STREAM:
1404*5d5fbe79SDavid van Moolenbroek conn = netconn_new_with_callback(DOMAIN_TO_NETCONN_TYPE(domain, NETCONN_TCP), DEFAULT_SOCKET_EVENTCB);
1405*5d5fbe79SDavid van Moolenbroek LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_socket(%s, SOCK_STREAM, %d) = ",
1406*5d5fbe79SDavid van Moolenbroek domain == PF_INET ? "PF_INET" : "UNKNOWN", protocol));
1407*5d5fbe79SDavid van Moolenbroek break;
1408*5d5fbe79SDavid van Moolenbroek default:
1409*5d5fbe79SDavid van Moolenbroek LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_socket(%d, %d/UNKNOWN, %d) = -1\n",
1410*5d5fbe79SDavid van Moolenbroek domain, type, protocol));
1411*5d5fbe79SDavid van Moolenbroek set_errno(EINVAL);
1412*5d5fbe79SDavid van Moolenbroek return -1;
1413*5d5fbe79SDavid van Moolenbroek }
1414*5d5fbe79SDavid van Moolenbroek
1415*5d5fbe79SDavid van Moolenbroek if (!conn) {
1416*5d5fbe79SDavid van Moolenbroek LWIP_DEBUGF(SOCKETS_DEBUG, ("-1 / ENOBUFS (could not create netconn)\n"));
1417*5d5fbe79SDavid van Moolenbroek set_errno(ENOBUFS);
1418*5d5fbe79SDavid van Moolenbroek return -1;
1419*5d5fbe79SDavid van Moolenbroek }
1420*5d5fbe79SDavid van Moolenbroek
1421*5d5fbe79SDavid van Moolenbroek i = alloc_socket(conn, 0);
1422*5d5fbe79SDavid van Moolenbroek
1423*5d5fbe79SDavid van Moolenbroek if (i == -1) {
1424*5d5fbe79SDavid van Moolenbroek netconn_delete(conn);
1425*5d5fbe79SDavid van Moolenbroek set_errno(ENFILE);
1426*5d5fbe79SDavid van Moolenbroek return -1;
1427*5d5fbe79SDavid van Moolenbroek }
1428*5d5fbe79SDavid van Moolenbroek conn->socket = i;
1429*5d5fbe79SDavid van Moolenbroek done_socket(&sockets[i - LWIP_SOCKET_OFFSET]);
1430*5d5fbe79SDavid van Moolenbroek LWIP_DEBUGF(SOCKETS_DEBUG, ("%d\n", i));
1431*5d5fbe79SDavid van Moolenbroek set_errno(0);
1432*5d5fbe79SDavid van Moolenbroek return i;
1433*5d5fbe79SDavid van Moolenbroek }
1434*5d5fbe79SDavid van Moolenbroek
1435*5d5fbe79SDavid van Moolenbroek int
lwip_write(int s,const void * data,size_t size)1436*5d5fbe79SDavid van Moolenbroek lwip_write(int s, const void *data, size_t size)
1437*5d5fbe79SDavid van Moolenbroek {
1438*5d5fbe79SDavid van Moolenbroek return lwip_send(s, data, size, 0);
1439*5d5fbe79SDavid van Moolenbroek }
1440*5d5fbe79SDavid van Moolenbroek
1441*5d5fbe79SDavid van Moolenbroek int
lwip_writev(int s,const struct iovec * iov,int iovcnt)1442*5d5fbe79SDavid van Moolenbroek lwip_writev(int s, const struct iovec *iov, int iovcnt)
1443*5d5fbe79SDavid van Moolenbroek {
1444*5d5fbe79SDavid van Moolenbroek struct msghdr msg;
1445*5d5fbe79SDavid van Moolenbroek
1446*5d5fbe79SDavid van Moolenbroek msg.msg_name = NULL;
1447*5d5fbe79SDavid van Moolenbroek msg.msg_namelen = 0;
1448*5d5fbe79SDavid van Moolenbroek /* Hack: we have to cast via number to cast from 'const' pointer to non-const.
1449*5d5fbe79SDavid van Moolenbroek Blame the opengroup standard for this inconsistency. */
1450*5d5fbe79SDavid van Moolenbroek msg.msg_iov = LWIP_CONST_CAST(struct iovec *, iov);
1451*5d5fbe79SDavid van Moolenbroek msg.msg_iovlen = iovcnt;
1452*5d5fbe79SDavid van Moolenbroek msg.msg_control = NULL;
1453*5d5fbe79SDavid van Moolenbroek msg.msg_controllen = 0;
1454*5d5fbe79SDavid van Moolenbroek msg.msg_flags = 0;
1455*5d5fbe79SDavid van Moolenbroek return lwip_sendmsg(s, &msg, 0);
1456*5d5fbe79SDavid van Moolenbroek }
1457*5d5fbe79SDavid van Moolenbroek
1458*5d5fbe79SDavid van Moolenbroek #if LWIP_SOCKET_SELECT
1459*5d5fbe79SDavid van Moolenbroek /**
1460*5d5fbe79SDavid van Moolenbroek * Go through the readset and writeset lists and see which socket of the sockets
1461*5d5fbe79SDavid van Moolenbroek * set in the sets has events. On return, readset, writeset and exceptset have
1462*5d5fbe79SDavid van Moolenbroek * the sockets enabled that had events.
1463*5d5fbe79SDavid van Moolenbroek *
1464*5d5fbe79SDavid van Moolenbroek * @param maxfdp1 the highest socket index in the sets
1465*5d5fbe79SDavid van Moolenbroek * @param readset_in set of sockets to check for read events
1466*5d5fbe79SDavid van Moolenbroek * @param writeset_in set of sockets to check for write events
1467*5d5fbe79SDavid van Moolenbroek * @param exceptset_in set of sockets to check for error events
1468*5d5fbe79SDavid van Moolenbroek * @param readset_out set of sockets that had read events
1469*5d5fbe79SDavid van Moolenbroek * @param writeset_out set of sockets that had write events
1470*5d5fbe79SDavid van Moolenbroek * @param exceptset_out set os sockets that had error events
1471*5d5fbe79SDavid van Moolenbroek * @return number of sockets that had events (read/write/exception) (>= 0)
1472*5d5fbe79SDavid van Moolenbroek */
1473*5d5fbe79SDavid van Moolenbroek static int
lwip_selscan(int maxfdp1,fd_set * readset_in,fd_set * writeset_in,fd_set * exceptset_in,fd_set * readset_out,fd_set * writeset_out,fd_set * exceptset_out)1474*5d5fbe79SDavid van Moolenbroek lwip_selscan(int maxfdp1, fd_set *readset_in, fd_set *writeset_in, fd_set *exceptset_in,
1475*5d5fbe79SDavid van Moolenbroek fd_set *readset_out, fd_set *writeset_out, fd_set *exceptset_out)
1476*5d5fbe79SDavid van Moolenbroek {
1477*5d5fbe79SDavid van Moolenbroek int i, nready = 0;
1478*5d5fbe79SDavid van Moolenbroek fd_set lreadset, lwriteset, lexceptset;
1479*5d5fbe79SDavid van Moolenbroek struct lwip_sock *sock;
1480*5d5fbe79SDavid van Moolenbroek SYS_ARCH_DECL_PROTECT(lev);
1481*5d5fbe79SDavid van Moolenbroek
1482*5d5fbe79SDavid van Moolenbroek FD_ZERO(&lreadset);
1483*5d5fbe79SDavid van Moolenbroek FD_ZERO(&lwriteset);
1484*5d5fbe79SDavid van Moolenbroek FD_ZERO(&lexceptset);
1485*5d5fbe79SDavid van Moolenbroek
1486*5d5fbe79SDavid van Moolenbroek /* Go through each socket in each list to count number of sockets which
1487*5d5fbe79SDavid van Moolenbroek currently match */
1488*5d5fbe79SDavid van Moolenbroek for (i = LWIP_SOCKET_OFFSET; i < maxfdp1; i++) {
1489*5d5fbe79SDavid van Moolenbroek /* if this FD is not in the set, continue */
1490*5d5fbe79SDavid van Moolenbroek if (!(readset_in && FD_ISSET(i, readset_in)) &&
1491*5d5fbe79SDavid van Moolenbroek !(writeset_in && FD_ISSET(i, writeset_in)) &&
1492*5d5fbe79SDavid van Moolenbroek !(exceptset_in && FD_ISSET(i, exceptset_in))) {
1493*5d5fbe79SDavid van Moolenbroek continue;
1494*5d5fbe79SDavid van Moolenbroek }
1495*5d5fbe79SDavid van Moolenbroek /* First get the socket's status (protected)... */
1496*5d5fbe79SDavid van Moolenbroek SYS_ARCH_PROTECT(lev);
1497*5d5fbe79SDavid van Moolenbroek sock = tryget_socket_unconn(i);
1498*5d5fbe79SDavid van Moolenbroek if (sock != NULL) {
1499*5d5fbe79SDavid van Moolenbroek void* lastdata = sock->lastdata.pbuf;
1500*5d5fbe79SDavid van Moolenbroek s16_t rcvevent = sock->rcvevent;
1501*5d5fbe79SDavid van Moolenbroek u16_t sendevent = sock->sendevent;
1502*5d5fbe79SDavid van Moolenbroek u16_t errevent = sock->errevent;
1503*5d5fbe79SDavid van Moolenbroek SYS_ARCH_UNPROTECT(lev);
1504*5d5fbe79SDavid van Moolenbroek
1505*5d5fbe79SDavid van Moolenbroek /* ... then examine it: */
1506*5d5fbe79SDavid van Moolenbroek /* See if netconn of this socket is ready for read */
1507*5d5fbe79SDavid van Moolenbroek if (readset_in && FD_ISSET(i, readset_in) && ((lastdata != NULL) || (rcvevent > 0))) {
1508*5d5fbe79SDavid van Moolenbroek FD_SET(i, &lreadset);
1509*5d5fbe79SDavid van Moolenbroek LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_selscan: fd=%d ready for reading\n", i));
1510*5d5fbe79SDavid van Moolenbroek nready++;
1511*5d5fbe79SDavid van Moolenbroek }
1512*5d5fbe79SDavid van Moolenbroek /* See if netconn of this socket is ready for write */
1513*5d5fbe79SDavid van Moolenbroek if (writeset_in && FD_ISSET(i, writeset_in) && (sendevent != 0)) {
1514*5d5fbe79SDavid van Moolenbroek FD_SET(i, &lwriteset);
1515*5d5fbe79SDavid van Moolenbroek LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_selscan: fd=%d ready for writing\n", i));
1516*5d5fbe79SDavid van Moolenbroek nready++;
1517*5d5fbe79SDavid van Moolenbroek }
1518*5d5fbe79SDavid van Moolenbroek /* See if netconn of this socket had an error */
1519*5d5fbe79SDavid van Moolenbroek if (exceptset_in && FD_ISSET(i, exceptset_in) && (errevent != 0)) {
1520*5d5fbe79SDavid van Moolenbroek FD_SET(i, &lexceptset);
1521*5d5fbe79SDavid van Moolenbroek LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_selscan: fd=%d ready for exception\n", i));
1522*5d5fbe79SDavid van Moolenbroek nready++;
1523*5d5fbe79SDavid van Moolenbroek }
1524*5d5fbe79SDavid van Moolenbroek done_socket(sock);
1525*5d5fbe79SDavid van Moolenbroek } else {
1526*5d5fbe79SDavid van Moolenbroek SYS_ARCH_UNPROTECT(lev);
1527*5d5fbe79SDavid van Moolenbroek /* no a valid open socket */
1528*5d5fbe79SDavid van Moolenbroek return -1;
1529*5d5fbe79SDavid van Moolenbroek }
1530*5d5fbe79SDavid van Moolenbroek }
1531*5d5fbe79SDavid van Moolenbroek /* copy local sets to the ones provided as arguments */
1532*5d5fbe79SDavid van Moolenbroek *readset_out = lreadset;
1533*5d5fbe79SDavid van Moolenbroek *writeset_out = lwriteset;
1534*5d5fbe79SDavid van Moolenbroek *exceptset_out = lexceptset;
1535*5d5fbe79SDavid van Moolenbroek
1536*5d5fbe79SDavid van Moolenbroek LWIP_ASSERT("nready >= 0", nready >= 0);
1537*5d5fbe79SDavid van Moolenbroek return nready;
1538*5d5fbe79SDavid van Moolenbroek }
1539*5d5fbe79SDavid van Moolenbroek
1540*5d5fbe79SDavid van Moolenbroek #if LWIP_NETCONN_FULLDUPLEX
1541*5d5fbe79SDavid van Moolenbroek /* Mark all of the set sockets in one of the three fdsets passed to select as used.
1542*5d5fbe79SDavid van Moolenbroek * All sockets are marked (and later unmarked), whether they are open or not.
1543*5d5fbe79SDavid van Moolenbroek * This is OK as lwip_selscan aborts select when non-open sockets are found.
1544*5d5fbe79SDavid van Moolenbroek */
1545*5d5fbe79SDavid van Moolenbroek static void
lwip_select_inc_sockets_used_set(int maxfdp,fd_set * fdset,fd_set * used_sockets)1546*5d5fbe79SDavid van Moolenbroek lwip_select_inc_sockets_used_set(int maxfdp, fd_set *fdset, fd_set *used_sockets)
1547*5d5fbe79SDavid van Moolenbroek {
1548*5d5fbe79SDavid van Moolenbroek SYS_ARCH_DECL_PROTECT(lev);
1549*5d5fbe79SDavid van Moolenbroek if (fdset) {
1550*5d5fbe79SDavid van Moolenbroek int i;
1551*5d5fbe79SDavid van Moolenbroek for (i = LWIP_SOCKET_OFFSET; i < maxfdp; i++) {
1552*5d5fbe79SDavid van Moolenbroek /* if this FD is in the set, lock it (unless already done) */
1553*5d5fbe79SDavid van Moolenbroek if (FD_ISSET(i, fdset) && !FD_ISSET(i, used_sockets)) {
1554*5d5fbe79SDavid van Moolenbroek struct lwip_sock *sock;
1555*5d5fbe79SDavid van Moolenbroek SYS_ARCH_PROTECT(lev);
1556*5d5fbe79SDavid van Moolenbroek sock = tryget_socket_unconn(i);
1557*5d5fbe79SDavid van Moolenbroek if (sock != NULL) {
1558*5d5fbe79SDavid van Moolenbroek sock_inc_used(sock);
1559*5d5fbe79SDavid van Moolenbroek FD_SET(i, used_sockets);
1560*5d5fbe79SDavid van Moolenbroek }
1561*5d5fbe79SDavid van Moolenbroek SYS_ARCH_UNPROTECT(lev);
1562*5d5fbe79SDavid van Moolenbroek }
1563*5d5fbe79SDavid van Moolenbroek }
1564*5d5fbe79SDavid van Moolenbroek }
1565*5d5fbe79SDavid van Moolenbroek }
1566*5d5fbe79SDavid van Moolenbroek
1567*5d5fbe79SDavid van Moolenbroek /* Mark all sockets passed to select as used to prevent them from being freed
1568*5d5fbe79SDavid van Moolenbroek * from other threads while select is running.
1569*5d5fbe79SDavid van Moolenbroek * Marked sockets are added to 'used_sockets' to mark them only once an be able
1570*5d5fbe79SDavid van Moolenbroek * to unmark them correctly.
1571*5d5fbe79SDavid van Moolenbroek */
1572*5d5fbe79SDavid van Moolenbroek static void
lwip_select_inc_sockets_used(int maxfdp,fd_set * fdset1,fd_set * fdset2,fd_set * fdset3,fd_set * used_sockets)1573*5d5fbe79SDavid van Moolenbroek lwip_select_inc_sockets_used(int maxfdp, fd_set *fdset1, fd_set *fdset2, fd_set *fdset3, fd_set *used_sockets)
1574*5d5fbe79SDavid van Moolenbroek {
1575*5d5fbe79SDavid van Moolenbroek FD_ZERO(used_sockets);
1576*5d5fbe79SDavid van Moolenbroek lwip_select_inc_sockets_used_set(maxfdp, fdset1, used_sockets);
1577*5d5fbe79SDavid van Moolenbroek lwip_select_inc_sockets_used_set(maxfdp, fdset2, used_sockets);
1578*5d5fbe79SDavid van Moolenbroek lwip_select_inc_sockets_used_set(maxfdp, fdset3, used_sockets);
1579*5d5fbe79SDavid van Moolenbroek }
1580*5d5fbe79SDavid van Moolenbroek
1581*5d5fbe79SDavid van Moolenbroek /* Let go all sockets that were marked as used when starting select */
1582*5d5fbe79SDavid van Moolenbroek static void
lwip_select_dec_sockets_used(int maxfdp,fd_set * used_sockets)1583*5d5fbe79SDavid van Moolenbroek lwip_select_dec_sockets_used(int maxfdp, fd_set *used_sockets)
1584*5d5fbe79SDavid van Moolenbroek {
1585*5d5fbe79SDavid van Moolenbroek int i;
1586*5d5fbe79SDavid van Moolenbroek for (i = LWIP_SOCKET_OFFSET; i < maxfdp; i++) {
1587*5d5fbe79SDavid van Moolenbroek /* if this FD is not in the set, continue */
1588*5d5fbe79SDavid van Moolenbroek if (FD_ISSET(i, used_sockets)) {
1589*5d5fbe79SDavid van Moolenbroek struct lwip_sock *sock = tryget_socket_unconn(i);
1590*5d5fbe79SDavid van Moolenbroek LWIP_ASSERT("socket gone at the end of select", sock != NULL);
1591*5d5fbe79SDavid van Moolenbroek if (sock != NULL) {
1592*5d5fbe79SDavid van Moolenbroek done_socket(sock);
1593*5d5fbe79SDavid van Moolenbroek }
1594*5d5fbe79SDavid van Moolenbroek }
1595*5d5fbe79SDavid van Moolenbroek }
1596*5d5fbe79SDavid van Moolenbroek }
1597*5d5fbe79SDavid van Moolenbroek #else /* LWIP_NETCONN_FULLDUPLEX */
1598*5d5fbe79SDavid van Moolenbroek #define lwip_select_inc_sockets_used(maxfdp1, readset, writeset, exceptset, used_sockets)
1599*5d5fbe79SDavid van Moolenbroek #define lwip_select_dec_sockets_used(maxfdp1, used_sockets)
1600*5d5fbe79SDavid van Moolenbroek #endif /* LWIP_NETCONN_FULLDUPLEX */
1601*5d5fbe79SDavid van Moolenbroek
1602*5d5fbe79SDavid van Moolenbroek int
lwip_select(int maxfdp1,fd_set * readset,fd_set * writeset,fd_set * exceptset,struct timeval * timeout)1603*5d5fbe79SDavid van Moolenbroek lwip_select(int maxfdp1, fd_set *readset, fd_set *writeset, fd_set *exceptset,
1604*5d5fbe79SDavid van Moolenbroek struct timeval *timeout)
1605*5d5fbe79SDavid van Moolenbroek {
1606*5d5fbe79SDavid van Moolenbroek u32_t waitres = 0;
1607*5d5fbe79SDavid van Moolenbroek int nready;
1608*5d5fbe79SDavid van Moolenbroek fd_set lreadset, lwriteset, lexceptset;
1609*5d5fbe79SDavid van Moolenbroek u32_t msectimeout;
1610*5d5fbe79SDavid van Moolenbroek struct lwip_select_cb select_cb;
1611*5d5fbe79SDavid van Moolenbroek int i;
1612*5d5fbe79SDavid van Moolenbroek int maxfdp2;
1613*5d5fbe79SDavid van Moolenbroek #if LWIP_NETCONN_SEM_PER_THREAD
1614*5d5fbe79SDavid van Moolenbroek int waited = 0;
1615*5d5fbe79SDavid van Moolenbroek #endif
1616*5d5fbe79SDavid van Moolenbroek #if LWIP_NETCONN_FULLDUPLEX
1617*5d5fbe79SDavid van Moolenbroek fd_set used_sockets;
1618*5d5fbe79SDavid van Moolenbroek #endif
1619*5d5fbe79SDavid van Moolenbroek SYS_ARCH_DECL_PROTECT(lev);
1620*5d5fbe79SDavid van Moolenbroek
1621*5d5fbe79SDavid van Moolenbroek LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_select(%d, %p, %p, %p, tvsec=%"S32_F" tvusec=%"S32_F")\n",
1622*5d5fbe79SDavid van Moolenbroek maxfdp1, (void *)readset, (void *) writeset, (void *) exceptset,
1623*5d5fbe79SDavid van Moolenbroek timeout ? (s32_t)timeout->tv_sec : (s32_t)-1,
1624*5d5fbe79SDavid van Moolenbroek timeout ? (s32_t)timeout->tv_usec : (s32_t)-1));
1625*5d5fbe79SDavid van Moolenbroek
1626*5d5fbe79SDavid van Moolenbroek if ((maxfdp1 < 0) || (maxfdp1 > (FD_SETSIZE + LWIP_SOCKET_OFFSET))) {
1627*5d5fbe79SDavid van Moolenbroek set_errno(EINVAL);
1628*5d5fbe79SDavid van Moolenbroek return -1;
1629*5d5fbe79SDavid van Moolenbroek }
1630*5d5fbe79SDavid van Moolenbroek
1631*5d5fbe79SDavid van Moolenbroek lwip_select_inc_sockets_used(maxfdp1, readset, writeset, exceptset, &used_sockets);
1632*5d5fbe79SDavid van Moolenbroek
1633*5d5fbe79SDavid van Moolenbroek /* Go through each socket in each list to count number of sockets which
1634*5d5fbe79SDavid van Moolenbroek currently match */
1635*5d5fbe79SDavid van Moolenbroek nready = lwip_selscan(maxfdp1, readset, writeset, exceptset, &lreadset, &lwriteset, &lexceptset);
1636*5d5fbe79SDavid van Moolenbroek
1637*5d5fbe79SDavid van Moolenbroek if (nready < 0) {
1638*5d5fbe79SDavid van Moolenbroek set_errno(EBADF);
1639*5d5fbe79SDavid van Moolenbroek return -1;
1640*5d5fbe79SDavid van Moolenbroek }
1641*5d5fbe79SDavid van Moolenbroek
1642*5d5fbe79SDavid van Moolenbroek /* If we don't have any current events, then suspend if we are supposed to */
1643*5d5fbe79SDavid van Moolenbroek if (!nready) {
1644*5d5fbe79SDavid van Moolenbroek if (timeout && timeout->tv_sec == 0 && timeout->tv_usec == 0) {
1645*5d5fbe79SDavid van Moolenbroek LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_select: no timeout, returning 0\n"));
1646*5d5fbe79SDavid van Moolenbroek /* This is OK as the local fdsets are empty and nready is zero,
1647*5d5fbe79SDavid van Moolenbroek or we would have returned earlier. */
1648*5d5fbe79SDavid van Moolenbroek goto return_copy_fdsets;
1649*5d5fbe79SDavid van Moolenbroek }
1650*5d5fbe79SDavid van Moolenbroek
1651*5d5fbe79SDavid van Moolenbroek /* None ready: add our semaphore to list:
1652*5d5fbe79SDavid van Moolenbroek We don't actually need any dynamic memory. Our entry on the
1653*5d5fbe79SDavid van Moolenbroek list is only valid while we are in this function, so it's ok
1654*5d5fbe79SDavid van Moolenbroek to use local variables. */
1655*5d5fbe79SDavid van Moolenbroek
1656*5d5fbe79SDavid van Moolenbroek select_cb.next = NULL;
1657*5d5fbe79SDavid van Moolenbroek select_cb.prev = NULL;
1658*5d5fbe79SDavid van Moolenbroek select_cb.readset = readset;
1659*5d5fbe79SDavid van Moolenbroek select_cb.writeset = writeset;
1660*5d5fbe79SDavid van Moolenbroek select_cb.exceptset = exceptset;
1661*5d5fbe79SDavid van Moolenbroek select_cb.sem_signalled = 0;
1662*5d5fbe79SDavid van Moolenbroek #if LWIP_NETCONN_SEM_PER_THREAD
1663*5d5fbe79SDavid van Moolenbroek select_cb.sem = LWIP_NETCONN_THREAD_SEM_GET();
1664*5d5fbe79SDavid van Moolenbroek #else /* LWIP_NETCONN_SEM_PER_THREAD */
1665*5d5fbe79SDavid van Moolenbroek if (sys_sem_new(&select_cb.sem, 0) != ERR_OK) {
1666*5d5fbe79SDavid van Moolenbroek /* failed to create semaphore */
1667*5d5fbe79SDavid van Moolenbroek set_errno(ENOMEM);
1668*5d5fbe79SDavid van Moolenbroek lwip_select_dec_sockets_used(maxfdp1, &used_sockets);
1669*5d5fbe79SDavid van Moolenbroek return -1;
1670*5d5fbe79SDavid van Moolenbroek }
1671*5d5fbe79SDavid van Moolenbroek #endif /* LWIP_NETCONN_SEM_PER_THREAD */
1672*5d5fbe79SDavid van Moolenbroek
1673*5d5fbe79SDavid van Moolenbroek /* Protect the select_cb_list */
1674*5d5fbe79SDavid van Moolenbroek SYS_ARCH_PROTECT(lev);
1675*5d5fbe79SDavid van Moolenbroek
1676*5d5fbe79SDavid van Moolenbroek /* Put this select_cb on top of list */
1677*5d5fbe79SDavid van Moolenbroek select_cb.next = select_cb_list;
1678*5d5fbe79SDavid van Moolenbroek if (select_cb_list != NULL) {
1679*5d5fbe79SDavid van Moolenbroek select_cb_list->prev = &select_cb;
1680*5d5fbe79SDavid van Moolenbroek }
1681*5d5fbe79SDavid van Moolenbroek select_cb_list = &select_cb;
1682*5d5fbe79SDavid van Moolenbroek /* Increasing this counter tells event_callback that the list has changed. */
1683*5d5fbe79SDavid van Moolenbroek select_cb_ctr++;
1684*5d5fbe79SDavid van Moolenbroek
1685*5d5fbe79SDavid van Moolenbroek /* Now we can safely unprotect */
1686*5d5fbe79SDavid van Moolenbroek SYS_ARCH_UNPROTECT(lev);
1687*5d5fbe79SDavid van Moolenbroek
1688*5d5fbe79SDavid van Moolenbroek /* Increase select_waiting for each socket we are interested in */
1689*5d5fbe79SDavid van Moolenbroek maxfdp2 = maxfdp1;
1690*5d5fbe79SDavid van Moolenbroek for (i = LWIP_SOCKET_OFFSET; i < maxfdp1; i++) {
1691*5d5fbe79SDavid van Moolenbroek if ((readset && FD_ISSET(i, readset)) ||
1692*5d5fbe79SDavid van Moolenbroek (writeset && FD_ISSET(i, writeset)) ||
1693*5d5fbe79SDavid van Moolenbroek (exceptset && FD_ISSET(i, exceptset))) {
1694*5d5fbe79SDavid van Moolenbroek struct lwip_sock *sock;
1695*5d5fbe79SDavid van Moolenbroek SYS_ARCH_PROTECT(lev);
1696*5d5fbe79SDavid van Moolenbroek sock = tryget_socket_unconn(i);
1697*5d5fbe79SDavid van Moolenbroek if (sock != NULL) {
1698*5d5fbe79SDavid van Moolenbroek sock->select_waiting++;
1699*5d5fbe79SDavid van Moolenbroek LWIP_ASSERT("sock->select_waiting > 0", sock->select_waiting > 0);
1700*5d5fbe79SDavid van Moolenbroek done_socket(sock);
1701*5d5fbe79SDavid van Moolenbroek } else {
1702*5d5fbe79SDavid van Moolenbroek /* Not a valid socket */
1703*5d5fbe79SDavid van Moolenbroek nready = -1;
1704*5d5fbe79SDavid van Moolenbroek maxfdp2 = i;
1705*5d5fbe79SDavid van Moolenbroek SYS_ARCH_UNPROTECT(lev);
1706*5d5fbe79SDavid van Moolenbroek break;
1707*5d5fbe79SDavid van Moolenbroek }
1708*5d5fbe79SDavid van Moolenbroek SYS_ARCH_UNPROTECT(lev);
1709*5d5fbe79SDavid van Moolenbroek }
1710*5d5fbe79SDavid van Moolenbroek }
1711*5d5fbe79SDavid van Moolenbroek
1712*5d5fbe79SDavid van Moolenbroek if (nready >= 0) {
1713*5d5fbe79SDavid van Moolenbroek /* Call lwip_selscan again: there could have been events between
1714*5d5fbe79SDavid van Moolenbroek the last scan (without us on the list) and putting us on the list! */
1715*5d5fbe79SDavid van Moolenbroek nready = lwip_selscan(maxfdp1, readset, writeset, exceptset, &lreadset, &lwriteset, &lexceptset);
1716*5d5fbe79SDavid van Moolenbroek if (!nready) {
1717*5d5fbe79SDavid van Moolenbroek /* Still none ready, just wait to be woken */
1718*5d5fbe79SDavid van Moolenbroek if (timeout == 0) {
1719*5d5fbe79SDavid van Moolenbroek /* Wait forever */
1720*5d5fbe79SDavid van Moolenbroek msectimeout = 0;
1721*5d5fbe79SDavid van Moolenbroek } else {
1722*5d5fbe79SDavid van Moolenbroek msectimeout = ((timeout->tv_sec * 1000) + ((timeout->tv_usec + 500)/1000));
1723*5d5fbe79SDavid van Moolenbroek if (msectimeout == 0) {
1724*5d5fbe79SDavid van Moolenbroek /* Wait 1ms at least (0 means wait forever) */
1725*5d5fbe79SDavid van Moolenbroek msectimeout = 1;
1726*5d5fbe79SDavid van Moolenbroek }
1727*5d5fbe79SDavid van Moolenbroek }
1728*5d5fbe79SDavid van Moolenbroek
1729*5d5fbe79SDavid van Moolenbroek waitres = sys_arch_sem_wait(SELECT_SEM_PTR(select_cb.sem), msectimeout);
1730*5d5fbe79SDavid van Moolenbroek #if LWIP_NETCONN_SEM_PER_THREAD
1731*5d5fbe79SDavid van Moolenbroek waited = 1;
1732*5d5fbe79SDavid van Moolenbroek #endif
1733*5d5fbe79SDavid van Moolenbroek }
1734*5d5fbe79SDavid van Moolenbroek }
1735*5d5fbe79SDavid van Moolenbroek
1736*5d5fbe79SDavid van Moolenbroek /* Decrease select_waiting for each socket we are interested in */
1737*5d5fbe79SDavid van Moolenbroek for (i = LWIP_SOCKET_OFFSET; i < maxfdp2; i++) {
1738*5d5fbe79SDavid van Moolenbroek if ((readset && FD_ISSET(i, readset)) ||
1739*5d5fbe79SDavid van Moolenbroek (writeset && FD_ISSET(i, writeset)) ||
1740*5d5fbe79SDavid van Moolenbroek (exceptset && FD_ISSET(i, exceptset))) {
1741*5d5fbe79SDavid van Moolenbroek struct lwip_sock *sock;
1742*5d5fbe79SDavid van Moolenbroek SYS_ARCH_PROTECT(lev);
1743*5d5fbe79SDavid van Moolenbroek sock = tryget_socket_unconn(i);
1744*5d5fbe79SDavid van Moolenbroek if (sock != NULL) {
1745*5d5fbe79SDavid van Moolenbroek /* for now, handle select_waiting==0... */
1746*5d5fbe79SDavid van Moolenbroek LWIP_ASSERT("sock->select_waiting > 0", sock->select_waiting > 0);
1747*5d5fbe79SDavid van Moolenbroek if (sock->select_waiting > 0) {
1748*5d5fbe79SDavid van Moolenbroek sock->select_waiting--;
1749*5d5fbe79SDavid van Moolenbroek }
1750*5d5fbe79SDavid van Moolenbroek done_socket(sock);
1751*5d5fbe79SDavid van Moolenbroek } else {
1752*5d5fbe79SDavid van Moolenbroek /* Not a valid socket */
1753*5d5fbe79SDavid van Moolenbroek nready = -1;
1754*5d5fbe79SDavid van Moolenbroek }
1755*5d5fbe79SDavid van Moolenbroek SYS_ARCH_UNPROTECT(lev);
1756*5d5fbe79SDavid van Moolenbroek }
1757*5d5fbe79SDavid van Moolenbroek }
1758*5d5fbe79SDavid van Moolenbroek /* Take us off the list */
1759*5d5fbe79SDavid van Moolenbroek SYS_ARCH_PROTECT(lev);
1760*5d5fbe79SDavid van Moolenbroek if (select_cb.next != NULL) {
1761*5d5fbe79SDavid van Moolenbroek select_cb.next->prev = select_cb.prev;
1762*5d5fbe79SDavid van Moolenbroek }
1763*5d5fbe79SDavid van Moolenbroek if (select_cb_list == &select_cb) {
1764*5d5fbe79SDavid van Moolenbroek LWIP_ASSERT("select_cb.prev == NULL", select_cb.prev == NULL);
1765*5d5fbe79SDavid van Moolenbroek select_cb_list = select_cb.next;
1766*5d5fbe79SDavid van Moolenbroek } else {
1767*5d5fbe79SDavid van Moolenbroek LWIP_ASSERT("select_cb.prev != NULL", select_cb.prev != NULL);
1768*5d5fbe79SDavid van Moolenbroek select_cb.prev->next = select_cb.next;
1769*5d5fbe79SDavid van Moolenbroek }
1770*5d5fbe79SDavid van Moolenbroek /* Increasing this counter tells event_callback that the list has changed. */
1771*5d5fbe79SDavid van Moolenbroek select_cb_ctr++;
1772*5d5fbe79SDavid van Moolenbroek SYS_ARCH_UNPROTECT(lev);
1773*5d5fbe79SDavid van Moolenbroek
1774*5d5fbe79SDavid van Moolenbroek #if LWIP_NETCONN_SEM_PER_THREAD
1775*5d5fbe79SDavid van Moolenbroek if (select_cb.sem_signalled && (!waited || (waitres == SYS_ARCH_TIMEOUT))) {
1776*5d5fbe79SDavid van Moolenbroek /* don't leave the thread-local semaphore signalled */
1777*5d5fbe79SDavid van Moolenbroek sys_arch_sem_wait(select_cb.sem, 1);
1778*5d5fbe79SDavid van Moolenbroek }
1779*5d5fbe79SDavid van Moolenbroek #else /* LWIP_NETCONN_SEM_PER_THREAD */
1780*5d5fbe79SDavid van Moolenbroek sys_sem_free(&select_cb.sem);
1781*5d5fbe79SDavid van Moolenbroek #endif /* LWIP_NETCONN_SEM_PER_THREAD */
1782*5d5fbe79SDavid van Moolenbroek
1783*5d5fbe79SDavid van Moolenbroek if (nready < 0) {
1784*5d5fbe79SDavid van Moolenbroek /* This happens when a socket got closed while waiting */
1785*5d5fbe79SDavid van Moolenbroek set_errno(EBADF);
1786*5d5fbe79SDavid van Moolenbroek lwip_select_dec_sockets_used(maxfdp1, &used_sockets);
1787*5d5fbe79SDavid van Moolenbroek return -1;
1788*5d5fbe79SDavid van Moolenbroek }
1789*5d5fbe79SDavid van Moolenbroek
1790*5d5fbe79SDavid van Moolenbroek if (waitres == SYS_ARCH_TIMEOUT) {
1791*5d5fbe79SDavid van Moolenbroek /* Timeout */
1792*5d5fbe79SDavid van Moolenbroek LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_select: timeout expired\n"));
1793*5d5fbe79SDavid van Moolenbroek /* This is OK as the local fdsets are empty and nready is zero,
1794*5d5fbe79SDavid van Moolenbroek or we would have returned earlier. */
1795*5d5fbe79SDavid van Moolenbroek goto return_copy_fdsets;
1796*5d5fbe79SDavid van Moolenbroek }
1797*5d5fbe79SDavid van Moolenbroek
1798*5d5fbe79SDavid van Moolenbroek /* See what's set */
1799*5d5fbe79SDavid van Moolenbroek nready = lwip_selscan(maxfdp1, readset, writeset, exceptset, &lreadset, &lwriteset, &lexceptset);
1800*5d5fbe79SDavid van Moolenbroek }
1801*5d5fbe79SDavid van Moolenbroek
1802*5d5fbe79SDavid van Moolenbroek LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_select: nready=%d\n", nready));
1803*5d5fbe79SDavid van Moolenbroek return_copy_fdsets:
1804*5d5fbe79SDavid van Moolenbroek lwip_select_dec_sockets_used(maxfdp1, &used_sockets);
1805*5d5fbe79SDavid van Moolenbroek set_errno(0);
1806*5d5fbe79SDavid van Moolenbroek if (readset) {
1807*5d5fbe79SDavid van Moolenbroek *readset = lreadset;
1808*5d5fbe79SDavid van Moolenbroek }
1809*5d5fbe79SDavid van Moolenbroek if (writeset) {
1810*5d5fbe79SDavid van Moolenbroek *writeset = lwriteset;
1811*5d5fbe79SDavid van Moolenbroek }
1812*5d5fbe79SDavid van Moolenbroek if (exceptset) {
1813*5d5fbe79SDavid van Moolenbroek *exceptset = lexceptset;
1814*5d5fbe79SDavid van Moolenbroek }
1815*5d5fbe79SDavid van Moolenbroek return nready;
1816*5d5fbe79SDavid van Moolenbroek }
1817*5d5fbe79SDavid van Moolenbroek
1818*5d5fbe79SDavid van Moolenbroek /**
1819*5d5fbe79SDavid van Moolenbroek * Callback registered in the netconn layer for each socket-netconn.
1820*5d5fbe79SDavid van Moolenbroek * Processes recvevent (data available) and wakes up tasks waiting for select.
1821*5d5fbe79SDavid van Moolenbroek */
1822*5d5fbe79SDavid van Moolenbroek static void
event_callback(struct netconn * conn,enum netconn_evt evt,u16_t len)1823*5d5fbe79SDavid van Moolenbroek event_callback(struct netconn *conn, enum netconn_evt evt, u16_t len)
1824*5d5fbe79SDavid van Moolenbroek {
1825*5d5fbe79SDavid van Moolenbroek int s;
1826*5d5fbe79SDavid van Moolenbroek struct lwip_sock *sock;
1827*5d5fbe79SDavid van Moolenbroek struct lwip_select_cb *scb;
1828*5d5fbe79SDavid van Moolenbroek int last_select_cb_ctr;
1829*5d5fbe79SDavid van Moolenbroek SYS_ARCH_DECL_PROTECT(lev);
1830*5d5fbe79SDavid van Moolenbroek
1831*5d5fbe79SDavid van Moolenbroek LWIP_UNUSED_ARG(len);
1832*5d5fbe79SDavid van Moolenbroek
1833*5d5fbe79SDavid van Moolenbroek /* Get socket */
1834*5d5fbe79SDavid van Moolenbroek if (conn) {
1835*5d5fbe79SDavid van Moolenbroek s = conn->socket;
1836*5d5fbe79SDavid van Moolenbroek if (s < 0) {
1837*5d5fbe79SDavid van Moolenbroek /* Data comes in right away after an accept, even though
1838*5d5fbe79SDavid van Moolenbroek * the server task might not have created a new socket yet.
1839*5d5fbe79SDavid van Moolenbroek * Just count down (or up) if that's the case and we
1840*5d5fbe79SDavid van Moolenbroek * will use the data later. Note that only receive events
1841*5d5fbe79SDavid van Moolenbroek * can happen before the new socket is set up. */
1842*5d5fbe79SDavid van Moolenbroek SYS_ARCH_PROTECT(lev);
1843*5d5fbe79SDavid van Moolenbroek if (conn->socket < 0) {
1844*5d5fbe79SDavid van Moolenbroek if (evt == NETCONN_EVT_RCVPLUS) {
1845*5d5fbe79SDavid van Moolenbroek /* conn->socket is -1 on initialization
1846*5d5fbe79SDavid van Moolenbroek lwip_accept adjusts sock->recvevent if conn->socket < -1 */
1847*5d5fbe79SDavid van Moolenbroek conn->socket--;
1848*5d5fbe79SDavid van Moolenbroek }
1849*5d5fbe79SDavid van Moolenbroek SYS_ARCH_UNPROTECT(lev);
1850*5d5fbe79SDavid van Moolenbroek return;
1851*5d5fbe79SDavid van Moolenbroek }
1852*5d5fbe79SDavid van Moolenbroek s = conn->socket;
1853*5d5fbe79SDavid van Moolenbroek SYS_ARCH_UNPROTECT(lev);
1854*5d5fbe79SDavid van Moolenbroek }
1855*5d5fbe79SDavid van Moolenbroek
1856*5d5fbe79SDavid van Moolenbroek sock = get_socket(s);
1857*5d5fbe79SDavid van Moolenbroek if (!sock) {
1858*5d5fbe79SDavid van Moolenbroek return;
1859*5d5fbe79SDavid van Moolenbroek }
1860*5d5fbe79SDavid van Moolenbroek } else {
1861*5d5fbe79SDavid van Moolenbroek return;
1862*5d5fbe79SDavid van Moolenbroek }
1863*5d5fbe79SDavid van Moolenbroek
1864*5d5fbe79SDavid van Moolenbroek SYS_ARCH_PROTECT(lev);
1865*5d5fbe79SDavid van Moolenbroek /* Set event as required */
1866*5d5fbe79SDavid van Moolenbroek switch (evt) {
1867*5d5fbe79SDavid van Moolenbroek case NETCONN_EVT_RCVPLUS:
1868*5d5fbe79SDavid van Moolenbroek sock->rcvevent++;
1869*5d5fbe79SDavid van Moolenbroek break;
1870*5d5fbe79SDavid van Moolenbroek case NETCONN_EVT_RCVMINUS:
1871*5d5fbe79SDavid van Moolenbroek sock->rcvevent--;
1872*5d5fbe79SDavid van Moolenbroek break;
1873*5d5fbe79SDavid van Moolenbroek case NETCONN_EVT_SENDPLUS:
1874*5d5fbe79SDavid van Moolenbroek sock->sendevent = 1;
1875*5d5fbe79SDavid van Moolenbroek break;
1876*5d5fbe79SDavid van Moolenbroek case NETCONN_EVT_SENDMINUS:
1877*5d5fbe79SDavid van Moolenbroek sock->sendevent = 0;
1878*5d5fbe79SDavid van Moolenbroek break;
1879*5d5fbe79SDavid van Moolenbroek case NETCONN_EVT_ERROR:
1880*5d5fbe79SDavid van Moolenbroek sock->errevent = 1;
1881*5d5fbe79SDavid van Moolenbroek break;
1882*5d5fbe79SDavid van Moolenbroek default:
1883*5d5fbe79SDavid van Moolenbroek LWIP_ASSERT("unknown event", 0);
1884*5d5fbe79SDavid van Moolenbroek break;
1885*5d5fbe79SDavid van Moolenbroek }
1886*5d5fbe79SDavid van Moolenbroek
1887*5d5fbe79SDavid van Moolenbroek if (sock->select_waiting == 0) {
1888*5d5fbe79SDavid van Moolenbroek /* noone is waiting for this socket, no need to check select_cb_list */
1889*5d5fbe79SDavid van Moolenbroek SYS_ARCH_UNPROTECT(lev);
1890*5d5fbe79SDavid van Moolenbroek done_socket(sock);
1891*5d5fbe79SDavid van Moolenbroek return;
1892*5d5fbe79SDavid van Moolenbroek }
1893*5d5fbe79SDavid van Moolenbroek
1894*5d5fbe79SDavid van Moolenbroek /* Now decide if anyone is waiting for this socket */
1895*5d5fbe79SDavid van Moolenbroek /* NOTE: This code goes through the select_cb_list list multiple times
1896*5d5fbe79SDavid van Moolenbroek ONLY IF a select was actually waiting. We go through the list the number
1897*5d5fbe79SDavid van Moolenbroek of waiting select calls + 1. This list is expected to be small. */
1898*5d5fbe79SDavid van Moolenbroek
1899*5d5fbe79SDavid van Moolenbroek /* At this point, SYS_ARCH is still protected! */
1900*5d5fbe79SDavid van Moolenbroek again:
1901*5d5fbe79SDavid van Moolenbroek for (scb = select_cb_list; scb != NULL; scb = scb->next) {
1902*5d5fbe79SDavid van Moolenbroek /* remember the state of select_cb_list to detect changes */
1903*5d5fbe79SDavid van Moolenbroek last_select_cb_ctr = select_cb_ctr;
1904*5d5fbe79SDavid van Moolenbroek if (scb->sem_signalled == 0) {
1905*5d5fbe79SDavid van Moolenbroek /* semaphore not signalled yet */
1906*5d5fbe79SDavid van Moolenbroek int do_signal = 0;
1907*5d5fbe79SDavid van Moolenbroek /* Test this select call for our socket */
1908*5d5fbe79SDavid van Moolenbroek if (sock->rcvevent > 0) {
1909*5d5fbe79SDavid van Moolenbroek if (scb->readset && FD_ISSET(s, scb->readset)) {
1910*5d5fbe79SDavid van Moolenbroek do_signal = 1;
1911*5d5fbe79SDavid van Moolenbroek }
1912*5d5fbe79SDavid van Moolenbroek }
1913*5d5fbe79SDavid van Moolenbroek if (sock->sendevent != 0) {
1914*5d5fbe79SDavid van Moolenbroek if (!do_signal && scb->writeset && FD_ISSET(s, scb->writeset)) {
1915*5d5fbe79SDavid van Moolenbroek do_signal = 1;
1916*5d5fbe79SDavid van Moolenbroek }
1917*5d5fbe79SDavid van Moolenbroek }
1918*5d5fbe79SDavid van Moolenbroek if (sock->errevent != 0) {
1919*5d5fbe79SDavid van Moolenbroek if (!do_signal && scb->exceptset && FD_ISSET(s, scb->exceptset)) {
1920*5d5fbe79SDavid van Moolenbroek do_signal = 1;
1921*5d5fbe79SDavid van Moolenbroek }
1922*5d5fbe79SDavid van Moolenbroek }
1923*5d5fbe79SDavid van Moolenbroek if (do_signal) {
1924*5d5fbe79SDavid van Moolenbroek scb->sem_signalled = 1;
1925*5d5fbe79SDavid van Moolenbroek /* Don't call SYS_ARCH_UNPROTECT() before signaling the semaphore, as this might
1926*5d5fbe79SDavid van Moolenbroek lead to the select thread taking itself off the list, invalidating the semaphore. */
1927*5d5fbe79SDavid van Moolenbroek sys_sem_signal(SELECT_SEM_PTR(scb->sem));
1928*5d5fbe79SDavid van Moolenbroek }
1929*5d5fbe79SDavid van Moolenbroek }
1930*5d5fbe79SDavid van Moolenbroek /* unlock interrupts with each step */
1931*5d5fbe79SDavid van Moolenbroek SYS_ARCH_UNPROTECT(lev);
1932*5d5fbe79SDavid van Moolenbroek /* this makes sure interrupt protection time is short */
1933*5d5fbe79SDavid van Moolenbroek SYS_ARCH_PROTECT(lev);
1934*5d5fbe79SDavid van Moolenbroek if (last_select_cb_ctr != select_cb_ctr) {
1935*5d5fbe79SDavid van Moolenbroek /* someone has changed select_cb_list, restart at the beginning */
1936*5d5fbe79SDavid van Moolenbroek goto again;
1937*5d5fbe79SDavid van Moolenbroek }
1938*5d5fbe79SDavid van Moolenbroek }
1939*5d5fbe79SDavid van Moolenbroek SYS_ARCH_UNPROTECT(lev);
1940*5d5fbe79SDavid van Moolenbroek done_socket(sock);
1941*5d5fbe79SDavid van Moolenbroek }
1942*5d5fbe79SDavid van Moolenbroek #endif /* LWIP_SOCKET_SELECT */
1943*5d5fbe79SDavid van Moolenbroek
1944*5d5fbe79SDavid van Moolenbroek /**
1945*5d5fbe79SDavid van Moolenbroek * Close one end of a full-duplex connection.
1946*5d5fbe79SDavid van Moolenbroek */
1947*5d5fbe79SDavid van Moolenbroek int
lwip_shutdown(int s,int how)1948*5d5fbe79SDavid van Moolenbroek lwip_shutdown(int s, int how)
1949*5d5fbe79SDavid van Moolenbroek {
1950*5d5fbe79SDavid van Moolenbroek struct lwip_sock *sock;
1951*5d5fbe79SDavid van Moolenbroek err_t err;
1952*5d5fbe79SDavid van Moolenbroek u8_t shut_rx = 0, shut_tx = 0;
1953*5d5fbe79SDavid van Moolenbroek
1954*5d5fbe79SDavid van Moolenbroek LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_shutdown(%d, how=%d)\n", s, how));
1955*5d5fbe79SDavid van Moolenbroek
1956*5d5fbe79SDavid van Moolenbroek sock = get_socket(s);
1957*5d5fbe79SDavid van Moolenbroek if (!sock) {
1958*5d5fbe79SDavid van Moolenbroek return -1;
1959*5d5fbe79SDavid van Moolenbroek }
1960*5d5fbe79SDavid van Moolenbroek
1961*5d5fbe79SDavid van Moolenbroek if (sock->conn != NULL) {
1962*5d5fbe79SDavid van Moolenbroek if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_TCP) {
1963*5d5fbe79SDavid van Moolenbroek sock_set_errno(sock, EOPNOTSUPP);
1964*5d5fbe79SDavid van Moolenbroek done_socket(sock);
1965*5d5fbe79SDavid van Moolenbroek return -1;
1966*5d5fbe79SDavid van Moolenbroek }
1967*5d5fbe79SDavid van Moolenbroek } else {
1968*5d5fbe79SDavid van Moolenbroek sock_set_errno(sock, ENOTCONN);
1969*5d5fbe79SDavid van Moolenbroek done_socket(sock);
1970*5d5fbe79SDavid van Moolenbroek return -1;
1971*5d5fbe79SDavid van Moolenbroek }
1972*5d5fbe79SDavid van Moolenbroek
1973*5d5fbe79SDavid van Moolenbroek if (how == SHUT_RD) {
1974*5d5fbe79SDavid van Moolenbroek shut_rx = 1;
1975*5d5fbe79SDavid van Moolenbroek } else if (how == SHUT_WR) {
1976*5d5fbe79SDavid van Moolenbroek shut_tx = 1;
1977*5d5fbe79SDavid van Moolenbroek } else if (how == SHUT_RDWR) {
1978*5d5fbe79SDavid van Moolenbroek shut_rx = 1;
1979*5d5fbe79SDavid van Moolenbroek shut_tx = 1;
1980*5d5fbe79SDavid van Moolenbroek } else {
1981*5d5fbe79SDavid van Moolenbroek sock_set_errno(sock, EINVAL);
1982*5d5fbe79SDavid van Moolenbroek done_socket(sock);
1983*5d5fbe79SDavid van Moolenbroek return -1;
1984*5d5fbe79SDavid van Moolenbroek }
1985*5d5fbe79SDavid van Moolenbroek err = netconn_shutdown(sock->conn, shut_rx, shut_tx);
1986*5d5fbe79SDavid van Moolenbroek
1987*5d5fbe79SDavid van Moolenbroek sock_set_errno(sock, err_to_errno(err));
1988*5d5fbe79SDavid van Moolenbroek done_socket(sock);
1989*5d5fbe79SDavid van Moolenbroek return (err == ERR_OK ? 0 : -1);
1990*5d5fbe79SDavid van Moolenbroek }
1991*5d5fbe79SDavid van Moolenbroek
1992*5d5fbe79SDavid van Moolenbroek static int
lwip_getaddrname(int s,struct sockaddr * name,socklen_t * namelen,u8_t local)1993*5d5fbe79SDavid van Moolenbroek lwip_getaddrname(int s, struct sockaddr *name, socklen_t *namelen, u8_t local)
1994*5d5fbe79SDavid van Moolenbroek {
1995*5d5fbe79SDavid van Moolenbroek struct lwip_sock *sock;
1996*5d5fbe79SDavid van Moolenbroek union sockaddr_aligned saddr;
1997*5d5fbe79SDavid van Moolenbroek ip_addr_t naddr;
1998*5d5fbe79SDavid van Moolenbroek u16_t port;
1999*5d5fbe79SDavid van Moolenbroek err_t err;
2000*5d5fbe79SDavid van Moolenbroek
2001*5d5fbe79SDavid van Moolenbroek sock = get_socket(s);
2002*5d5fbe79SDavid van Moolenbroek if (!sock) {
2003*5d5fbe79SDavid van Moolenbroek return -1;
2004*5d5fbe79SDavid van Moolenbroek }
2005*5d5fbe79SDavid van Moolenbroek
2006*5d5fbe79SDavid van Moolenbroek /* get the IP address and port */
2007*5d5fbe79SDavid van Moolenbroek err = netconn_getaddr(sock->conn, &naddr, &port, local);
2008*5d5fbe79SDavid van Moolenbroek if (err != ERR_OK) {
2009*5d5fbe79SDavid van Moolenbroek sock_set_errno(sock, err_to_errno(err));
2010*5d5fbe79SDavid van Moolenbroek done_socket(sock);
2011*5d5fbe79SDavid van Moolenbroek return -1;
2012*5d5fbe79SDavid van Moolenbroek }
2013*5d5fbe79SDavid van Moolenbroek
2014*5d5fbe79SDavid van Moolenbroek #if LWIP_IPV4 && LWIP_IPV6
2015*5d5fbe79SDavid van Moolenbroek /* Dual-stack: Map IPv4 addresses to IPv4 mapped IPv6 */
2016*5d5fbe79SDavid van Moolenbroek if (NETCONNTYPE_ISIPV6(netconn_type(sock->conn)) &&
2017*5d5fbe79SDavid van Moolenbroek IP_IS_V4_VAL(naddr)) {
2018*5d5fbe79SDavid van Moolenbroek ip4_2_ipv4_mapped_ipv6(ip_2_ip6(&naddr), ip_2_ip4(&naddr));
2019*5d5fbe79SDavid van Moolenbroek IP_SET_TYPE_VAL(naddr, IPADDR_TYPE_V6);
2020*5d5fbe79SDavid van Moolenbroek }
2021*5d5fbe79SDavid van Moolenbroek #endif /* LWIP_IPV4 && LWIP_IPV6 */
2022*5d5fbe79SDavid van Moolenbroek
2023*5d5fbe79SDavid van Moolenbroek IPADDR_PORT_TO_SOCKADDR(&saddr, &naddr, port);
2024*5d5fbe79SDavid van Moolenbroek
2025*5d5fbe79SDavid van Moolenbroek LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getaddrname(%d, addr=", s));
2026*5d5fbe79SDavid van Moolenbroek ip_addr_debug_print_val(SOCKETS_DEBUG, naddr);
2027*5d5fbe79SDavid van Moolenbroek LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F")\n", port));
2028*5d5fbe79SDavid van Moolenbroek
2029*5d5fbe79SDavid van Moolenbroek if (*namelen > saddr.sa.sa_len) {
2030*5d5fbe79SDavid van Moolenbroek *namelen = saddr.sa.sa_len;
2031*5d5fbe79SDavid van Moolenbroek }
2032*5d5fbe79SDavid van Moolenbroek MEMCPY(name, &saddr, *namelen);
2033*5d5fbe79SDavid van Moolenbroek
2034*5d5fbe79SDavid van Moolenbroek sock_set_errno(sock, 0);
2035*5d5fbe79SDavid van Moolenbroek done_socket(sock);
2036*5d5fbe79SDavid van Moolenbroek return 0;
2037*5d5fbe79SDavid van Moolenbroek }
2038*5d5fbe79SDavid van Moolenbroek
2039*5d5fbe79SDavid van Moolenbroek int
lwip_getpeername(int s,struct sockaddr * name,socklen_t * namelen)2040*5d5fbe79SDavid van Moolenbroek lwip_getpeername(int s, struct sockaddr *name, socklen_t *namelen)
2041*5d5fbe79SDavid van Moolenbroek {
2042*5d5fbe79SDavid van Moolenbroek return lwip_getaddrname(s, name, namelen, 0);
2043*5d5fbe79SDavid van Moolenbroek }
2044*5d5fbe79SDavid van Moolenbroek
2045*5d5fbe79SDavid van Moolenbroek int
lwip_getsockname(int s,struct sockaddr * name,socklen_t * namelen)2046*5d5fbe79SDavid van Moolenbroek lwip_getsockname(int s, struct sockaddr *name, socklen_t *namelen)
2047*5d5fbe79SDavid van Moolenbroek {
2048*5d5fbe79SDavid van Moolenbroek return lwip_getaddrname(s, name, namelen, 1);
2049*5d5fbe79SDavid van Moolenbroek }
2050*5d5fbe79SDavid van Moolenbroek
2051*5d5fbe79SDavid van Moolenbroek int
lwip_getsockopt(int s,int level,int optname,void * optval,socklen_t * optlen)2052*5d5fbe79SDavid van Moolenbroek lwip_getsockopt(int s, int level, int optname, void *optval, socklen_t *optlen)
2053*5d5fbe79SDavid van Moolenbroek {
2054*5d5fbe79SDavid van Moolenbroek int err;
2055*5d5fbe79SDavid van Moolenbroek struct lwip_sock *sock = get_socket(s);
2056*5d5fbe79SDavid van Moolenbroek #if !LWIP_TCPIP_CORE_LOCKING
2057*5d5fbe79SDavid van Moolenbroek err_t cberr;
2058*5d5fbe79SDavid van Moolenbroek LWIP_SETGETSOCKOPT_DATA_VAR_DECLARE(data);
2059*5d5fbe79SDavid van Moolenbroek #endif /* !LWIP_TCPIP_CORE_LOCKING */
2060*5d5fbe79SDavid van Moolenbroek
2061*5d5fbe79SDavid van Moolenbroek if (!sock) {
2062*5d5fbe79SDavid van Moolenbroek return -1;
2063*5d5fbe79SDavid van Moolenbroek }
2064*5d5fbe79SDavid van Moolenbroek
2065*5d5fbe79SDavid van Moolenbroek if ((NULL == optval) || (NULL == optlen)) {
2066*5d5fbe79SDavid van Moolenbroek sock_set_errno(sock, EFAULT);
2067*5d5fbe79SDavid van Moolenbroek done_socket(sock);
2068*5d5fbe79SDavid van Moolenbroek return -1;
2069*5d5fbe79SDavid van Moolenbroek }
2070*5d5fbe79SDavid van Moolenbroek
2071*5d5fbe79SDavid van Moolenbroek #if LWIP_TCPIP_CORE_LOCKING
2072*5d5fbe79SDavid van Moolenbroek /* core-locking can just call the -impl function */
2073*5d5fbe79SDavid van Moolenbroek LOCK_TCPIP_CORE();
2074*5d5fbe79SDavid van Moolenbroek err = lwip_getsockopt_impl(s, level, optname, optval, optlen);
2075*5d5fbe79SDavid van Moolenbroek UNLOCK_TCPIP_CORE();
2076*5d5fbe79SDavid van Moolenbroek
2077*5d5fbe79SDavid van Moolenbroek #else /* LWIP_TCPIP_CORE_LOCKING */
2078*5d5fbe79SDavid van Moolenbroek
2079*5d5fbe79SDavid van Moolenbroek #if LWIP_MPU_COMPATIBLE
2080*5d5fbe79SDavid van Moolenbroek /* MPU_COMPATIBLE copies the optval data, so check for max size here */
2081*5d5fbe79SDavid van Moolenbroek if (*optlen > LWIP_SETGETSOCKOPT_MAXOPTLEN) {
2082*5d5fbe79SDavid van Moolenbroek sock_set_errno(sock, ENOBUFS);
2083*5d5fbe79SDavid van Moolenbroek done_socket(sock);
2084*5d5fbe79SDavid van Moolenbroek return -1;
2085*5d5fbe79SDavid van Moolenbroek }
2086*5d5fbe79SDavid van Moolenbroek #endif /* LWIP_MPU_COMPATIBLE */
2087*5d5fbe79SDavid van Moolenbroek
2088*5d5fbe79SDavid van Moolenbroek LWIP_SETGETSOCKOPT_DATA_VAR_ALLOC(data, sock);
2089*5d5fbe79SDavid van Moolenbroek LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).s = s;
2090*5d5fbe79SDavid van Moolenbroek LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).level = level;
2091*5d5fbe79SDavid van Moolenbroek LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).optname = optname;
2092*5d5fbe79SDavid van Moolenbroek LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).optlen = *optlen;
2093*5d5fbe79SDavid van Moolenbroek #if !LWIP_MPU_COMPATIBLE
2094*5d5fbe79SDavid van Moolenbroek LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).optval.p = optval;
2095*5d5fbe79SDavid van Moolenbroek #endif /* !LWIP_MPU_COMPATIBLE */
2096*5d5fbe79SDavid van Moolenbroek LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).err = 0;
2097*5d5fbe79SDavid van Moolenbroek #if LWIP_NETCONN_SEM_PER_THREAD
2098*5d5fbe79SDavid van Moolenbroek LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).completed_sem = LWIP_NETCONN_THREAD_SEM_GET();
2099*5d5fbe79SDavid van Moolenbroek #else
2100*5d5fbe79SDavid van Moolenbroek LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).completed_sem = &sock->conn->op_completed;
2101*5d5fbe79SDavid van Moolenbroek #endif
2102*5d5fbe79SDavid van Moolenbroek cberr = tcpip_callback(lwip_getsockopt_callback, &LWIP_SETGETSOCKOPT_DATA_VAR_REF(data));
2103*5d5fbe79SDavid van Moolenbroek if (cberr != ERR_OK) {
2104*5d5fbe79SDavid van Moolenbroek LWIP_SETGETSOCKOPT_DATA_VAR_FREE(data);
2105*5d5fbe79SDavid van Moolenbroek sock_set_errno(sock, err_to_errno(cberr));
2106*5d5fbe79SDavid van Moolenbroek done_socket(sock);
2107*5d5fbe79SDavid van Moolenbroek return -1;
2108*5d5fbe79SDavid van Moolenbroek }
2109*5d5fbe79SDavid van Moolenbroek sys_arch_sem_wait((sys_sem_t*)(LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).completed_sem), 0);
2110*5d5fbe79SDavid van Moolenbroek
2111*5d5fbe79SDavid van Moolenbroek /* write back optlen and optval */
2112*5d5fbe79SDavid van Moolenbroek *optlen = LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).optlen;
2113*5d5fbe79SDavid van Moolenbroek #if LWIP_MPU_COMPATIBLE
2114*5d5fbe79SDavid van Moolenbroek MEMCPY(optval, LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).optval,
2115*5d5fbe79SDavid van Moolenbroek LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).optlen);
2116*5d5fbe79SDavid van Moolenbroek #endif /* LWIP_MPU_COMPATIBLE */
2117*5d5fbe79SDavid van Moolenbroek
2118*5d5fbe79SDavid van Moolenbroek /* maybe lwip_getsockopt_internal has changed err */
2119*5d5fbe79SDavid van Moolenbroek err = LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).err;
2120*5d5fbe79SDavid van Moolenbroek LWIP_SETGETSOCKOPT_DATA_VAR_FREE(data);
2121*5d5fbe79SDavid van Moolenbroek #endif /* LWIP_TCPIP_CORE_LOCKING */
2122*5d5fbe79SDavid van Moolenbroek
2123*5d5fbe79SDavid van Moolenbroek sock_set_errno(sock, err);
2124*5d5fbe79SDavid van Moolenbroek done_socket(sock);
2125*5d5fbe79SDavid van Moolenbroek return err ? -1 : 0;
2126*5d5fbe79SDavid van Moolenbroek }
2127*5d5fbe79SDavid van Moolenbroek
2128*5d5fbe79SDavid van Moolenbroek #if !LWIP_TCPIP_CORE_LOCKING
2129*5d5fbe79SDavid van Moolenbroek /** lwip_getsockopt_callback: only used without CORE_LOCKING
2130*5d5fbe79SDavid van Moolenbroek * to get into the tcpip_thread
2131*5d5fbe79SDavid van Moolenbroek */
2132*5d5fbe79SDavid van Moolenbroek static void
lwip_getsockopt_callback(void * arg)2133*5d5fbe79SDavid van Moolenbroek lwip_getsockopt_callback(void *arg)
2134*5d5fbe79SDavid van Moolenbroek {
2135*5d5fbe79SDavid van Moolenbroek struct lwip_setgetsockopt_data *data;
2136*5d5fbe79SDavid van Moolenbroek LWIP_ASSERT("arg != NULL", arg != NULL);
2137*5d5fbe79SDavid van Moolenbroek data = (struct lwip_setgetsockopt_data*)arg;
2138*5d5fbe79SDavid van Moolenbroek
2139*5d5fbe79SDavid van Moolenbroek data->err = lwip_getsockopt_impl(data->s, data->level, data->optname,
2140*5d5fbe79SDavid van Moolenbroek #if LWIP_MPU_COMPATIBLE
2141*5d5fbe79SDavid van Moolenbroek data->optval,
2142*5d5fbe79SDavid van Moolenbroek #else /* LWIP_MPU_COMPATIBLE */
2143*5d5fbe79SDavid van Moolenbroek data->optval.p,
2144*5d5fbe79SDavid van Moolenbroek #endif /* LWIP_MPU_COMPATIBLE */
2145*5d5fbe79SDavid van Moolenbroek &data->optlen);
2146*5d5fbe79SDavid van Moolenbroek
2147*5d5fbe79SDavid van Moolenbroek sys_sem_signal((sys_sem_t*)(data->completed_sem));
2148*5d5fbe79SDavid van Moolenbroek }
2149*5d5fbe79SDavid van Moolenbroek #endif /* LWIP_TCPIP_CORE_LOCKING */
2150*5d5fbe79SDavid van Moolenbroek
2151*5d5fbe79SDavid van Moolenbroek /** lwip_getsockopt_impl: the actual implementation of getsockopt:
2152*5d5fbe79SDavid van Moolenbroek * same argument as lwip_getsockopt, either called directly or through callback
2153*5d5fbe79SDavid van Moolenbroek */
2154*5d5fbe79SDavid van Moolenbroek static int
lwip_getsockopt_impl(int s,int level,int optname,void * optval,socklen_t * optlen)2155*5d5fbe79SDavid van Moolenbroek lwip_getsockopt_impl(int s, int level, int optname, void *optval, socklen_t *optlen)
2156*5d5fbe79SDavid van Moolenbroek {
2157*5d5fbe79SDavid van Moolenbroek int err = 0;
2158*5d5fbe79SDavid van Moolenbroek struct lwip_sock *sock = tryget_socket(s);
2159*5d5fbe79SDavid van Moolenbroek if (!sock) {
2160*5d5fbe79SDavid van Moolenbroek return EBADF;
2161*5d5fbe79SDavid van Moolenbroek }
2162*5d5fbe79SDavid van Moolenbroek
2163*5d5fbe79SDavid van Moolenbroek switch (level) {
2164*5d5fbe79SDavid van Moolenbroek
2165*5d5fbe79SDavid van Moolenbroek /* Level: SOL_SOCKET */
2166*5d5fbe79SDavid van Moolenbroek case SOL_SOCKET:
2167*5d5fbe79SDavid van Moolenbroek switch (optname) {
2168*5d5fbe79SDavid van Moolenbroek
2169*5d5fbe79SDavid van Moolenbroek #if LWIP_TCP
2170*5d5fbe79SDavid van Moolenbroek case SO_ACCEPTCONN:
2171*5d5fbe79SDavid van Moolenbroek LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, *optlen, int);
2172*5d5fbe79SDavid van Moolenbroek if (NETCONNTYPE_GROUP(sock->conn->type) != NETCONN_TCP) {
2173*5d5fbe79SDavid van Moolenbroek done_socket(sock);
2174*5d5fbe79SDavid van Moolenbroek return ENOPROTOOPT;
2175*5d5fbe79SDavid van Moolenbroek }
2176*5d5fbe79SDavid van Moolenbroek if ((sock->conn->pcb.tcp != NULL) && (sock->conn->pcb.tcp->state == LISTEN)) {
2177*5d5fbe79SDavid van Moolenbroek *(int*)optval = 1;
2178*5d5fbe79SDavid van Moolenbroek } else {
2179*5d5fbe79SDavid van Moolenbroek *(int*)optval = 0;
2180*5d5fbe79SDavid van Moolenbroek }
2181*5d5fbe79SDavid van Moolenbroek break;
2182*5d5fbe79SDavid van Moolenbroek #endif /* LWIP_TCP */
2183*5d5fbe79SDavid van Moolenbroek
2184*5d5fbe79SDavid van Moolenbroek /* The option flags */
2185*5d5fbe79SDavid van Moolenbroek case SO_BROADCAST:
2186*5d5fbe79SDavid van Moolenbroek case SO_KEEPALIVE:
2187*5d5fbe79SDavid van Moolenbroek #if SO_REUSE
2188*5d5fbe79SDavid van Moolenbroek case SO_REUSEADDR:
2189*5d5fbe79SDavid van Moolenbroek #endif /* SO_REUSE */
2190*5d5fbe79SDavid van Moolenbroek LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, *optlen, int);
2191*5d5fbe79SDavid van Moolenbroek *(int*)optval = ip_get_option(sock->conn->pcb.ip, optname);
2192*5d5fbe79SDavid van Moolenbroek LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, SOL_SOCKET, optname=0x%x, ..) = %s\n",
2193*5d5fbe79SDavid van Moolenbroek s, optname, (*(int*)optval?"on":"off")));
2194*5d5fbe79SDavid van Moolenbroek break;
2195*5d5fbe79SDavid van Moolenbroek
2196*5d5fbe79SDavid van Moolenbroek case SO_TYPE:
2197*5d5fbe79SDavid van Moolenbroek LWIP_SOCKOPT_CHECK_OPTLEN_CONN(sock, *optlen, int);
2198*5d5fbe79SDavid van Moolenbroek switch (NETCONNTYPE_GROUP(netconn_type(sock->conn))) {
2199*5d5fbe79SDavid van Moolenbroek case NETCONN_RAW:
2200*5d5fbe79SDavid van Moolenbroek *(int*)optval = SOCK_RAW;
2201*5d5fbe79SDavid van Moolenbroek break;
2202*5d5fbe79SDavid van Moolenbroek case NETCONN_TCP:
2203*5d5fbe79SDavid van Moolenbroek *(int*)optval = SOCK_STREAM;
2204*5d5fbe79SDavid van Moolenbroek break;
2205*5d5fbe79SDavid van Moolenbroek case NETCONN_UDP:
2206*5d5fbe79SDavid van Moolenbroek *(int*)optval = SOCK_DGRAM;
2207*5d5fbe79SDavid van Moolenbroek break;
2208*5d5fbe79SDavid van Moolenbroek default: /* unrecognized socket type */
2209*5d5fbe79SDavid van Moolenbroek *(int*)optval = netconn_type(sock->conn);
2210*5d5fbe79SDavid van Moolenbroek LWIP_DEBUGF(SOCKETS_DEBUG,
2211*5d5fbe79SDavid van Moolenbroek ("lwip_getsockopt(%d, SOL_SOCKET, SO_TYPE): unrecognized socket type %d\n",
2212*5d5fbe79SDavid van Moolenbroek s, *(int *)optval));
2213*5d5fbe79SDavid van Moolenbroek } /* switch (netconn_type(sock->conn)) */
2214*5d5fbe79SDavid van Moolenbroek LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, SOL_SOCKET, SO_TYPE) = %d\n",
2215*5d5fbe79SDavid van Moolenbroek s, *(int *)optval));
2216*5d5fbe79SDavid van Moolenbroek break;
2217*5d5fbe79SDavid van Moolenbroek
2218*5d5fbe79SDavid van Moolenbroek case SO_ERROR:
2219*5d5fbe79SDavid van Moolenbroek LWIP_SOCKOPT_CHECK_OPTLEN(*optlen, int);
2220*5d5fbe79SDavid van Moolenbroek /* only overwrite ERR_OK or temporary errors */
2221*5d5fbe79SDavid van Moolenbroek if (((sock->err == 0) || (sock->err == EINPROGRESS)) && (sock->conn != NULL)) {
2222*5d5fbe79SDavid van Moolenbroek sock_set_errno(sock, err_to_errno(sock->conn->last_err));
2223*5d5fbe79SDavid van Moolenbroek }
2224*5d5fbe79SDavid van Moolenbroek *(int *)optval = sock->err;
2225*5d5fbe79SDavid van Moolenbroek sock->err = 0;
2226*5d5fbe79SDavid van Moolenbroek LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, SOL_SOCKET, SO_ERROR) = %d\n",
2227*5d5fbe79SDavid van Moolenbroek s, *(int *)optval));
2228*5d5fbe79SDavid van Moolenbroek break;
2229*5d5fbe79SDavid van Moolenbroek
2230*5d5fbe79SDavid van Moolenbroek #if LWIP_SO_SNDTIMEO
2231*5d5fbe79SDavid van Moolenbroek case SO_SNDTIMEO:
2232*5d5fbe79SDavid van Moolenbroek LWIP_SOCKOPT_CHECK_OPTLEN_CONN(sock, *optlen, LWIP_SO_SNDRCVTIMEO_OPTTYPE);
2233*5d5fbe79SDavid van Moolenbroek LWIP_SO_SNDRCVTIMEO_SET(optval, netconn_get_sendtimeout(sock->conn));
2234*5d5fbe79SDavid van Moolenbroek break;
2235*5d5fbe79SDavid van Moolenbroek #endif /* LWIP_SO_SNDTIMEO */
2236*5d5fbe79SDavid van Moolenbroek #if LWIP_SO_RCVTIMEO
2237*5d5fbe79SDavid van Moolenbroek case SO_RCVTIMEO:
2238*5d5fbe79SDavid van Moolenbroek LWIP_SOCKOPT_CHECK_OPTLEN_CONN(sock, *optlen, LWIP_SO_SNDRCVTIMEO_OPTTYPE);
2239*5d5fbe79SDavid van Moolenbroek LWIP_SO_SNDRCVTIMEO_SET(optval, netconn_get_recvtimeout(sock->conn));
2240*5d5fbe79SDavid van Moolenbroek break;
2241*5d5fbe79SDavid van Moolenbroek #endif /* LWIP_SO_RCVTIMEO */
2242*5d5fbe79SDavid van Moolenbroek #if LWIP_SO_RCVBUF
2243*5d5fbe79SDavid van Moolenbroek case SO_RCVBUF:
2244*5d5fbe79SDavid van Moolenbroek LWIP_SOCKOPT_CHECK_OPTLEN_CONN(sock, *optlen, int);
2245*5d5fbe79SDavid van Moolenbroek *(int *)optval = netconn_get_recvbufsize(sock->conn);
2246*5d5fbe79SDavid van Moolenbroek break;
2247*5d5fbe79SDavid van Moolenbroek #endif /* LWIP_SO_RCVBUF */
2248*5d5fbe79SDavid van Moolenbroek #if LWIP_SO_LINGER
2249*5d5fbe79SDavid van Moolenbroek case SO_LINGER:
2250*5d5fbe79SDavid van Moolenbroek {
2251*5d5fbe79SDavid van Moolenbroek s16_t conn_linger;
2252*5d5fbe79SDavid van Moolenbroek struct linger* linger = (struct linger*)optval;
2253*5d5fbe79SDavid van Moolenbroek LWIP_SOCKOPT_CHECK_OPTLEN_CONN(sock, *optlen, struct linger);
2254*5d5fbe79SDavid van Moolenbroek conn_linger = sock->conn->linger;
2255*5d5fbe79SDavid van Moolenbroek if (conn_linger >= 0) {
2256*5d5fbe79SDavid van Moolenbroek linger->l_onoff = 1;
2257*5d5fbe79SDavid van Moolenbroek linger->l_linger = (int)conn_linger;
2258*5d5fbe79SDavid van Moolenbroek } else {
2259*5d5fbe79SDavid van Moolenbroek linger->l_onoff = 0;
2260*5d5fbe79SDavid van Moolenbroek linger->l_linger = 0;
2261*5d5fbe79SDavid van Moolenbroek }
2262*5d5fbe79SDavid van Moolenbroek }
2263*5d5fbe79SDavid van Moolenbroek break;
2264*5d5fbe79SDavid van Moolenbroek #endif /* LWIP_SO_LINGER */
2265*5d5fbe79SDavid van Moolenbroek #if LWIP_UDP
2266*5d5fbe79SDavid van Moolenbroek case SO_NO_CHECK:
2267*5d5fbe79SDavid van Moolenbroek LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, *optlen, int, NETCONN_UDP);
2268*5d5fbe79SDavid van Moolenbroek #if LWIP_UDPLITE
2269*5d5fbe79SDavid van Moolenbroek if ((udp_flags(sock->conn->pcb.udp) & UDP_FLAGS_UDPLITE) != 0) {
2270*5d5fbe79SDavid van Moolenbroek /* this flag is only available for UDP, not for UDP lite */
2271*5d5fbe79SDavid van Moolenbroek done_socket(sock);
2272*5d5fbe79SDavid van Moolenbroek return EAFNOSUPPORT;
2273*5d5fbe79SDavid van Moolenbroek }
2274*5d5fbe79SDavid van Moolenbroek #endif /* LWIP_UDPLITE */
2275*5d5fbe79SDavid van Moolenbroek *(int*)optval = (udp_flags(sock->conn->pcb.udp) & UDP_FLAGS_NOCHKSUM) ? 1 : 0;
2276*5d5fbe79SDavid van Moolenbroek break;
2277*5d5fbe79SDavid van Moolenbroek #endif /* LWIP_UDP*/
2278*5d5fbe79SDavid van Moolenbroek default:
2279*5d5fbe79SDavid van Moolenbroek LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, SOL_SOCKET, UNIMPL: optname=0x%x, ..)\n",
2280*5d5fbe79SDavid van Moolenbroek s, optname));
2281*5d5fbe79SDavid van Moolenbroek err = ENOPROTOOPT;
2282*5d5fbe79SDavid van Moolenbroek break;
2283*5d5fbe79SDavid van Moolenbroek } /* switch (optname) */
2284*5d5fbe79SDavid van Moolenbroek break;
2285*5d5fbe79SDavid van Moolenbroek
2286*5d5fbe79SDavid van Moolenbroek /* Level: IPPROTO_IP */
2287*5d5fbe79SDavid van Moolenbroek case IPPROTO_IP:
2288*5d5fbe79SDavid van Moolenbroek switch (optname) {
2289*5d5fbe79SDavid van Moolenbroek case IP_TTL:
2290*5d5fbe79SDavid van Moolenbroek LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, *optlen, int);
2291*5d5fbe79SDavid van Moolenbroek *(int*)optval = sock->conn->pcb.ip->ttl;
2292*5d5fbe79SDavid van Moolenbroek LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, IP_TTL) = %d\n",
2293*5d5fbe79SDavid van Moolenbroek s, *(int *)optval));
2294*5d5fbe79SDavid van Moolenbroek break;
2295*5d5fbe79SDavid van Moolenbroek case IP_TOS:
2296*5d5fbe79SDavid van Moolenbroek LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, *optlen, int);
2297*5d5fbe79SDavid van Moolenbroek *(int*)optval = sock->conn->pcb.ip->tos;
2298*5d5fbe79SDavid van Moolenbroek LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, IP_TOS) = %d\n",
2299*5d5fbe79SDavid van Moolenbroek s, *(int *)optval));
2300*5d5fbe79SDavid van Moolenbroek break;
2301*5d5fbe79SDavid van Moolenbroek #if LWIP_IPV4 && LWIP_MULTICAST_TX_OPTIONS
2302*5d5fbe79SDavid van Moolenbroek case IP_MULTICAST_TTL:
2303*5d5fbe79SDavid van Moolenbroek LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, *optlen, u8_t);
2304*5d5fbe79SDavid van Moolenbroek if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_UDP) {
2305*5d5fbe79SDavid van Moolenbroek done_socket(sock);
2306*5d5fbe79SDavid van Moolenbroek return ENOPROTOOPT;
2307*5d5fbe79SDavid van Moolenbroek }
2308*5d5fbe79SDavid van Moolenbroek *(u8_t*)optval = udp_get_multicast_ttl(sock->conn->pcb.udp);
2309*5d5fbe79SDavid van Moolenbroek LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, IP_MULTICAST_TTL) = %d\n",
2310*5d5fbe79SDavid van Moolenbroek s, *(int *)optval));
2311*5d5fbe79SDavid van Moolenbroek break;
2312*5d5fbe79SDavid van Moolenbroek case IP_MULTICAST_IF:
2313*5d5fbe79SDavid van Moolenbroek LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, *optlen, struct in_addr);
2314*5d5fbe79SDavid van Moolenbroek if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_UDP) {
2315*5d5fbe79SDavid van Moolenbroek done_socket(sock);
2316*5d5fbe79SDavid van Moolenbroek return ENOPROTOOPT;
2317*5d5fbe79SDavid van Moolenbroek }
2318*5d5fbe79SDavid van Moolenbroek inet_addr_from_ip4addr((struct in_addr*)optval, udp_get_multicast_netif_addr(sock->conn->pcb.udp));
2319*5d5fbe79SDavid van Moolenbroek LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, IP_MULTICAST_IF) = 0x%"X32_F"\n",
2320*5d5fbe79SDavid van Moolenbroek s, *(u32_t *)optval));
2321*5d5fbe79SDavid van Moolenbroek break;
2322*5d5fbe79SDavid van Moolenbroek case IP_MULTICAST_LOOP:
2323*5d5fbe79SDavid van Moolenbroek LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, *optlen, u8_t);
2324*5d5fbe79SDavid van Moolenbroek if ((sock->conn->pcb.udp->flags & UDP_FLAGS_MULTICAST_LOOP) != 0) {
2325*5d5fbe79SDavid van Moolenbroek *(u8_t*)optval = 1;
2326*5d5fbe79SDavid van Moolenbroek } else {
2327*5d5fbe79SDavid van Moolenbroek *(u8_t*)optval = 0;
2328*5d5fbe79SDavid van Moolenbroek }
2329*5d5fbe79SDavid van Moolenbroek LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, IP_MULTICAST_LOOP) = %d\n",
2330*5d5fbe79SDavid van Moolenbroek s, *(int *)optval));
2331*5d5fbe79SDavid van Moolenbroek break;
2332*5d5fbe79SDavid van Moolenbroek #endif /* LWIP_IPV4 && LWIP_MULTICAST_TX_OPTIONS */
2333*5d5fbe79SDavid van Moolenbroek default:
2334*5d5fbe79SDavid van Moolenbroek LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, UNIMPL: optname=0x%x, ..)\n",
2335*5d5fbe79SDavid van Moolenbroek s, optname));
2336*5d5fbe79SDavid van Moolenbroek err = ENOPROTOOPT;
2337*5d5fbe79SDavid van Moolenbroek break;
2338*5d5fbe79SDavid van Moolenbroek } /* switch (optname) */
2339*5d5fbe79SDavid van Moolenbroek break;
2340*5d5fbe79SDavid van Moolenbroek
2341*5d5fbe79SDavid van Moolenbroek #if LWIP_TCP
2342*5d5fbe79SDavid van Moolenbroek /* Level: IPPROTO_TCP */
2343*5d5fbe79SDavid van Moolenbroek case IPPROTO_TCP:
2344*5d5fbe79SDavid van Moolenbroek /* Special case: all IPPROTO_TCP option take an int */
2345*5d5fbe79SDavid van Moolenbroek LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, *optlen, int, NETCONN_TCP);
2346*5d5fbe79SDavid van Moolenbroek if (sock->conn->pcb.tcp->state == LISTEN) {
2347*5d5fbe79SDavid van Moolenbroek done_socket(sock);
2348*5d5fbe79SDavid van Moolenbroek return EINVAL;
2349*5d5fbe79SDavid van Moolenbroek }
2350*5d5fbe79SDavid van Moolenbroek switch (optname) {
2351*5d5fbe79SDavid van Moolenbroek case TCP_NODELAY:
2352*5d5fbe79SDavid van Moolenbroek *(int*)optval = tcp_nagle_disabled(sock->conn->pcb.tcp);
2353*5d5fbe79SDavid van Moolenbroek LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_TCP, TCP_NODELAY) = %s\n",
2354*5d5fbe79SDavid van Moolenbroek s, (*(int*)optval)?"on":"off") );
2355*5d5fbe79SDavid van Moolenbroek break;
2356*5d5fbe79SDavid van Moolenbroek case TCP_KEEPALIVE:
2357*5d5fbe79SDavid van Moolenbroek *(int*)optval = (int)sock->conn->pcb.tcp->keep_idle;
2358*5d5fbe79SDavid van Moolenbroek LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_TCP, TCP_KEEPALIVE) = %d\n",
2359*5d5fbe79SDavid van Moolenbroek s, *(int *)optval));
2360*5d5fbe79SDavid van Moolenbroek break;
2361*5d5fbe79SDavid van Moolenbroek
2362*5d5fbe79SDavid van Moolenbroek #if LWIP_TCP_KEEPALIVE
2363*5d5fbe79SDavid van Moolenbroek case TCP_KEEPIDLE:
2364*5d5fbe79SDavid van Moolenbroek *(int*)optval = (int)(sock->conn->pcb.tcp->keep_idle/1000);
2365*5d5fbe79SDavid van Moolenbroek LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_TCP, TCP_KEEPIDLE) = %d\n",
2366*5d5fbe79SDavid van Moolenbroek s, *(int *)optval));
2367*5d5fbe79SDavid van Moolenbroek break;
2368*5d5fbe79SDavid van Moolenbroek case TCP_KEEPINTVL:
2369*5d5fbe79SDavid van Moolenbroek *(int*)optval = (int)(sock->conn->pcb.tcp->keep_intvl/1000);
2370*5d5fbe79SDavid van Moolenbroek LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_TCP, TCP_KEEPINTVL) = %d\n",
2371*5d5fbe79SDavid van Moolenbroek s, *(int *)optval));
2372*5d5fbe79SDavid van Moolenbroek break;
2373*5d5fbe79SDavid van Moolenbroek case TCP_KEEPCNT:
2374*5d5fbe79SDavid van Moolenbroek *(int*)optval = (int)sock->conn->pcb.tcp->keep_cnt;
2375*5d5fbe79SDavid van Moolenbroek LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_TCP, TCP_KEEPCNT) = %d\n",
2376*5d5fbe79SDavid van Moolenbroek s, *(int *)optval));
2377*5d5fbe79SDavid van Moolenbroek break;
2378*5d5fbe79SDavid van Moolenbroek #endif /* LWIP_TCP_KEEPALIVE */
2379*5d5fbe79SDavid van Moolenbroek default:
2380*5d5fbe79SDavid van Moolenbroek LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_TCP, UNIMPL: optname=0x%x, ..)\n",
2381*5d5fbe79SDavid van Moolenbroek s, optname));
2382*5d5fbe79SDavid van Moolenbroek err = ENOPROTOOPT;
2383*5d5fbe79SDavid van Moolenbroek break;
2384*5d5fbe79SDavid van Moolenbroek } /* switch (optname) */
2385*5d5fbe79SDavid van Moolenbroek break;
2386*5d5fbe79SDavid van Moolenbroek #endif /* LWIP_TCP */
2387*5d5fbe79SDavid van Moolenbroek
2388*5d5fbe79SDavid van Moolenbroek #if LWIP_IPV6
2389*5d5fbe79SDavid van Moolenbroek /* Level: IPPROTO_IPV6 */
2390*5d5fbe79SDavid van Moolenbroek case IPPROTO_IPV6:
2391*5d5fbe79SDavid van Moolenbroek switch (optname) {
2392*5d5fbe79SDavid van Moolenbroek case IPV6_V6ONLY:
2393*5d5fbe79SDavid van Moolenbroek LWIP_SOCKOPT_CHECK_OPTLEN_CONN(sock, *optlen, int);
2394*5d5fbe79SDavid van Moolenbroek *(int*)optval = (netconn_get_ipv6only(sock->conn) ? 1 : 0);
2395*5d5fbe79SDavid van Moolenbroek LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IPV6, IPV6_V6ONLY) = %d\n",
2396*5d5fbe79SDavid van Moolenbroek s, *(int *)optval));
2397*5d5fbe79SDavid van Moolenbroek break;
2398*5d5fbe79SDavid van Moolenbroek default:
2399*5d5fbe79SDavid van Moolenbroek LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IPV6, UNIMPL: optname=0x%x, ..)\n",
2400*5d5fbe79SDavid van Moolenbroek s, optname));
2401*5d5fbe79SDavid van Moolenbroek err = ENOPROTOOPT;
2402*5d5fbe79SDavid van Moolenbroek break;
2403*5d5fbe79SDavid van Moolenbroek } /* switch (optname) */
2404*5d5fbe79SDavid van Moolenbroek break;
2405*5d5fbe79SDavid van Moolenbroek #endif /* LWIP_IPV6 */
2406*5d5fbe79SDavid van Moolenbroek
2407*5d5fbe79SDavid van Moolenbroek #if LWIP_UDP && LWIP_UDPLITE
2408*5d5fbe79SDavid van Moolenbroek /* Level: IPPROTO_UDPLITE */
2409*5d5fbe79SDavid van Moolenbroek case IPPROTO_UDPLITE:
2410*5d5fbe79SDavid van Moolenbroek /* Special case: all IPPROTO_UDPLITE option take an int */
2411*5d5fbe79SDavid van Moolenbroek LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, *optlen, int);
2412*5d5fbe79SDavid van Moolenbroek /* If this is no UDP lite socket, ignore any options. */
2413*5d5fbe79SDavid van Moolenbroek if (!NETCONNTYPE_ISUDPLITE(netconn_type(sock->conn))) {
2414*5d5fbe79SDavid van Moolenbroek done_socket(sock);
2415*5d5fbe79SDavid van Moolenbroek return ENOPROTOOPT;
2416*5d5fbe79SDavid van Moolenbroek }
2417*5d5fbe79SDavid van Moolenbroek switch (optname) {
2418*5d5fbe79SDavid van Moolenbroek case UDPLITE_SEND_CSCOV:
2419*5d5fbe79SDavid van Moolenbroek *(int*)optval = sock->conn->pcb.udp->chksum_len_tx;
2420*5d5fbe79SDavid van Moolenbroek LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_UDPLITE, UDPLITE_SEND_CSCOV) = %d\n",
2421*5d5fbe79SDavid van Moolenbroek s, (*(int*)optval)) );
2422*5d5fbe79SDavid van Moolenbroek break;
2423*5d5fbe79SDavid van Moolenbroek case UDPLITE_RECV_CSCOV:
2424*5d5fbe79SDavid van Moolenbroek *(int*)optval = sock->conn->pcb.udp->chksum_len_rx;
2425*5d5fbe79SDavid van Moolenbroek LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_UDPLITE, UDPLITE_RECV_CSCOV) = %d\n",
2426*5d5fbe79SDavid van Moolenbroek s, (*(int*)optval)) );
2427*5d5fbe79SDavid van Moolenbroek break;
2428*5d5fbe79SDavid van Moolenbroek default:
2429*5d5fbe79SDavid van Moolenbroek LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_UDPLITE, UNIMPL: optname=0x%x, ..)\n",
2430*5d5fbe79SDavid van Moolenbroek s, optname));
2431*5d5fbe79SDavid van Moolenbroek err = ENOPROTOOPT;
2432*5d5fbe79SDavid van Moolenbroek break;
2433*5d5fbe79SDavid van Moolenbroek } /* switch (optname) */
2434*5d5fbe79SDavid van Moolenbroek break;
2435*5d5fbe79SDavid van Moolenbroek #endif /* LWIP_UDP */
2436*5d5fbe79SDavid van Moolenbroek /* Level: IPPROTO_RAW */
2437*5d5fbe79SDavid van Moolenbroek case IPPROTO_RAW:
2438*5d5fbe79SDavid van Moolenbroek switch (optname) {
2439*5d5fbe79SDavid van Moolenbroek #if LWIP_IPV6 && LWIP_RAW
2440*5d5fbe79SDavid van Moolenbroek case IPV6_CHECKSUM:
2441*5d5fbe79SDavid van Moolenbroek LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, *optlen, int, NETCONN_RAW);
2442*5d5fbe79SDavid van Moolenbroek if (sock->conn->pcb.raw->chksum_reqd == 0) {
2443*5d5fbe79SDavid van Moolenbroek *(int *)optval = -1;
2444*5d5fbe79SDavid van Moolenbroek } else {
2445*5d5fbe79SDavid van Moolenbroek *(int *)optval = sock->conn->pcb.raw->chksum_offset;
2446*5d5fbe79SDavid van Moolenbroek }
2447*5d5fbe79SDavid van Moolenbroek LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_RAW, IPV6_CHECKSUM) = %d\n",
2448*5d5fbe79SDavid van Moolenbroek s, (*(int*)optval)) );
2449*5d5fbe79SDavid van Moolenbroek break;
2450*5d5fbe79SDavid van Moolenbroek #endif /* LWIP_IPV6 && LWIP_RAW */
2451*5d5fbe79SDavid van Moolenbroek default:
2452*5d5fbe79SDavid van Moolenbroek LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_RAW, UNIMPL: optname=0x%x, ..)\n",
2453*5d5fbe79SDavid van Moolenbroek s, optname));
2454*5d5fbe79SDavid van Moolenbroek err = ENOPROTOOPT;
2455*5d5fbe79SDavid van Moolenbroek break;
2456*5d5fbe79SDavid van Moolenbroek } /* switch (optname) */
2457*5d5fbe79SDavid van Moolenbroek break;
2458*5d5fbe79SDavid van Moolenbroek default:
2459*5d5fbe79SDavid van Moolenbroek LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, level=0x%x, UNIMPL: optname=0x%x, ..)\n",
2460*5d5fbe79SDavid van Moolenbroek s, level, optname));
2461*5d5fbe79SDavid van Moolenbroek err = ENOPROTOOPT;
2462*5d5fbe79SDavid van Moolenbroek break;
2463*5d5fbe79SDavid van Moolenbroek } /* switch (level) */
2464*5d5fbe79SDavid van Moolenbroek
2465*5d5fbe79SDavid van Moolenbroek done_socket(sock);
2466*5d5fbe79SDavid van Moolenbroek return err;
2467*5d5fbe79SDavid van Moolenbroek }
2468*5d5fbe79SDavid van Moolenbroek
2469*5d5fbe79SDavid van Moolenbroek int
lwip_setsockopt(int s,int level,int optname,const void * optval,socklen_t optlen)2470*5d5fbe79SDavid van Moolenbroek lwip_setsockopt(int s, int level, int optname, const void *optval, socklen_t optlen)
2471*5d5fbe79SDavid van Moolenbroek {
2472*5d5fbe79SDavid van Moolenbroek int err = 0;
2473*5d5fbe79SDavid van Moolenbroek struct lwip_sock *sock = get_socket(s);
2474*5d5fbe79SDavid van Moolenbroek #if !LWIP_TCPIP_CORE_LOCKING
2475*5d5fbe79SDavid van Moolenbroek err_t cberr;
2476*5d5fbe79SDavid van Moolenbroek LWIP_SETGETSOCKOPT_DATA_VAR_DECLARE(data);
2477*5d5fbe79SDavid van Moolenbroek #endif /* !LWIP_TCPIP_CORE_LOCKING */
2478*5d5fbe79SDavid van Moolenbroek
2479*5d5fbe79SDavid van Moolenbroek if (!sock) {
2480*5d5fbe79SDavid van Moolenbroek return -1;
2481*5d5fbe79SDavid van Moolenbroek }
2482*5d5fbe79SDavid van Moolenbroek
2483*5d5fbe79SDavid van Moolenbroek if (NULL == optval) {
2484*5d5fbe79SDavid van Moolenbroek sock_set_errno(sock, EFAULT);
2485*5d5fbe79SDavid van Moolenbroek done_socket(sock);
2486*5d5fbe79SDavid van Moolenbroek return -1;
2487*5d5fbe79SDavid van Moolenbroek }
2488*5d5fbe79SDavid van Moolenbroek
2489*5d5fbe79SDavid van Moolenbroek #if LWIP_TCPIP_CORE_LOCKING
2490*5d5fbe79SDavid van Moolenbroek /* core-locking can just call the -impl function */
2491*5d5fbe79SDavid van Moolenbroek LOCK_TCPIP_CORE();
2492*5d5fbe79SDavid van Moolenbroek err = lwip_setsockopt_impl(s, level, optname, optval, optlen);
2493*5d5fbe79SDavid van Moolenbroek UNLOCK_TCPIP_CORE();
2494*5d5fbe79SDavid van Moolenbroek
2495*5d5fbe79SDavid van Moolenbroek #else /* LWIP_TCPIP_CORE_LOCKING */
2496*5d5fbe79SDavid van Moolenbroek
2497*5d5fbe79SDavid van Moolenbroek #if LWIP_MPU_COMPATIBLE
2498*5d5fbe79SDavid van Moolenbroek /* MPU_COMPATIBLE copies the optval data, so check for max size here */
2499*5d5fbe79SDavid van Moolenbroek if (optlen > LWIP_SETGETSOCKOPT_MAXOPTLEN) {
2500*5d5fbe79SDavid van Moolenbroek sock_set_errno(sock, ENOBUFS);
2501*5d5fbe79SDavid van Moolenbroek done_socket(sock);
2502*5d5fbe79SDavid van Moolenbroek return -1;
2503*5d5fbe79SDavid van Moolenbroek }
2504*5d5fbe79SDavid van Moolenbroek #endif /* LWIP_MPU_COMPATIBLE */
2505*5d5fbe79SDavid van Moolenbroek
2506*5d5fbe79SDavid van Moolenbroek LWIP_SETGETSOCKOPT_DATA_VAR_ALLOC(data, sock);
2507*5d5fbe79SDavid van Moolenbroek LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).s = s;
2508*5d5fbe79SDavid van Moolenbroek LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).level = level;
2509*5d5fbe79SDavid van Moolenbroek LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).optname = optname;
2510*5d5fbe79SDavid van Moolenbroek LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).optlen = optlen;
2511*5d5fbe79SDavid van Moolenbroek #if LWIP_MPU_COMPATIBLE
2512*5d5fbe79SDavid van Moolenbroek MEMCPY(LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).optval, optval, optlen);
2513*5d5fbe79SDavid van Moolenbroek #else /* LWIP_MPU_COMPATIBLE */
2514*5d5fbe79SDavid van Moolenbroek LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).optval.pc = (const void*)optval;
2515*5d5fbe79SDavid van Moolenbroek #endif /* LWIP_MPU_COMPATIBLE */
2516*5d5fbe79SDavid van Moolenbroek LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).err = 0;
2517*5d5fbe79SDavid van Moolenbroek #if LWIP_NETCONN_SEM_PER_THREAD
2518*5d5fbe79SDavid van Moolenbroek LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).completed_sem = LWIP_NETCONN_THREAD_SEM_GET();
2519*5d5fbe79SDavid van Moolenbroek #else
2520*5d5fbe79SDavid van Moolenbroek LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).completed_sem = &sock->conn->op_completed;
2521*5d5fbe79SDavid van Moolenbroek #endif
2522*5d5fbe79SDavid van Moolenbroek cberr = tcpip_callback(lwip_setsockopt_callback, &LWIP_SETGETSOCKOPT_DATA_VAR_REF(data));
2523*5d5fbe79SDavid van Moolenbroek if (cberr != ERR_OK) {
2524*5d5fbe79SDavid van Moolenbroek LWIP_SETGETSOCKOPT_DATA_VAR_FREE(data);
2525*5d5fbe79SDavid van Moolenbroek sock_set_errno(sock, err_to_errno(cberr));
2526*5d5fbe79SDavid van Moolenbroek done_socket(sock);
2527*5d5fbe79SDavid van Moolenbroek return -1;
2528*5d5fbe79SDavid van Moolenbroek }
2529*5d5fbe79SDavid van Moolenbroek sys_arch_sem_wait((sys_sem_t*)(LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).completed_sem), 0);
2530*5d5fbe79SDavid van Moolenbroek
2531*5d5fbe79SDavid van Moolenbroek /* maybe lwip_getsockopt_internal has changed err */
2532*5d5fbe79SDavid van Moolenbroek err = LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).err;
2533*5d5fbe79SDavid van Moolenbroek LWIP_SETGETSOCKOPT_DATA_VAR_FREE(data);
2534*5d5fbe79SDavid van Moolenbroek #endif /* LWIP_TCPIP_CORE_LOCKING */
2535*5d5fbe79SDavid van Moolenbroek
2536*5d5fbe79SDavid van Moolenbroek sock_set_errno(sock, err);
2537*5d5fbe79SDavid van Moolenbroek done_socket(sock);
2538*5d5fbe79SDavid van Moolenbroek return err ? -1 : 0;
2539*5d5fbe79SDavid van Moolenbroek }
2540*5d5fbe79SDavid van Moolenbroek
2541*5d5fbe79SDavid van Moolenbroek #if !LWIP_TCPIP_CORE_LOCKING
2542*5d5fbe79SDavid van Moolenbroek /** lwip_setsockopt_callback: only used without CORE_LOCKING
2543*5d5fbe79SDavid van Moolenbroek * to get into the tcpip_thread
2544*5d5fbe79SDavid van Moolenbroek */
2545*5d5fbe79SDavid van Moolenbroek static void
lwip_setsockopt_callback(void * arg)2546*5d5fbe79SDavid van Moolenbroek lwip_setsockopt_callback(void *arg)
2547*5d5fbe79SDavid van Moolenbroek {
2548*5d5fbe79SDavid van Moolenbroek struct lwip_setgetsockopt_data *data;
2549*5d5fbe79SDavid van Moolenbroek LWIP_ASSERT("arg != NULL", arg != NULL);
2550*5d5fbe79SDavid van Moolenbroek data = (struct lwip_setgetsockopt_data*)arg;
2551*5d5fbe79SDavid van Moolenbroek
2552*5d5fbe79SDavid van Moolenbroek data->err = lwip_setsockopt_impl(data->s, data->level, data->optname,
2553*5d5fbe79SDavid van Moolenbroek #if LWIP_MPU_COMPATIBLE
2554*5d5fbe79SDavid van Moolenbroek data->optval,
2555*5d5fbe79SDavid van Moolenbroek #else /* LWIP_MPU_COMPATIBLE */
2556*5d5fbe79SDavid van Moolenbroek data->optval.pc,
2557*5d5fbe79SDavid van Moolenbroek #endif /* LWIP_MPU_COMPATIBLE */
2558*5d5fbe79SDavid van Moolenbroek data->optlen);
2559*5d5fbe79SDavid van Moolenbroek
2560*5d5fbe79SDavid van Moolenbroek sys_sem_signal((sys_sem_t*)(data->completed_sem));
2561*5d5fbe79SDavid van Moolenbroek }
2562*5d5fbe79SDavid van Moolenbroek #endif /* LWIP_TCPIP_CORE_LOCKING */
2563*5d5fbe79SDavid van Moolenbroek
2564*5d5fbe79SDavid van Moolenbroek /** lwip_setsockopt_impl: the actual implementation of setsockopt:
2565*5d5fbe79SDavid van Moolenbroek * same argument as lwip_setsockopt, either called directly or through callback
2566*5d5fbe79SDavid van Moolenbroek */
2567*5d5fbe79SDavid van Moolenbroek static int
lwip_setsockopt_impl(int s,int level,int optname,const void * optval,socklen_t optlen)2568*5d5fbe79SDavid van Moolenbroek lwip_setsockopt_impl(int s, int level, int optname, const void *optval, socklen_t optlen)
2569*5d5fbe79SDavid van Moolenbroek {
2570*5d5fbe79SDavid van Moolenbroek int err = 0;
2571*5d5fbe79SDavid van Moolenbroek struct lwip_sock *sock = tryget_socket(s);
2572*5d5fbe79SDavid van Moolenbroek if (!sock) {
2573*5d5fbe79SDavid van Moolenbroek return EBADF;
2574*5d5fbe79SDavid van Moolenbroek }
2575*5d5fbe79SDavid van Moolenbroek
2576*5d5fbe79SDavid van Moolenbroek switch (level) {
2577*5d5fbe79SDavid van Moolenbroek
2578*5d5fbe79SDavid van Moolenbroek /* Level: SOL_SOCKET */
2579*5d5fbe79SDavid van Moolenbroek case SOL_SOCKET:
2580*5d5fbe79SDavid van Moolenbroek switch (optname) {
2581*5d5fbe79SDavid van Moolenbroek
2582*5d5fbe79SDavid van Moolenbroek /* SO_ACCEPTCONN is get-only */
2583*5d5fbe79SDavid van Moolenbroek
2584*5d5fbe79SDavid van Moolenbroek /* The option flags */
2585*5d5fbe79SDavid van Moolenbroek case SO_BROADCAST:
2586*5d5fbe79SDavid van Moolenbroek case SO_KEEPALIVE:
2587*5d5fbe79SDavid van Moolenbroek #if SO_REUSE
2588*5d5fbe79SDavid van Moolenbroek case SO_REUSEADDR:
2589*5d5fbe79SDavid van Moolenbroek #endif /* SO_REUSE */
2590*5d5fbe79SDavid van Moolenbroek LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, optlen, int);
2591*5d5fbe79SDavid van Moolenbroek if (*(const int*)optval) {
2592*5d5fbe79SDavid van Moolenbroek ip_set_option(sock->conn->pcb.ip, optname);
2593*5d5fbe79SDavid van Moolenbroek } else {
2594*5d5fbe79SDavid van Moolenbroek ip_reset_option(sock->conn->pcb.ip, optname);
2595*5d5fbe79SDavid van Moolenbroek }
2596*5d5fbe79SDavid van Moolenbroek LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, SOL_SOCKET, optname=0x%x, ..) -> %s\n",
2597*5d5fbe79SDavid van Moolenbroek s, optname, (*(const int*)optval?"on":"off")));
2598*5d5fbe79SDavid van Moolenbroek break;
2599*5d5fbe79SDavid van Moolenbroek
2600*5d5fbe79SDavid van Moolenbroek /* SO_TYPE is get-only */
2601*5d5fbe79SDavid van Moolenbroek /* SO_ERROR is get-only */
2602*5d5fbe79SDavid van Moolenbroek
2603*5d5fbe79SDavid van Moolenbroek #if LWIP_SO_SNDTIMEO
2604*5d5fbe79SDavid van Moolenbroek case SO_SNDTIMEO:
2605*5d5fbe79SDavid van Moolenbroek LWIP_SOCKOPT_CHECK_OPTLEN_CONN(sock, optlen, LWIP_SO_SNDRCVTIMEO_OPTTYPE);
2606*5d5fbe79SDavid van Moolenbroek netconn_set_sendtimeout(sock->conn, LWIP_SO_SNDRCVTIMEO_GET_MS(optval));
2607*5d5fbe79SDavid van Moolenbroek break;
2608*5d5fbe79SDavid van Moolenbroek #endif /* LWIP_SO_SNDTIMEO */
2609*5d5fbe79SDavid van Moolenbroek #if LWIP_SO_RCVTIMEO
2610*5d5fbe79SDavid van Moolenbroek case SO_RCVTIMEO:
2611*5d5fbe79SDavid van Moolenbroek LWIP_SOCKOPT_CHECK_OPTLEN_CONN(sock, optlen, LWIP_SO_SNDRCVTIMEO_OPTTYPE);
2612*5d5fbe79SDavid van Moolenbroek netconn_set_recvtimeout(sock->conn, (int)LWIP_SO_SNDRCVTIMEO_GET_MS(optval));
2613*5d5fbe79SDavid van Moolenbroek break;
2614*5d5fbe79SDavid van Moolenbroek #endif /* LWIP_SO_RCVTIMEO */
2615*5d5fbe79SDavid van Moolenbroek #if LWIP_SO_RCVBUF
2616*5d5fbe79SDavid van Moolenbroek case SO_RCVBUF:
2617*5d5fbe79SDavid van Moolenbroek LWIP_SOCKOPT_CHECK_OPTLEN_CONN(sock, optlen, int);
2618*5d5fbe79SDavid van Moolenbroek netconn_set_recvbufsize(sock->conn, *(const int*)optval);
2619*5d5fbe79SDavid van Moolenbroek break;
2620*5d5fbe79SDavid van Moolenbroek #endif /* LWIP_SO_RCVBUF */
2621*5d5fbe79SDavid van Moolenbroek #if LWIP_SO_LINGER
2622*5d5fbe79SDavid van Moolenbroek case SO_LINGER:
2623*5d5fbe79SDavid van Moolenbroek {
2624*5d5fbe79SDavid van Moolenbroek const struct linger* linger = (const struct linger*)optval;
2625*5d5fbe79SDavid van Moolenbroek LWIP_SOCKOPT_CHECK_OPTLEN_CONN(sock, optlen, struct linger);
2626*5d5fbe79SDavid van Moolenbroek if (linger->l_onoff) {
2627*5d5fbe79SDavid van Moolenbroek int lingersec = linger->l_linger;
2628*5d5fbe79SDavid van Moolenbroek if (lingersec < 0) {
2629*5d5fbe79SDavid van Moolenbroek done_socket(sock);
2630*5d5fbe79SDavid van Moolenbroek return EINVAL;
2631*5d5fbe79SDavid van Moolenbroek }
2632*5d5fbe79SDavid van Moolenbroek if (lingersec > 0xFFFF) {
2633*5d5fbe79SDavid van Moolenbroek lingersec = 0xFFFF;
2634*5d5fbe79SDavid van Moolenbroek }
2635*5d5fbe79SDavid van Moolenbroek sock->conn->linger = (s16_t)lingersec;
2636*5d5fbe79SDavid van Moolenbroek } else {
2637*5d5fbe79SDavid van Moolenbroek sock->conn->linger = -1;
2638*5d5fbe79SDavid van Moolenbroek }
2639*5d5fbe79SDavid van Moolenbroek }
2640*5d5fbe79SDavid van Moolenbroek break;
2641*5d5fbe79SDavid van Moolenbroek #endif /* LWIP_SO_LINGER */
2642*5d5fbe79SDavid van Moolenbroek #if LWIP_UDP
2643*5d5fbe79SDavid van Moolenbroek case SO_NO_CHECK:
2644*5d5fbe79SDavid van Moolenbroek LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, int, NETCONN_UDP);
2645*5d5fbe79SDavid van Moolenbroek #if LWIP_UDPLITE
2646*5d5fbe79SDavid van Moolenbroek if ((udp_flags(sock->conn->pcb.udp) & UDP_FLAGS_UDPLITE) != 0) {
2647*5d5fbe79SDavid van Moolenbroek /* this flag is only available for UDP, not for UDP lite */
2648*5d5fbe79SDavid van Moolenbroek done_socket(sock);
2649*5d5fbe79SDavid van Moolenbroek return EAFNOSUPPORT;
2650*5d5fbe79SDavid van Moolenbroek }
2651*5d5fbe79SDavid van Moolenbroek #endif /* LWIP_UDPLITE */
2652*5d5fbe79SDavid van Moolenbroek if (*(const int*)optval) {
2653*5d5fbe79SDavid van Moolenbroek udp_setflags(sock->conn->pcb.udp, udp_flags(sock->conn->pcb.udp) | UDP_FLAGS_NOCHKSUM);
2654*5d5fbe79SDavid van Moolenbroek } else {
2655*5d5fbe79SDavid van Moolenbroek udp_setflags(sock->conn->pcb.udp, udp_flags(sock->conn->pcb.udp) & ~UDP_FLAGS_NOCHKSUM);
2656*5d5fbe79SDavid van Moolenbroek }
2657*5d5fbe79SDavid van Moolenbroek break;
2658*5d5fbe79SDavid van Moolenbroek #endif /* LWIP_UDP */
2659*5d5fbe79SDavid van Moolenbroek default:
2660*5d5fbe79SDavid van Moolenbroek LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, SOL_SOCKET, UNIMPL: optname=0x%x, ..)\n",
2661*5d5fbe79SDavid van Moolenbroek s, optname));
2662*5d5fbe79SDavid van Moolenbroek err = ENOPROTOOPT;
2663*5d5fbe79SDavid van Moolenbroek break;
2664*5d5fbe79SDavid van Moolenbroek } /* switch (optname) */
2665*5d5fbe79SDavid van Moolenbroek break;
2666*5d5fbe79SDavid van Moolenbroek
2667*5d5fbe79SDavid van Moolenbroek /* Level: IPPROTO_IP */
2668*5d5fbe79SDavid van Moolenbroek case IPPROTO_IP:
2669*5d5fbe79SDavid van Moolenbroek switch (optname) {
2670*5d5fbe79SDavid van Moolenbroek case IP_TTL:
2671*5d5fbe79SDavid van Moolenbroek LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, optlen, int);
2672*5d5fbe79SDavid van Moolenbroek sock->conn->pcb.ip->ttl = (u8_t)(*(const int*)optval);
2673*5d5fbe79SDavid van Moolenbroek LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_IP, IP_TTL, ..) -> %d\n",
2674*5d5fbe79SDavid van Moolenbroek s, sock->conn->pcb.ip->ttl));
2675*5d5fbe79SDavid van Moolenbroek break;
2676*5d5fbe79SDavid van Moolenbroek case IP_TOS:
2677*5d5fbe79SDavid van Moolenbroek LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, optlen, int);
2678*5d5fbe79SDavid van Moolenbroek sock->conn->pcb.ip->tos = (u8_t)(*(const int*)optval);
2679*5d5fbe79SDavid van Moolenbroek LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_IP, IP_TOS, ..)-> %d\n",
2680*5d5fbe79SDavid van Moolenbroek s, sock->conn->pcb.ip->tos));
2681*5d5fbe79SDavid van Moolenbroek break;
2682*5d5fbe79SDavid van Moolenbroek #if LWIP_IPV4 && LWIP_MULTICAST_TX_OPTIONS
2683*5d5fbe79SDavid van Moolenbroek case IP_MULTICAST_TTL:
2684*5d5fbe79SDavid van Moolenbroek LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, u8_t, NETCONN_UDP);
2685*5d5fbe79SDavid van Moolenbroek udp_set_multicast_ttl(sock->conn->pcb.udp, (u8_t)(*(const u8_t*)optval));
2686*5d5fbe79SDavid van Moolenbroek break;
2687*5d5fbe79SDavid van Moolenbroek case IP_MULTICAST_IF:
2688*5d5fbe79SDavid van Moolenbroek {
2689*5d5fbe79SDavid van Moolenbroek ip4_addr_t if_addr;
2690*5d5fbe79SDavid van Moolenbroek LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, struct in_addr, NETCONN_UDP);
2691*5d5fbe79SDavid van Moolenbroek inet_addr_to_ip4addr(&if_addr, (const struct in_addr*)optval);
2692*5d5fbe79SDavid van Moolenbroek udp_set_multicast_netif_addr(sock->conn->pcb.udp, &if_addr);
2693*5d5fbe79SDavid van Moolenbroek }
2694*5d5fbe79SDavid van Moolenbroek break;
2695*5d5fbe79SDavid van Moolenbroek case IP_MULTICAST_LOOP:
2696*5d5fbe79SDavid van Moolenbroek LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, u8_t, NETCONN_UDP);
2697*5d5fbe79SDavid van Moolenbroek if (*(const u8_t*)optval) {
2698*5d5fbe79SDavid van Moolenbroek udp_setflags(sock->conn->pcb.udp, udp_flags(sock->conn->pcb.udp) | UDP_FLAGS_MULTICAST_LOOP);
2699*5d5fbe79SDavid van Moolenbroek } else {
2700*5d5fbe79SDavid van Moolenbroek udp_setflags(sock->conn->pcb.udp, udp_flags(sock->conn->pcb.udp) & ~UDP_FLAGS_MULTICAST_LOOP);
2701*5d5fbe79SDavid van Moolenbroek }
2702*5d5fbe79SDavid van Moolenbroek break;
2703*5d5fbe79SDavid van Moolenbroek #endif /* LWIP_IPV4 && LWIP_MULTICAST_TX_OPTIONS */
2704*5d5fbe79SDavid van Moolenbroek #if LWIP_IGMP
2705*5d5fbe79SDavid van Moolenbroek case IP_ADD_MEMBERSHIP:
2706*5d5fbe79SDavid van Moolenbroek case IP_DROP_MEMBERSHIP:
2707*5d5fbe79SDavid van Moolenbroek {
2708*5d5fbe79SDavid van Moolenbroek /* If this is a TCP or a RAW socket, ignore these options. */
2709*5d5fbe79SDavid van Moolenbroek /* @todo: assign membership to this socket so that it is dropped when closing the socket */
2710*5d5fbe79SDavid van Moolenbroek err_t igmp_err;
2711*5d5fbe79SDavid van Moolenbroek const struct ip_mreq *imr = (const struct ip_mreq *)optval;
2712*5d5fbe79SDavid van Moolenbroek ip4_addr_t if_addr;
2713*5d5fbe79SDavid van Moolenbroek ip4_addr_t multi_addr;
2714*5d5fbe79SDavid van Moolenbroek LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, struct ip_mreq, NETCONN_UDP);
2715*5d5fbe79SDavid van Moolenbroek inet_addr_to_ip4addr(&if_addr, &imr->imr_interface);
2716*5d5fbe79SDavid van Moolenbroek inet_addr_to_ip4addr(&multi_addr, &imr->imr_multiaddr);
2717*5d5fbe79SDavid van Moolenbroek if (optname == IP_ADD_MEMBERSHIP) {
2718*5d5fbe79SDavid van Moolenbroek if (!lwip_socket_register_membership(s, &if_addr, &multi_addr)) {
2719*5d5fbe79SDavid van Moolenbroek /* cannot track membership (out of memory) */
2720*5d5fbe79SDavid van Moolenbroek err = ENOMEM;
2721*5d5fbe79SDavid van Moolenbroek igmp_err = ERR_OK;
2722*5d5fbe79SDavid van Moolenbroek } else {
2723*5d5fbe79SDavid van Moolenbroek igmp_err = igmp_joingroup(&if_addr, &multi_addr);
2724*5d5fbe79SDavid van Moolenbroek }
2725*5d5fbe79SDavid van Moolenbroek } else {
2726*5d5fbe79SDavid van Moolenbroek igmp_err = igmp_leavegroup(&if_addr, &multi_addr);
2727*5d5fbe79SDavid van Moolenbroek lwip_socket_unregister_membership(s, &if_addr, &multi_addr);
2728*5d5fbe79SDavid van Moolenbroek }
2729*5d5fbe79SDavid van Moolenbroek if (igmp_err != ERR_OK) {
2730*5d5fbe79SDavid van Moolenbroek err = EADDRNOTAVAIL;
2731*5d5fbe79SDavid van Moolenbroek }
2732*5d5fbe79SDavid van Moolenbroek }
2733*5d5fbe79SDavid van Moolenbroek break;
2734*5d5fbe79SDavid van Moolenbroek #endif /* LWIP_IGMP */
2735*5d5fbe79SDavid van Moolenbroek default:
2736*5d5fbe79SDavid van Moolenbroek LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_IP, UNIMPL: optname=0x%x, ..)\n",
2737*5d5fbe79SDavid van Moolenbroek s, optname));
2738*5d5fbe79SDavid van Moolenbroek err = ENOPROTOOPT;
2739*5d5fbe79SDavid van Moolenbroek break;
2740*5d5fbe79SDavid van Moolenbroek } /* switch (optname) */
2741*5d5fbe79SDavid van Moolenbroek break;
2742*5d5fbe79SDavid van Moolenbroek
2743*5d5fbe79SDavid van Moolenbroek #if LWIP_TCP
2744*5d5fbe79SDavid van Moolenbroek /* Level: IPPROTO_TCP */
2745*5d5fbe79SDavid van Moolenbroek case IPPROTO_TCP:
2746*5d5fbe79SDavid van Moolenbroek /* Special case: all IPPROTO_TCP option take an int */
2747*5d5fbe79SDavid van Moolenbroek LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, int, NETCONN_TCP);
2748*5d5fbe79SDavid van Moolenbroek if (sock->conn->pcb.tcp->state == LISTEN) {
2749*5d5fbe79SDavid van Moolenbroek done_socket(sock);
2750*5d5fbe79SDavid van Moolenbroek return EINVAL;
2751*5d5fbe79SDavid van Moolenbroek }
2752*5d5fbe79SDavid van Moolenbroek switch (optname) {
2753*5d5fbe79SDavid van Moolenbroek case TCP_NODELAY:
2754*5d5fbe79SDavid van Moolenbroek if (*(const int*)optval) {
2755*5d5fbe79SDavid van Moolenbroek tcp_nagle_disable(sock->conn->pcb.tcp);
2756*5d5fbe79SDavid van Moolenbroek } else {
2757*5d5fbe79SDavid van Moolenbroek tcp_nagle_enable(sock->conn->pcb.tcp);
2758*5d5fbe79SDavid van Moolenbroek }
2759*5d5fbe79SDavid van Moolenbroek LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_TCP, TCP_NODELAY) -> %s\n",
2760*5d5fbe79SDavid van Moolenbroek s, (*(const int *)optval)?"on":"off") );
2761*5d5fbe79SDavid van Moolenbroek break;
2762*5d5fbe79SDavid van Moolenbroek case TCP_KEEPALIVE:
2763*5d5fbe79SDavid van Moolenbroek sock->conn->pcb.tcp->keep_idle = (u32_t)(*(const int*)optval);
2764*5d5fbe79SDavid van Moolenbroek LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_TCP, TCP_KEEPALIVE) -> %"U32_F"\n",
2765*5d5fbe79SDavid van Moolenbroek s, sock->conn->pcb.tcp->keep_idle));
2766*5d5fbe79SDavid van Moolenbroek break;
2767*5d5fbe79SDavid van Moolenbroek
2768*5d5fbe79SDavid van Moolenbroek #if LWIP_TCP_KEEPALIVE
2769*5d5fbe79SDavid van Moolenbroek case TCP_KEEPIDLE:
2770*5d5fbe79SDavid van Moolenbroek sock->conn->pcb.tcp->keep_idle = 1000*(u32_t)(*(const int*)optval);
2771*5d5fbe79SDavid van Moolenbroek LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_TCP, TCP_KEEPIDLE) -> %"U32_F"\n",
2772*5d5fbe79SDavid van Moolenbroek s, sock->conn->pcb.tcp->keep_idle));
2773*5d5fbe79SDavid van Moolenbroek break;
2774*5d5fbe79SDavid van Moolenbroek case TCP_KEEPINTVL:
2775*5d5fbe79SDavid van Moolenbroek sock->conn->pcb.tcp->keep_intvl = 1000*(u32_t)(*(const int*)optval);
2776*5d5fbe79SDavid van Moolenbroek LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_TCP, TCP_KEEPINTVL) -> %"U32_F"\n",
2777*5d5fbe79SDavid van Moolenbroek s, sock->conn->pcb.tcp->keep_intvl));
2778*5d5fbe79SDavid van Moolenbroek break;
2779*5d5fbe79SDavid van Moolenbroek case TCP_KEEPCNT:
2780*5d5fbe79SDavid van Moolenbroek sock->conn->pcb.tcp->keep_cnt = (u32_t)(*(const int*)optval);
2781*5d5fbe79SDavid van Moolenbroek LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_TCP, TCP_KEEPCNT) -> %"U32_F"\n",
2782*5d5fbe79SDavid van Moolenbroek s, sock->conn->pcb.tcp->keep_cnt));
2783*5d5fbe79SDavid van Moolenbroek break;
2784*5d5fbe79SDavid van Moolenbroek #endif /* LWIP_TCP_KEEPALIVE */
2785*5d5fbe79SDavid van Moolenbroek default:
2786*5d5fbe79SDavid van Moolenbroek LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_TCP, UNIMPL: optname=0x%x, ..)\n",
2787*5d5fbe79SDavid van Moolenbroek s, optname));
2788*5d5fbe79SDavid van Moolenbroek err = ENOPROTOOPT;
2789*5d5fbe79SDavid van Moolenbroek break;
2790*5d5fbe79SDavid van Moolenbroek } /* switch (optname) */
2791*5d5fbe79SDavid van Moolenbroek break;
2792*5d5fbe79SDavid van Moolenbroek #endif /* LWIP_TCP*/
2793*5d5fbe79SDavid van Moolenbroek
2794*5d5fbe79SDavid van Moolenbroek #if LWIP_IPV6
2795*5d5fbe79SDavid van Moolenbroek /* Level: IPPROTO_IPV6 */
2796*5d5fbe79SDavid van Moolenbroek case IPPROTO_IPV6:
2797*5d5fbe79SDavid van Moolenbroek switch (optname) {
2798*5d5fbe79SDavid van Moolenbroek case IPV6_V6ONLY:
2799*5d5fbe79SDavid van Moolenbroek LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, int, NETCONN_TCP);
2800*5d5fbe79SDavid van Moolenbroek if (*(const int*)optval) {
2801*5d5fbe79SDavid van Moolenbroek netconn_set_ipv6only(sock->conn, 1);
2802*5d5fbe79SDavid van Moolenbroek } else {
2803*5d5fbe79SDavid van Moolenbroek netconn_set_ipv6only(sock->conn, 0);
2804*5d5fbe79SDavid van Moolenbroek }
2805*5d5fbe79SDavid van Moolenbroek LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_IPV6, IPV6_V6ONLY, ..) -> %d\n",
2806*5d5fbe79SDavid van Moolenbroek s, (netconn_get_ipv6only(sock->conn) ? 1 : 0)));
2807*5d5fbe79SDavid van Moolenbroek break;
2808*5d5fbe79SDavid van Moolenbroek default:
2809*5d5fbe79SDavid van Moolenbroek LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_IPV6, UNIMPL: optname=0x%x, ..)\n",
2810*5d5fbe79SDavid van Moolenbroek s, optname));
2811*5d5fbe79SDavid van Moolenbroek err = ENOPROTOOPT;
2812*5d5fbe79SDavid van Moolenbroek break;
2813*5d5fbe79SDavid van Moolenbroek } /* switch (optname) */
2814*5d5fbe79SDavid van Moolenbroek break;
2815*5d5fbe79SDavid van Moolenbroek #endif /* LWIP_IPV6 */
2816*5d5fbe79SDavid van Moolenbroek
2817*5d5fbe79SDavid van Moolenbroek #if LWIP_UDP && LWIP_UDPLITE
2818*5d5fbe79SDavid van Moolenbroek /* Level: IPPROTO_UDPLITE */
2819*5d5fbe79SDavid van Moolenbroek case IPPROTO_UDPLITE:
2820*5d5fbe79SDavid van Moolenbroek /* Special case: all IPPROTO_UDPLITE option take an int */
2821*5d5fbe79SDavid van Moolenbroek LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, optlen, int);
2822*5d5fbe79SDavid van Moolenbroek /* If this is no UDP lite socket, ignore any options. */
2823*5d5fbe79SDavid van Moolenbroek if (!NETCONNTYPE_ISUDPLITE(netconn_type(sock->conn))) {
2824*5d5fbe79SDavid van Moolenbroek done_socket(sock);
2825*5d5fbe79SDavid van Moolenbroek return ENOPROTOOPT;
2826*5d5fbe79SDavid van Moolenbroek }
2827*5d5fbe79SDavid van Moolenbroek switch (optname) {
2828*5d5fbe79SDavid van Moolenbroek case UDPLITE_SEND_CSCOV:
2829*5d5fbe79SDavid van Moolenbroek if ((*(const int*)optval != 0) && ((*(const int*)optval < 8) || (*(const int*)optval > 0xffff))) {
2830*5d5fbe79SDavid van Moolenbroek /* don't allow illegal values! */
2831*5d5fbe79SDavid van Moolenbroek sock->conn->pcb.udp->chksum_len_tx = 8;
2832*5d5fbe79SDavid van Moolenbroek } else {
2833*5d5fbe79SDavid van Moolenbroek sock->conn->pcb.udp->chksum_len_tx = (u16_t)*(const int*)optval;
2834*5d5fbe79SDavid van Moolenbroek }
2835*5d5fbe79SDavid van Moolenbroek LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_UDPLITE, UDPLITE_SEND_CSCOV) -> %d\n",
2836*5d5fbe79SDavid van Moolenbroek s, (*(const int*)optval)) );
2837*5d5fbe79SDavid van Moolenbroek break;
2838*5d5fbe79SDavid van Moolenbroek case UDPLITE_RECV_CSCOV:
2839*5d5fbe79SDavid van Moolenbroek if ((*(const int*)optval != 0) && ((*(const int*)optval < 8) || (*(const int*)optval > 0xffff))) {
2840*5d5fbe79SDavid van Moolenbroek /* don't allow illegal values! */
2841*5d5fbe79SDavid van Moolenbroek sock->conn->pcb.udp->chksum_len_rx = 8;
2842*5d5fbe79SDavid van Moolenbroek } else {
2843*5d5fbe79SDavid van Moolenbroek sock->conn->pcb.udp->chksum_len_rx = (u16_t)*(const int*)optval;
2844*5d5fbe79SDavid van Moolenbroek }
2845*5d5fbe79SDavid van Moolenbroek LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_UDPLITE, UDPLITE_RECV_CSCOV) -> %d\n",
2846*5d5fbe79SDavid van Moolenbroek s, (*(const int*)optval)) );
2847*5d5fbe79SDavid van Moolenbroek break;
2848*5d5fbe79SDavid van Moolenbroek default:
2849*5d5fbe79SDavid van Moolenbroek LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_UDPLITE, UNIMPL: optname=0x%x, ..)\n",
2850*5d5fbe79SDavid van Moolenbroek s, optname));
2851*5d5fbe79SDavid van Moolenbroek err = ENOPROTOOPT;
2852*5d5fbe79SDavid van Moolenbroek break;
2853*5d5fbe79SDavid van Moolenbroek } /* switch (optname) */
2854*5d5fbe79SDavid van Moolenbroek break;
2855*5d5fbe79SDavid van Moolenbroek #endif /* LWIP_UDP */
2856*5d5fbe79SDavid van Moolenbroek /* Level: IPPROTO_RAW */
2857*5d5fbe79SDavid van Moolenbroek case IPPROTO_RAW:
2858*5d5fbe79SDavid van Moolenbroek switch (optname) {
2859*5d5fbe79SDavid van Moolenbroek #if LWIP_IPV6 && LWIP_RAW
2860*5d5fbe79SDavid van Moolenbroek case IPV6_CHECKSUM:
2861*5d5fbe79SDavid van Moolenbroek /* It should not be possible to disable the checksum generation with ICMPv6
2862*5d5fbe79SDavid van Moolenbroek * as per RFC 3542 chapter 3.1 */
2863*5d5fbe79SDavid van Moolenbroek if(sock->conn->pcb.raw->protocol == IPPROTO_ICMPV6) {
2864*5d5fbe79SDavid van Moolenbroek done_socket(sock);
2865*5d5fbe79SDavid van Moolenbroek return EINVAL;
2866*5d5fbe79SDavid van Moolenbroek }
2867*5d5fbe79SDavid van Moolenbroek
2868*5d5fbe79SDavid van Moolenbroek LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, int, NETCONN_RAW);
2869*5d5fbe79SDavid van Moolenbroek if (*(const int *)optval < 0) {
2870*5d5fbe79SDavid van Moolenbroek sock->conn->pcb.raw->chksum_reqd = 0;
2871*5d5fbe79SDavid van Moolenbroek } else if (*(const int *)optval & 1) {
2872*5d5fbe79SDavid van Moolenbroek /* Per RFC3542, odd offsets are not allowed */
2873*5d5fbe79SDavid van Moolenbroek done_socket(sock);
2874*5d5fbe79SDavid van Moolenbroek return EINVAL;
2875*5d5fbe79SDavid van Moolenbroek } else {
2876*5d5fbe79SDavid van Moolenbroek sock->conn->pcb.raw->chksum_reqd = 1;
2877*5d5fbe79SDavid van Moolenbroek sock->conn->pcb.raw->chksum_offset = (u16_t)*(const int *)optval;
2878*5d5fbe79SDavid van Moolenbroek }
2879*5d5fbe79SDavid van Moolenbroek LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_RAW, IPV6_CHECKSUM, ..) -> %d\n",
2880*5d5fbe79SDavid van Moolenbroek s, sock->conn->pcb.raw->chksum_reqd));
2881*5d5fbe79SDavid van Moolenbroek break;
2882*5d5fbe79SDavid van Moolenbroek #endif /* LWIP_IPV6 && LWIP_RAW */
2883*5d5fbe79SDavid van Moolenbroek default:
2884*5d5fbe79SDavid van Moolenbroek LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_RAW, UNIMPL: optname=0x%x, ..)\n",
2885*5d5fbe79SDavid van Moolenbroek s, optname));
2886*5d5fbe79SDavid van Moolenbroek err = ENOPROTOOPT;
2887*5d5fbe79SDavid van Moolenbroek break;
2888*5d5fbe79SDavid van Moolenbroek } /* switch (optname) */
2889*5d5fbe79SDavid van Moolenbroek break;
2890*5d5fbe79SDavid van Moolenbroek default:
2891*5d5fbe79SDavid van Moolenbroek LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, level=0x%x, UNIMPL: optname=0x%x, ..)\n",
2892*5d5fbe79SDavid van Moolenbroek s, level, optname));
2893*5d5fbe79SDavid van Moolenbroek err = ENOPROTOOPT;
2894*5d5fbe79SDavid van Moolenbroek break;
2895*5d5fbe79SDavid van Moolenbroek } /* switch (level) */
2896*5d5fbe79SDavid van Moolenbroek
2897*5d5fbe79SDavid van Moolenbroek done_socket(sock);
2898*5d5fbe79SDavid van Moolenbroek return err;
2899*5d5fbe79SDavid van Moolenbroek }
2900*5d5fbe79SDavid van Moolenbroek
2901*5d5fbe79SDavid van Moolenbroek int
lwip_ioctl(int s,long cmd,void * argp)2902*5d5fbe79SDavid van Moolenbroek lwip_ioctl(int s, long cmd, void *argp)
2903*5d5fbe79SDavid van Moolenbroek {
2904*5d5fbe79SDavid van Moolenbroek struct lwip_sock *sock = get_socket(s);
2905*5d5fbe79SDavid van Moolenbroek u8_t val;
2906*5d5fbe79SDavid van Moolenbroek #if LWIP_SO_RCVBUF
2907*5d5fbe79SDavid van Moolenbroek int recv_avail;
2908*5d5fbe79SDavid van Moolenbroek #endif /* LWIP_SO_RCVBUF */
2909*5d5fbe79SDavid van Moolenbroek
2910*5d5fbe79SDavid van Moolenbroek if (!sock) {
2911*5d5fbe79SDavid van Moolenbroek return -1;
2912*5d5fbe79SDavid van Moolenbroek }
2913*5d5fbe79SDavid van Moolenbroek
2914*5d5fbe79SDavid van Moolenbroek switch (cmd) {
2915*5d5fbe79SDavid van Moolenbroek #if LWIP_SO_RCVBUF || LWIP_FIONREAD_LINUXMODE
2916*5d5fbe79SDavid van Moolenbroek case FIONREAD:
2917*5d5fbe79SDavid van Moolenbroek if (!argp) {
2918*5d5fbe79SDavid van Moolenbroek sock_set_errno(sock, EINVAL);
2919*5d5fbe79SDavid van Moolenbroek done_socket(sock);
2920*5d5fbe79SDavid van Moolenbroek return -1;
2921*5d5fbe79SDavid van Moolenbroek }
2922*5d5fbe79SDavid van Moolenbroek #if LWIP_FIONREAD_LINUXMODE
2923*5d5fbe79SDavid van Moolenbroek if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_TCP) {
2924*5d5fbe79SDavid van Moolenbroek struct netbuf *nb;
2925*5d5fbe79SDavid van Moolenbroek if (sock->lastdata.netbuf) {
2926*5d5fbe79SDavid van Moolenbroek nb = sock->lastdata.netbuf;
2927*5d5fbe79SDavid van Moolenbroek *((int*)argp) = nb->p->tot_len;
2928*5d5fbe79SDavid van Moolenbroek } else {
2929*5d5fbe79SDavid van Moolenbroek struct netbuf *rxbuf;
2930*5d5fbe79SDavid van Moolenbroek err_t err = netconn_recv_udp_raw_netbuf_flags(sock->conn, &rxbuf, NETCONN_DONTBLOCK);
2931*5d5fbe79SDavid van Moolenbroek if (err != ERR_OK) {
2932*5d5fbe79SDavid van Moolenbroek *((int*)argp) = 0;
2933*5d5fbe79SDavid van Moolenbroek } else {
2934*5d5fbe79SDavid van Moolenbroek sock->lastdata.netbuf = rxbuf;
2935*5d5fbe79SDavid van Moolenbroek *((int*)argp) = rxbuf->p->tot_len;
2936*5d5fbe79SDavid van Moolenbroek }
2937*5d5fbe79SDavid van Moolenbroek }
2938*5d5fbe79SDavid van Moolenbroek done_socket(sock);
2939*5d5fbe79SDavid van Moolenbroek return 0;
2940*5d5fbe79SDavid van Moolenbroek }
2941*5d5fbe79SDavid van Moolenbroek #endif /* LWIP_FIONREAD_LINUXMODE */
2942*5d5fbe79SDavid van Moolenbroek
2943*5d5fbe79SDavid van Moolenbroek #if LWIP_SO_RCVBUF
2944*5d5fbe79SDavid van Moolenbroek /* we come here if either LWIP_FIONREAD_LINUXMODE==0 or this is a TCP socket */
2945*5d5fbe79SDavid van Moolenbroek SYS_ARCH_GET(sock->conn->recv_avail, recv_avail);
2946*5d5fbe79SDavid van Moolenbroek if (recv_avail < 0) {
2947*5d5fbe79SDavid van Moolenbroek recv_avail = 0;
2948*5d5fbe79SDavid van Moolenbroek }
2949*5d5fbe79SDavid van Moolenbroek
2950*5d5fbe79SDavid van Moolenbroek /* Check if there is data left from the last recv operation. /maq 041215 */
2951*5d5fbe79SDavid van Moolenbroek if (sock->lastdata.netbuf) {
2952*5d5fbe79SDavid van Moolenbroek if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_TCP) {
2953*5d5fbe79SDavid van Moolenbroek recv_avail += sock->lastdata.pbuf->tot_len;
2954*5d5fbe79SDavid van Moolenbroek } else {
2955*5d5fbe79SDavid van Moolenbroek recv_avail += sock->lastdata.netbuf->p->tot_len;
2956*5d5fbe79SDavid van Moolenbroek }
2957*5d5fbe79SDavid van Moolenbroek }
2958*5d5fbe79SDavid van Moolenbroek *((int*)argp) = recv_avail;
2959*5d5fbe79SDavid van Moolenbroek
2960*5d5fbe79SDavid van Moolenbroek LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_ioctl(%d, FIONREAD, %p) = %"U16_F"\n", s, argp, *((u16_t*)argp)));
2961*5d5fbe79SDavid van Moolenbroek sock_set_errno(sock, 0);
2962*5d5fbe79SDavid van Moolenbroek done_socket(sock);
2963*5d5fbe79SDavid van Moolenbroek return 0;
2964*5d5fbe79SDavid van Moolenbroek #else /* LWIP_SO_RCVBUF */
2965*5d5fbe79SDavid van Moolenbroek break;
2966*5d5fbe79SDavid van Moolenbroek #endif /* LWIP_SO_RCVBUF */
2967*5d5fbe79SDavid van Moolenbroek #endif /* LWIP_SO_RCVBUF || LWIP_FIONREAD_LINUXMODE */
2968*5d5fbe79SDavid van Moolenbroek
2969*5d5fbe79SDavid van Moolenbroek case (long)FIONBIO:
2970*5d5fbe79SDavid van Moolenbroek val = 0;
2971*5d5fbe79SDavid van Moolenbroek if (argp && *(u32_t*)argp) {
2972*5d5fbe79SDavid van Moolenbroek val = 1;
2973*5d5fbe79SDavid van Moolenbroek }
2974*5d5fbe79SDavid van Moolenbroek netconn_set_nonblocking(sock->conn, val);
2975*5d5fbe79SDavid van Moolenbroek LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_ioctl(%d, FIONBIO, %d)\n", s, val));
2976*5d5fbe79SDavid van Moolenbroek sock_set_errno(sock, 0);
2977*5d5fbe79SDavid van Moolenbroek done_socket(sock);
2978*5d5fbe79SDavid van Moolenbroek return 0;
2979*5d5fbe79SDavid van Moolenbroek
2980*5d5fbe79SDavid van Moolenbroek default:
2981*5d5fbe79SDavid van Moolenbroek break;
2982*5d5fbe79SDavid van Moolenbroek } /* switch (cmd) */
2983*5d5fbe79SDavid van Moolenbroek LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_ioctl(%d, UNIMPL: 0x%lx, %p)\n", s, cmd, argp));
2984*5d5fbe79SDavid van Moolenbroek sock_set_errno(sock, ENOSYS); /* not yet implemented */
2985*5d5fbe79SDavid van Moolenbroek done_socket(sock);
2986*5d5fbe79SDavid van Moolenbroek return -1;
2987*5d5fbe79SDavid van Moolenbroek }
2988*5d5fbe79SDavid van Moolenbroek
2989*5d5fbe79SDavid van Moolenbroek /** A minimal implementation of fcntl.
2990*5d5fbe79SDavid van Moolenbroek * Currently only the commands F_GETFL and F_SETFL are implemented.
2991*5d5fbe79SDavid van Moolenbroek * Only the flag O_NONBLOCK is implemented.
2992*5d5fbe79SDavid van Moolenbroek */
2993*5d5fbe79SDavid van Moolenbroek int
lwip_fcntl(int s,int cmd,int val)2994*5d5fbe79SDavid van Moolenbroek lwip_fcntl(int s, int cmd, int val)
2995*5d5fbe79SDavid van Moolenbroek {
2996*5d5fbe79SDavid van Moolenbroek struct lwip_sock *sock = get_socket(s);
2997*5d5fbe79SDavid van Moolenbroek int ret = -1;
2998*5d5fbe79SDavid van Moolenbroek
2999*5d5fbe79SDavid van Moolenbroek if (!sock) {
3000*5d5fbe79SDavid van Moolenbroek return -1;
3001*5d5fbe79SDavid van Moolenbroek }
3002*5d5fbe79SDavid van Moolenbroek
3003*5d5fbe79SDavid van Moolenbroek switch (cmd) {
3004*5d5fbe79SDavid van Moolenbroek case F_GETFL:
3005*5d5fbe79SDavid van Moolenbroek ret = netconn_is_nonblocking(sock->conn) ? O_NONBLOCK : 0;
3006*5d5fbe79SDavid van Moolenbroek sock_set_errno(sock, 0);
3007*5d5fbe79SDavid van Moolenbroek break;
3008*5d5fbe79SDavid van Moolenbroek case F_SETFL:
3009*5d5fbe79SDavid van Moolenbroek if ((val & ~O_NONBLOCK) == 0) {
3010*5d5fbe79SDavid van Moolenbroek /* only O_NONBLOCK, all other bits are zero */
3011*5d5fbe79SDavid van Moolenbroek netconn_set_nonblocking(sock->conn, val & O_NONBLOCK);
3012*5d5fbe79SDavid van Moolenbroek ret = 0;
3013*5d5fbe79SDavid van Moolenbroek sock_set_errno(sock, 0);
3014*5d5fbe79SDavid van Moolenbroek } else {
3015*5d5fbe79SDavid van Moolenbroek sock_set_errno(sock, ENOSYS); /* not yet implemented */
3016*5d5fbe79SDavid van Moolenbroek }
3017*5d5fbe79SDavid van Moolenbroek break;
3018*5d5fbe79SDavid van Moolenbroek default:
3019*5d5fbe79SDavid van Moolenbroek LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_fcntl(%d, UNIMPL: %d, %d)\n", s, cmd, val));
3020*5d5fbe79SDavid van Moolenbroek sock_set_errno(sock, ENOSYS); /* not yet implemented */
3021*5d5fbe79SDavid van Moolenbroek break;
3022*5d5fbe79SDavid van Moolenbroek }
3023*5d5fbe79SDavid van Moolenbroek done_socket(sock);
3024*5d5fbe79SDavid van Moolenbroek return ret;
3025*5d5fbe79SDavid van Moolenbroek }
3026*5d5fbe79SDavid van Moolenbroek
3027*5d5fbe79SDavid van Moolenbroek #if LWIP_IGMP
3028*5d5fbe79SDavid van Moolenbroek /** Register a new IGMP membership. On socket close, the membership is dropped automatically.
3029*5d5fbe79SDavid van Moolenbroek *
3030*5d5fbe79SDavid van Moolenbroek * ATTENTION: this function is called from tcpip_thread (or under CORE_LOCK).
3031*5d5fbe79SDavid van Moolenbroek *
3032*5d5fbe79SDavid van Moolenbroek * @return 1 on success, 0 on failure
3033*5d5fbe79SDavid van Moolenbroek */
3034*5d5fbe79SDavid van Moolenbroek static int
lwip_socket_register_membership(int s,const ip4_addr_t * if_addr,const ip4_addr_t * multi_addr)3035*5d5fbe79SDavid van Moolenbroek lwip_socket_register_membership(int s, const ip4_addr_t *if_addr, const ip4_addr_t *multi_addr)
3036*5d5fbe79SDavid van Moolenbroek {
3037*5d5fbe79SDavid van Moolenbroek struct lwip_sock *sock = get_socket(s);
3038*5d5fbe79SDavid van Moolenbroek int i;
3039*5d5fbe79SDavid van Moolenbroek
3040*5d5fbe79SDavid van Moolenbroek if (!sock) {
3041*5d5fbe79SDavid van Moolenbroek return 0;
3042*5d5fbe79SDavid van Moolenbroek }
3043*5d5fbe79SDavid van Moolenbroek
3044*5d5fbe79SDavid van Moolenbroek for (i = 0; i < LWIP_SOCKET_MAX_MEMBERSHIPS; i++) {
3045*5d5fbe79SDavid van Moolenbroek if (socket_ipv4_multicast_memberships[i].sock == NULL) {
3046*5d5fbe79SDavid van Moolenbroek socket_ipv4_multicast_memberships[i].sock = sock;
3047*5d5fbe79SDavid van Moolenbroek ip4_addr_copy(socket_ipv4_multicast_memberships[i].if_addr, *if_addr);
3048*5d5fbe79SDavid van Moolenbroek ip4_addr_copy(socket_ipv4_multicast_memberships[i].multi_addr, *multi_addr);
3049*5d5fbe79SDavid van Moolenbroek done_socket(sock);
3050*5d5fbe79SDavid van Moolenbroek return 1;
3051*5d5fbe79SDavid van Moolenbroek }
3052*5d5fbe79SDavid van Moolenbroek }
3053*5d5fbe79SDavid van Moolenbroek done_socket(sock);
3054*5d5fbe79SDavid van Moolenbroek return 0;
3055*5d5fbe79SDavid van Moolenbroek }
3056*5d5fbe79SDavid van Moolenbroek
3057*5d5fbe79SDavid van Moolenbroek /** Unregister a previously registered membership. This prevents dropping the membership
3058*5d5fbe79SDavid van Moolenbroek * on socket close.
3059*5d5fbe79SDavid van Moolenbroek *
3060*5d5fbe79SDavid van Moolenbroek * ATTENTION: this function is called from tcpip_thread (or under CORE_LOCK).
3061*5d5fbe79SDavid van Moolenbroek */
3062*5d5fbe79SDavid van Moolenbroek static void
lwip_socket_unregister_membership(int s,const ip4_addr_t * if_addr,const ip4_addr_t * multi_addr)3063*5d5fbe79SDavid van Moolenbroek lwip_socket_unregister_membership(int s, const ip4_addr_t *if_addr, const ip4_addr_t *multi_addr)
3064*5d5fbe79SDavid van Moolenbroek {
3065*5d5fbe79SDavid van Moolenbroek struct lwip_sock *sock = get_socket(s);
3066*5d5fbe79SDavid van Moolenbroek int i;
3067*5d5fbe79SDavid van Moolenbroek
3068*5d5fbe79SDavid van Moolenbroek if (!sock) {
3069*5d5fbe79SDavid van Moolenbroek return;
3070*5d5fbe79SDavid van Moolenbroek }
3071*5d5fbe79SDavid van Moolenbroek
3072*5d5fbe79SDavid van Moolenbroek for (i = 0; i < LWIP_SOCKET_MAX_MEMBERSHIPS; i++) {
3073*5d5fbe79SDavid van Moolenbroek if ((socket_ipv4_multicast_memberships[i].sock == sock) &&
3074*5d5fbe79SDavid van Moolenbroek ip4_addr_cmp(&socket_ipv4_multicast_memberships[i].if_addr, if_addr) &&
3075*5d5fbe79SDavid van Moolenbroek ip4_addr_cmp(&socket_ipv4_multicast_memberships[i].multi_addr, multi_addr)) {
3076*5d5fbe79SDavid van Moolenbroek socket_ipv4_multicast_memberships[i].sock = NULL;
3077*5d5fbe79SDavid van Moolenbroek ip4_addr_set_zero(&socket_ipv4_multicast_memberships[i].if_addr);
3078*5d5fbe79SDavid van Moolenbroek ip4_addr_set_zero(&socket_ipv4_multicast_memberships[i].multi_addr);
3079*5d5fbe79SDavid van Moolenbroek break;
3080*5d5fbe79SDavid van Moolenbroek }
3081*5d5fbe79SDavid van Moolenbroek }
3082*5d5fbe79SDavid van Moolenbroek done_socket(sock);
3083*5d5fbe79SDavid van Moolenbroek }
3084*5d5fbe79SDavid van Moolenbroek
3085*5d5fbe79SDavid van Moolenbroek /** Drop all memberships of a socket that were not dropped explicitly via setsockopt.
3086*5d5fbe79SDavid van Moolenbroek *
3087*5d5fbe79SDavid van Moolenbroek * ATTENTION: this function is NOT called from tcpip_thread (or under CORE_LOCK).
3088*5d5fbe79SDavid van Moolenbroek */
3089*5d5fbe79SDavid van Moolenbroek static void
lwip_socket_drop_registered_memberships(int s)3090*5d5fbe79SDavid van Moolenbroek lwip_socket_drop_registered_memberships(int s)
3091*5d5fbe79SDavid van Moolenbroek {
3092*5d5fbe79SDavid van Moolenbroek struct lwip_sock *sock = get_socket(s);
3093*5d5fbe79SDavid van Moolenbroek int i;
3094*5d5fbe79SDavid van Moolenbroek
3095*5d5fbe79SDavid van Moolenbroek if (!sock) {
3096*5d5fbe79SDavid van Moolenbroek return;
3097*5d5fbe79SDavid van Moolenbroek }
3098*5d5fbe79SDavid van Moolenbroek
3099*5d5fbe79SDavid van Moolenbroek for (i = 0; i < LWIP_SOCKET_MAX_MEMBERSHIPS; i++) {
3100*5d5fbe79SDavid van Moolenbroek if (socket_ipv4_multicast_memberships[i].sock == sock) {
3101*5d5fbe79SDavid van Moolenbroek ip_addr_t multi_addr, if_addr;
3102*5d5fbe79SDavid van Moolenbroek ip_addr_copy_from_ip4(multi_addr, socket_ipv4_multicast_memberships[i].multi_addr);
3103*5d5fbe79SDavid van Moolenbroek ip_addr_copy_from_ip4(if_addr, socket_ipv4_multicast_memberships[i].if_addr);
3104*5d5fbe79SDavid van Moolenbroek socket_ipv4_multicast_memberships[i].sock = NULL;
3105*5d5fbe79SDavid van Moolenbroek ip4_addr_set_zero(&socket_ipv4_multicast_memberships[i].if_addr);
3106*5d5fbe79SDavid van Moolenbroek ip4_addr_set_zero(&socket_ipv4_multicast_memberships[i].multi_addr);
3107*5d5fbe79SDavid van Moolenbroek
3108*5d5fbe79SDavid van Moolenbroek netconn_join_leave_group(sock->conn, &multi_addr, &if_addr, NETCONN_LEAVE);
3109*5d5fbe79SDavid van Moolenbroek }
3110*5d5fbe79SDavid van Moolenbroek }
3111*5d5fbe79SDavid van Moolenbroek done_socket(sock);
3112*5d5fbe79SDavid van Moolenbroek }
3113*5d5fbe79SDavid van Moolenbroek #endif /* LWIP_IGMP */
3114*5d5fbe79SDavid van Moolenbroek #endif /* LWIP_SOCKET */
3115