xref: /nrf52832-nimble/rt-thread/components/net/lwip-2.1.0/src/api/sockets.c (revision 104654410c56c573564690304ae786df310c91fc)
1 /**
2  * @file
3  * Sockets BSD-Like API module
4  */
5 
6 /*
7  * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
8  * All rights reserved.
9  *
10  * Redistribution and use in source and binary forms, with or without modification,
11  * are permitted provided that the following conditions are met:
12  *
13  * 1. Redistributions of source code must retain the above copyright notice,
14  *    this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright notice,
16  *    this list of conditions and the following disclaimer in the documentation
17  *    and/or other materials provided with the distribution.
18  * 3. The name of the author may not be used to endorse or promote products
19  *    derived from this software without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
22  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
23  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
24  * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
26  * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
29  * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
30  * OF SUCH DAMAGE.
31  *
32  * This file is part of the lwIP TCP/IP stack.
33  *
34  * Author: Adam Dunkels <[email protected]>
35  *
36  * Improved by Marc Boucher <[email protected]> and David Haas <[email protected]>
37  *
38  */
39 
40 #include "lwip/opt.h"
41 
42 #if LWIP_SOCKET /* don't build if not configured for use in lwipopts.h */
43 
44 #include "lwip/sockets.h"
45 #include "lwip/priv/sockets_priv.h"
46 #include "lwip/api.h"
47 #include "lwip/igmp.h"
48 #include "lwip/inet.h"
49 #include "lwip/tcp.h"
50 #include "lwip/raw.h"
51 #include "lwip/udp.h"
52 #include "lwip/memp.h"
53 #include "lwip/pbuf.h"
54 #include "lwip/netif.h"
55 #include "lwip/priv/tcpip_priv.h"
56 #include "lwip/mld6.h"
57 #if LWIP_CHECKSUM_ON_COPY
58 #include "lwip/inet_chksum.h"
59 #endif
60 
61 #if LWIP_COMPAT_SOCKETS == 2 && LWIP_POSIX_SOCKETS_IO_NAMES
62 #include <stdarg.h>
63 #endif
64 
65 #include <string.h>
66 
67 #ifdef LWIP_HOOK_FILENAME
68 #include LWIP_HOOK_FILENAME
69 #endif
70 
71 /* If the netconn API is not required publicly, then we include the necessary
72    files here to get the implementation */
73 #if !LWIP_NETCONN
74 #undef LWIP_NETCONN
75 #define LWIP_NETCONN 1
76 #include "api_msg.c"
77 #include "api_lib.c"
78 #include "netbuf.c"
79 #undef LWIP_NETCONN
80 #define LWIP_NETCONN 0
81 #endif
82 
83 #define API_SELECT_CB_VAR_REF(name)               API_VAR_REF(name)
84 #define API_SELECT_CB_VAR_DECLARE(name)           API_VAR_DECLARE(struct lwip_select_cb, name)
85 #define API_SELECT_CB_VAR_ALLOC(name, retblock)   API_VAR_ALLOC_EXT(struct lwip_select_cb, MEMP_SELECT_CB, name, retblock)
86 #define API_SELECT_CB_VAR_FREE(name)              API_VAR_FREE(MEMP_SELECT_CB, name)
87 
88 #if LWIP_IPV4
89 #define IP4ADDR_PORT_TO_SOCKADDR(sin, ipaddr, port) do { \
90       (sin)->sin_len = sizeof(struct sockaddr_in); \
91       (sin)->sin_family = AF_INET; \
92       (sin)->sin_port = lwip_htons((port)); \
93       inet_addr_from_ip4addr(&(sin)->sin_addr, ipaddr); \
94       memset((sin)->sin_zero, 0, SIN_ZERO_LEN); }while(0)
95 #define SOCKADDR4_TO_IP4ADDR_PORT(sin, ipaddr, port) do { \
96     inet_addr_to_ip4addr(ip_2_ip4(ipaddr), &((sin)->sin_addr)); \
97     (port) = lwip_ntohs((sin)->sin_port); }while(0)
98 #endif /* LWIP_IPV4 */
99 
100 #if LWIP_IPV6
101 #define IP6ADDR_PORT_TO_SOCKADDR(sin6, ipaddr, port) do { \
102       (sin6)->sin6_len = sizeof(struct sockaddr_in6); \
103       (sin6)->sin6_family = AF_INET6; \
104       (sin6)->sin6_port = lwip_htons((port)); \
105       (sin6)->sin6_flowinfo = 0; \
106       inet6_addr_from_ip6addr(&(sin6)->sin6_addr, ipaddr); \
107       (sin6)->sin6_scope_id = ip6_addr_zone(ipaddr); }while(0)
108 #define SOCKADDR6_TO_IP6ADDR_PORT(sin6, ipaddr, port) do { \
109     inet6_addr_to_ip6addr(ip_2_ip6(ipaddr), &((sin6)->sin6_addr)); \
110     if (ip6_addr_has_scope(ip_2_ip6(ipaddr), IP6_UNKNOWN)) { \
111       ip6_addr_set_zone(ip_2_ip6(ipaddr), (u8_t)((sin6)->sin6_scope_id)); \
112     } \
113     (port) = lwip_ntohs((sin6)->sin6_port); }while(0)
114 #endif /* LWIP_IPV6 */
115 
116 #if LWIP_IPV4 && LWIP_IPV6
117 static void sockaddr_to_ipaddr_port(const struct sockaddr *sockaddr, ip_addr_t *ipaddr, u16_t *port);
118 
119 #define IS_SOCK_ADDR_LEN_VALID(namelen)  (((namelen) == sizeof(struct sockaddr_in)) || \
120                                          ((namelen) == sizeof(struct sockaddr_in6)))
121 #define IS_SOCK_ADDR_TYPE_VALID(name)    (((name)->sa_family == AF_INET) || \
122                                          ((name)->sa_family == AF_INET6))
123 #define SOCK_ADDR_TYPE_MATCH(name, sock) \
124        ((((name)->sa_family == AF_INET) && !(NETCONNTYPE_ISIPV6((sock)->conn->type))) || \
125        (((name)->sa_family == AF_INET6) && (NETCONNTYPE_ISIPV6((sock)->conn->type))))
126 #define IPADDR_PORT_TO_SOCKADDR(sockaddr, ipaddr, port) do { \
127     if (IP_IS_ANY_TYPE_VAL(*ipaddr) || IP_IS_V6_VAL(*ipaddr)) { \
128       IP6ADDR_PORT_TO_SOCKADDR((struct sockaddr_in6*)(void*)(sockaddr), ip_2_ip6(ipaddr), port); \
129     } else { \
130       IP4ADDR_PORT_TO_SOCKADDR((struct sockaddr_in*)(void*)(sockaddr), ip_2_ip4(ipaddr), port); \
131     } } while(0)
132 #define SOCKADDR_TO_IPADDR_PORT(sockaddr, ipaddr, port) sockaddr_to_ipaddr_port(sockaddr, ipaddr, &(port))
133 #define DOMAIN_TO_NETCONN_TYPE(domain, type) (((domain) == AF_INET) ? \
134   (type) : (enum netconn_type)((type) | NETCONN_TYPE_IPV6))
135 #elif LWIP_IPV6 /* LWIP_IPV4 && LWIP_IPV6 */
136 #define IS_SOCK_ADDR_LEN_VALID(namelen)  ((namelen) == sizeof(struct sockaddr_in6))
137 #define IS_SOCK_ADDR_TYPE_VALID(name)    ((name)->sa_family == AF_INET6)
138 #define SOCK_ADDR_TYPE_MATCH(name, sock) 1
139 #define IPADDR_PORT_TO_SOCKADDR(sockaddr, ipaddr, port) \
140         IP6ADDR_PORT_TO_SOCKADDR((struct sockaddr_in6*)(void*)(sockaddr), ip_2_ip6(ipaddr), port)
141 #define SOCKADDR_TO_IPADDR_PORT(sockaddr, ipaddr, port) \
142         SOCKADDR6_TO_IP6ADDR_PORT((const struct sockaddr_in6*)(const void*)(sockaddr), ipaddr, port)
143 #define DOMAIN_TO_NETCONN_TYPE(domain, netconn_type) (netconn_type)
144 #else /*-> LWIP_IPV4: LWIP_IPV4 && LWIP_IPV6 */
145 #define IS_SOCK_ADDR_LEN_VALID(namelen)  ((namelen) == sizeof(struct sockaddr_in))
146 #define IS_SOCK_ADDR_TYPE_VALID(name)    ((name)->sa_family == AF_INET)
147 #define SOCK_ADDR_TYPE_MATCH(name, sock) 1
148 #define IPADDR_PORT_TO_SOCKADDR(sockaddr, ipaddr, port) \
149         IP4ADDR_PORT_TO_SOCKADDR((struct sockaddr_in*)(void*)(sockaddr), ip_2_ip4(ipaddr), port)
150 #define SOCKADDR_TO_IPADDR_PORT(sockaddr, ipaddr, port) \
151         SOCKADDR4_TO_IP4ADDR_PORT((const struct sockaddr_in*)(const void*)(sockaddr), ipaddr, port)
152 #define DOMAIN_TO_NETCONN_TYPE(domain, netconn_type) (netconn_type)
153 #endif /* LWIP_IPV6 */
154 
155 #define IS_SOCK_ADDR_TYPE_VALID_OR_UNSPEC(name)    (((name)->sa_family == AF_UNSPEC) || \
156                                                     IS_SOCK_ADDR_TYPE_VALID(name))
157 #define SOCK_ADDR_TYPE_MATCH_OR_UNSPEC(name, sock) (((name)->sa_family == AF_UNSPEC) || \
158                                                     SOCK_ADDR_TYPE_MATCH(name, sock))
159 #define IS_SOCK_ADDR_ALIGNED(name)      ((((mem_ptr_t)(name)) % 4) == 0)
160 
161 
162 #define LWIP_SOCKOPT_CHECK_OPTLEN(sock, optlen, opttype) do { if ((optlen) < sizeof(opttype)) { done_socket(sock); return EINVAL; }}while(0)
163 #define LWIP_SOCKOPT_CHECK_OPTLEN_CONN(sock, optlen, opttype) do { \
164   LWIP_SOCKOPT_CHECK_OPTLEN(sock, optlen, opttype); \
165   if ((sock)->conn == NULL) { done_socket(sock); return EINVAL; } }while(0)
166 #define LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, optlen, opttype) do { \
167   LWIP_SOCKOPT_CHECK_OPTLEN(sock, optlen, opttype); \
168   if (((sock)->conn == NULL) || ((sock)->conn->pcb.tcp == NULL)) { done_socket(sock); return EINVAL; } }while(0)
169 #define LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, opttype, netconntype) do { \
170   LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, optlen, opttype); \
171   if (NETCONNTYPE_GROUP(netconn_type((sock)->conn)) != netconntype) { done_socket(sock); return ENOPROTOOPT; } }while(0)
172 
173 
174 #define LWIP_SETGETSOCKOPT_DATA_VAR_REF(name)     API_VAR_REF(name)
175 #define LWIP_SETGETSOCKOPT_DATA_VAR_DECLARE(name) API_VAR_DECLARE(struct lwip_setgetsockopt_data, name)
176 #define LWIP_SETGETSOCKOPT_DATA_VAR_FREE(name)    API_VAR_FREE(MEMP_SOCKET_SETGETSOCKOPT_DATA, name)
177 #if LWIP_MPU_COMPATIBLE
178 #define LWIP_SETGETSOCKOPT_DATA_VAR_ALLOC(name, sock) do { \
179   name = (struct lwip_setgetsockopt_data *)memp_malloc(MEMP_SOCKET_SETGETSOCKOPT_DATA); \
180   if (name == NULL) { \
181     sock_set_errno(sock, ENOMEM); \
182     done_socket(sock); \
183     return -1; \
184   } }while(0)
185 #else /* LWIP_MPU_COMPATIBLE */
186 #define LWIP_SETGETSOCKOPT_DATA_VAR_ALLOC(name, sock)
187 #endif /* LWIP_MPU_COMPATIBLE */
188 
189 #if LWIP_SO_SNDRCVTIMEO_NONSTANDARD
190 #define LWIP_SO_SNDRCVTIMEO_OPTTYPE int
191 #define LWIP_SO_SNDRCVTIMEO_SET(optval, val) (*(int *)(optval) = (val))
192 #define LWIP_SO_SNDRCVTIMEO_GET_MS(optval)   ((long)*(const int*)(optval))
193 #else
194 #define LWIP_SO_SNDRCVTIMEO_OPTTYPE struct timeval
195 #define LWIP_SO_SNDRCVTIMEO_SET(optval, val)  do { \
196   u32_t loc = (val); \
197   ((struct timeval *)(optval))->tv_sec = (long)((loc) / 1000U); \
198   ((struct timeval *)(optval))->tv_usec = (long)(((loc) % 1000U) * 1000U); }while(0)
199 #define LWIP_SO_SNDRCVTIMEO_GET_MS(optval) ((((const struct timeval *)(optval))->tv_sec * 1000) + (((const struct timeval *)(optval))->tv_usec / 1000))
200 #endif
201 
202 
203 /** A struct sockaddr replacement that has the same alignment as sockaddr_in/
204  *  sockaddr_in6 if instantiated.
205  */
206 union sockaddr_aligned {
207   struct sockaddr sa;
208 #if LWIP_IPV6
209   struct sockaddr_in6 sin6;
210 #endif /* LWIP_IPV6 */
211 #if LWIP_IPV4
212   struct sockaddr_in sin;
213 #endif /* LWIP_IPV4 */
214 };
215 
216 /* Define the number of IPv4 multicast memberships, default is one per socket */
217 #ifndef LWIP_SOCKET_MAX_MEMBERSHIPS
218 #define LWIP_SOCKET_MAX_MEMBERSHIPS NUM_SOCKETS
219 #endif
220 
221 #if LWIP_IGMP
222 /* This is to keep track of IP_ADD_MEMBERSHIP calls to drop the membership when
223    a socket is closed */
224 struct lwip_socket_multicast_pair {
225   /** the socket */
226   struct lwip_sock *sock;
227   /** the interface address */
228   ip4_addr_t if_addr;
229   /** the group address */
230   ip4_addr_t multi_addr;
231 };
232 
233 static struct lwip_socket_multicast_pair socket_ipv4_multicast_memberships[LWIP_SOCKET_MAX_MEMBERSHIPS];
234 
235 static int  lwip_socket_register_membership(int s, const ip4_addr_t *if_addr, const ip4_addr_t *multi_addr);
236 static void lwip_socket_unregister_membership(int s, const ip4_addr_t *if_addr, const ip4_addr_t *multi_addr);
237 static void lwip_socket_drop_registered_memberships(int s);
238 #endif /* LWIP_IGMP */
239 
240 #if LWIP_IPV6_MLD
241 /* This is to keep track of IP_JOIN_GROUP calls to drop the membership when
242    a socket is closed */
243 struct lwip_socket_multicast_mld6_pair {
244   /** the socket */
245   struct lwip_sock *sock;
246   /** the interface index */
247   u8_t if_idx;
248   /** the group address */
249   ip6_addr_t multi_addr;
250 };
251 
252 static struct lwip_socket_multicast_mld6_pair socket_ipv6_multicast_memberships[LWIP_SOCKET_MAX_MEMBERSHIPS];
253 
254 static int  lwip_socket_register_mld6_membership(int s, unsigned int if_idx, const ip6_addr_t *multi_addr);
255 static void lwip_socket_unregister_mld6_membership(int s, unsigned int if_idx, const ip6_addr_t *multi_addr);
256 static void lwip_socket_drop_registered_mld6_memberships(int s);
257 #endif /* LWIP_IPV6_MLD */
258 
259 /** The global array of available sockets */
260 static struct lwip_sock sockets[NUM_SOCKETS];
261 
262 #if LWIP_SOCKET_SELECT || LWIP_SOCKET_POLL
263 #if LWIP_TCPIP_CORE_LOCKING
264 /* protect the select_cb_list using core lock */
265 #define LWIP_SOCKET_SELECT_DECL_PROTECT(lev)
266 #define LWIP_SOCKET_SELECT_PROTECT(lev)   LOCK_TCPIP_CORE()
267 #define LWIP_SOCKET_SELECT_UNPROTECT(lev) UNLOCK_TCPIP_CORE()
268 #else /* LWIP_TCPIP_CORE_LOCKING */
269 /* protect the select_cb_list using SYS_LIGHTWEIGHT_PROT */
270 #define LWIP_SOCKET_SELECT_DECL_PROTECT(lev)  SYS_ARCH_DECL_PROTECT(lev)
271 #define LWIP_SOCKET_SELECT_PROTECT(lev)       SYS_ARCH_PROTECT(lev)
272 #define LWIP_SOCKET_SELECT_UNPROTECT(lev)     SYS_ARCH_UNPROTECT(lev)
273 /** This counter is increased from lwip_select when the list is changed
274     and checked in select_check_waiters to see if it has changed. */
275 static volatile int select_cb_ctr;
276 #endif /* LWIP_TCPIP_CORE_LOCKING */
277 /** The global list of tasks waiting for select */
278 static struct lwip_select_cb *select_cb_list;
279 #endif /* LWIP_SOCKET_SELECT || LWIP_SOCKET_POLL */
280 
281 #define sock_set_errno(sk, e) do { \
282   const int sockerr = (e); \
283   set_errno(sockerr); \
284 } while (0)
285 
286 /* Forward declaration of some functions */
287 #if LWIP_SOCKET_SELECT || LWIP_SOCKET_POLL
288 static void event_callback(struct netconn *conn, enum netconn_evt evt, u16_t len);
289 #define DEFAULT_SOCKET_EVENTCB event_callback
290 static void select_check_waiters(int s, int has_recvevent, int has_sendevent, int has_errevent);
291 #else
292 #define DEFAULT_SOCKET_EVENTCB NULL
293 #endif
294 #if !LWIP_TCPIP_CORE_LOCKING
295 static void lwip_getsockopt_callback(void *arg);
296 static void lwip_setsockopt_callback(void *arg);
297 #endif
298 static int lwip_getsockopt_impl(int s, int level, int optname, void *optval, socklen_t *optlen);
299 static int lwip_setsockopt_impl(int s, int level, int optname, const void *optval, socklen_t optlen);
300 static int free_socket_locked(struct lwip_sock *sock, int is_tcp, struct netconn **conn,
301                               union lwip_sock_lastdata *lastdata);
302 static void free_socket_free_elements(int is_tcp, struct netconn *conn, union lwip_sock_lastdata *lastdata);
303 
304 #if LWIP_IPV4 && LWIP_IPV6
305 static void
sockaddr_to_ipaddr_port(const struct sockaddr * sockaddr,ip_addr_t * ipaddr,u16_t * port)306 sockaddr_to_ipaddr_port(const struct sockaddr *sockaddr, ip_addr_t *ipaddr, u16_t *port)
307 {
308   if ((sockaddr->sa_family) == AF_INET6) {
309     SOCKADDR6_TO_IP6ADDR_PORT((const struct sockaddr_in6 *)(const void *)(sockaddr), ipaddr, *port);
310     ipaddr->type = IPADDR_TYPE_V6;
311   } else {
312     SOCKADDR4_TO_IP4ADDR_PORT((const struct sockaddr_in *)(const void *)(sockaddr), ipaddr, *port);
313     ipaddr->type = IPADDR_TYPE_V4;
314   }
315 }
316 #endif /* LWIP_IPV4 && LWIP_IPV6 */
317 
318 /** LWIP_NETCONN_SEM_PER_THREAD==1: initialize thread-local semaphore */
319 void
lwip_socket_thread_init(void)320 lwip_socket_thread_init(void)
321 {
322   netconn_thread_init();
323 }
324 
325 /** LWIP_NETCONN_SEM_PER_THREAD==1: destroy thread-local semaphore */
326 void
lwip_socket_thread_cleanup(void)327 lwip_socket_thread_cleanup(void)
328 {
329   netconn_thread_cleanup();
330 }
331 
332 #if LWIP_NETCONN_FULLDUPLEX
333 /* Thread-safe increment of sock->fd_used, with overflow check */
334 static int
sock_inc_used(struct lwip_sock * sock)335 sock_inc_used(struct lwip_sock *sock)
336 {
337   int ret;
338   SYS_ARCH_DECL_PROTECT(lev);
339 
340   LWIP_ASSERT("sock != NULL", sock != NULL);
341 
342   SYS_ARCH_PROTECT(lev);
343   if (sock->fd_free_pending) {
344     /* prevent new usage of this socket if free is pending */
345     ret = 0;
346   } else {
347     ++sock->fd_used;
348     ret = 1;
349     LWIP_ASSERT("sock->fd_used != 0", sock->fd_used != 0);
350   }
351   SYS_ARCH_UNPROTECT(lev);
352   return ret;
353 }
354 
355 /* Like sock_inc_used(), but called under SYS_ARCH_PROTECT lock. */
356 static int
sock_inc_used_locked(struct lwip_sock * sock)357 sock_inc_used_locked(struct lwip_sock *sock)
358 {
359   LWIP_ASSERT("sock != NULL", sock != NULL);
360 
361   if (sock->fd_free_pending) {
362     LWIP_ASSERT("sock->fd_used != 0", sock->fd_used != 0);
363     return 0;
364   }
365 
366   ++sock->fd_used;
367   LWIP_ASSERT("sock->fd_used != 0", sock->fd_used != 0);
368   return 1;
369 }
370 
371 /* In full-duplex mode,sock->fd_used != 0 prevents a socket descriptor from being
372  * released (and possibly reused) when used from more than one thread
373  * (e.g. read-while-write or close-while-write, etc)
374  * This function is called at the end of functions using (try)get_socket*().
375  */
376 static void
done_socket(struct lwip_sock * sock)377 done_socket(struct lwip_sock *sock)
378 {
379   int freed = 0;
380   int is_tcp = 0;
381   struct netconn *conn = NULL;
382   union lwip_sock_lastdata lastdata;
383   SYS_ARCH_DECL_PROTECT(lev);
384   LWIP_ASSERT("sock != NULL", sock != NULL);
385 
386   SYS_ARCH_PROTECT(lev);
387   LWIP_ASSERT("sock->fd_used > 0", sock->fd_used > 0);
388   if (--sock->fd_used == 0) {
389     if (sock->fd_free_pending) {
390       /* free the socket */
391       sock->fd_used = 1;
392       is_tcp = sock->fd_free_pending & LWIP_SOCK_FD_FREE_TCP;
393       freed = free_socket_locked(sock, is_tcp, &conn, &lastdata);
394     }
395   }
396   SYS_ARCH_UNPROTECT(lev);
397 
398   if (freed) {
399     free_socket_free_elements(is_tcp, conn, &lastdata);
400   }
401 }
402 
403 #else /* LWIP_NETCONN_FULLDUPLEX */
404 #define sock_inc_used(sock)         1
405 #define sock_inc_used_locked(sock)  1
406 #define done_socket(sock)
407 #endif /* LWIP_NETCONN_FULLDUPLEX */
408 
409 /* Translate a socket 'int' into a pointer (only fails if the index is invalid) */
410 static struct lwip_sock *
tryget_socket_unconn_nouse(int fd)411 tryget_socket_unconn_nouse(int fd)
412 {
413   int s = fd - LWIP_SOCKET_OFFSET;
414   if ((s < 0) || (s >= NUM_SOCKETS)) {
415     LWIP_DEBUGF(SOCKETS_DEBUG, ("tryget_socket_unconn(%d): invalid\n", fd));
416     return NULL;
417   }
418   return &sockets[s];
419 }
420 
421 struct lwip_sock *
lwip_socket_dbg_get_socket(int fd)422 lwip_socket_dbg_get_socket(int fd)
423 {
424   return tryget_socket_unconn_nouse(fd);
425 }
426 
427 /* Translate a socket 'int' into a pointer (only fails if the index is invalid) */
428 static struct lwip_sock *
tryget_socket_unconn(int fd)429 tryget_socket_unconn(int fd)
430 {
431   struct lwip_sock *ret = tryget_socket_unconn_nouse(fd);
432   if (ret != NULL) {
433     if (!sock_inc_used(ret)) {
434       return NULL;
435     }
436   }
437   return ret;
438 }
439 
440 /* Like tryget_socket_unconn(), but called under SYS_ARCH_PROTECT lock. */
441 static struct lwip_sock *
tryget_socket_unconn_locked(int fd)442 tryget_socket_unconn_locked(int fd)
443 {
444   struct lwip_sock *ret = tryget_socket_unconn_nouse(fd);
445   if (ret != NULL) {
446     if (!sock_inc_used_locked(ret)) {
447       return NULL;
448     }
449   }
450   return ret;
451 }
452 
453 /**
454  * Same as get_socket but doesn't set errno
455  *
456  * @param fd externally used socket index
457  * @return struct lwip_sock for the socket or NULL if not found
458  */
459 static struct lwip_sock *
tryget_socket(int fd)460 tryget_socket(int fd)
461 {
462   struct lwip_sock *sock = tryget_socket_unconn(fd);
463   if (sock != NULL) {
464     if (sock->conn) {
465       return sock;
466     }
467     done_socket(sock);
468   }
469   return NULL;
470 }
471 
472 /**
473  * Same as tryget_socket but a global routine.
474  *
475  * @param fd externally used socket index
476  * @return struct lwip_sock for the socket or NULL if not found
477  */
478 struct lwip_sock *
lwip_tryget_socket(int fd)479 lwip_tryget_socket(int fd)
480 {
481   struct lwip_sock *sock = tryget_socket_unconn(fd);
482   if (sock != NULL) {
483     if (sock->conn) {
484       return sock;
485     }
486     done_socket(sock);
487   }
488   return NULL;
489 }
490 
491 /**
492  * Map a externally used socket index to the internal socket representation.
493  *
494  * @param fd externally used socket index
495  * @return struct lwip_sock for the socket or NULL if not found
496  */
497 static struct lwip_sock *
get_socket(int fd)498 get_socket(int fd)
499 {
500   struct lwip_sock *sock = tryget_socket(fd);
501   if (!sock) {
502     if ((fd < LWIP_SOCKET_OFFSET) || (fd >= (LWIP_SOCKET_OFFSET + NUM_SOCKETS))) {
503       LWIP_DEBUGF(SOCKETS_DEBUG, ("get_socket(%d): invalid\n", fd));
504     }
505     set_errno(EBADF);
506     return NULL;
507   }
508   return sock;
509 }
510 
511 /**
512  * Allocate a new socket for a given netconn.
513  *
514  * @param newconn the netconn for which to allocate a socket
515  * @param accepted 1 if socket has been created by accept(),
516  *                 0 if socket has been created by socket()
517  * @return the index of the new socket; -1 on error
518  */
519 static int
alloc_socket(struct netconn * newconn,int accepted)520 alloc_socket(struct netconn *newconn, int accepted)
521 {
522   int i;
523   SYS_ARCH_DECL_PROTECT(lev);
524   LWIP_UNUSED_ARG(accepted);
525 
526   /* allocate a new socket identifier */
527   for (i = 0; i < NUM_SOCKETS; ++i) {
528     /* Protect socket array */
529     SYS_ARCH_PROTECT(lev);
530     if (!sockets[i].conn) {
531 #if LWIP_NETCONN_FULLDUPLEX
532       if (sockets[i].fd_used) {
533         SYS_ARCH_UNPROTECT(lev);
534         continue;
535       }
536       sockets[i].fd_used    = 1;
537       sockets[i].fd_free_pending = 0;
538 #endif
539       sockets[i].conn       = newconn;
540       /* The socket is not yet known to anyone, so no need to protect
541          after having marked it as used. */
542       SYS_ARCH_UNPROTECT(lev);
543       sockets[i].lastdata.pbuf = NULL;
544 #if LWIP_SOCKET_SELECT
545       LWIP_ASSERT("sockets[i].select_waiting == 0", sockets[i].select_waiting == 0);
546       sockets[i].rcvevent   = 0;
547       /* TCP sendbuf is empty, but the socket is not yet writable until connected
548        * (unless it has been created by accept()). */
549       sockets[i].sendevent  = (NETCONNTYPE_GROUP(newconn->type) == NETCONN_TCP ? (accepted != 0) : 1);
550       sockets[i].errevent   = 0;
551 #endif /* LWIP_SOCKET_SELECT */
552       #ifdef SAL_USING_POSIX
553             rt_wqueue_init(&sockets[i].wait_head);
554       #endif
555       return i + LWIP_SOCKET_OFFSET;
556     }
557     SYS_ARCH_UNPROTECT(lev);
558   }
559   return -1;
560 }
561 
562 /** Free a socket (under lock)
563  *
564  * @param sock the socket to free
565  * @param is_tcp != 0 for TCP sockets, used to free lastdata
566  * @param conn the socekt's netconn is stored here, must be freed externally
567  * @param lastdata lastdata is stored here, must be freed externally
568  */
569 static int
free_socket_locked(struct lwip_sock * sock,int is_tcp,struct netconn ** conn,union lwip_sock_lastdata * lastdata)570 free_socket_locked(struct lwip_sock *sock, int is_tcp, struct netconn **conn,
571                    union lwip_sock_lastdata *lastdata)
572 {
573 #if LWIP_NETCONN_FULLDUPLEX
574   LWIP_ASSERT("sock->fd_used > 0", sock->fd_used > 0);
575   sock->fd_used--;
576   if (sock->fd_used > 0) {
577     sock->fd_free_pending = LWIP_SOCK_FD_FREE_FREE | (is_tcp ? LWIP_SOCK_FD_FREE_TCP : 0);
578     return 0;
579   }
580 #else /* LWIP_NETCONN_FULLDUPLEX */
581   LWIP_UNUSED_ARG(is_tcp);
582 #endif /* LWIP_NETCONN_FULLDUPLEX */
583 
584   *lastdata = sock->lastdata;
585   sock->lastdata.pbuf = NULL;
586   *conn = sock->conn;
587   sock->conn = NULL;
588   return 1;
589 }
590 
591 /** Free a socket's leftover members.
592  */
593 static void
free_socket_free_elements(int is_tcp,struct netconn * conn,union lwip_sock_lastdata * lastdata)594 free_socket_free_elements(int is_tcp, struct netconn *conn, union lwip_sock_lastdata *lastdata)
595 {
596   if (lastdata->pbuf != NULL) {
597     if (is_tcp) {
598       pbuf_free(lastdata->pbuf);
599     } else {
600       netbuf_delete(lastdata->netbuf);
601     }
602   }
603   if (conn != NULL) {
604     /* netconn_prepare_delete() has already been called, here we only free the conn */
605     netconn_delete(conn);
606   }
607 }
608 
609 /** Free a socket. The socket's netconn must have been
610  * delete before!
611  *
612  * @param sock the socket to free
613  * @param is_tcp != 0 for TCP sockets, used to free lastdata
614  */
615 static void
free_socket(struct lwip_sock * sock,int is_tcp)616 free_socket(struct lwip_sock *sock, int is_tcp)
617 {
618   int freed;
619   struct netconn *conn;
620   union lwip_sock_lastdata lastdata;
621   SYS_ARCH_DECL_PROTECT(lev);
622 
623   /* Protect socket array */
624   SYS_ARCH_PROTECT(lev);
625 
626   freed = free_socket_locked(sock, is_tcp, &conn, &lastdata);
627   SYS_ARCH_UNPROTECT(lev);
628   /* don't use 'sock' after this line, as another task might have allocated it */
629 
630   if (freed) {
631     free_socket_free_elements(is_tcp, conn, &lastdata);
632   }
633 }
634 
635 /* Below this, the well-known socket functions are implemented.
636  * Use google.com or opengroup.org to get a good description :-)
637  *
638  * Exceptions are documented!
639  */
640 
641 int
lwip_accept(int s,struct sockaddr * addr,socklen_t * addrlen)642 lwip_accept(int s, struct sockaddr *addr, socklen_t *addrlen)
643 {
644   struct lwip_sock *sock, *nsock;
645   struct netconn *newconn;
646   ip_addr_t naddr;
647   u16_t port = 0;
648   int newsock;
649   err_t err;
650   int recvevent;
651   SYS_ARCH_DECL_PROTECT(lev);
652 
653   LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_accept(%d)...\n", s));
654   sock = get_socket(s);
655   if (!sock) {
656     return -1;
657   }
658 
659   /* wait for a new connection */
660   err = netconn_accept(sock->conn, &newconn);
661   if (err != ERR_OK) {
662     LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_accept(%d): netconn_acept failed, err=%d\n", s, err));
663     if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_TCP) {
664       sock_set_errno(sock, EOPNOTSUPP);
665     } else if (err == ERR_CLSD) {
666       sock_set_errno(sock, EINVAL);
667     } else {
668       sock_set_errno(sock, err_to_errno(err));
669     }
670     done_socket(sock);
671     return -1;
672   }
673   LWIP_ASSERT("newconn != NULL", newconn != NULL);
674 
675   newsock = alloc_socket(newconn, 1);
676   if (newsock == -1) {
677     netconn_delete(newconn);
678     sock_set_errno(sock, ENFILE);
679     done_socket(sock);
680     return -1;
681   }
682   LWIP_ASSERT("invalid socket index", (newsock >= LWIP_SOCKET_OFFSET) && (newsock < NUM_SOCKETS + LWIP_SOCKET_OFFSET));
683   nsock = &sockets[newsock - LWIP_SOCKET_OFFSET];
684 
685   /* See event_callback: If data comes in right away after an accept, even
686    * though the server task might not have created a new socket yet.
687    * In that case, newconn->socket is counted down (newconn->socket--),
688    * so nsock->rcvevent is >= 1 here!
689    */
690   SYS_ARCH_PROTECT(lev);
691   recvevent = (s16_t)(-1 - newconn->socket);
692   newconn->socket = newsock;
693   SYS_ARCH_UNPROTECT(lev);
694 
695   if (newconn->callback) {
696     LOCK_TCPIP_CORE();
697     while (recvevent > 0) {
698       recvevent--;
699       newconn->callback(newconn, NETCONN_EVT_RCVPLUS, 0);
700     }
701     UNLOCK_TCPIP_CORE();
702   }
703 
704   /* Note that POSIX only requires us to check addr is non-NULL. addrlen must
705    * not be NULL if addr is valid.
706    */
707   if ((addr != NULL) && (addrlen != NULL)) {
708     union sockaddr_aligned tempaddr;
709     /* get the IP address and port of the remote host */
710     err = netconn_peer(newconn, &naddr, &port);
711     if (err != ERR_OK) {
712       LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_accept(%d): netconn_peer failed, err=%d\n", s, err));
713       netconn_delete(newconn);
714       free_socket(nsock, 1);
715       sock_set_errno(sock, err_to_errno(err));
716       done_socket(sock);
717       return -1;
718     }
719 
720     IPADDR_PORT_TO_SOCKADDR(&tempaddr, &naddr, port);
721     if (*addrlen > tempaddr.sa.sa_len) {
722       *addrlen = tempaddr.sa.sa_len;
723     }
724     MEMCPY(addr, &tempaddr, *addrlen);
725 
726     LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_accept(%d) returning new sock=%d addr=", s, newsock));
727     ip_addr_debug_print_val(SOCKETS_DEBUG, naddr);
728     LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F"\n", port));
729   } else {
730     LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_accept(%d) returning new sock=%d", s, newsock));
731   }
732 
733   sock_set_errno(sock, 0);
734   done_socket(sock);
735   done_socket(nsock);
736   return newsock;
737 }
738 
739 int
lwip_bind(int s,const struct sockaddr * name,socklen_t namelen)740 lwip_bind(int s, const struct sockaddr *name, socklen_t namelen)
741 {
742   struct lwip_sock *sock;
743   ip_addr_t local_addr;
744   u16_t local_port;
745   err_t err;
746 
747   sock = get_socket(s);
748   if (!sock) {
749     return -1;
750   }
751 
752   if (!SOCK_ADDR_TYPE_MATCH(name, sock)) {
753     /* sockaddr does not match socket type (IPv4/IPv6) */
754     sock_set_errno(sock, err_to_errno(ERR_VAL));
755     done_socket(sock);
756     return -1;
757   }
758 
759   /* check size, family and alignment of 'name' */
760   LWIP_ERROR("lwip_bind: invalid address", (IS_SOCK_ADDR_LEN_VALID(namelen) &&
761              IS_SOCK_ADDR_TYPE_VALID(name) && IS_SOCK_ADDR_ALIGNED(name)),
762              sock_set_errno(sock, err_to_errno(ERR_ARG)); done_socket(sock); return -1;);
763   LWIP_UNUSED_ARG(namelen);
764 
765   SOCKADDR_TO_IPADDR_PORT(name, &local_addr, local_port);
766   LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_bind(%d, addr=", s));
767   ip_addr_debug_print_val(SOCKETS_DEBUG, local_addr);
768   LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F")\n", local_port));
769 
770 #if LWIP_IPV4 && LWIP_IPV6
771   /* Dual-stack: Unmap IPv4 mapped IPv6 addresses */
772   if (IP_IS_V6_VAL(local_addr) && ip6_addr_isipv4mappedipv6(ip_2_ip6(&local_addr))) {
773     unmap_ipv4_mapped_ipv6(ip_2_ip4(&local_addr), ip_2_ip6(&local_addr));
774     IP_SET_TYPE_VAL(local_addr, IPADDR_TYPE_V4);
775   }
776 #endif /* LWIP_IPV4 && LWIP_IPV6 */
777 
778   err = netconn_bind(sock->conn, &local_addr, local_port);
779 
780   if (err != ERR_OK) {
781     LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_bind(%d) failed, err=%d\n", s, err));
782     sock_set_errno(sock, err_to_errno(err));
783     done_socket(sock);
784     return -1;
785   }
786 
787   LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_bind(%d) succeeded\n", s));
788   sock_set_errno(sock, 0);
789   done_socket(sock);
790   return 0;
791 }
792 
793 int
lwip_close(int s)794 lwip_close(int s)
795 {
796   struct lwip_sock *sock;
797   int is_tcp = 0;
798   err_t err;
799 
800   LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_close(%d)\n", s));
801 
802   sock = get_socket(s);
803   if (!sock) {
804     return -1;
805   }
806 
807   if (sock->conn != NULL) {
808     is_tcp = NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_TCP;
809   } else {
810     LWIP_ASSERT("sock->lastdata == NULL", sock->lastdata.pbuf == NULL);
811   }
812 
813 #if LWIP_IGMP
814   /* drop all possibly joined IGMP memberships */
815   lwip_socket_drop_registered_memberships(s);
816 #endif /* LWIP_IGMP */
817 #if LWIP_IPV6_MLD
818   /* drop all possibly joined MLD6 memberships */
819   lwip_socket_drop_registered_mld6_memberships(s);
820 #endif /* LWIP_IPV6_MLD */
821 
822   err = netconn_prepare_delete(sock->conn);
823   if (err != ERR_OK) {
824     sock_set_errno(sock, err_to_errno(err));
825     done_socket(sock);
826     return -1;
827   }
828 
829   free_socket(sock, is_tcp);
830   set_errno(0);
831   return 0;
832 }
833 
834 int
lwip_connect(int s,const struct sockaddr * name,socklen_t namelen)835 lwip_connect(int s, const struct sockaddr *name, socklen_t namelen)
836 {
837   struct lwip_sock *sock;
838   err_t err;
839 
840   sock = get_socket(s);
841   if (!sock) {
842     return -1;
843   }
844 
845   if (!SOCK_ADDR_TYPE_MATCH_OR_UNSPEC(name, sock)) {
846     /* sockaddr does not match socket type (IPv4/IPv6) */
847     sock_set_errno(sock, err_to_errno(ERR_VAL));
848     done_socket(sock);
849     return -1;
850   }
851 
852   LWIP_UNUSED_ARG(namelen);
853   if (name->sa_family == AF_UNSPEC) {
854     LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_connect(%d, AF_UNSPEC)\n", s));
855     err = netconn_disconnect(sock->conn);
856   } else {
857     ip_addr_t remote_addr;
858     u16_t remote_port;
859 
860     /* check size, family and alignment of 'name' */
861     LWIP_ERROR("lwip_connect: invalid address", IS_SOCK_ADDR_LEN_VALID(namelen) &&
862                IS_SOCK_ADDR_TYPE_VALID_OR_UNSPEC(name) && IS_SOCK_ADDR_ALIGNED(name),
863                sock_set_errno(sock, err_to_errno(ERR_ARG)); done_socket(sock); return -1;);
864 
865     SOCKADDR_TO_IPADDR_PORT(name, &remote_addr, remote_port);
866     LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_connect(%d, addr=", s));
867     ip_addr_debug_print_val(SOCKETS_DEBUG, remote_addr);
868     LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F")\n", remote_port));
869 
870 #if LWIP_IPV4 && LWIP_IPV6
871     /* Dual-stack: Unmap IPv4 mapped IPv6 addresses */
872     if (IP_IS_V6_VAL(remote_addr) && ip6_addr_isipv4mappedipv6(ip_2_ip6(&remote_addr))) {
873       unmap_ipv4_mapped_ipv6(ip_2_ip4(&remote_addr), ip_2_ip6(&remote_addr));
874       IP_SET_TYPE_VAL(remote_addr, IPADDR_TYPE_V4);
875     }
876 #endif /* LWIP_IPV4 && LWIP_IPV6 */
877 
878     err = netconn_connect(sock->conn, &remote_addr, remote_port);
879   }
880 
881   if (err != ERR_OK) {
882     LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_connect(%d) failed, err=%d\n", s, err));
883     sock_set_errno(sock, err_to_errno(err));
884     done_socket(sock);
885     return -1;
886   }
887 
888   LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_connect(%d) succeeded\n", s));
889   sock_set_errno(sock, 0);
890   done_socket(sock);
891   return 0;
892 }
893 
894 /**
895  * Set a socket into listen mode.
896  * The socket may not have been used for another connection previously.
897  *
898  * @param s the socket to set to listening mode
899  * @param backlog (ATTENTION: needs TCP_LISTEN_BACKLOG=1)
900  * @return 0 on success, non-zero on failure
901  */
902 int
lwip_listen(int s,int backlog)903 lwip_listen(int s, int backlog)
904 {
905   struct lwip_sock *sock;
906   err_t err;
907 
908   LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_listen(%d, backlog=%d)\n", s, backlog));
909 
910   sock = get_socket(s);
911   if (!sock) {
912     return -1;
913   }
914 
915   /* limit the "backlog" parameter to fit in an u8_t */
916   backlog = LWIP_MIN(LWIP_MAX(backlog, 0), 0xff);
917 
918   err = netconn_listen_with_backlog(sock->conn, (u8_t)backlog);
919 
920   if (err != ERR_OK) {
921     LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_listen(%d) failed, err=%d\n", s, err));
922     if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_TCP) {
923       sock_set_errno(sock, EOPNOTSUPP);
924     } else {
925       sock_set_errno(sock, err_to_errno(err));
926     }
927     done_socket(sock);
928     return -1;
929   }
930 
931   sock_set_errno(sock, 0);
932   done_socket(sock);
933   return 0;
934 }
935 
936 #if LWIP_TCP
937 /* Helper function to loop over receiving pbufs from netconn
938  * until "len" bytes are received or we're otherwise done.
939  * Keeps sock->lastdata for peeking or partly copying.
940  */
941 static ssize_t
lwip_recv_tcp(struct lwip_sock * sock,void * mem,size_t len,int flags)942 lwip_recv_tcp(struct lwip_sock *sock, void *mem, size_t len, int flags)
943 {
944   u8_t apiflags = NETCONN_NOAUTORCVD;
945   ssize_t recvd = 0;
946   ssize_t recv_left = (len <= SSIZE_MAX) ? (ssize_t)len : SSIZE_MAX;
947 
948   LWIP_ASSERT("no socket given", sock != NULL);
949   LWIP_ASSERT("this should be checked internally", NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_TCP);
950 
951   if (flags & MSG_DONTWAIT) {
952     apiflags |= NETCONN_DONTBLOCK;
953   }
954 
955   do {
956     struct pbuf *p;
957     err_t err;
958     u16_t copylen;
959 
960     LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recv_tcp: top while sock->lastdata=%p\n", (void *)sock->lastdata.pbuf));
961     /* Check if there is data left from the last recv operation. */
962     if (sock->lastdata.pbuf) {
963       p = sock->lastdata.pbuf;
964     } else {
965       /* No data was left from the previous operation, so we try to get
966          some from the network. */
967       err = netconn_recv_tcp_pbuf_flags(sock->conn, &p, apiflags);
968       LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recv_tcp: netconn_recv err=%d, pbuf=%p\n",
969                                   err, (void *)p));
970 
971       if (err != ERR_OK) {
972         if (recvd > 0) {
973           /* already received data, return that (this trusts in getting the same error from
974              netconn layer again next time netconn_recv is called) */
975           goto lwip_recv_tcp_done;
976         }
977         /* We should really do some error checking here. */
978         LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recv_tcp: p == NULL, error is \"%s\"!\n",
979                                     lwip_strerr(err)));
980         sock_set_errno(sock, err_to_errno(err));
981         if (err == ERR_CLSD) {
982           return 0;
983         } else {
984           return -1;
985         }
986       }
987       LWIP_ASSERT("p != NULL", p != NULL);
988       sock->lastdata.pbuf = p;
989     }
990 
991     LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recv_tcp: buflen=%"U16_F" recv_left=%d off=%d\n",
992                                 p->tot_len, (int)recv_left, (int)recvd));
993 
994     if (recv_left > p->tot_len) {
995       copylen = p->tot_len;
996     } else {
997       copylen = (u16_t)recv_left;
998     }
999     if (recvd + copylen < recvd) {
1000       /* overflow */
1001       copylen = (u16_t)(SSIZE_MAX - recvd);
1002     }
1003 
1004     /* copy the contents of the received buffer into
1005     the supplied memory pointer mem */
1006     pbuf_copy_partial(p, (u8_t *)mem + recvd, copylen, 0);
1007 
1008     recvd += copylen;
1009 
1010     /* TCP combines multiple pbufs for one recv */
1011     LWIP_ASSERT("invalid copylen, len would underflow", recv_left >= copylen);
1012     recv_left -= copylen;
1013 
1014     /* Unless we peek the incoming message... */
1015     if ((flags & MSG_PEEK) == 0) {
1016       /* ... check if there is data left in the pbuf */
1017       LWIP_ASSERT("invalid copylen", p->tot_len >= copylen);
1018       if (p->tot_len - copylen > 0) {
1019         /* If so, it should be saved in the sock structure for the next recv call.
1020            We store the pbuf but hide/free the consumed data: */
1021         sock->lastdata.pbuf = pbuf_free_header(p, copylen);
1022         LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recv_tcp: lastdata now pbuf=%p\n", (void *)sock->lastdata.pbuf));
1023       } else {
1024         sock->lastdata.pbuf = NULL;
1025         LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recv_tcp: deleting pbuf=%p\n", (void *)p));
1026         pbuf_free(p);
1027       }
1028     }
1029     /* once we have some data to return, only add more if we don't need to wait */
1030     apiflags |= NETCONN_DONTBLOCK | NETCONN_NOFIN;
1031     /* @todo: do we need to support peeking more than one pbuf? */
1032   } while ((recv_left > 0) && !(flags & MSG_PEEK));
1033 lwip_recv_tcp_done:
1034   if ((recvd > 0) && !(flags & MSG_PEEK)) {
1035     /* ensure window update after copying all data */
1036     netconn_tcp_recvd(sock->conn, (size_t)recvd);
1037   }
1038   sock_set_errno(sock, 0);
1039   return recvd;
1040 }
1041 #endif
1042 
1043 /* Convert a netbuf's address data to struct sockaddr */
1044 static int
lwip_sock_make_addr(struct netconn * conn,ip_addr_t * fromaddr,u16_t port,struct sockaddr * from,socklen_t * fromlen)1045 lwip_sock_make_addr(struct netconn *conn, ip_addr_t *fromaddr, u16_t port,
1046                     struct sockaddr *from, socklen_t *fromlen)
1047 {
1048   int truncated = 0;
1049   union sockaddr_aligned saddr;
1050 
1051   LWIP_UNUSED_ARG(conn);
1052 
1053   LWIP_ASSERT("fromaddr != NULL", fromaddr != NULL);
1054   LWIP_ASSERT("from != NULL", from != NULL);
1055   LWIP_ASSERT("fromlen != NULL", fromlen != NULL);
1056 
1057 #if LWIP_IPV4 && LWIP_IPV6
1058   /* Dual-stack: Map IPv4 addresses to IPv4 mapped IPv6 */
1059   if (NETCONNTYPE_ISIPV6(netconn_type(conn)) && IP_IS_V4(fromaddr)) {
1060     ip4_2_ipv4_mapped_ipv6(ip_2_ip6(fromaddr), ip_2_ip4(fromaddr));
1061     IP_SET_TYPE(fromaddr, IPADDR_TYPE_V6);
1062   }
1063 #endif /* LWIP_IPV4 && LWIP_IPV6 */
1064 
1065   IPADDR_PORT_TO_SOCKADDR(&saddr, fromaddr, port);
1066   if (*fromlen < saddr.sa.sa_len) {
1067     truncated = 1;
1068   } else if (*fromlen > saddr.sa.sa_len) {
1069     *fromlen = saddr.sa.sa_len;
1070   }
1071   MEMCPY(from, &saddr, *fromlen);
1072   return truncated;
1073 }
1074 
1075 #if LWIP_TCP
1076 /* Helper function to get a tcp socket's remote address info */
1077 static int
lwip_recv_tcp_from(struct lwip_sock * sock,struct sockaddr * from,socklen_t * fromlen,const char * dbg_fn,int dbg_s,ssize_t dbg_ret)1078 lwip_recv_tcp_from(struct lwip_sock *sock, struct sockaddr *from, socklen_t *fromlen, const char *dbg_fn, int dbg_s, ssize_t dbg_ret)
1079 {
1080   if (sock == NULL) {
1081     return 0;
1082   }
1083   LWIP_UNUSED_ARG(dbg_fn);
1084   LWIP_UNUSED_ARG(dbg_s);
1085   LWIP_UNUSED_ARG(dbg_ret);
1086 
1087 #if !SOCKETS_DEBUG
1088   if (from && fromlen)
1089 #endif /* !SOCKETS_DEBUG */
1090   {
1091     /* get remote addr/port from tcp_pcb */
1092     u16_t port;
1093     ip_addr_t tmpaddr;
1094     netconn_getaddr(sock->conn, &tmpaddr, &port, 0);
1095     LWIP_DEBUGF(SOCKETS_DEBUG, ("%s(%d):  addr=", dbg_fn, dbg_s));
1096     ip_addr_debug_print_val(SOCKETS_DEBUG, tmpaddr);
1097     LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F" len=%d\n", port, (int)dbg_ret));
1098     if (from && fromlen) {
1099       return lwip_sock_make_addr(sock->conn, &tmpaddr, port, from, fromlen);
1100     }
1101   }
1102   return 0;
1103 }
1104 #endif
1105 
1106 /* Helper function to receive a netbuf from a udp or raw netconn.
1107  * Keeps sock->lastdata for peeking.
1108  */
1109 static err_t
lwip_recvfrom_udp_raw(struct lwip_sock * sock,int flags,struct msghdr * msg,u16_t * datagram_len,int dbg_s)1110 lwip_recvfrom_udp_raw(struct lwip_sock *sock, int flags, struct msghdr *msg, u16_t *datagram_len, int dbg_s)
1111 {
1112   struct netbuf *buf;
1113   u8_t apiflags;
1114   err_t err;
1115   u16_t buflen, copylen, copied;
1116   int i;
1117 
1118   LWIP_UNUSED_ARG(dbg_s);
1119   LWIP_ERROR("lwip_recvfrom_udp_raw: invalid arguments", (msg->msg_iov != NULL) || (msg->msg_iovlen <= 0), return ERR_ARG;);
1120 
1121   if (flags & MSG_DONTWAIT) {
1122     apiflags = NETCONN_DONTBLOCK;
1123   } else {
1124     apiflags = 0;
1125   }
1126 
1127   LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom_udp_raw[UDP/RAW]: top sock->lastdata=%p\n", (void *)sock->lastdata.netbuf));
1128   /* Check if there is data left from the last recv operation. */
1129   buf = sock->lastdata.netbuf;
1130   if (buf == NULL) {
1131     /* No data was left from the previous operation, so we try to get
1132         some from the network. */
1133     err = netconn_recv_udp_raw_netbuf_flags(sock->conn, &buf, apiflags);
1134     LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom_udp_raw[UDP/RAW]: netconn_recv err=%d, netbuf=%p\n",
1135                                 err, (void *)buf));
1136 
1137     if (err != ERR_OK) {
1138       return err;
1139     }
1140     LWIP_ASSERT("buf != NULL", buf != NULL);
1141     sock->lastdata.netbuf = buf;
1142   }
1143   buflen = buf->p->tot_len;
1144   LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom_udp_raw: buflen=%"U16_F"\n", buflen));
1145 
1146   copied = 0;
1147   /* copy the pbuf payload into the iovs */
1148   for (i = 0; (i < msg->msg_iovlen) && (copied < buflen); i++) {
1149     u16_t len_left = (u16_t)(buflen - copied);
1150     if (msg->msg_iov[i].iov_len > len_left) {
1151       copylen = len_left;
1152     } else {
1153       copylen = (u16_t)msg->msg_iov[i].iov_len;
1154     }
1155 
1156     /* copy the contents of the received buffer into
1157         the supplied memory buffer */
1158     pbuf_copy_partial(buf->p, (u8_t *)msg->msg_iov[i].iov_base, copylen, copied);
1159     copied = (u16_t)(copied + copylen);
1160   }
1161 
1162   /* Check to see from where the data was.*/
1163 #if !SOCKETS_DEBUG
1164   if (msg->msg_name && msg->msg_namelen)
1165 #endif /* !SOCKETS_DEBUG */
1166   {
1167     LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom_udp_raw(%d):  addr=", dbg_s));
1168     ip_addr_debug_print_val(SOCKETS_DEBUG, *netbuf_fromaddr(buf));
1169     LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F" len=%d\n", netbuf_fromport(buf), copied));
1170     if (msg->msg_name && msg->msg_namelen) {
1171       lwip_sock_make_addr(sock->conn, netbuf_fromaddr(buf), netbuf_fromport(buf),
1172                           (struct sockaddr *)msg->msg_name, &msg->msg_namelen);
1173     }
1174   }
1175 
1176   /* Initialize flag output */
1177   msg->msg_flags = 0;
1178 
1179   if (msg->msg_control) {
1180     u8_t wrote_msg = 0;
1181 #if LWIP_NETBUF_RECVINFO
1182     /* Check if packet info was recorded */
1183     if (buf->flags & NETBUF_FLAG_DESTADDR) {
1184       if (IP_IS_V4(&buf->toaddr)) {
1185 #if LWIP_IPV4
1186         if (msg->msg_controllen >= CMSG_SPACE(sizeof(struct in_pktinfo))) {
1187           struct cmsghdr *chdr = CMSG_FIRSTHDR(msg); /* This will always return a header!! */
1188           struct in_pktinfo *pkti = (struct in_pktinfo *)CMSG_DATA(chdr);
1189           chdr->cmsg_level = IPPROTO_IP;
1190           chdr->cmsg_type = IP_PKTINFO;
1191           chdr->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo));
1192           pkti->ipi_ifindex = buf->p->if_idx;
1193           inet_addr_from_ip4addr(&pkti->ipi_addr, ip_2_ip4(netbuf_destaddr(buf)));
1194           msg->msg_controllen = CMSG_SPACE(sizeof(struct in_pktinfo));
1195           wrote_msg = 1;
1196         } else {
1197           msg->msg_flags |= MSG_CTRUNC;
1198         }
1199 #endif /* LWIP_IPV4 */
1200       }
1201     }
1202 #endif /* LWIP_NETBUF_RECVINFO */
1203 
1204     if (!wrote_msg) {
1205       msg->msg_controllen = 0;
1206     }
1207   }
1208 
1209   /* If we don't peek the incoming message: zero lastdata pointer and free the netbuf */
1210   if ((flags & MSG_PEEK) == 0) {
1211     sock->lastdata.netbuf = NULL;
1212     netbuf_delete(buf);
1213   }
1214   if (datagram_len) {
1215     *datagram_len = buflen;
1216   }
1217   return ERR_OK;
1218 }
1219 
1220 ssize_t
lwip_recvfrom(int s,void * mem,size_t len,int flags,struct sockaddr * from,socklen_t * fromlen)1221 lwip_recvfrom(int s, void *mem, size_t len, int flags,
1222               struct sockaddr *from, socklen_t *fromlen)
1223 {
1224   struct lwip_sock *sock;
1225   ssize_t ret;
1226 
1227   LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom(%d, %p, %"SZT_F", 0x%x, ..)\n", s, mem, len, flags));
1228   sock = get_socket(s);
1229   if (!sock) {
1230     return -1;
1231   }
1232 #if LWIP_TCP
1233   if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_TCP) {
1234     ret = lwip_recv_tcp(sock, mem, len, flags);
1235     lwip_recv_tcp_from(sock, from, fromlen, "lwip_recvfrom", s, ret);
1236     done_socket(sock);
1237     return ret;
1238   } else
1239 #endif
1240   {
1241     u16_t datagram_len = 0;
1242     struct iovec vec;
1243     struct msghdr msg;
1244     err_t err;
1245     vec.iov_base = mem;
1246     vec.iov_len = len;
1247     msg.msg_control = NULL;
1248     msg.msg_controllen = 0;
1249     msg.msg_flags = 0;
1250     msg.msg_iov = &vec;
1251     msg.msg_iovlen = 1;
1252     msg.msg_name = from;
1253     msg.msg_namelen = (fromlen ? *fromlen : 0);
1254     err = lwip_recvfrom_udp_raw(sock, flags, &msg, &datagram_len, s);
1255     if (err != ERR_OK) {
1256       LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom[UDP/RAW](%d): buf == NULL, error is \"%s\"!\n",
1257                                   s, lwip_strerr(err)));
1258       sock_set_errno(sock, err_to_errno(err));
1259       done_socket(sock);
1260       return -1;
1261     }
1262     ret = (ssize_t)LWIP_MIN(LWIP_MIN(len, datagram_len), SSIZE_MAX);
1263     if (fromlen) {
1264       *fromlen = msg.msg_namelen;
1265     }
1266   }
1267 
1268   sock_set_errno(sock, 0);
1269   done_socket(sock);
1270   return ret;
1271 }
1272 
1273 ssize_t
lwip_read(int s,void * mem,size_t len)1274 lwip_read(int s, void *mem, size_t len)
1275 {
1276   return lwip_recvfrom(s, mem, len, 0, NULL, NULL);
1277 }
1278 
1279 ssize_t
lwip_readv(int s,const struct iovec * iov,int iovcnt)1280 lwip_readv(int s, const struct iovec *iov, int iovcnt)
1281 {
1282   struct msghdr msg;
1283 
1284   msg.msg_name = NULL;
1285   msg.msg_namelen = 0;
1286   /* Hack: we have to cast via number to cast from 'const' pointer to non-const.
1287      Blame the opengroup standard for this inconsistency. */
1288   msg.msg_iov = LWIP_CONST_CAST(struct iovec *, iov);
1289   msg.msg_iovlen = iovcnt;
1290   msg.msg_control = NULL;
1291   msg.msg_controllen = 0;
1292   msg.msg_flags = 0;
1293   return lwip_recvmsg(s, &msg, 0);
1294 }
1295 
1296 ssize_t
lwip_recv(int s,void * mem,size_t len,int flags)1297 lwip_recv(int s, void *mem, size_t len, int flags)
1298 {
1299   return lwip_recvfrom(s, mem, len, flags, NULL, NULL);
1300 }
1301 
1302 ssize_t
lwip_recvmsg(int s,struct msghdr * message,int flags)1303 lwip_recvmsg(int s, struct msghdr *message, int flags)
1304 {
1305   struct lwip_sock *sock;
1306   int i;
1307   ssize_t buflen;
1308 
1309   LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvmsg(%d, message=%p, flags=0x%x)\n", s, (void *)message, flags));
1310   LWIP_ERROR("lwip_recvmsg: invalid message pointer", message != NULL, return ERR_ARG;);
1311   LWIP_ERROR("lwip_recvmsg: unsupported flags", (flags & ~(MSG_PEEK|MSG_DONTWAIT)) == 0,
1312              set_errno(EOPNOTSUPP); return -1;);
1313 
1314   if ((message->msg_iovlen <= 0) || (message->msg_iovlen > IOV_MAX)) {
1315     set_errno(EMSGSIZE);
1316     return -1;
1317   }
1318 
1319   sock = get_socket(s);
1320   if (!sock) {
1321     return -1;
1322   }
1323 
1324   /* check for valid vectors */
1325   buflen = 0;
1326   for (i = 0; i < message->msg_iovlen; i++) {
1327     if ((message->msg_iov[i].iov_base == NULL) || ((ssize_t)message->msg_iov[i].iov_len <= 0) ||
1328         ((size_t)(ssize_t)message->msg_iov[i].iov_len != message->msg_iov[i].iov_len) ||
1329         ((ssize_t)(buflen + (ssize_t)message->msg_iov[i].iov_len) <= 0)) {
1330       sock_set_errno(sock, ERR_VAL);
1331       done_socket(sock);
1332       return -1;
1333     }
1334     buflen = (ssize_t)(buflen + (ssize_t)message->msg_iov[i].iov_len);
1335   }
1336 
1337   if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_TCP) {
1338 #if LWIP_TCP
1339     int recv_flags = flags;
1340     message->msg_flags = 0;
1341     /* recv the data */
1342     buflen = 0;
1343     for (i = 0; i < message->msg_iovlen; i++) {
1344       /* try to receive into this vector's buffer */
1345       ssize_t recvd_local = lwip_recv_tcp(sock, message->msg_iov[i].iov_base, message->msg_iov[i].iov_len, recv_flags);
1346       if (recvd_local > 0) {
1347         /* sum up received bytes */
1348         buflen += recvd_local;
1349       }
1350       if ((recvd_local < 0) || (recvd_local < (int)message->msg_iov[i].iov_len) ||
1351           (flags & MSG_PEEK)) {
1352         /* returned prematurely (or peeking, which might actually be limitated to the first iov) */
1353         if (buflen <= 0) {
1354           /* nothing received at all, propagate the error */
1355           buflen = recvd_local;
1356         }
1357         break;
1358       }
1359       /* pass MSG_DONTWAIT to lwip_recv_tcp() to prevent waiting for more data */
1360       recv_flags |= MSG_DONTWAIT;
1361     }
1362     if (buflen > 0) {
1363       /* reset socket error since we have received something */
1364       sock_set_errno(sock, 0);
1365     }
1366     /* " If the socket is connected, the msg_name and msg_namelen members shall be ignored." */
1367     done_socket(sock);
1368     return buflen;
1369 #else /* LWIP_TCP */
1370     sock_set_errno(sock, err_to_errno(ERR_ARG));
1371     done_socket(sock);
1372     return -1;
1373 #endif /* LWIP_TCP */
1374   }
1375   /* else, UDP and RAW NETCONNs */
1376 #if LWIP_UDP || LWIP_RAW
1377   {
1378     u16_t datagram_len = 0;
1379     err_t err;
1380     err = lwip_recvfrom_udp_raw(sock, flags, message, &datagram_len, s);
1381     if (err != ERR_OK) {
1382       LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvmsg[UDP/RAW](%d): buf == NULL, error is \"%s\"!\n",
1383                                   s, lwip_strerr(err)));
1384       sock_set_errno(sock, err_to_errno(err));
1385       done_socket(sock);
1386       return -1;
1387     }
1388     if (datagram_len > buflen) {
1389       message->msg_flags |= MSG_TRUNC;
1390     }
1391 
1392     sock_set_errno(sock, 0);
1393     done_socket(sock);
1394     return (int)datagram_len;
1395   }
1396 #else /* LWIP_UDP || LWIP_RAW */
1397   sock_set_errno(sock, err_to_errno(ERR_ARG));
1398   done_socket(sock);
1399   return -1;
1400 #endif /* LWIP_UDP || LWIP_RAW */
1401 }
1402 
1403 ssize_t
lwip_send(int s,const void * data,size_t size,int flags)1404 lwip_send(int s, const void *data, size_t size, int flags)
1405 {
1406   struct lwip_sock *sock;
1407   err_t err;
1408   u8_t write_flags;
1409   size_t written;
1410 
1411   LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_send(%d, data=%p, size=%"SZT_F", flags=0x%x)\n",
1412                               s, data, size, flags));
1413 
1414   sock = get_socket(s);
1415   if (!sock) {
1416     return -1;
1417   }
1418 
1419   if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_TCP) {
1420 #if (LWIP_UDP || LWIP_RAW)
1421     done_socket(sock);
1422     return lwip_sendto(s, data, size, flags, NULL, 0);
1423 #else /* (LWIP_UDP || LWIP_RAW) */
1424     sock_set_errno(sock, err_to_errno(ERR_ARG));
1425     done_socket(sock);
1426     return -1;
1427 #endif /* (LWIP_UDP || LWIP_RAW) */
1428   }
1429 
1430   write_flags = (u8_t)(NETCONN_COPY |
1431                        ((flags & MSG_MORE)     ? NETCONN_MORE      : 0) |
1432                        ((flags & MSG_DONTWAIT) ? NETCONN_DONTBLOCK : 0));
1433   written = 0;
1434   err = netconn_write_partly(sock->conn, data, size, write_flags, &written);
1435 
1436   LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_send(%d) err=%d written=%"SZT_F"\n", s, err, written));
1437   sock_set_errno(sock, err_to_errno(err));
1438   done_socket(sock);
1439   /* casting 'written' to ssize_t is OK here since the netconn API limits it to SSIZE_MAX */
1440   return (err == ERR_OK ? (ssize_t)written : -1);
1441 }
1442 
1443 ssize_t
lwip_sendmsg(int s,const struct msghdr * msg,int flags)1444 lwip_sendmsg(int s, const struct msghdr *msg, int flags)
1445 {
1446   struct lwip_sock *sock;
1447 #if LWIP_TCP
1448   u8_t write_flags;
1449   size_t written;
1450 #endif
1451   err_t err = ERR_OK;
1452 
1453   sock = get_socket(s);
1454   if (!sock) {
1455     return -1;
1456   }
1457 
1458   LWIP_ERROR("lwip_sendmsg: invalid msghdr", msg != NULL,
1459              sock_set_errno(sock, err_to_errno(ERR_ARG)); done_socket(sock); return -1;);
1460   LWIP_ERROR("lwip_sendmsg: invalid msghdr iov", msg->msg_iov != NULL,
1461              sock_set_errno(sock, err_to_errno(ERR_ARG)); done_socket(sock); return -1;);
1462   LWIP_ERROR("lwip_sendmsg: maximum iovs exceeded", (msg->msg_iovlen > 0) && (msg->msg_iovlen <= IOV_MAX),
1463              sock_set_errno(sock, EMSGSIZE); done_socket(sock); return -1;);
1464   LWIP_ERROR("lwip_sendmsg: unsupported flags", (flags & ~(MSG_DONTWAIT | MSG_MORE)) == 0,
1465              sock_set_errno(sock, EOPNOTSUPP); done_socket(sock); return -1;);
1466 
1467   LWIP_UNUSED_ARG(msg->msg_control);
1468   LWIP_UNUSED_ARG(msg->msg_controllen);
1469   LWIP_UNUSED_ARG(msg->msg_flags);
1470 
1471   if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_TCP) {
1472 #if LWIP_TCP
1473     write_flags = (u8_t)(NETCONN_COPY |
1474                          ((flags & MSG_MORE)     ? NETCONN_MORE      : 0) |
1475                          ((flags & MSG_DONTWAIT) ? NETCONN_DONTBLOCK : 0));
1476 
1477     written = 0;
1478     err = netconn_write_vectors_partly(sock->conn, (struct netvector *)msg->msg_iov, (u16_t)msg->msg_iovlen, write_flags, &written);
1479     sock_set_errno(sock, err_to_errno(err));
1480     done_socket(sock);
1481     /* casting 'written' to ssize_t is OK here since the netconn API limits it to SSIZE_MAX */
1482     return (err == ERR_OK ? (ssize_t)written : -1);
1483 #else /* LWIP_TCP */
1484     sock_set_errno(sock, err_to_errno(ERR_ARG));
1485     done_socket(sock);
1486     return -1;
1487 #endif /* LWIP_TCP */
1488   }
1489   /* else, UDP and RAW NETCONNs */
1490 #if LWIP_UDP || LWIP_RAW
1491   {
1492     struct netbuf chain_buf;
1493     int i;
1494     ssize_t size = 0;
1495 
1496     LWIP_UNUSED_ARG(flags);
1497     LWIP_ERROR("lwip_sendmsg: invalid msghdr name", (((msg->msg_name == NULL) && (msg->msg_namelen == 0)) ||
1498                IS_SOCK_ADDR_LEN_VALID(msg->msg_namelen)),
1499                sock_set_errno(sock, err_to_errno(ERR_ARG)); done_socket(sock); return -1;);
1500 
1501     /* initialize chain buffer with destination */
1502     memset(&chain_buf, 0, sizeof(struct netbuf));
1503     if (msg->msg_name) {
1504       u16_t remote_port;
1505       SOCKADDR_TO_IPADDR_PORT((const struct sockaddr *)msg->msg_name, &chain_buf.addr, remote_port);
1506       netbuf_fromport(&chain_buf) = remote_port;
1507     }
1508 #if LWIP_NETIF_TX_SINGLE_PBUF
1509     for (i = 0; i < msg->msg_iovlen; i++) {
1510       size += msg->msg_iov[i].iov_len;
1511       if ((msg->msg_iov[i].iov_len > INT_MAX) || (size < (int)msg->msg_iov[i].iov_len)) {
1512         /* overflow */
1513         goto sendmsg_emsgsize;
1514       }
1515     }
1516     if (size > 0xFFFF) {
1517       /* overflow */
1518       goto sendmsg_emsgsize;
1519     }
1520     /* Allocate a new netbuf and copy the data into it. */
1521     if (netbuf_alloc(&chain_buf, (u16_t)size) == NULL) {
1522       err = ERR_MEM;
1523     } else {
1524       /* flatten the IO vectors */
1525       size_t offset = 0;
1526       for (i = 0; i < msg->msg_iovlen; i++) {
1527         MEMCPY(&((u8_t *)chain_buf.p->payload)[offset], msg->msg_iov[i].iov_base, msg->msg_iov[i].iov_len);
1528         offset += msg->msg_iov[i].iov_len;
1529       }
1530 #if LWIP_CHECKSUM_ON_COPY
1531       {
1532         /* This can be improved by using LWIP_CHKSUM_COPY() and aggregating the checksum for each IO vector */
1533         u16_t chksum = ~inet_chksum_pbuf(chain_buf.p);
1534         netbuf_set_chksum(&chain_buf, chksum);
1535       }
1536 #endif /* LWIP_CHECKSUM_ON_COPY */
1537       err = ERR_OK;
1538     }
1539 #else /* LWIP_NETIF_TX_SINGLE_PBUF */
1540     /* create a chained netbuf from the IO vectors. NOTE: we assemble a pbuf chain
1541        manually to avoid having to allocate, chain, and delete a netbuf for each iov */
1542     for (i = 0; i < msg->msg_iovlen; i++) {
1543       struct pbuf *p;
1544       if (msg->msg_iov[i].iov_len > 0xFFFF) {
1545         /* overflow */
1546         goto sendmsg_emsgsize;
1547       }
1548       p = pbuf_alloc(PBUF_TRANSPORT, 0, PBUF_REF);
1549       if (p == NULL) {
1550         err = ERR_MEM; /* let netbuf_delete() cleanup chain_buf */
1551         break;
1552       }
1553       p->payload = msg->msg_iov[i].iov_base;
1554       p->len = p->tot_len = (u16_t)msg->msg_iov[i].iov_len;
1555       /* netbuf empty, add new pbuf */
1556       if (chain_buf.p == NULL) {
1557         chain_buf.p = chain_buf.ptr = p;
1558         /* add pbuf to existing pbuf chain */
1559       } else {
1560         if (chain_buf.p->tot_len + p->len > 0xffff) {
1561           /* overflow */
1562           pbuf_free(p);
1563           goto sendmsg_emsgsize;
1564         }
1565         pbuf_cat(chain_buf.p, p);
1566       }
1567     }
1568     /* save size of total chain */
1569     if (err == ERR_OK) {
1570       size = netbuf_len(&chain_buf);
1571     }
1572 #endif /* LWIP_NETIF_TX_SINGLE_PBUF */
1573 
1574     if (err == ERR_OK) {
1575 #if LWIP_IPV4 && LWIP_IPV6
1576       /* Dual-stack: Unmap IPv4 mapped IPv6 addresses */
1577       if (IP_IS_V6_VAL(chain_buf.addr) && ip6_addr_isipv4mappedipv6(ip_2_ip6(&chain_buf.addr))) {
1578         unmap_ipv4_mapped_ipv6(ip_2_ip4(&chain_buf.addr), ip_2_ip6(&chain_buf.addr));
1579         IP_SET_TYPE_VAL(chain_buf.addr, IPADDR_TYPE_V4);
1580       }
1581 #endif /* LWIP_IPV4 && LWIP_IPV6 */
1582 
1583       /* send the data */
1584       err = netconn_send(sock->conn, &chain_buf);
1585     }
1586 
1587     /* deallocated the buffer */
1588     netbuf_free(&chain_buf);
1589 
1590     sock_set_errno(sock, err_to_errno(err));
1591     done_socket(sock);
1592     return (err == ERR_OK ? size : -1);
1593 sendmsg_emsgsize:
1594     sock_set_errno(sock, EMSGSIZE);
1595     netbuf_free(&chain_buf);
1596     done_socket(sock);
1597     return -1;
1598   }
1599 #else /* LWIP_UDP || LWIP_RAW */
1600   sock_set_errno(sock, err_to_errno(ERR_ARG));
1601   done_socket(sock);
1602   return -1;
1603 #endif /* LWIP_UDP || LWIP_RAW */
1604 }
1605 
1606 ssize_t
lwip_sendto(int s,const void * data,size_t size,int flags,const struct sockaddr * to,socklen_t tolen)1607 lwip_sendto(int s, const void *data, size_t size, int flags,
1608             const struct sockaddr *to, socklen_t tolen)
1609 {
1610   struct lwip_sock *sock;
1611   err_t err;
1612   u16_t short_size;
1613   u16_t remote_port;
1614   struct netbuf buf;
1615 
1616   sock = get_socket(s);
1617   if (!sock) {
1618     return -1;
1619   }
1620 
1621   if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_TCP) {
1622 #if LWIP_TCP
1623     done_socket(sock);
1624     return lwip_send(s, data, size, flags);
1625 #else /* LWIP_TCP */
1626     LWIP_UNUSED_ARG(flags);
1627     sock_set_errno(sock, err_to_errno(ERR_ARG));
1628     done_socket(sock);
1629     return -1;
1630 #endif /* LWIP_TCP */
1631   }
1632 
1633   if (size > LWIP_MIN(0xFFFF, SSIZE_MAX)) {
1634     /* cannot fit into one datagram (at least for us) */
1635     sock_set_errno(sock, EMSGSIZE);
1636     done_socket(sock);
1637     return -1;
1638   }
1639   short_size = (u16_t)size;
1640   LWIP_ERROR("lwip_sendto: invalid address", (((to == NULL) && (tolen == 0)) ||
1641              (IS_SOCK_ADDR_LEN_VALID(tolen) &&
1642               ((to != NULL) && (IS_SOCK_ADDR_TYPE_VALID(to) && IS_SOCK_ADDR_ALIGNED(to))))),
1643              sock_set_errno(sock, err_to_errno(ERR_ARG)); done_socket(sock); return -1;);
1644   LWIP_UNUSED_ARG(tolen);
1645 
1646   /* initialize a buffer */
1647   buf.p = buf.ptr = NULL;
1648 #if LWIP_CHECKSUM_ON_COPY
1649   buf.flags = 0;
1650 #endif /* LWIP_CHECKSUM_ON_COPY */
1651   if (to) {
1652     SOCKADDR_TO_IPADDR_PORT(to, &buf.addr, remote_port);
1653   } else {
1654     remote_port = 0;
1655     ip_addr_set_any(NETCONNTYPE_ISIPV6(netconn_type(sock->conn)), &buf.addr);
1656   }
1657   netbuf_fromport(&buf) = remote_port;
1658 
1659 
1660   LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_sendto(%d, data=%p, short_size=%"U16_F", flags=0x%x to=",
1661                               s, data, short_size, flags));
1662   ip_addr_debug_print_val(SOCKETS_DEBUG, buf.addr);
1663   LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F"\n", remote_port));
1664 
1665   /* make the buffer point to the data that should be sent */
1666 #if LWIP_NETIF_TX_SINGLE_PBUF
1667   /* Allocate a new netbuf and copy the data into it. */
1668   if (netbuf_alloc(&buf, short_size) == NULL) {
1669     err = ERR_MEM;
1670   } else {
1671 #if LWIP_CHECKSUM_ON_COPY
1672     if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_RAW) {
1673       u16_t chksum = LWIP_CHKSUM_COPY(buf.p->payload, data, short_size);
1674       netbuf_set_chksum(&buf, chksum);
1675     } else
1676 #endif /* LWIP_CHECKSUM_ON_COPY */
1677     {
1678       MEMCPY(buf.p->payload, data, short_size);
1679     }
1680     err = ERR_OK;
1681   }
1682 #else /* LWIP_NETIF_TX_SINGLE_PBUF */
1683   err = netbuf_ref(&buf, data, short_size);
1684 #endif /* LWIP_NETIF_TX_SINGLE_PBUF */
1685   if (err == ERR_OK) {
1686 #if LWIP_IPV4 && LWIP_IPV6
1687     /* Dual-stack: Unmap IPv4 mapped IPv6 addresses */
1688     if (IP_IS_V6_VAL(buf.addr) && ip6_addr_isipv4mappedipv6(ip_2_ip6(&buf.addr))) {
1689       unmap_ipv4_mapped_ipv6(ip_2_ip4(&buf.addr), ip_2_ip6(&buf.addr));
1690       IP_SET_TYPE_VAL(buf.addr, IPADDR_TYPE_V4);
1691     }
1692 #endif /* LWIP_IPV4 && LWIP_IPV6 */
1693 
1694     /* send the data */
1695     err = netconn_send(sock->conn, &buf);
1696   }
1697 
1698   /* deallocated the buffer */
1699   netbuf_free(&buf);
1700 
1701   sock_set_errno(sock, err_to_errno(err));
1702   done_socket(sock);
1703   return (err == ERR_OK ? short_size : -1);
1704 }
1705 
1706 int
lwip_socket(int domain,int type,int protocol)1707 lwip_socket(int domain, int type, int protocol)
1708 {
1709   struct netconn *conn;
1710   int i;
1711 
1712   LWIP_UNUSED_ARG(domain); /* @todo: check this */
1713 
1714   /* create a netconn */
1715   switch (type) {
1716     case SOCK_RAW:
1717       conn = netconn_new_with_proto_and_callback(DOMAIN_TO_NETCONN_TYPE(domain, NETCONN_RAW),
1718              (u8_t)protocol, DEFAULT_SOCKET_EVENTCB);
1719       LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_socket(%s, SOCK_RAW, %d) = ",
1720                                   domain == PF_INET ? "PF_INET" : "UNKNOWN", protocol));
1721       break;
1722     case SOCK_DGRAM:
1723       conn = netconn_new_with_callback(DOMAIN_TO_NETCONN_TYPE(domain,
1724                                        ((protocol == IPPROTO_UDPLITE) ? NETCONN_UDPLITE : NETCONN_UDP)),
1725                                        DEFAULT_SOCKET_EVENTCB);
1726       LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_socket(%s, SOCK_DGRAM, %d) = ",
1727                                   domain == PF_INET ? "PF_INET" : "UNKNOWN", protocol));
1728 #if LWIP_NETBUF_RECVINFO
1729       if (conn) {
1730         /* netconn layer enables pktinfo by default, sockets default to off */
1731         conn->flags &= ~NETCONN_FLAG_PKTINFO;
1732       }
1733 #endif /* LWIP_NETBUF_RECVINFO */
1734       break;
1735     case SOCK_STREAM:
1736       conn = netconn_new_with_callback(DOMAIN_TO_NETCONN_TYPE(domain, NETCONN_TCP), DEFAULT_SOCKET_EVENTCB);
1737       LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_socket(%s, SOCK_STREAM, %d) = ",
1738                                   domain == PF_INET ? "PF_INET" : "UNKNOWN", protocol));
1739       break;
1740     default:
1741       LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_socket(%d, %d/UNKNOWN, %d) = -1\n",
1742                                   domain, type, protocol));
1743       set_errno(EINVAL);
1744       return -1;
1745   }
1746 
1747   if (!conn) {
1748     LWIP_DEBUGF(SOCKETS_DEBUG, ("-1 / ENOBUFS (could not create netconn)\n"));
1749     set_errno(ENOBUFS);
1750     return -1;
1751   }
1752 
1753   i = alloc_socket(conn, 0);
1754 
1755   if (i == -1) {
1756     netconn_delete(conn);
1757     set_errno(ENFILE);
1758     return -1;
1759   }
1760   conn->socket = i;
1761   done_socket(&sockets[i - LWIP_SOCKET_OFFSET]);
1762   LWIP_DEBUGF(SOCKETS_DEBUG, ("%d\n", i));
1763   set_errno(0);
1764   return i;
1765 }
1766 
1767 ssize_t
lwip_write(int s,const void * data,size_t size)1768 lwip_write(int s, const void *data, size_t size)
1769 {
1770   return lwip_send(s, data, size, 0);
1771 }
1772 
1773 ssize_t
lwip_writev(int s,const struct iovec * iov,int iovcnt)1774 lwip_writev(int s, const struct iovec *iov, int iovcnt)
1775 {
1776   struct msghdr msg;
1777 
1778   msg.msg_name = NULL;
1779   msg.msg_namelen = 0;
1780   /* Hack: we have to cast via number to cast from 'const' pointer to non-const.
1781      Blame the opengroup standard for this inconsistency. */
1782   msg.msg_iov = LWIP_CONST_CAST(struct iovec *, iov);
1783   msg.msg_iovlen = iovcnt;
1784   msg.msg_control = NULL;
1785   msg.msg_controllen = 0;
1786   msg.msg_flags = 0;
1787   return lwip_sendmsg(s, &msg, 0);
1788 }
1789 
1790 #if LWIP_SOCKET_SELECT || LWIP_SOCKET_POLL
1791 /* Add select_cb to select_cb_list. */
1792 static void
lwip_link_select_cb(struct lwip_select_cb * select_cb)1793 lwip_link_select_cb(struct lwip_select_cb *select_cb)
1794 {
1795   LWIP_SOCKET_SELECT_DECL_PROTECT(lev);
1796 
1797   /* Protect the select_cb_list */
1798   LWIP_SOCKET_SELECT_PROTECT(lev);
1799 
1800   /* Put this select_cb on top of list */
1801   select_cb->next = select_cb_list;
1802   if (select_cb_list != NULL) {
1803     select_cb_list->prev = select_cb;
1804   }
1805   select_cb_list = select_cb;
1806 #if !LWIP_TCPIP_CORE_LOCKING
1807   /* Increasing this counter tells select_check_waiters that the list has changed. */
1808   select_cb_ctr++;
1809 #endif
1810 
1811   /* Now we can safely unprotect */
1812   LWIP_SOCKET_SELECT_UNPROTECT(lev);
1813 }
1814 
1815 /* Remove select_cb from select_cb_list. */
1816 static void
lwip_unlink_select_cb(struct lwip_select_cb * select_cb)1817 lwip_unlink_select_cb(struct lwip_select_cb *select_cb)
1818 {
1819   LWIP_SOCKET_SELECT_DECL_PROTECT(lev);
1820 
1821   /* Take us off the list */
1822   LWIP_SOCKET_SELECT_PROTECT(lev);
1823   if (select_cb->next != NULL) {
1824     select_cb->next->prev = select_cb->prev;
1825   }
1826   if (select_cb_list == select_cb) {
1827     LWIP_ASSERT("select_cb->prev == NULL", select_cb->prev == NULL);
1828     select_cb_list = select_cb->next;
1829   } else {
1830     LWIP_ASSERT("select_cb->prev != NULL", select_cb->prev != NULL);
1831     select_cb->prev->next = select_cb->next;
1832   }
1833 #if !LWIP_TCPIP_CORE_LOCKING
1834   /* Increasing this counter tells select_check_waiters that the list has changed. */
1835   select_cb_ctr++;
1836 #endif
1837   LWIP_SOCKET_SELECT_UNPROTECT(lev);
1838 }
1839 #endif /* LWIP_SOCKET_SELECT || LWIP_SOCKET_POLL */
1840 
1841 #if LWIP_SOCKET_SELECT
1842 /**
1843  * Go through the readset and writeset lists and see which socket of the sockets
1844  * set in the sets has events. On return, readset, writeset and exceptset have
1845  * the sockets enabled that had events.
1846  *
1847  * @param maxfdp1 the highest socket index in the sets
1848  * @param readset_in    set of sockets to check for read events
1849  * @param writeset_in   set of sockets to check for write events
1850  * @param exceptset_in  set of sockets to check for error events
1851  * @param readset_out   set of sockets that had read events
1852  * @param writeset_out  set of sockets that had write events
1853  * @param exceptset_out set os sockets that had error events
1854  * @return number of sockets that had events (read/write/exception) (>= 0)
1855  */
1856 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)1857 lwip_selscan(int maxfdp1, fd_set *readset_in, fd_set *writeset_in, fd_set *exceptset_in,
1858              fd_set *readset_out, fd_set *writeset_out, fd_set *exceptset_out)
1859 {
1860   int i, nready = 0;
1861   fd_set lreadset, lwriteset, lexceptset;
1862   struct lwip_sock *sock;
1863   SYS_ARCH_DECL_PROTECT(lev);
1864 
1865   FD_ZERO(&lreadset);
1866   FD_ZERO(&lwriteset);
1867   FD_ZERO(&lexceptset);
1868 
1869   /* Go through each socket in each list to count number of sockets which
1870      currently match */
1871   for (i = LWIP_SOCKET_OFFSET; i < maxfdp1; i++) {
1872     /* if this FD is not in the set, continue */
1873     if (!(readset_in && FD_ISSET(i, readset_in)) &&
1874         !(writeset_in && FD_ISSET(i, writeset_in)) &&
1875         !(exceptset_in && FD_ISSET(i, exceptset_in))) {
1876       continue;
1877     }
1878     /* First get the socket's status (protected)... */
1879     SYS_ARCH_PROTECT(lev);
1880     sock = tryget_socket_unconn_locked(i);
1881     if (sock != NULL) {
1882       void *lastdata = sock->lastdata.pbuf;
1883       s16_t rcvevent = sock->rcvevent;
1884       u16_t sendevent = sock->sendevent;
1885       u16_t errevent = sock->errevent;
1886       SYS_ARCH_UNPROTECT(lev);
1887 
1888       /* ... then examine it: */
1889       /* See if netconn of this socket is ready for read */
1890       if (readset_in && FD_ISSET(i, readset_in) && ((lastdata != NULL) || (rcvevent > 0))) {
1891         FD_SET(i, &lreadset);
1892         LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_selscan: fd=%d ready for reading\n", i));
1893         nready++;
1894       }
1895       /* See if netconn of this socket is ready for write */
1896       if (writeset_in && FD_ISSET(i, writeset_in) && (sendevent != 0)) {
1897         FD_SET(i, &lwriteset);
1898         LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_selscan: fd=%d ready for writing\n", i));
1899         nready++;
1900       }
1901       /* See if netconn of this socket had an error */
1902       if (exceptset_in && FD_ISSET(i, exceptset_in) && (errevent != 0)) {
1903         FD_SET(i, &lexceptset);
1904         LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_selscan: fd=%d ready for exception\n", i));
1905         nready++;
1906       }
1907       done_socket(sock);
1908     } else {
1909       SYS_ARCH_UNPROTECT(lev);
1910       /* no a valid open socket */
1911       return -1;
1912     }
1913   }
1914   /* copy local sets to the ones provided as arguments */
1915   *readset_out = lreadset;
1916   *writeset_out = lwriteset;
1917   *exceptset_out = lexceptset;
1918 
1919   LWIP_ASSERT("nready >= 0", nready >= 0);
1920   return nready;
1921 }
1922 
1923 #if LWIP_NETCONN_FULLDUPLEX
1924 /* Mark all of the set sockets in one of the three fdsets passed to select as used.
1925  * All sockets are marked (and later unmarked), whether they are open or not.
1926  * This is OK as lwip_selscan aborts select when non-open sockets are found.
1927  */
1928 static void
lwip_select_inc_sockets_used_set(int maxfdp,fd_set * fdset,fd_set * used_sockets)1929 lwip_select_inc_sockets_used_set(int maxfdp, fd_set *fdset, fd_set *used_sockets)
1930 {
1931   SYS_ARCH_DECL_PROTECT(lev);
1932   if (fdset) {
1933     int i;
1934     for (i = LWIP_SOCKET_OFFSET; i < maxfdp; i++) {
1935       /* if this FD is in the set, lock it (unless already done) */
1936       if (FD_ISSET(i, fdset) && !FD_ISSET(i, used_sockets)) {
1937         struct lwip_sock *sock;
1938         SYS_ARCH_PROTECT(lev);
1939         sock = tryget_socket_unconn_locked(i);
1940         if (sock != NULL) {
1941           /* leave the socket used until released by lwip_select_dec_sockets_used */
1942           FD_SET(i, used_sockets);
1943         }
1944         SYS_ARCH_UNPROTECT(lev);
1945       }
1946     }
1947   }
1948 }
1949 
1950 /* Mark all sockets passed to select as used to prevent them from being freed
1951  * from other threads while select is running.
1952  * Marked sockets are added to 'used_sockets' to mark them only once an be able
1953  * to unmark them correctly.
1954  */
1955 static void
lwip_select_inc_sockets_used(int maxfdp,fd_set * fdset1,fd_set * fdset2,fd_set * fdset3,fd_set * used_sockets)1956 lwip_select_inc_sockets_used(int maxfdp, fd_set *fdset1, fd_set *fdset2, fd_set *fdset3, fd_set *used_sockets)
1957 {
1958   FD_ZERO(used_sockets);
1959   lwip_select_inc_sockets_used_set(maxfdp, fdset1, used_sockets);
1960   lwip_select_inc_sockets_used_set(maxfdp, fdset2, used_sockets);
1961   lwip_select_inc_sockets_used_set(maxfdp, fdset3, used_sockets);
1962 }
1963 
1964 /* Let go all sockets that were marked as used when starting select */
1965 static void
lwip_select_dec_sockets_used(int maxfdp,fd_set * used_sockets)1966 lwip_select_dec_sockets_used(int maxfdp, fd_set *used_sockets)
1967 {
1968   int i;
1969   for (i = LWIP_SOCKET_OFFSET; i < maxfdp; i++) {
1970     /* if this FD is not in the set, continue */
1971     if (FD_ISSET(i, used_sockets)) {
1972       struct lwip_sock *sock = tryget_socket_unconn_nouse(i);
1973       LWIP_ASSERT("socket gone at the end of select", sock != NULL);
1974       if (sock != NULL) {
1975         done_socket(sock);
1976       }
1977     }
1978   }
1979 }
1980 #else /* LWIP_NETCONN_FULLDUPLEX */
1981 #define lwip_select_inc_sockets_used(maxfdp1, readset, writeset, exceptset, used_sockets)
1982 #define lwip_select_dec_sockets_used(maxfdp1, used_sockets)
1983 #endif /* LWIP_NETCONN_FULLDUPLEX */
1984 
1985 int
lwip_select(int maxfdp1,fd_set * readset,fd_set * writeset,fd_set * exceptset,struct timeval * timeout)1986 lwip_select(int maxfdp1, fd_set *readset, fd_set *writeset, fd_set *exceptset,
1987             struct timeval *timeout)
1988 {
1989   u32_t waitres = 0;
1990   int nready;
1991   fd_set lreadset, lwriteset, lexceptset;
1992   u32_t msectimeout;
1993   int i;
1994   int maxfdp2;
1995 #if LWIP_NETCONN_SEM_PER_THREAD
1996   int waited = 0;
1997 #endif
1998 #if LWIP_NETCONN_FULLDUPLEX
1999   fd_set used_sockets;
2000 #endif
2001   SYS_ARCH_DECL_PROTECT(lev);
2002 
2003   LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_select(%d, %p, %p, %p, tvsec=%"S32_F" tvusec=%"S32_F")\n",
2004                               maxfdp1, (void *)readset, (void *) writeset, (void *) exceptset,
2005                               timeout ? (s32_t)timeout->tv_sec : (s32_t) - 1,
2006                               timeout ? (s32_t)timeout->tv_usec : (s32_t) - 1));
2007 
2008   if ((maxfdp1 < 0) || (maxfdp1 > LWIP_SELECT_MAXNFDS)) {
2009     set_errno(EINVAL);
2010     return -1;
2011   }
2012 
2013   lwip_select_inc_sockets_used(maxfdp1, readset, writeset, exceptset, &used_sockets);
2014 
2015   /* Go through each socket in each list to count number of sockets which
2016      currently match */
2017   nready = lwip_selscan(maxfdp1, readset, writeset, exceptset, &lreadset, &lwriteset, &lexceptset);
2018 
2019   if (nready < 0) {
2020     /* one of the sockets in one of the fd_sets was invalid */
2021     set_errno(EBADF);
2022     lwip_select_dec_sockets_used(maxfdp1, &used_sockets);
2023     return -1;
2024   } else if (nready > 0) {
2025     /* one or more sockets are set, no need to wait */
2026     LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_select: nready=%d\n", nready));
2027   } else {
2028     /* If we don't have any current events, then suspend if we are supposed to */
2029     if (timeout && timeout->tv_sec == 0 && timeout->tv_usec == 0) {
2030       LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_select: no timeout, returning 0\n"));
2031       /* This is OK as the local fdsets are empty and nready is zero,
2032          or we would have returned earlier. */
2033     } else {
2034       /* None ready: add our semaphore to list:
2035          We don't actually need any dynamic memory. Our entry on the
2036          list is only valid while we are in this function, so it's ok
2037          to use local variables (unless we're running in MPU compatible
2038          mode). */
2039       API_SELECT_CB_VAR_DECLARE(select_cb);
2040       API_SELECT_CB_VAR_ALLOC(select_cb, set_errno(ENOMEM); lwip_select_dec_sockets_used(maxfdp1, &used_sockets); return -1);
2041       memset(&API_SELECT_CB_VAR_REF(select_cb), 0, sizeof(struct lwip_select_cb));
2042 
2043       API_SELECT_CB_VAR_REF(select_cb).readset = readset;
2044       API_SELECT_CB_VAR_REF(select_cb).writeset = writeset;
2045       API_SELECT_CB_VAR_REF(select_cb).exceptset = exceptset;
2046 #if LWIP_NETCONN_SEM_PER_THREAD
2047       API_SELECT_CB_VAR_REF(select_cb).sem = LWIP_NETCONN_THREAD_SEM_GET();
2048 #else /* LWIP_NETCONN_SEM_PER_THREAD */
2049       if (sys_sem_new(&API_SELECT_CB_VAR_REF(select_cb).sem, 0) != ERR_OK) {
2050         /* failed to create semaphore */
2051         set_errno(ENOMEM);
2052         lwip_select_dec_sockets_used(maxfdp1, &used_sockets);
2053         API_SELECT_CB_VAR_FREE(select_cb);
2054         return -1;
2055       }
2056 #endif /* LWIP_NETCONN_SEM_PER_THREAD */
2057 
2058       lwip_link_select_cb(&API_SELECT_CB_VAR_REF(select_cb));
2059 
2060       /* Increase select_waiting for each socket we are interested in */
2061       maxfdp2 = maxfdp1;
2062       for (i = LWIP_SOCKET_OFFSET; i < maxfdp1; i++) {
2063         if ((readset && FD_ISSET(i, readset)) ||
2064             (writeset && FD_ISSET(i, writeset)) ||
2065             (exceptset && FD_ISSET(i, exceptset))) {
2066           struct lwip_sock *sock;
2067           SYS_ARCH_PROTECT(lev);
2068           sock = tryget_socket_unconn_locked(i);
2069           if (sock != NULL) {
2070             sock->select_waiting++;
2071             if (sock->select_waiting == 0) {
2072               /* overflow - too many threads waiting */
2073               sock->select_waiting--;
2074               nready = -1;
2075               maxfdp2 = i;
2076               SYS_ARCH_UNPROTECT(lev);
2077               done_socket(sock);
2078               set_errno(EBUSY);
2079               break;
2080             }
2081             SYS_ARCH_UNPROTECT(lev);
2082             done_socket(sock);
2083           } else {
2084             /* Not a valid socket */
2085             nready = -1;
2086             maxfdp2 = i;
2087             SYS_ARCH_UNPROTECT(lev);
2088             set_errno(EBADF);
2089             break;
2090           }
2091         }
2092       }
2093 
2094       if (nready >= 0) {
2095         /* Call lwip_selscan again: there could have been events between
2096            the last scan (without us on the list) and putting us on the list! */
2097         nready = lwip_selscan(maxfdp1, readset, writeset, exceptset, &lreadset, &lwriteset, &lexceptset);
2098         if (!nready) {
2099           /* Still none ready, just wait to be woken */
2100           if (timeout == 0) {
2101             /* Wait forever */
2102             msectimeout = 0;
2103           } else {
2104             long msecs_long = ((timeout->tv_sec * 1000) + ((timeout->tv_usec + 500) / 1000));
2105             if (msecs_long <= 0) {
2106               /* Wait 1ms at least (0 means wait forever) */
2107               msectimeout = 1;
2108             } else {
2109               msectimeout = (u32_t)msecs_long;
2110             }
2111           }
2112 
2113           waitres = sys_arch_sem_wait(SELECT_SEM_PTR(API_SELECT_CB_VAR_REF(select_cb).sem), msectimeout);
2114 #if LWIP_NETCONN_SEM_PER_THREAD
2115           waited = 1;
2116 #endif
2117         }
2118       }
2119 
2120       /* Decrease select_waiting for each socket we are interested in */
2121       for (i = LWIP_SOCKET_OFFSET; i < maxfdp2; i++) {
2122         if ((readset && FD_ISSET(i, readset)) ||
2123             (writeset && FD_ISSET(i, writeset)) ||
2124             (exceptset && FD_ISSET(i, exceptset))) {
2125           struct lwip_sock *sock;
2126           SYS_ARCH_PROTECT(lev);
2127           sock = tryget_socket_unconn_locked(i);
2128           if (sock != NULL) {
2129             /* for now, handle select_waiting==0... */
2130             LWIP_ASSERT("sock->select_waiting > 0", sock->select_waiting > 0);
2131             if (sock->select_waiting > 0) {
2132               sock->select_waiting--;
2133             }
2134             SYS_ARCH_UNPROTECT(lev);
2135             done_socket(sock);
2136           } else {
2137             SYS_ARCH_UNPROTECT(lev);
2138             /* Not a valid socket */
2139             nready = -1;
2140             set_errno(EBADF);
2141           }
2142         }
2143       }
2144 
2145       lwip_unlink_select_cb(&API_SELECT_CB_VAR_REF(select_cb));
2146 
2147 #if LWIP_NETCONN_SEM_PER_THREAD
2148       if (API_SELECT_CB_VAR_REF(select_cb).sem_signalled && (!waited || (waitres == SYS_ARCH_TIMEOUT))) {
2149         /* don't leave the thread-local semaphore signalled */
2150         sys_arch_sem_wait(API_SELECT_CB_VAR_REF(select_cb).sem, 1);
2151       }
2152 #else /* LWIP_NETCONN_SEM_PER_THREAD */
2153       sys_sem_free(&API_SELECT_CB_VAR_REF(select_cb).sem);
2154 #endif /* LWIP_NETCONN_SEM_PER_THREAD */
2155       API_SELECT_CB_VAR_FREE(select_cb);
2156 
2157       if (nready < 0) {
2158         /* This happens when a socket got closed while waiting */
2159         lwip_select_dec_sockets_used(maxfdp1, &used_sockets);
2160         return -1;
2161       }
2162 
2163       if (waitres == SYS_ARCH_TIMEOUT) {
2164         /* Timeout */
2165         LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_select: timeout expired\n"));
2166         /* This is OK as the local fdsets are empty and nready is zero,
2167            or we would have returned earlier. */
2168       } else {
2169         /* See what's set now after waiting */
2170         nready = lwip_selscan(maxfdp1, readset, writeset, exceptset, &lreadset, &lwriteset, &lexceptset);
2171         LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_select: nready=%d\n", nready));
2172       }
2173     }
2174   }
2175 
2176   lwip_select_dec_sockets_used(maxfdp1, &used_sockets);
2177   set_errno(0);
2178   if (readset) {
2179     *readset = lreadset;
2180   }
2181   if (writeset) {
2182     *writeset = lwriteset;
2183   }
2184   if (exceptset) {
2185     *exceptset = lexceptset;
2186   }
2187   return nready;
2188 }
2189 #endif /* LWIP_SOCKET_SELECT */
2190 
2191 #if LWIP_SOCKET_POLL
2192 /** Options for the lwip_pollscan function. */
2193 enum lwip_pollscan_opts
2194 {
2195   /** Clear revents in each struct pollfd. */
2196   LWIP_POLLSCAN_CLEAR = 1,
2197 
2198   /** Increment select_waiting in each struct lwip_sock. */
2199   LWIP_POLLSCAN_INC_WAIT = 2,
2200 
2201   /** Decrement select_waiting in each struct lwip_sock. */
2202   LWIP_POLLSCAN_DEC_WAIT = 4
2203 };
2204 
2205 /**
2206  * Update revents in each struct pollfd.
2207  * Optionally update select_waiting in struct lwip_sock.
2208  *
2209  * @param fds          array of structures to update
2210  * @param nfds         number of structures in fds
2211  * @param opts         what to update and how
2212  * @return number of structures that have revents != 0
2213  */
2214 static int
lwip_pollscan(struct pollfd * fds,nfds_t nfds,enum lwip_pollscan_opts opts)2215 lwip_pollscan(struct pollfd *fds, nfds_t nfds, enum lwip_pollscan_opts opts)
2216 {
2217   int nready = 0;
2218   nfds_t fdi;
2219   struct lwip_sock *sock;
2220   SYS_ARCH_DECL_PROTECT(lev);
2221 
2222   /* Go through each struct pollfd in the array. */
2223   for (fdi = 0; fdi < nfds; fdi++) {
2224     if ((opts & LWIP_POLLSCAN_CLEAR) != 0) {
2225       fds[fdi].revents = 0;
2226     }
2227 
2228     /* Negative fd means the caller wants us to ignore this struct.
2229        POLLNVAL means we already detected that the fd is invalid;
2230        if another thread has since opened a new socket with that fd,
2231        we must not use that socket. */
2232     if (fds[fdi].fd >= 0 && (fds[fdi].revents & POLLNVAL) == 0) {
2233       /* First get the socket's status (protected)... */
2234       SYS_ARCH_PROTECT(lev);
2235       sock = tryget_socket_unconn_locked(fds[fdi].fd);
2236       if (sock != NULL) {
2237         void* lastdata = sock->lastdata.pbuf;
2238         s16_t rcvevent = sock->rcvevent;
2239         u16_t sendevent = sock->sendevent;
2240         u16_t errevent = sock->errevent;
2241 
2242         if ((opts & LWIP_POLLSCAN_INC_WAIT) != 0) {
2243           sock->select_waiting++;
2244           if (sock->select_waiting == 0) {
2245             /* overflow - too many threads waiting */
2246             sock->select_waiting--;
2247             nready = -1;
2248             SYS_ARCH_UNPROTECT(lev);
2249             done_socket(sock);
2250             break;
2251           }
2252         } else if ((opts & LWIP_POLLSCAN_DEC_WAIT) != 0) {
2253           /* for now, handle select_waiting==0... */
2254           LWIP_ASSERT("sock->select_waiting > 0", sock->select_waiting > 0);
2255           if (sock->select_waiting > 0) {
2256             sock->select_waiting--;
2257           }
2258         }
2259         SYS_ARCH_UNPROTECT(lev);
2260         done_socket(sock);
2261 
2262         /* ... then examine it: */
2263         /* See if netconn of this socket is ready for read */
2264         if ((fds[fdi].events & POLLIN) != 0 && ((lastdata != NULL) || (rcvevent > 0))) {
2265           fds[fdi].revents |= POLLIN;
2266           LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_pollscan: fd=%d ready for reading\n", fds[fdi].fd));
2267         }
2268         /* See if netconn of this socket is ready for write */
2269         if ((fds[fdi].events & POLLOUT) != 0 && (sendevent != 0)) {
2270           fds[fdi].revents |= POLLOUT;
2271           LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_pollscan: fd=%d ready for writing\n", fds[fdi].fd));
2272         }
2273         /* See if netconn of this socket had an error */
2274         if (errevent != 0) {
2275           /* POLLERR is output only. */
2276           fds[fdi].revents |= POLLERR;
2277           LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_pollscan: fd=%d ready for exception\n", fds[fdi].fd));
2278         }
2279       } else {
2280         /* Not a valid socket */
2281         SYS_ARCH_UNPROTECT(lev);
2282         /* POLLNVAL is output only. */
2283         fds[fdi].revents |= POLLNVAL;
2284         return -1;
2285       }
2286     }
2287 
2288     /* Will return the number of structures that have events,
2289        not the number of events. */
2290     if (fds[fdi].revents != 0) {
2291       nready++;
2292     }
2293   }
2294 
2295   LWIP_ASSERT("nready >= 0", nready >= 0);
2296   return nready;
2297 }
2298 
2299 #if LWIP_NETCONN_FULLDUPLEX
2300 /* Mark all sockets as used.
2301  *
2302  * All sockets are marked (and later unmarked), whether they are open or not.
2303  * This is OK as lwip_pollscan aborts select when non-open sockets are found.
2304  */
2305 static void
lwip_poll_inc_sockets_used(struct pollfd * fds,nfds_t nfds)2306 lwip_poll_inc_sockets_used(struct pollfd *fds, nfds_t nfds)
2307 {
2308   nfds_t fdi;
2309 
2310   if(fds) {
2311     /* Go through each struct pollfd in the array. */
2312     for (fdi = 0; fdi < nfds; fdi++) {
2313       /* Increase the reference counter */
2314       tryget_socket_unconn(fds[fdi].fd);
2315     }
2316   }
2317 }
2318 
2319 /* Let go all sockets that were marked as used when starting poll */
2320 static void
lwip_poll_dec_sockets_used(struct pollfd * fds,nfds_t nfds)2321 lwip_poll_dec_sockets_used(struct pollfd *fds, nfds_t nfds)
2322 {
2323   nfds_t fdi;
2324 
2325   if(fds) {
2326     /* Go through each struct pollfd in the array. */
2327     for (fdi = 0; fdi < nfds; fdi++) {
2328       struct lwip_sock *sock = tryget_socket_unconn_nouse(fds[fdi].fd);
2329       LWIP_ASSERT("socket gone at the end of select", sock != NULL);
2330       if (sock != NULL) {
2331         done_socket(sock);
2332       }
2333     }
2334   }
2335 }
2336 #else /* LWIP_NETCONN_FULLDUPLEX */
2337 #define lwip_poll_inc_sockets_used(fds, nfds)
2338 #define lwip_poll_dec_sockets_used(fds, nfds)
2339 #endif /* LWIP_NETCONN_FULLDUPLEX */
2340 
2341 int
lwip_poll(struct pollfd * fds,nfds_t nfds,int timeout)2342 lwip_poll(struct pollfd *fds, nfds_t nfds, int timeout)
2343 {
2344   u32_t waitres = 0;
2345   int nready;
2346   u32_t msectimeout;
2347 #if LWIP_NETCONN_SEM_PER_THREAD
2348   int waited = 0;
2349 #endif
2350 
2351   LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_poll(%p, %d, %d)\n",
2352                   (void*)fds, (int)nfds, timeout));
2353   LWIP_ERROR("lwip_poll: invalid fds", ((fds != NULL && nfds > 0) || (fds == NULL && nfds == 0)),
2354              set_errno(EINVAL); return -1;);
2355 
2356   lwip_poll_inc_sockets_used(fds, nfds);
2357 
2358   /* Go through each struct pollfd to count number of structures
2359      which currently match */
2360   nready = lwip_pollscan(fds, nfds, LWIP_POLLSCAN_CLEAR);
2361 
2362   if (nready < 0) {
2363     lwip_poll_dec_sockets_used(fds, nfds);
2364     return -1;
2365   }
2366 
2367   /* If we don't have any current events, then suspend if we are supposed to */
2368   if (!nready) {
2369     API_SELECT_CB_VAR_DECLARE(select_cb);
2370 
2371     if (timeout == 0) {
2372       LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_poll: no timeout, returning 0\n"));
2373       goto return_success;
2374     }
2375     API_SELECT_CB_VAR_ALLOC(select_cb, set_errno(EAGAIN); lwip_poll_dec_sockets_used(fds, nfds); return -1);
2376     memset(&API_SELECT_CB_VAR_REF(select_cb), 0, sizeof(struct lwip_select_cb));
2377 
2378     /* None ready: add our semaphore to list:
2379        We don't actually need any dynamic memory. Our entry on the
2380        list is only valid while we are in this function, so it's ok
2381        to use local variables. */
2382 
2383     API_SELECT_CB_VAR_REF(select_cb).poll_fds = fds;
2384     API_SELECT_CB_VAR_REF(select_cb).poll_nfds = nfds;
2385 #if LWIP_NETCONN_SEM_PER_THREAD
2386     API_SELECT_CB_VAR_REF(select_cb).sem = LWIP_NETCONN_THREAD_SEM_GET();
2387 #else /* LWIP_NETCONN_SEM_PER_THREAD */
2388     if (sys_sem_new(&API_SELECT_CB_VAR_REF(select_cb).sem, 0) != ERR_OK) {
2389       /* failed to create semaphore */
2390       set_errno(EAGAIN);
2391       lwip_poll_dec_sockets_used(fds, nfds);
2392       API_SELECT_CB_VAR_FREE(select_cb);
2393       return -1;
2394     }
2395 #endif /* LWIP_NETCONN_SEM_PER_THREAD */
2396 
2397     lwip_link_select_cb(&API_SELECT_CB_VAR_REF(select_cb));
2398 
2399     /* Increase select_waiting for each socket we are interested in.
2400        Also, check for events again: there could have been events between
2401        the last scan (without us on the list) and putting us on the list! */
2402     nready = lwip_pollscan(fds, nfds, LWIP_POLLSCAN_INC_WAIT);
2403 
2404     if (!nready) {
2405       /* Still none ready, just wait to be woken */
2406       if (timeout < 0) {
2407         /* Wait forever */
2408         msectimeout = 0;
2409       } else {
2410         /* timeout == 0 would have been handled earlier. */
2411         LWIP_ASSERT("timeout > 0", timeout > 0);
2412         msectimeout = timeout;
2413       }
2414       waitres = sys_arch_sem_wait(SELECT_SEM_PTR(API_SELECT_CB_VAR_REF(select_cb).sem), msectimeout);
2415 #if LWIP_NETCONN_SEM_PER_THREAD
2416       waited = 1;
2417 #endif
2418     }
2419 
2420     /* Decrease select_waiting for each socket we are interested in,
2421        and check which events occurred while we waited. */
2422     nready = lwip_pollscan(fds, nfds, LWIP_POLLSCAN_DEC_WAIT);
2423 
2424     lwip_unlink_select_cb(&API_SELECT_CB_VAR_REF(select_cb));
2425 
2426 #if LWIP_NETCONN_SEM_PER_THREAD
2427     if (select_cb.sem_signalled && (!waited || (waitres == SYS_ARCH_TIMEOUT))) {
2428       /* don't leave the thread-local semaphore signalled */
2429       sys_arch_sem_wait(API_SELECT_CB_VAR_REF(select_cb).sem, 1);
2430     }
2431 #else /* LWIP_NETCONN_SEM_PER_THREAD */
2432     sys_sem_free(&API_SELECT_CB_VAR_REF(select_cb).sem);
2433 #endif /* LWIP_NETCONN_SEM_PER_THREAD */
2434     API_SELECT_CB_VAR_FREE(select_cb);
2435 
2436     if (nready < 0) {
2437       /* This happens when a socket got closed while waiting */
2438       lwip_poll_dec_sockets_used(fds, nfds);
2439       return -1;
2440     }
2441 
2442     if (waitres == SYS_ARCH_TIMEOUT) {
2443       /* Timeout */
2444       LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_poll: timeout expired\n"));
2445       goto return_success;
2446     }
2447   }
2448 
2449   LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_poll: nready=%d\n", nready));
2450 return_success:
2451   lwip_poll_dec_sockets_used(fds, nfds);
2452   set_errno(0);
2453   return nready;
2454 }
2455 
2456 /**
2457  * Check whether event_callback should wake up a thread waiting in
2458  * lwip_poll.
2459  */
2460 static int
lwip_poll_should_wake(const struct lwip_select_cb * scb,int fd,int has_recvevent,int has_sendevent,int has_errevent)2461 lwip_poll_should_wake(const struct lwip_select_cb *scb, int fd, int has_recvevent, int has_sendevent, int has_errevent)
2462 {
2463   nfds_t fdi;
2464   for (fdi = 0; fdi < scb->poll_nfds; fdi++) {
2465     const struct pollfd *pollfd = &scb->poll_fds[fdi];
2466     if (pollfd->fd == fd) {
2467       /* Do not update pollfd->revents right here;
2468          that would be a data race because lwip_pollscan
2469          accesses revents without protecting. */
2470       if (has_recvevent && (pollfd->events & POLLIN) != 0) {
2471         return 1;
2472       }
2473       if (has_sendevent && (pollfd->events & POLLOUT) != 0) {
2474         return 1;
2475       }
2476       if (has_errevent) {
2477         /* POLLERR is output only. */
2478         return 1;
2479       }
2480     }
2481   }
2482   return 0;
2483 }
2484 #endif /* LWIP_SOCKET_POLL */
2485 
2486 #if LWIP_SOCKET_SELECT || LWIP_SOCKET_POLL
2487 /**
2488  * Callback registered in the netconn layer for each socket-netconn.
2489  * Processes recvevent (data available) and wakes up tasks waiting for select.
2490  *
2491  * @note for LWIP_TCPIP_CORE_LOCKING any caller of this function
2492  * must have the core lock held when signaling the following events
2493  * as they might cause select_list_cb to be checked:
2494  *   NETCONN_EVT_RCVPLUS
2495  *   NETCONN_EVT_SENDPLUS
2496  *   NETCONN_EVT_ERROR
2497  * This requirement will be asserted in select_check_waiters()
2498  */
2499 static void
event_callback(struct netconn * conn,enum netconn_evt evt,u16_t len)2500 event_callback(struct netconn *conn, enum netconn_evt evt, u16_t len)
2501 {
2502   int s, check_waiters;
2503   struct lwip_sock *sock;
2504   SYS_ARCH_DECL_PROTECT(lev);
2505 
2506   LWIP_UNUSED_ARG(len);
2507 
2508   /* Get socket */
2509   if (conn) {
2510     s = conn->socket;
2511     if (s < 0) {
2512       /* Data comes in right away after an accept, even though
2513        * the server task might not have created a new socket yet.
2514        * Just count down (or up) if that's the case and we
2515        * will use the data later. Note that only receive events
2516        * can happen before the new socket is set up. */
2517       SYS_ARCH_PROTECT(lev);
2518       if (conn->socket < 0) {
2519         if (evt == NETCONN_EVT_RCVPLUS) {
2520           /* conn->socket is -1 on initialization
2521              lwip_accept adjusts sock->recvevent if conn->socket < -1 */
2522           conn->socket--;
2523         }
2524         SYS_ARCH_UNPROTECT(lev);
2525         return;
2526       }
2527       s = conn->socket;
2528       SYS_ARCH_UNPROTECT(lev);
2529     }
2530 
2531     sock = get_socket(s);
2532     if (!sock) {
2533       return;
2534     }
2535   } else {
2536     return;
2537   }
2538 
2539   check_waiters = 1;
2540   SYS_ARCH_PROTECT(lev);
2541   /* Set event as required */
2542   switch (evt) {
2543     case NETCONN_EVT_RCVPLUS:
2544       sock->rcvevent++;
2545       if (sock->rcvevent > 1) {
2546         check_waiters = 0;
2547       }
2548       break;
2549     case NETCONN_EVT_RCVMINUS:
2550       sock->rcvevent--;
2551       check_waiters = 0;
2552       break;
2553     case NETCONN_EVT_SENDPLUS:
2554       if (sock->sendevent) {
2555         check_waiters = 0;
2556       }
2557       sock->sendevent = 1;
2558       break;
2559     case NETCONN_EVT_SENDMINUS:
2560       sock->sendevent = 0;
2561       check_waiters = 0;
2562       break;
2563     case NETCONN_EVT_ERROR:
2564       sock->errevent = 1;
2565       break;
2566     default:
2567       LWIP_ASSERT("unknown event", 0);
2568       break;
2569   }
2570 
2571   if (sock->select_waiting && check_waiters) {
2572     /* Save which events are active */
2573     int has_recvevent, has_sendevent, has_errevent;
2574     has_recvevent = sock->rcvevent > 0;
2575     has_sendevent = sock->sendevent != 0;
2576     has_errevent = sock->errevent != 0;
2577     SYS_ARCH_UNPROTECT(lev);
2578     /* Check any select calls waiting on this socket */
2579     select_check_waiters(s, has_recvevent, has_sendevent, has_errevent);
2580   } else {
2581     SYS_ARCH_UNPROTECT(lev);
2582   }
2583   done_socket(sock);
2584 }
2585 
2586 /**
2587  * Check if any select waiters are waiting on this socket and its events
2588  *
2589  * @note on synchronization of select_cb_list:
2590  * LWIP_TCPIP_CORE_LOCKING: the select_cb_list must only be accessed while holding
2591  * the core lock. We do a single pass through the list and signal any waiters.
2592  * Core lock should already be held when calling here!!!!
2593 
2594  * !LWIP_TCPIP_CORE_LOCKING: we use SYS_ARCH_PROTECT but unlock on each iteration
2595  * of the loop, thus creating a possibility where a thread could modify the
2596  * select_cb_list during our UNPROTECT/PROTECT. We use a generational counter to
2597  * detect this change and restart the list walk. The list is expected to be small
2598  */
select_check_waiters(int s,int has_recvevent,int has_sendevent,int has_errevent)2599 static void select_check_waiters(int s, int has_recvevent, int has_sendevent, int has_errevent)
2600 {
2601   struct lwip_select_cb *scb;
2602 #if !LWIP_TCPIP_CORE_LOCKING
2603   int last_select_cb_ctr;
2604   SYS_ARCH_DECL_PROTECT(lev);
2605 #endif /* !LWIP_TCPIP_CORE_LOCKING */
2606 
2607   LWIP_ASSERT_CORE_LOCKED();
2608 
2609 #if !LWIP_TCPIP_CORE_LOCKING
2610   SYS_ARCH_PROTECT(lev);
2611 again:
2612   /* remember the state of select_cb_list to detect changes */
2613   last_select_cb_ctr = select_cb_ctr;
2614 #endif /* !LWIP_TCPIP_CORE_LOCKING */
2615   for (scb = select_cb_list; scb != NULL; scb = scb->next) {
2616     if (scb->sem_signalled == 0) {
2617       /* semaphore not signalled yet */
2618       int do_signal = 0;
2619 #if LWIP_SOCKET_POLL
2620       if (scb->poll_fds != NULL) {
2621         do_signal = lwip_poll_should_wake(scb, s, has_recvevent, has_sendevent, has_errevent);
2622       }
2623 #endif /* LWIP_SOCKET_POLL */
2624 #if LWIP_SOCKET_SELECT && LWIP_SOCKET_POLL
2625       else
2626 #endif /* LWIP_SOCKET_SELECT && LWIP_SOCKET_POLL */
2627 #if LWIP_SOCKET_SELECT
2628       {
2629         /* Test this select call for our socket */
2630         if (has_recvevent) {
2631           if (scb->readset && FD_ISSET(s, scb->readset)) {
2632             do_signal = 1;
2633           }
2634         }
2635         if (has_sendevent) {
2636           if (!do_signal && scb->writeset && FD_ISSET(s, scb->writeset)) {
2637             do_signal = 1;
2638           }
2639         }
2640         if (has_errevent) {
2641           if (!do_signal && scb->exceptset && FD_ISSET(s, scb->exceptset)) {
2642             do_signal = 1;
2643           }
2644         }
2645       }
2646 #endif /* LWIP_SOCKET_SELECT */
2647       if (do_signal) {
2648         scb->sem_signalled = 1;
2649         /* For !LWIP_TCPIP_CORE_LOCKING, we don't call SYS_ARCH_UNPROTECT() before signaling
2650            the semaphore, as this might lead to the select thread taking itself off the list,
2651            invalidating the semaphore. */
2652         sys_sem_signal(SELECT_SEM_PTR(scb->sem));
2653       }
2654     }
2655 #if LWIP_TCPIP_CORE_LOCKING
2656   }
2657 #else
2658     /* unlock interrupts with each step */
2659     SYS_ARCH_UNPROTECT(lev);
2660     /* this makes sure interrupt protection time is short */
2661     SYS_ARCH_PROTECT(lev);
2662     if (last_select_cb_ctr != select_cb_ctr) {
2663       /* someone has changed select_cb_list, restart at the beginning */
2664       goto again;
2665     }
2666     /* remember the state of select_cb_list to detect changes */
2667     last_select_cb_ctr = select_cb_ctr;
2668   }
2669   SYS_ARCH_UNPROTECT(lev);
2670 #endif
2671 }
2672 #endif /* LWIP_SOCKET_SELECT || LWIP_SOCKET_POLL */
2673 
2674 /**
2675  * Close one end of a full-duplex connection.
2676  */
2677 int
lwip_shutdown(int s,int how)2678 lwip_shutdown(int s, int how)
2679 {
2680   struct lwip_sock *sock;
2681   err_t err;
2682   u8_t shut_rx = 0, shut_tx = 0;
2683 
2684   LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_shutdown(%d, how=%d)\n", s, how));
2685 
2686   sock = get_socket(s);
2687   if (!sock) {
2688     return -1;
2689   }
2690 
2691   if (sock->conn != NULL) {
2692     if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_TCP) {
2693       sock_set_errno(sock, EOPNOTSUPP);
2694       done_socket(sock);
2695       return -1;
2696     }
2697   } else {
2698     sock_set_errno(sock, ENOTCONN);
2699     done_socket(sock);
2700     return -1;
2701   }
2702 
2703   if (how == SHUT_RD) {
2704     shut_rx = 1;
2705   } else if (how == SHUT_WR) {
2706     shut_tx = 1;
2707   } else if (how == SHUT_RDWR) {
2708     shut_rx = 1;
2709     shut_tx = 1;
2710   } else {
2711     sock_set_errno(sock, EINVAL);
2712     done_socket(sock);
2713     return -1;
2714   }
2715   err = netconn_shutdown(sock->conn, shut_rx, shut_tx);
2716 
2717   sock_set_errno(sock, err_to_errno(err));
2718   done_socket(sock);
2719   return (err == ERR_OK ? 0 : -1);
2720 }
2721 
2722 static int
lwip_getaddrname(int s,struct sockaddr * name,socklen_t * namelen,u8_t local)2723 lwip_getaddrname(int s, struct sockaddr *name, socklen_t *namelen, u8_t local)
2724 {
2725   struct lwip_sock *sock;
2726   union sockaddr_aligned saddr;
2727   ip_addr_t naddr;
2728   u16_t port;
2729   err_t err;
2730 
2731   sock = get_socket(s);
2732   if (!sock) {
2733     return -1;
2734   }
2735 
2736   /* get the IP address and port */
2737   err = netconn_getaddr(sock->conn, &naddr, &port, local);
2738   if (err != ERR_OK) {
2739     sock_set_errno(sock, err_to_errno(err));
2740     done_socket(sock);
2741     return -1;
2742   }
2743 
2744 #if LWIP_IPV4 && LWIP_IPV6
2745   /* Dual-stack: Map IPv4 addresses to IPv4 mapped IPv6 */
2746   if (NETCONNTYPE_ISIPV6(netconn_type(sock->conn)) &&
2747       IP_IS_V4_VAL(naddr)) {
2748     ip4_2_ipv4_mapped_ipv6(ip_2_ip6(&naddr), ip_2_ip4(&naddr));
2749     IP_SET_TYPE_VAL(naddr, IPADDR_TYPE_V6);
2750   }
2751 #endif /* LWIP_IPV4 && LWIP_IPV6 */
2752 
2753   IPADDR_PORT_TO_SOCKADDR(&saddr, &naddr, port);
2754 
2755   LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getaddrname(%d, addr=", s));
2756   ip_addr_debug_print_val(SOCKETS_DEBUG, naddr);
2757   LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F")\n", port));
2758 
2759   if (*namelen > saddr.sa.sa_len) {
2760     *namelen = saddr.sa.sa_len;
2761   }
2762   MEMCPY(name, &saddr, *namelen);
2763 
2764   sock_set_errno(sock, 0);
2765   done_socket(sock);
2766   return 0;
2767 }
2768 
2769 int
lwip_getpeername(int s,struct sockaddr * name,socklen_t * namelen)2770 lwip_getpeername(int s, struct sockaddr *name, socklen_t *namelen)
2771 {
2772   return lwip_getaddrname(s, name, namelen, 0);
2773 }
2774 
2775 int
lwip_getsockname(int s,struct sockaddr * name,socklen_t * namelen)2776 lwip_getsockname(int s, struct sockaddr *name, socklen_t *namelen)
2777 {
2778   return lwip_getaddrname(s, name, namelen, 1);
2779 }
2780 
2781 int
lwip_getsockopt(int s,int level,int optname,void * optval,socklen_t * optlen)2782 lwip_getsockopt(int s, int level, int optname, void *optval, socklen_t *optlen)
2783 {
2784   int err;
2785   struct lwip_sock *sock = get_socket(s);
2786 #if !LWIP_TCPIP_CORE_LOCKING
2787   err_t cberr;
2788   LWIP_SETGETSOCKOPT_DATA_VAR_DECLARE(data);
2789 #endif /* !LWIP_TCPIP_CORE_LOCKING */
2790 
2791   if (!sock) {
2792     return -1;
2793   }
2794 
2795   if ((NULL == optval) || (NULL == optlen)) {
2796     sock_set_errno(sock, EFAULT);
2797     done_socket(sock);
2798     return -1;
2799   }
2800 
2801 #if LWIP_TCPIP_CORE_LOCKING
2802   /* core-locking can just call the -impl function */
2803   LOCK_TCPIP_CORE();
2804   err = lwip_getsockopt_impl(s, level, optname, optval, optlen);
2805   UNLOCK_TCPIP_CORE();
2806 
2807 #else /* LWIP_TCPIP_CORE_LOCKING */
2808 
2809 #if LWIP_MPU_COMPATIBLE
2810   /* MPU_COMPATIBLE copies the optval data, so check for max size here */
2811   if (*optlen > LWIP_SETGETSOCKOPT_MAXOPTLEN) {
2812     sock_set_errno(sock, ENOBUFS);
2813     done_socket(sock);
2814     return -1;
2815   }
2816 #endif /* LWIP_MPU_COMPATIBLE */
2817 
2818   LWIP_SETGETSOCKOPT_DATA_VAR_ALLOC(data, sock);
2819   LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).s = s;
2820   LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).level = level;
2821   LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).optname = optname;
2822   LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).optlen = *optlen;
2823 #if !LWIP_MPU_COMPATIBLE
2824   LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).optval.p = optval;
2825 #endif /* !LWIP_MPU_COMPATIBLE */
2826   LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).err = 0;
2827 #if LWIP_NETCONN_SEM_PER_THREAD
2828   LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).completed_sem = LWIP_NETCONN_THREAD_SEM_GET();
2829 #else
2830   LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).completed_sem = &sock->conn->op_completed;
2831 #endif
2832   cberr = tcpip_callback(lwip_getsockopt_callback, &LWIP_SETGETSOCKOPT_DATA_VAR_REF(data));
2833   if (cberr != ERR_OK) {
2834     LWIP_SETGETSOCKOPT_DATA_VAR_FREE(data);
2835     sock_set_errno(sock, err_to_errno(cberr));
2836     done_socket(sock);
2837     return -1;
2838   }
2839   sys_arch_sem_wait((sys_sem_t *)(LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).completed_sem), 0);
2840 
2841   /* write back optlen and optval */
2842   *optlen = LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).optlen;
2843 #if LWIP_MPU_COMPATIBLE
2844   MEMCPY(optval, LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).optval,
2845          LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).optlen);
2846 #endif /* LWIP_MPU_COMPATIBLE */
2847 
2848   /* maybe lwip_getsockopt_internal has changed err */
2849   err = LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).err;
2850   LWIP_SETGETSOCKOPT_DATA_VAR_FREE(data);
2851 #endif /* LWIP_TCPIP_CORE_LOCKING */
2852 
2853   sock_set_errno(sock, err);
2854   done_socket(sock);
2855   return err ? -1 : 0;
2856 }
2857 
2858 #if !LWIP_TCPIP_CORE_LOCKING
2859 /** lwip_getsockopt_callback: only used without CORE_LOCKING
2860  * to get into the tcpip_thread
2861  */
2862 static void
lwip_getsockopt_callback(void * arg)2863 lwip_getsockopt_callback(void *arg)
2864 {
2865   struct lwip_setgetsockopt_data *data;
2866   LWIP_ASSERT("arg != NULL", arg != NULL);
2867   data = (struct lwip_setgetsockopt_data *)arg;
2868 
2869   data->err = lwip_getsockopt_impl(data->s, data->level, data->optname,
2870 #if LWIP_MPU_COMPATIBLE
2871                                    data->optval,
2872 #else /* LWIP_MPU_COMPATIBLE */
2873                                    data->optval.p,
2874 #endif /* LWIP_MPU_COMPATIBLE */
2875                                    &data->optlen);
2876 
2877   sys_sem_signal((sys_sem_t *)(data->completed_sem));
2878 }
2879 #endif  /* LWIP_TCPIP_CORE_LOCKING */
2880 
2881 static int
lwip_sockopt_to_ipopt(int optname)2882 lwip_sockopt_to_ipopt(int optname)
2883 {
2884   /* Map SO_* values to our internal SOF_* values
2885    * We should not rely on #defines in socket.h
2886    * being in sync with ip.h.
2887    */
2888   switch (optname) {
2889   case SO_BROADCAST:
2890     return SOF_BROADCAST;
2891   case SO_KEEPALIVE:
2892     return SOF_KEEPALIVE;
2893   case SO_REUSEADDR:
2894     return SOF_REUSEADDR;
2895   default:
2896     LWIP_ASSERT("Unknown socket option", 0);
2897     return 0;
2898   }
2899 }
2900 
2901 /** lwip_getsockopt_impl: the actual implementation of getsockopt:
2902  * same argument as lwip_getsockopt, either called directly or through callback
2903  */
2904 static int
lwip_getsockopt_impl(int s,int level,int optname,void * optval,socklen_t * optlen)2905 lwip_getsockopt_impl(int s, int level, int optname, void *optval, socklen_t *optlen)
2906 {
2907   int err = 0;
2908   struct lwip_sock *sock = tryget_socket(s);
2909   if (!sock) {
2910     return EBADF;
2911   }
2912 
2913 #ifdef LWIP_HOOK_SOCKETS_GETSOCKOPT
2914   if (LWIP_HOOK_SOCKETS_GETSOCKOPT(s, sock, level, optname, optval, optlen, &err)) {
2915     return err;
2916   }
2917 #endif
2918 
2919   switch (level) {
2920 
2921     /* Level: SOL_SOCKET */
2922     case SOL_SOCKET:
2923       switch (optname) {
2924 
2925 #if LWIP_TCP
2926         case SO_ACCEPTCONN:
2927           LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, *optlen, int);
2928           if (NETCONNTYPE_GROUP(sock->conn->type) != NETCONN_TCP) {
2929             done_socket(sock);
2930             return ENOPROTOOPT;
2931           }
2932           if ((sock->conn->pcb.tcp != NULL) && (sock->conn->pcb.tcp->state == LISTEN)) {
2933             *(int *)optval = 1;
2934           } else {
2935             *(int *)optval = 0;
2936           }
2937           break;
2938 #endif /* LWIP_TCP */
2939 
2940         /* The option flags */
2941         case SO_BROADCAST:
2942         case SO_KEEPALIVE:
2943 #if SO_REUSE
2944         case SO_REUSEADDR:
2945 #endif /* SO_REUSE */
2946           if ((optname == SO_BROADCAST) &&
2947               (NETCONNTYPE_GROUP(sock->conn->type) != NETCONN_UDP)) {
2948             done_socket(sock);
2949             return ENOPROTOOPT;
2950           }
2951 
2952           optname = lwip_sockopt_to_ipopt(optname);
2953 
2954           LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, *optlen, int);
2955           *(int *)optval = ip_get_option(sock->conn->pcb.ip, optname);
2956           LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, SOL_SOCKET, optname=0x%x, ..) = %s\n",
2957                                       s, optname, (*(int *)optval ? "on" : "off")));
2958           break;
2959 
2960         case SO_TYPE:
2961           LWIP_SOCKOPT_CHECK_OPTLEN_CONN(sock, *optlen, int);
2962           switch (NETCONNTYPE_GROUP(netconn_type(sock->conn))) {
2963             case NETCONN_RAW:
2964               *(int *)optval = SOCK_RAW;
2965               break;
2966             case NETCONN_TCP:
2967               *(int *)optval = SOCK_STREAM;
2968               break;
2969             case NETCONN_UDP:
2970               *(int *)optval = SOCK_DGRAM;
2971               break;
2972             default: /* unrecognized socket type */
2973               *(int *)optval = netconn_type(sock->conn);
2974               LWIP_DEBUGF(SOCKETS_DEBUG,
2975                           ("lwip_getsockopt(%d, SOL_SOCKET, SO_TYPE): unrecognized socket type %d\n",
2976                            s, *(int *)optval));
2977           }  /* switch (netconn_type(sock->conn)) */
2978           LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, SOL_SOCKET, SO_TYPE) = %d\n",
2979                                       s, *(int *)optval));
2980           break;
2981 
2982         case SO_ERROR:
2983           LWIP_SOCKOPT_CHECK_OPTLEN(sock, *optlen, int);
2984           *(int *)optval = err_to_errno(netconn_err(sock->conn));
2985           LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, SOL_SOCKET, SO_ERROR) = %d\n",
2986                                       s, *(int *)optval));
2987           break;
2988 
2989 #if LWIP_SO_SNDTIMEO
2990         case SO_SNDTIMEO:
2991           LWIP_SOCKOPT_CHECK_OPTLEN_CONN(sock, *optlen, LWIP_SO_SNDRCVTIMEO_OPTTYPE);
2992           LWIP_SO_SNDRCVTIMEO_SET(optval, netconn_get_sendtimeout(sock->conn));
2993           break;
2994 #endif /* LWIP_SO_SNDTIMEO */
2995 #if LWIP_SO_RCVTIMEO
2996         case SO_RCVTIMEO:
2997           LWIP_SOCKOPT_CHECK_OPTLEN_CONN(sock, *optlen, LWIP_SO_SNDRCVTIMEO_OPTTYPE);
2998           LWIP_SO_SNDRCVTIMEO_SET(optval, netconn_get_recvtimeout(sock->conn));
2999           break;
3000 #endif /* LWIP_SO_RCVTIMEO */
3001 #if LWIP_SO_RCVBUF
3002         case SO_RCVBUF:
3003           LWIP_SOCKOPT_CHECK_OPTLEN_CONN(sock, *optlen, int);
3004           *(int *)optval = netconn_get_recvbufsize(sock->conn);
3005           break;
3006 #endif /* LWIP_SO_RCVBUF */
3007 #if LWIP_SO_LINGER
3008         case SO_LINGER: {
3009           s16_t conn_linger;
3010           struct linger *linger = (struct linger *)optval;
3011           LWIP_SOCKOPT_CHECK_OPTLEN_CONN(sock, *optlen, struct linger);
3012           conn_linger = sock->conn->linger;
3013           if (conn_linger >= 0) {
3014             linger->l_onoff = 1;
3015             linger->l_linger = (int)conn_linger;
3016           } else {
3017             linger->l_onoff = 0;
3018             linger->l_linger = 0;
3019           }
3020         }
3021         break;
3022 #endif /* LWIP_SO_LINGER */
3023 #if LWIP_UDP
3024         case SO_NO_CHECK:
3025           LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, *optlen, int, NETCONN_UDP);
3026 #if LWIP_UDPLITE
3027           if (udp_is_flag_set(sock->conn->pcb.udp, UDP_FLAGS_UDPLITE)) {
3028             /* this flag is only available for UDP, not for UDP lite */
3029             done_socket(sock);
3030             return EAFNOSUPPORT;
3031           }
3032 #endif /* LWIP_UDPLITE */
3033           *(int *)optval = udp_is_flag_set(sock->conn->pcb.udp, UDP_FLAGS_NOCHKSUM) ? 1 : 0;
3034           break;
3035 #endif /* LWIP_UDP*/
3036         default:
3037           LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, SOL_SOCKET, UNIMPL: optname=0x%x, ..)\n",
3038                                       s, optname));
3039           err = ENOPROTOOPT;
3040           break;
3041       }  /* switch (optname) */
3042       break;
3043 
3044     /* Level: IPPROTO_IP */
3045     case IPPROTO_IP:
3046       switch (optname) {
3047         case IP_TTL:
3048           LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, *optlen, int);
3049           *(int *)optval = sock->conn->pcb.ip->ttl;
3050           LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, IP_TTL) = %d\n",
3051                                       s, *(int *)optval));
3052           break;
3053         case IP_TOS:
3054           LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, *optlen, int);
3055           *(int *)optval = sock->conn->pcb.ip->tos;
3056           LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, IP_TOS) = %d\n",
3057                                       s, *(int *)optval));
3058           break;
3059 #if LWIP_IPV4 && LWIP_MULTICAST_TX_OPTIONS && LWIP_UDP
3060         case IP_MULTICAST_TTL:
3061           LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, *optlen, u8_t);
3062           if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_UDP) {
3063             done_socket(sock);
3064             return ENOPROTOOPT;
3065           }
3066           *(u8_t *)optval = udp_get_multicast_ttl(sock->conn->pcb.udp);
3067           LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, IP_MULTICAST_TTL) = %d\n",
3068                                       s, *(int *)optval));
3069           break;
3070         case IP_MULTICAST_IF:
3071           LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, *optlen, struct in_addr);
3072           if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_UDP) {
3073             done_socket(sock);
3074             return ENOPROTOOPT;
3075           }
3076           inet_addr_from_ip4addr((struct in_addr *)optval, udp_get_multicast_netif_addr(sock->conn->pcb.udp));
3077           LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, IP_MULTICAST_IF) = 0x%"X32_F"\n",
3078                                       s, *(u32_t *)optval));
3079           break;
3080         case IP_MULTICAST_LOOP:
3081           LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, *optlen, u8_t);
3082           if ((sock->conn->pcb.udp->flags & UDP_FLAGS_MULTICAST_LOOP) != 0) {
3083             *(u8_t *)optval = 1;
3084           } else {
3085             *(u8_t *)optval = 0;
3086           }
3087           LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, IP_MULTICAST_LOOP) = %d\n",
3088                                       s, *(int *)optval));
3089           break;
3090 #endif /* LWIP_IPV4 && LWIP_MULTICAST_TX_OPTIONS && LWIP_UDP */
3091         default:
3092           LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, UNIMPL: optname=0x%x, ..)\n",
3093                                       s, optname));
3094           err = ENOPROTOOPT;
3095           break;
3096       }  /* switch (optname) */
3097       break;
3098 
3099 #if LWIP_TCP
3100     /* Level: IPPROTO_TCP */
3101     case IPPROTO_TCP:
3102       /* Special case: all IPPROTO_TCP option take an int */
3103       LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, *optlen, int, NETCONN_TCP);
3104       if (sock->conn->pcb.tcp->state == LISTEN) {
3105         done_socket(sock);
3106         return EINVAL;
3107       }
3108       switch (optname) {
3109         case TCP_NODELAY:
3110           *(int *)optval = tcp_nagle_disabled(sock->conn->pcb.tcp);
3111           LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_TCP, TCP_NODELAY) = %s\n",
3112                                       s, (*(int *)optval) ? "on" : "off") );
3113           break;
3114         case TCP_KEEPALIVE:
3115           *(int *)optval = (int)sock->conn->pcb.tcp->keep_idle;
3116           LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_TCP, TCP_KEEPALIVE) = %d\n",
3117                                       s, *(int *)optval));
3118           break;
3119 
3120 #if LWIP_TCP_KEEPALIVE
3121         case TCP_KEEPIDLE:
3122           *(int *)optval = (int)(sock->conn->pcb.tcp->keep_idle / 1000);
3123           LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_TCP, TCP_KEEPIDLE) = %d\n",
3124                                       s, *(int *)optval));
3125           break;
3126         case TCP_KEEPINTVL:
3127           *(int *)optval = (int)(sock->conn->pcb.tcp->keep_intvl / 1000);
3128           LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_TCP, TCP_KEEPINTVL) = %d\n",
3129                                       s, *(int *)optval));
3130           break;
3131         case TCP_KEEPCNT:
3132           *(int *)optval = (int)sock->conn->pcb.tcp->keep_cnt;
3133           LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_TCP, TCP_KEEPCNT) = %d\n",
3134                                       s, *(int *)optval));
3135           break;
3136 #endif /* LWIP_TCP_KEEPALIVE */
3137         default:
3138           LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_TCP, UNIMPL: optname=0x%x, ..)\n",
3139                                       s, optname));
3140           err = ENOPROTOOPT;
3141           break;
3142       }  /* switch (optname) */
3143       break;
3144 #endif /* LWIP_TCP */
3145 
3146 #if LWIP_IPV6
3147     /* Level: IPPROTO_IPV6 */
3148     case IPPROTO_IPV6:
3149       switch (optname) {
3150         case IPV6_V6ONLY:
3151           LWIP_SOCKOPT_CHECK_OPTLEN_CONN(sock, *optlen, int);
3152           *(int *)optval = (netconn_get_ipv6only(sock->conn) ? 1 : 0);
3153           LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IPV6, IPV6_V6ONLY) = %d\n",
3154                                       s, *(int *)optval));
3155           break;
3156         default:
3157           LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IPV6, UNIMPL: optname=0x%x, ..)\n",
3158                                       s, optname));
3159           err = ENOPROTOOPT;
3160           break;
3161       }  /* switch (optname) */
3162       break;
3163 #endif /* LWIP_IPV6 */
3164 
3165 #if LWIP_UDP && LWIP_UDPLITE
3166     /* Level: IPPROTO_UDPLITE */
3167     case IPPROTO_UDPLITE:
3168       /* Special case: all IPPROTO_UDPLITE option take an int */
3169       LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, *optlen, int);
3170       /* If this is no UDP lite socket, ignore any options. */
3171       if (!NETCONNTYPE_ISUDPLITE(netconn_type(sock->conn))) {
3172         done_socket(sock);
3173         return ENOPROTOOPT;
3174       }
3175       switch (optname) {
3176         case UDPLITE_SEND_CSCOV:
3177           *(int *)optval = sock->conn->pcb.udp->chksum_len_tx;
3178           LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_UDPLITE, UDPLITE_SEND_CSCOV) = %d\n",
3179                                       s, (*(int *)optval)) );
3180           break;
3181         case UDPLITE_RECV_CSCOV:
3182           *(int *)optval = sock->conn->pcb.udp->chksum_len_rx;
3183           LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_UDPLITE, UDPLITE_RECV_CSCOV) = %d\n",
3184                                       s, (*(int *)optval)) );
3185           break;
3186         default:
3187           LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_UDPLITE, UNIMPL: optname=0x%x, ..)\n",
3188                                       s, optname));
3189           err = ENOPROTOOPT;
3190           break;
3191       }  /* switch (optname) */
3192       break;
3193 #endif /* LWIP_UDP */
3194     /* Level: IPPROTO_RAW */
3195     case IPPROTO_RAW:
3196       switch (optname) {
3197 #if LWIP_IPV6 && LWIP_RAW
3198         case IPV6_CHECKSUM:
3199           LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, *optlen, int, NETCONN_RAW);
3200           if (sock->conn->pcb.raw->chksum_reqd == 0) {
3201             *(int *)optval = -1;
3202           } else {
3203             *(int *)optval = sock->conn->pcb.raw->chksum_offset;
3204           }
3205           LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_RAW, IPV6_CHECKSUM) = %d\n",
3206                                       s, (*(int *)optval)) );
3207           break;
3208 #endif /* LWIP_IPV6 && LWIP_RAW */
3209         default:
3210           LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_RAW, UNIMPL: optname=0x%x, ..)\n",
3211                                       s, optname));
3212           err = ENOPROTOOPT;
3213           break;
3214       }  /* switch (optname) */
3215       break;
3216     default:
3217       LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, level=0x%x, UNIMPL: optname=0x%x, ..)\n",
3218                                   s, level, optname));
3219       err = ENOPROTOOPT;
3220       break;
3221   } /* switch (level) */
3222 
3223   done_socket(sock);
3224   return err;
3225 }
3226 
3227 int
lwip_setsockopt(int s,int level,int optname,const void * optval,socklen_t optlen)3228 lwip_setsockopt(int s, int level, int optname, const void *optval, socklen_t optlen)
3229 {
3230   int err = 0;
3231   struct lwip_sock *sock = get_socket(s);
3232 #if !LWIP_TCPIP_CORE_LOCKING
3233   err_t cberr;
3234   LWIP_SETGETSOCKOPT_DATA_VAR_DECLARE(data);
3235 #endif /* !LWIP_TCPIP_CORE_LOCKING */
3236 
3237   if (!sock) {
3238     return -1;
3239   }
3240 
3241   if (NULL == optval) {
3242     sock_set_errno(sock, EFAULT);
3243     done_socket(sock);
3244     return -1;
3245   }
3246 
3247 #if LWIP_TCPIP_CORE_LOCKING
3248   /* core-locking can just call the -impl function */
3249   LOCK_TCPIP_CORE();
3250   err = lwip_setsockopt_impl(s, level, optname, optval, optlen);
3251   UNLOCK_TCPIP_CORE();
3252 
3253 #else /* LWIP_TCPIP_CORE_LOCKING */
3254 
3255 #if LWIP_MPU_COMPATIBLE
3256   /* MPU_COMPATIBLE copies the optval data, so check for max size here */
3257   if (optlen > LWIP_SETGETSOCKOPT_MAXOPTLEN) {
3258     sock_set_errno(sock, ENOBUFS);
3259     done_socket(sock);
3260     return -1;
3261   }
3262 #endif /* LWIP_MPU_COMPATIBLE */
3263 
3264   LWIP_SETGETSOCKOPT_DATA_VAR_ALLOC(data, sock);
3265   LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).s = s;
3266   LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).level = level;
3267   LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).optname = optname;
3268   LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).optlen = optlen;
3269 #if LWIP_MPU_COMPATIBLE
3270   MEMCPY(LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).optval, optval, optlen);
3271 #else /* LWIP_MPU_COMPATIBLE */
3272   LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).optval.pc = (const void *)optval;
3273 #endif /* LWIP_MPU_COMPATIBLE */
3274   LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).err = 0;
3275 #if LWIP_NETCONN_SEM_PER_THREAD
3276   LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).completed_sem = LWIP_NETCONN_THREAD_SEM_GET();
3277 #else
3278   LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).completed_sem = &sock->conn->op_completed;
3279 #endif
3280   cberr = tcpip_callback(lwip_setsockopt_callback, &LWIP_SETGETSOCKOPT_DATA_VAR_REF(data));
3281   if (cberr != ERR_OK) {
3282     LWIP_SETGETSOCKOPT_DATA_VAR_FREE(data);
3283     sock_set_errno(sock, err_to_errno(cberr));
3284     done_socket(sock);
3285     return -1;
3286   }
3287   sys_arch_sem_wait((sys_sem_t *)(LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).completed_sem), 0);
3288 
3289   /* maybe lwip_getsockopt_internal has changed err */
3290   err = LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).err;
3291   LWIP_SETGETSOCKOPT_DATA_VAR_FREE(data);
3292 #endif  /* LWIP_TCPIP_CORE_LOCKING */
3293 
3294   sock_set_errno(sock, err);
3295   done_socket(sock);
3296   return err ? -1 : 0;
3297 }
3298 
3299 #if !LWIP_TCPIP_CORE_LOCKING
3300 /** lwip_setsockopt_callback: only used without CORE_LOCKING
3301  * to get into the tcpip_thread
3302  */
3303 static void
lwip_setsockopt_callback(void * arg)3304 lwip_setsockopt_callback(void *arg)
3305 {
3306   struct lwip_setgetsockopt_data *data;
3307   LWIP_ASSERT("arg != NULL", arg != NULL);
3308   data = (struct lwip_setgetsockopt_data *)arg;
3309 
3310   data->err = lwip_setsockopt_impl(data->s, data->level, data->optname,
3311 #if LWIP_MPU_COMPATIBLE
3312                                    data->optval,
3313 #else /* LWIP_MPU_COMPATIBLE */
3314                                    data->optval.pc,
3315 #endif /* LWIP_MPU_COMPATIBLE */
3316                                    data->optlen);
3317 
3318   sys_sem_signal((sys_sem_t *)(data->completed_sem));
3319 }
3320 #endif  /* LWIP_TCPIP_CORE_LOCKING */
3321 
3322 /** lwip_setsockopt_impl: the actual implementation of setsockopt:
3323  * same argument as lwip_setsockopt, either called directly or through callback
3324  */
3325 static int
lwip_setsockopt_impl(int s,int level,int optname,const void * optval,socklen_t optlen)3326 lwip_setsockopt_impl(int s, int level, int optname, const void *optval, socklen_t optlen)
3327 {
3328   int err = 0;
3329   struct lwip_sock *sock = tryget_socket(s);
3330   if (!sock) {
3331     return EBADF;
3332   }
3333 
3334 #ifdef LWIP_HOOK_SOCKETS_SETSOCKOPT
3335   if (LWIP_HOOK_SOCKETS_SETSOCKOPT(s, sock, level, optname, optval, optlen, &err)) {
3336     return err;
3337   }
3338 #endif
3339 
3340   switch (level) {
3341 
3342     /* Level: SOL_SOCKET */
3343     case SOL_SOCKET:
3344       switch (optname) {
3345 
3346         /* SO_ACCEPTCONN is get-only */
3347 
3348         /* The option flags */
3349         case SO_BROADCAST:
3350         case SO_KEEPALIVE:
3351 #if SO_REUSE
3352         case SO_REUSEADDR:
3353 #endif /* SO_REUSE */
3354           if ((optname == SO_BROADCAST) &&
3355               (NETCONNTYPE_GROUP(sock->conn->type) != NETCONN_UDP)) {
3356             done_socket(sock);
3357             return ENOPROTOOPT;
3358           }
3359 
3360           optname = lwip_sockopt_to_ipopt(optname);
3361 
3362           LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, optlen, int);
3363           if (*(const int *)optval) {
3364             ip_set_option(sock->conn->pcb.ip, optname);
3365           } else {
3366             ip_reset_option(sock->conn->pcb.ip, optname);
3367           }
3368           LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, SOL_SOCKET, optname=0x%x, ..) -> %s\n",
3369                                       s, optname, (*(const int *)optval ? "on" : "off")));
3370           break;
3371 
3372           /* SO_TYPE is get-only */
3373           /* SO_ERROR is get-only */
3374 
3375 #if LWIP_SO_SNDTIMEO
3376         case SO_SNDTIMEO: {
3377           long ms_long;
3378           LWIP_SOCKOPT_CHECK_OPTLEN_CONN(sock, optlen, LWIP_SO_SNDRCVTIMEO_OPTTYPE);
3379           ms_long = LWIP_SO_SNDRCVTIMEO_GET_MS(optval);
3380           if (ms_long < 0) {
3381             done_socket(sock);
3382             return EINVAL;
3383           }
3384           netconn_set_sendtimeout(sock->conn, ms_long);
3385           break;
3386         }
3387 #endif /* LWIP_SO_SNDTIMEO */
3388 #if LWIP_SO_RCVTIMEO
3389         case SO_RCVTIMEO: {
3390           long ms_long;
3391           LWIP_SOCKOPT_CHECK_OPTLEN_CONN(sock, optlen, LWIP_SO_SNDRCVTIMEO_OPTTYPE);
3392           ms_long = LWIP_SO_SNDRCVTIMEO_GET_MS(optval);
3393           if (ms_long < 0) {
3394             done_socket(sock);
3395             return EINVAL;
3396           }
3397           netconn_set_recvtimeout(sock->conn, (u32_t)ms_long);
3398           break;
3399         }
3400 #endif /* LWIP_SO_RCVTIMEO */
3401 #if LWIP_SO_RCVBUF
3402         case SO_RCVBUF:
3403           LWIP_SOCKOPT_CHECK_OPTLEN_CONN(sock, optlen, int);
3404           netconn_set_recvbufsize(sock->conn, *(const int *)optval);
3405           break;
3406 #endif /* LWIP_SO_RCVBUF */
3407 #if LWIP_SO_LINGER
3408         case SO_LINGER: {
3409           const struct linger *linger = (const struct linger *)optval;
3410           LWIP_SOCKOPT_CHECK_OPTLEN_CONN(sock, optlen, struct linger);
3411           if (linger->l_onoff) {
3412             int lingersec = linger->l_linger;
3413             if (lingersec < 0) {
3414               done_socket(sock);
3415               return EINVAL;
3416             }
3417             if (lingersec > 0xFFFF) {
3418               lingersec = 0xFFFF;
3419             }
3420             sock->conn->linger = (s16_t)lingersec;
3421           } else {
3422             sock->conn->linger = -1;
3423           }
3424         }
3425         break;
3426 #endif /* LWIP_SO_LINGER */
3427 #if LWIP_UDP
3428         case SO_NO_CHECK:
3429           LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, int, NETCONN_UDP);
3430 #if LWIP_UDPLITE
3431           if (udp_is_flag_set(sock->conn->pcb.udp, UDP_FLAGS_UDPLITE)) {
3432             /* this flag is only available for UDP, not for UDP lite */
3433             done_socket(sock);
3434             return EAFNOSUPPORT;
3435           }
3436 #endif /* LWIP_UDPLITE */
3437           if (*(const int *)optval) {
3438             udp_set_flags(sock->conn->pcb.udp, UDP_FLAGS_NOCHKSUM);
3439           } else {
3440             udp_clear_flags(sock->conn->pcb.udp, UDP_FLAGS_NOCHKSUM);
3441           }
3442           break;
3443 #endif /* LWIP_UDP */
3444         case SO_BINDTODEVICE: {
3445           const struct ifreq *iface;
3446           struct netif *n = NULL;
3447 
3448           LWIP_SOCKOPT_CHECK_OPTLEN_CONN(sock, optlen, struct ifreq);
3449 
3450           iface = (const struct ifreq *)optval;
3451           if (iface->ifr_name[0] != 0) {
3452             n = netif_find(iface->ifr_name);
3453             if (n == NULL) {
3454               done_socket(sock);
3455               return ENODEV;
3456             }
3457           }
3458 
3459           switch (NETCONNTYPE_GROUP(netconn_type(sock->conn))) {
3460 #if LWIP_TCP
3461             case NETCONN_TCP:
3462               tcp_bind_netif(sock->conn->pcb.tcp, n);
3463               break;
3464 #endif
3465 #if LWIP_UDP
3466             case NETCONN_UDP:
3467               udp_bind_netif(sock->conn->pcb.udp, n);
3468               break;
3469 #endif
3470 #if LWIP_RAW
3471             case NETCONN_RAW:
3472               raw_bind_netif(sock->conn->pcb.raw, n);
3473               break;
3474 #endif
3475             default:
3476               LWIP_ASSERT("Unhandled netconn type in SO_BINDTODEVICE", 0);
3477               break;
3478           }
3479         }
3480         break;
3481         default:
3482           LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, SOL_SOCKET, UNIMPL: optname=0x%x, ..)\n",
3483                                       s, optname));
3484           err = ENOPROTOOPT;
3485           break;
3486       }  /* switch (optname) */
3487       break;
3488 
3489     /* Level: IPPROTO_IP */
3490     case IPPROTO_IP:
3491       switch (optname) {
3492         case IP_TTL:
3493           LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, optlen, int);
3494           sock->conn->pcb.ip->ttl = (u8_t)(*(const int *)optval);
3495           LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_IP, IP_TTL, ..) -> %d\n",
3496                                       s, sock->conn->pcb.ip->ttl));
3497           break;
3498         case IP_TOS:
3499           LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, optlen, int);
3500           sock->conn->pcb.ip->tos = (u8_t)(*(const int *)optval);
3501           LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_IP, IP_TOS, ..)-> %d\n",
3502                                       s, sock->conn->pcb.ip->tos));
3503           break;
3504 #if LWIP_NETBUF_RECVINFO
3505         case IP_PKTINFO:
3506           LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, int, NETCONN_UDP);
3507           if (*(const int *)optval) {
3508             sock->conn->flags |= NETCONN_FLAG_PKTINFO;
3509           } else {
3510             sock->conn->flags &= ~NETCONN_FLAG_PKTINFO;
3511           }
3512           break;
3513 #endif /* LWIP_NETBUF_RECVINFO */
3514 #if LWIP_IPV4 && LWIP_MULTICAST_TX_OPTIONS && LWIP_UDP
3515         case IP_MULTICAST_TTL:
3516           LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, u8_t, NETCONN_UDP);
3517           udp_set_multicast_ttl(sock->conn->pcb.udp, (u8_t)(*(const u8_t *)optval));
3518           break;
3519         case IP_MULTICAST_IF: {
3520           ip4_addr_t if_addr;
3521           LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, struct in_addr, NETCONN_UDP);
3522           inet_addr_to_ip4addr(&if_addr, (const struct in_addr *)optval);
3523           udp_set_multicast_netif_addr(sock->conn->pcb.udp, &if_addr);
3524         }
3525         break;
3526         case IP_MULTICAST_LOOP:
3527           LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, u8_t, NETCONN_UDP);
3528           if (*(const u8_t *)optval) {
3529             udp_set_flags(sock->conn->pcb.udp, UDP_FLAGS_MULTICAST_LOOP);
3530           } else {
3531             udp_clear_flags(sock->conn->pcb.udp, UDP_FLAGS_MULTICAST_LOOP);
3532           }
3533           break;
3534 #endif /* LWIP_IPV4 && LWIP_MULTICAST_TX_OPTIONS && LWIP_UDP */
3535 #if LWIP_IGMP
3536         case IP_ADD_MEMBERSHIP:
3537         case IP_DROP_MEMBERSHIP: {
3538           /* If this is a TCP or a RAW socket, ignore these options. */
3539           err_t igmp_err;
3540           const struct ip_mreq *imr = (const struct ip_mreq *)optval;
3541           ip4_addr_t if_addr;
3542           ip4_addr_t multi_addr;
3543           LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, struct ip_mreq, NETCONN_UDP);
3544           inet_addr_to_ip4addr(&if_addr, &imr->imr_interface);
3545           inet_addr_to_ip4addr(&multi_addr, &imr->imr_multiaddr);
3546           if (optname == IP_ADD_MEMBERSHIP) {
3547             if (!lwip_socket_register_membership(s, &if_addr, &multi_addr)) {
3548               /* cannot track membership (out of memory) */
3549               err = ENOMEM;
3550               igmp_err = ERR_OK;
3551             } else {
3552               igmp_err = igmp_joingroup(&if_addr, &multi_addr);
3553             }
3554           } else {
3555             igmp_err = igmp_leavegroup(&if_addr, &multi_addr);
3556             lwip_socket_unregister_membership(s, &if_addr, &multi_addr);
3557           }
3558           if (igmp_err != ERR_OK) {
3559             err = EADDRNOTAVAIL;
3560           }
3561         }
3562         break;
3563 #endif /* LWIP_IGMP */
3564         default:
3565           LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_IP, UNIMPL: optname=0x%x, ..)\n",
3566                                       s, optname));
3567           err = ENOPROTOOPT;
3568           break;
3569       }  /* switch (optname) */
3570       break;
3571 
3572 #if LWIP_TCP
3573     /* Level: IPPROTO_TCP */
3574     case IPPROTO_TCP:
3575       /* Special case: all IPPROTO_TCP option take an int */
3576       LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, int, NETCONN_TCP);
3577       if (sock->conn->pcb.tcp->state == LISTEN) {
3578         done_socket(sock);
3579         return EINVAL;
3580       }
3581       switch (optname) {
3582         case TCP_NODELAY:
3583           if (*(const int *)optval) {
3584             tcp_nagle_disable(sock->conn->pcb.tcp);
3585           } else {
3586             tcp_nagle_enable(sock->conn->pcb.tcp);
3587           }
3588           LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_TCP, TCP_NODELAY) -> %s\n",
3589                                       s, (*(const int *)optval) ? "on" : "off") );
3590           break;
3591         case TCP_KEEPALIVE:
3592           sock->conn->pcb.tcp->keep_idle = (u32_t)(*(const int *)optval);
3593           LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_TCP, TCP_KEEPALIVE) -> %"U32_F"\n",
3594                                       s, sock->conn->pcb.tcp->keep_idle));
3595           break;
3596 
3597 #if LWIP_TCP_KEEPALIVE
3598         case TCP_KEEPIDLE:
3599           sock->conn->pcb.tcp->keep_idle = 1000 * (u32_t)(*(const int *)optval);
3600           LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_TCP, TCP_KEEPIDLE) -> %"U32_F"\n",
3601                                       s, sock->conn->pcb.tcp->keep_idle));
3602           break;
3603         case TCP_KEEPINTVL:
3604           sock->conn->pcb.tcp->keep_intvl = 1000 * (u32_t)(*(const int *)optval);
3605           LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_TCP, TCP_KEEPINTVL) -> %"U32_F"\n",
3606                                       s, sock->conn->pcb.tcp->keep_intvl));
3607           break;
3608         case TCP_KEEPCNT:
3609           sock->conn->pcb.tcp->keep_cnt = (u32_t)(*(const int *)optval);
3610           LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_TCP, TCP_KEEPCNT) -> %"U32_F"\n",
3611                                       s, sock->conn->pcb.tcp->keep_cnt));
3612           break;
3613 #endif /* LWIP_TCP_KEEPALIVE */
3614         default:
3615           LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_TCP, UNIMPL: optname=0x%x, ..)\n",
3616                                       s, optname));
3617           err = ENOPROTOOPT;
3618           break;
3619       }  /* switch (optname) */
3620       break;
3621 #endif /* LWIP_TCP*/
3622 
3623 #if LWIP_IPV6
3624     /* Level: IPPROTO_IPV6 */
3625     case IPPROTO_IPV6:
3626       switch (optname) {
3627         case IPV6_V6ONLY:
3628           LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, optlen, int);
3629           if (*(const int *)optval) {
3630             netconn_set_ipv6only(sock->conn, 1);
3631           } else {
3632             netconn_set_ipv6only(sock->conn, 0);
3633           }
3634           LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_IPV6, IPV6_V6ONLY, ..) -> %d\n",
3635                                       s, (netconn_get_ipv6only(sock->conn) ? 1 : 0)));
3636           break;
3637 #if LWIP_IPV6_MLD
3638         case IPV6_JOIN_GROUP:
3639         case IPV6_LEAVE_GROUP: {
3640           /* If this is a TCP or a RAW socket, ignore these options. */
3641           err_t mld6_err;
3642           struct netif *netif;
3643           ip6_addr_t multi_addr;
3644           const struct ipv6_mreq *imr = (const struct ipv6_mreq *)optval;
3645           LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, struct ipv6_mreq, NETCONN_UDP);
3646           inet6_addr_to_ip6addr(&multi_addr, &imr->ipv6mr_multiaddr);
3647           LWIP_ASSERT("Invalid netif index", imr->ipv6mr_interface <= 0xFFu);
3648           netif = netif_get_by_index((u8_t)imr->ipv6mr_interface);
3649           if (netif == NULL) {
3650             err = EADDRNOTAVAIL;
3651             break;
3652           }
3653 
3654           if (optname == IPV6_JOIN_GROUP) {
3655             if (!lwip_socket_register_mld6_membership(s, imr->ipv6mr_interface, &multi_addr)) {
3656               /* cannot track membership (out of memory) */
3657               err = ENOMEM;
3658               mld6_err = ERR_OK;
3659             } else {
3660               mld6_err = mld6_joingroup_netif(netif, &multi_addr);
3661             }
3662           } else {
3663             mld6_err = mld6_leavegroup_netif(netif, &multi_addr);
3664             lwip_socket_unregister_mld6_membership(s, imr->ipv6mr_interface, &multi_addr);
3665           }
3666           if (mld6_err != ERR_OK) {
3667             err = EADDRNOTAVAIL;
3668           }
3669         }
3670         break;
3671 #endif /* LWIP_IPV6_MLD */
3672         default:
3673           LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_IPV6, UNIMPL: optname=0x%x, ..)\n",
3674                                       s, optname));
3675           err = ENOPROTOOPT;
3676           break;
3677       }  /* switch (optname) */
3678       break;
3679 #endif /* LWIP_IPV6 */
3680 
3681 #if LWIP_UDP && LWIP_UDPLITE
3682     /* Level: IPPROTO_UDPLITE */
3683     case IPPROTO_UDPLITE:
3684       /* Special case: all IPPROTO_UDPLITE option take an int */
3685       LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, optlen, int);
3686       /* If this is no UDP lite socket, ignore any options. */
3687       if (!NETCONNTYPE_ISUDPLITE(netconn_type(sock->conn))) {
3688         done_socket(sock);
3689         return ENOPROTOOPT;
3690       }
3691       switch (optname) {
3692         case UDPLITE_SEND_CSCOV:
3693           if ((*(const int *)optval != 0) && ((*(const int *)optval < 8) || (*(const int *)optval > 0xffff))) {
3694             /* don't allow illegal values! */
3695             sock->conn->pcb.udp->chksum_len_tx = 8;
3696           } else {
3697             sock->conn->pcb.udp->chksum_len_tx = (u16_t) * (const int *)optval;
3698           }
3699           LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_UDPLITE, UDPLITE_SEND_CSCOV) -> %d\n",
3700                                       s, (*(const int *)optval)) );
3701           break;
3702         case UDPLITE_RECV_CSCOV:
3703           if ((*(const int *)optval != 0) && ((*(const int *)optval < 8) || (*(const int *)optval > 0xffff))) {
3704             /* don't allow illegal values! */
3705             sock->conn->pcb.udp->chksum_len_rx = 8;
3706           } else {
3707             sock->conn->pcb.udp->chksum_len_rx = (u16_t) * (const int *)optval;
3708           }
3709           LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_UDPLITE, UDPLITE_RECV_CSCOV) -> %d\n",
3710                                       s, (*(const int *)optval)) );
3711           break;
3712         default:
3713           LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_UDPLITE, UNIMPL: optname=0x%x, ..)\n",
3714                                       s, optname));
3715           err = ENOPROTOOPT;
3716           break;
3717       }  /* switch (optname) */
3718       break;
3719 #endif /* LWIP_UDP */
3720     /* Level: IPPROTO_RAW */
3721     case IPPROTO_RAW:
3722       switch (optname) {
3723 #if LWIP_IPV6 && LWIP_RAW
3724         case IPV6_CHECKSUM:
3725           /* It should not be possible to disable the checksum generation with ICMPv6
3726            * as per RFC 3542 chapter 3.1 */
3727           if (sock->conn->pcb.raw->protocol == IPPROTO_ICMPV6) {
3728             done_socket(sock);
3729             return EINVAL;
3730           }
3731 
3732           LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, int, NETCONN_RAW);
3733           if (*(const int *)optval < 0) {
3734             sock->conn->pcb.raw->chksum_reqd = 0;
3735           } else if (*(const int *)optval & 1) {
3736             /* Per RFC3542, odd offsets are not allowed */
3737             done_socket(sock);
3738             return EINVAL;
3739           } else {
3740             sock->conn->pcb.raw->chksum_reqd = 1;
3741             sock->conn->pcb.raw->chksum_offset = (u16_t) * (const int *)optval;
3742           }
3743           LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_RAW, IPV6_CHECKSUM, ..) -> %d\n",
3744                                       s, sock->conn->pcb.raw->chksum_reqd));
3745           break;
3746 #endif /* LWIP_IPV6 && LWIP_RAW */
3747         default:
3748           LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_RAW, UNIMPL: optname=0x%x, ..)\n",
3749                                       s, optname));
3750           err = ENOPROTOOPT;
3751           break;
3752       }  /* switch (optname) */
3753       break;
3754     default:
3755       LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, level=0x%x, UNIMPL: optname=0x%x, ..)\n",
3756                                   s, level, optname));
3757       err = ENOPROTOOPT;
3758       break;
3759   }  /* switch (level) */
3760 
3761   done_socket(sock);
3762   return err;
3763 }
3764 
3765 int
lwip_ioctl(int s,long cmd,void * argp)3766 lwip_ioctl(int s, long cmd, void *argp)
3767 {
3768   struct lwip_sock *sock = get_socket(s);
3769   u8_t val;
3770 #if LWIP_SO_RCVBUF
3771   int recv_avail;
3772 #endif /* LWIP_SO_RCVBUF */
3773 
3774   if (!sock) {
3775     return -1;
3776   }
3777 
3778   switch (cmd) {
3779 #if LWIP_SO_RCVBUF || LWIP_FIONREAD_LINUXMODE
3780     case FIONREAD:
3781       if (!argp) {
3782         sock_set_errno(sock, EINVAL);
3783         done_socket(sock);
3784         return -1;
3785       }
3786 #if LWIP_FIONREAD_LINUXMODE
3787       if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_TCP) {
3788         struct netbuf *nb;
3789         if (sock->lastdata.netbuf) {
3790           nb = sock->lastdata.netbuf;
3791           *((int *)argp) = nb->p->tot_len;
3792         } else {
3793           struct netbuf *rxbuf;
3794           err_t err = netconn_recv_udp_raw_netbuf_flags(sock->conn, &rxbuf, NETCONN_DONTBLOCK);
3795           if (err != ERR_OK) {
3796             *((int *)argp) = 0;
3797           } else {
3798             sock->lastdata.netbuf = rxbuf;
3799             *((int *)argp) = rxbuf->p->tot_len;
3800           }
3801         }
3802         done_socket(sock);
3803         return 0;
3804       }
3805 #endif /* LWIP_FIONREAD_LINUXMODE */
3806 
3807 #if LWIP_SO_RCVBUF
3808       /* we come here if either LWIP_FIONREAD_LINUXMODE==0 or this is a TCP socket */
3809       SYS_ARCH_GET(sock->conn->recv_avail, recv_avail);
3810       if (recv_avail < 0) {
3811         recv_avail = 0;
3812       }
3813 
3814       /* Check if there is data left from the last recv operation. /maq 041215 */
3815       if (sock->lastdata.netbuf) {
3816         if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_TCP) {
3817           recv_avail += sock->lastdata.pbuf->tot_len;
3818         } else {
3819           recv_avail += sock->lastdata.netbuf->p->tot_len;
3820         }
3821       }
3822       *((int *)argp) = recv_avail;
3823 
3824       LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_ioctl(%d, FIONREAD, %p) = %"U16_F"\n", s, argp, *((u16_t *)argp)));
3825       sock_set_errno(sock, 0);
3826       done_socket(sock);
3827       return 0;
3828 #else /* LWIP_SO_RCVBUF */
3829       break;
3830 #endif /* LWIP_SO_RCVBUF */
3831 #endif /* LWIP_SO_RCVBUF || LWIP_FIONREAD_LINUXMODE */
3832 
3833     case (long)FIONBIO:
3834       val = 0;
3835       if (argp && *(int *)argp) {
3836         val = 1;
3837       }
3838       netconn_set_nonblocking(sock->conn, val);
3839       LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_ioctl(%d, FIONBIO, %d)\n", s, val));
3840       sock_set_errno(sock, 0);
3841       done_socket(sock);
3842       return 0;
3843 
3844     default:
3845       break;
3846   } /* switch (cmd) */
3847   LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_ioctl(%d, UNIMPL: 0x%lx, %p)\n", s, cmd, argp));
3848   sock_set_errno(sock, ENOSYS); /* not yet implemented */
3849   done_socket(sock);
3850   return -1;
3851 }
3852 
3853 /** A minimal implementation of fcntl.
3854  * Currently only the commands F_GETFL and F_SETFL are implemented.
3855  * The flag O_NONBLOCK and access modes are supported for F_GETFL, only
3856  * the flag O_NONBLOCK is implemented for F_SETFL.
3857  */
3858 int
lwip_fcntl(int s,int cmd,int val)3859 lwip_fcntl(int s, int cmd, int val)
3860 {
3861   struct lwip_sock *sock = get_socket(s);
3862   int ret = -1;
3863   int op_mode = 0;
3864 
3865   if (!sock) {
3866     return -1;
3867   }
3868 
3869   switch (cmd) {
3870     case F_GETFL:
3871       ret = netconn_is_nonblocking(sock->conn) ? O_NONBLOCK : 0;
3872       sock_set_errno(sock, 0);
3873 
3874       if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_TCP) {
3875 #if LWIP_TCPIP_CORE_LOCKING
3876         LOCK_TCPIP_CORE();
3877 #else
3878         SYS_ARCH_DECL_PROTECT(lev);
3879         /* the proper thing to do here would be to get into the tcpip_thread,
3880            but locking should be OK as well since we only *read* some flags */
3881         SYS_ARCH_PROTECT(lev);
3882 #endif
3883 #if LWIP_TCP
3884         if (sock->conn->pcb.tcp) {
3885           if (!(sock->conn->pcb.tcp->flags & TF_RXCLOSED)) {
3886             op_mode |= O_RDONLY;
3887           }
3888           if (!(sock->conn->pcb.tcp->flags & TF_FIN)) {
3889             op_mode |= O_WRONLY;
3890           }
3891         }
3892 #endif
3893 #if LWIP_TCPIP_CORE_LOCKING
3894         UNLOCK_TCPIP_CORE();
3895 #else
3896         SYS_ARCH_UNPROTECT(lev);
3897 #endif
3898       } else {
3899         op_mode |= O_RDWR;
3900       }
3901 
3902       /* ensure O_RDWR for (O_RDONLY|O_WRONLY) != O_RDWR cases */
3903       ret |= (op_mode == (O_RDONLY | O_WRONLY)) ? O_RDWR : op_mode;
3904 
3905       break;
3906     case F_SETFL:
3907       /* Bits corresponding to the file access mode and the file creation flags [..] that are set in arg shall be ignored */
3908       val &= ~(O_RDONLY | O_WRONLY | O_RDWR);
3909       if ((val & ~O_NONBLOCK) == 0) {
3910         /* only O_NONBLOCK, all other bits are zero */
3911         netconn_set_nonblocking(sock->conn, val & O_NONBLOCK);
3912         ret = 0;
3913         sock_set_errno(sock, 0);
3914       } else {
3915         sock_set_errno(sock, ENOSYS); /* not yet implemented */
3916       }
3917       break;
3918     default:
3919       LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_fcntl(%d, UNIMPL: %d, %d)\n", s, cmd, val));
3920       sock_set_errno(sock, ENOSYS); /* not yet implemented */
3921       break;
3922   }
3923   done_socket(sock);
3924   return ret;
3925 }
3926 
3927 #if LWIP_COMPAT_SOCKETS == 2 && LWIP_POSIX_SOCKETS_IO_NAMES
3928 int
fcntl(int s,int cmd,...)3929 fcntl(int s, int cmd, ...)
3930 {
3931   va_list ap;
3932   int val;
3933 
3934   va_start(ap, cmd);
3935   val = va_arg(ap, int);
3936   va_end(ap);
3937   return lwip_fcntl(s, cmd, val);
3938 }
3939 #endif
3940 
3941 const char *
lwip_inet_ntop(int af,const void * src,char * dst,socklen_t size)3942 lwip_inet_ntop(int af, const void *src, char *dst, socklen_t size)
3943 {
3944   const char *ret = NULL;
3945   int size_int = (int)size;
3946   if (size_int < 0) {
3947     set_errno(ENOSPC);
3948     return NULL;
3949   }
3950   switch (af) {
3951 #if LWIP_IPV4
3952     case AF_INET:
3953       ret = ip4addr_ntoa_r((const ip4_addr_t *)src, dst, size_int);
3954       if (ret == NULL) {
3955         set_errno(ENOSPC);
3956       }
3957       break;
3958 #endif
3959 #if LWIP_IPV6
3960     case AF_INET6:
3961       ret = ip6addr_ntoa_r((const ip6_addr_t *)src, dst, size_int);
3962       if (ret == NULL) {
3963         set_errno(ENOSPC);
3964       }
3965       break;
3966 #endif
3967     default:
3968       set_errno(EAFNOSUPPORT);
3969       break;
3970   }
3971   return ret;
3972 }
3973 
3974 int
lwip_inet_pton(int af,const char * src,void * dst)3975 lwip_inet_pton(int af, const char *src, void *dst)
3976 {
3977   int err;
3978   switch (af) {
3979 #if LWIP_IPV4
3980     case AF_INET:
3981       err = ip4addr_aton(src, (ip4_addr_t *)dst);
3982       break;
3983 #endif
3984 #if LWIP_IPV6
3985     case AF_INET6: {
3986       /* convert into temporary variable since ip6_addr_t might be larger
3987          than in6_addr when scopes are enabled */
3988       ip6_addr_t addr;
3989       err = ip6addr_aton(src, &addr);
3990       if (err) {
3991         memcpy(dst, &addr.addr, sizeof(addr.addr));
3992       }
3993       break;
3994     }
3995 #endif
3996     default:
3997       err = -1;
3998       set_errno(EAFNOSUPPORT);
3999       break;
4000   }
4001   return err;
4002 }
4003 
4004 #if LWIP_IGMP
4005 /** Register a new IGMP membership. On socket close, the membership is dropped automatically.
4006  *
4007  * ATTENTION: this function is called from tcpip_thread (or under CORE_LOCK).
4008  *
4009  * @return 1 on success, 0 on failure
4010  */
4011 static int
lwip_socket_register_membership(int s,const ip4_addr_t * if_addr,const ip4_addr_t * multi_addr)4012 lwip_socket_register_membership(int s, const ip4_addr_t *if_addr, const ip4_addr_t *multi_addr)
4013 {
4014   struct lwip_sock *sock = get_socket(s);
4015   int i;
4016 
4017   if (!sock) {
4018     return 0;
4019   }
4020 
4021   for (i = 0; i < LWIP_SOCKET_MAX_MEMBERSHIPS; i++) {
4022     if (socket_ipv4_multicast_memberships[i].sock == NULL) {
4023       socket_ipv4_multicast_memberships[i].sock = sock;
4024       ip4_addr_copy(socket_ipv4_multicast_memberships[i].if_addr, *if_addr);
4025       ip4_addr_copy(socket_ipv4_multicast_memberships[i].multi_addr, *multi_addr);
4026       done_socket(sock);
4027       return 1;
4028     }
4029   }
4030   done_socket(sock);
4031   return 0;
4032 }
4033 
4034 /** Unregister a previously registered membership. This prevents dropping the membership
4035  * on socket close.
4036  *
4037  * ATTENTION: this function is called from tcpip_thread (or under CORE_LOCK).
4038  */
4039 static void
lwip_socket_unregister_membership(int s,const ip4_addr_t * if_addr,const ip4_addr_t * multi_addr)4040 lwip_socket_unregister_membership(int s, const ip4_addr_t *if_addr, const ip4_addr_t *multi_addr)
4041 {
4042   struct lwip_sock *sock = get_socket(s);
4043   int i;
4044 
4045   if (!sock) {
4046     return;
4047   }
4048 
4049   for (i = 0; i < LWIP_SOCKET_MAX_MEMBERSHIPS; i++) {
4050     if ((socket_ipv4_multicast_memberships[i].sock == sock) &&
4051         ip4_addr_cmp(&socket_ipv4_multicast_memberships[i].if_addr, if_addr) &&
4052         ip4_addr_cmp(&socket_ipv4_multicast_memberships[i].multi_addr, multi_addr)) {
4053       socket_ipv4_multicast_memberships[i].sock = NULL;
4054       ip4_addr_set_zero(&socket_ipv4_multicast_memberships[i].if_addr);
4055       ip4_addr_set_zero(&socket_ipv4_multicast_memberships[i].multi_addr);
4056       break;
4057     }
4058   }
4059   done_socket(sock);
4060 }
4061 
4062 /** Drop all memberships of a socket that were not dropped explicitly via setsockopt.
4063  *
4064  * ATTENTION: this function is NOT called from tcpip_thread (or under CORE_LOCK).
4065  */
4066 static void
lwip_socket_drop_registered_memberships(int s)4067 lwip_socket_drop_registered_memberships(int s)
4068 {
4069   struct lwip_sock *sock = get_socket(s);
4070   int i;
4071 
4072   if (!sock) {
4073     return;
4074   }
4075 
4076   for (i = 0; i < LWIP_SOCKET_MAX_MEMBERSHIPS; i++) {
4077     if (socket_ipv4_multicast_memberships[i].sock == sock) {
4078       ip_addr_t multi_addr, if_addr;
4079       ip_addr_copy_from_ip4(multi_addr, socket_ipv4_multicast_memberships[i].multi_addr);
4080       ip_addr_copy_from_ip4(if_addr, socket_ipv4_multicast_memberships[i].if_addr);
4081       socket_ipv4_multicast_memberships[i].sock = NULL;
4082       ip4_addr_set_zero(&socket_ipv4_multicast_memberships[i].if_addr);
4083       ip4_addr_set_zero(&socket_ipv4_multicast_memberships[i].multi_addr);
4084 
4085       netconn_join_leave_group(sock->conn, &multi_addr, &if_addr, NETCONN_LEAVE);
4086     }
4087   }
4088   done_socket(sock);
4089 }
4090 #endif /* LWIP_IGMP */
4091 
4092 #if LWIP_IPV6_MLD
4093 /** Register a new MLD6 membership. On socket close, the membership is dropped automatically.
4094  *
4095  * ATTENTION: this function is called from tcpip_thread (or under CORE_LOCK).
4096  *
4097  * @return 1 on success, 0 on failure
4098  */
4099 static int
lwip_socket_register_mld6_membership(int s,unsigned int if_idx,const ip6_addr_t * multi_addr)4100 lwip_socket_register_mld6_membership(int s, unsigned int if_idx, const ip6_addr_t *multi_addr)
4101 {
4102   struct lwip_sock *sock = get_socket(s);
4103   int i;
4104 
4105   if (!sock) {
4106     return 0;
4107   }
4108 
4109   for (i = 0; i < LWIP_SOCKET_MAX_MEMBERSHIPS; i++) {
4110     if (socket_ipv6_multicast_memberships[i].sock == NULL) {
4111       socket_ipv6_multicast_memberships[i].sock   = sock;
4112       socket_ipv6_multicast_memberships[i].if_idx = (u8_t)if_idx;
4113       ip6_addr_copy(socket_ipv6_multicast_memberships[i].multi_addr, *multi_addr);
4114       done_socket(sock);
4115       return 1;
4116     }
4117   }
4118   done_socket(sock);
4119   return 0;
4120 }
4121 
4122 /** Unregister a previously registered MLD6 membership. This prevents dropping the membership
4123  * on socket close.
4124  *
4125  * ATTENTION: this function is called from tcpip_thread (or under CORE_LOCK).
4126  */
4127 static void
lwip_socket_unregister_mld6_membership(int s,unsigned int if_idx,const ip6_addr_t * multi_addr)4128 lwip_socket_unregister_mld6_membership(int s, unsigned int if_idx, const ip6_addr_t *multi_addr)
4129 {
4130   struct lwip_sock *sock = get_socket(s);
4131   int i;
4132 
4133   if (!sock) {
4134     return;
4135   }
4136 
4137   for (i = 0; i < LWIP_SOCKET_MAX_MEMBERSHIPS; i++) {
4138     if ((socket_ipv6_multicast_memberships[i].sock   == sock) &&
4139         (socket_ipv6_multicast_memberships[i].if_idx == if_idx) &&
4140         ip6_addr_cmp(&socket_ipv6_multicast_memberships[i].multi_addr, multi_addr)) {
4141       socket_ipv6_multicast_memberships[i].sock   = NULL;
4142       socket_ipv6_multicast_memberships[i].if_idx = NETIF_NO_INDEX;
4143       ip6_addr_set_zero(&socket_ipv6_multicast_memberships[i].multi_addr);
4144       break;
4145     }
4146   }
4147   done_socket(sock);
4148 }
4149 
4150 /** Drop all MLD6 memberships of a socket that were not dropped explicitly via setsockopt.
4151  *
4152  * ATTENTION: this function is NOT called from tcpip_thread (or under CORE_LOCK).
4153  */
4154 static void
lwip_socket_drop_registered_mld6_memberships(int s)4155 lwip_socket_drop_registered_mld6_memberships(int s)
4156 {
4157   struct lwip_sock *sock = get_socket(s);
4158   int i;
4159 
4160   if (!sock) {
4161     return;
4162   }
4163 
4164   for (i = 0; i < LWIP_SOCKET_MAX_MEMBERSHIPS; i++) {
4165     if (socket_ipv6_multicast_memberships[i].sock == sock) {
4166       ip_addr_t multi_addr;
4167       u8_t if_idx;
4168 
4169       ip_addr_copy_from_ip6(multi_addr, socket_ipv6_multicast_memberships[i].multi_addr);
4170       if_idx = socket_ipv6_multicast_memberships[i].if_idx;
4171 
4172       socket_ipv6_multicast_memberships[i].sock   = NULL;
4173       socket_ipv6_multicast_memberships[i].if_idx = NETIF_NO_INDEX;
4174       ip6_addr_set_zero(&socket_ipv6_multicast_memberships[i].multi_addr);
4175 
4176       netconn_join_leave_group_netif(sock->conn, &multi_addr, if_idx, NETCONN_LEAVE);
4177     }
4178   }
4179   done_socket(sock);
4180 }
4181 #endif /* LWIP_IPV6_MLD */
4182 
4183 #endif /* LWIP_SOCKET */
4184