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