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