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