xref: /minix3/minix/lib/liblwip/dist/src/api/api_lib.c (revision 9f81acbc21316bcbade46d78b67a5f2e58d11bb9)
1 /**
2  * @file
3  * Sequential API External module
4  *
5  * @defgroup netconn Netconn API
6  * @ingroup sequential_api
7  * Thread-safe, to be called from non-TCPIP threads only.
8  * TX/RX handling based on @ref netbuf (containing @ref pbuf)
9  * to avoid copying data around.
10  *
11  * @defgroup netconn_common Common functions
12  * @ingroup netconn
13  * For use with TCP and UDP
14  *
15  * @defgroup netconn_tcp TCP only
16  * @ingroup netconn
17  * TCP only functions
18  *
19  * @defgroup netconn_udp UDP only
20  * @ingroup netconn
21  * UDP only functions
22  */
23 
24 /*
25  * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
26  * All rights reserved.
27  *
28  * Redistribution and use in source and binary forms, with or without modification,
29  * are permitted provided that the following conditions are met:
30  *
31  * 1. Redistributions of source code must retain the above copyright notice,
32  *    this list of conditions and the following disclaimer.
33  * 2. Redistributions in binary form must reproduce the above copyright notice,
34  *    this list of conditions and the following disclaimer in the documentation
35  *    and/or other materials provided with the distribution.
36  * 3. The name of the author may not be used to endorse or promote products
37  *    derived from this software without specific prior written permission.
38  *
39  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
40  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
41  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
42  * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
43  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
44  * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
45  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
46  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
47  * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
48  * OF SUCH DAMAGE.
49  *
50  * This file is part of the lwIP TCP/IP stack.
51  *
52  * Author: Adam Dunkels <adam@sics.se>
53  */
54 
55 /* This is the part of the API that is linked with
56    the application */
57 
58 #include "lwip/opt.h"
59 
60 #if LWIP_NETCONN /* don't build if not configured for use in lwipopts.h */
61 
62 #include "lwip/api.h"
63 #include "lwip/memp.h"
64 
65 #include "lwip/ip.h"
66 #include "lwip/raw.h"
67 #include "lwip/udp.h"
68 #include "lwip/priv/api_msg.h"
69 #include "lwip/priv/tcp_priv.h"
70 #include "lwip/priv/tcpip_priv.h"
71 
72 #include <string.h>
73 
74 #define API_MSG_VAR_REF(name)               API_VAR_REF(name)
75 #define API_MSG_VAR_DECLARE(name)           API_VAR_DECLARE(struct api_msg, name)
76 #define API_MSG_VAR_ALLOC(name)             API_VAR_ALLOC(struct api_msg, MEMP_API_MSG, name, ERR_MEM)
77 #define API_MSG_VAR_ALLOC_RETURN_NULL(name) API_VAR_ALLOC(struct api_msg, MEMP_API_MSG, name, NULL)
78 #define API_MSG_VAR_FREE(name)              API_VAR_FREE(MEMP_API_MSG, name)
79 
80 static err_t netconn_close_shutdown(struct netconn *conn, u8_t how);
81 
82 /**
83  * Call the lower part of a netconn_* function
84  * This function is then running in the thread context
85  * of tcpip_thread and has exclusive access to lwIP core code.
86  *
87  * @param fn function to call
88  * @param apimsg a struct containing the function to call and its parameters
89  * @return ERR_OK if the function was called, another err_t if not
90  */
91 static err_t
92 netconn_apimsg(tcpip_callback_fn fn, struct api_msg *apimsg)
93 {
94   err_t err;
95 
96 #ifdef LWIP_DEBUG
97   /* catch functions that don't set err */
98   apimsg->err = ERR_VAL;
99 #endif /* LWIP_DEBUG */
100 
101 #if LWIP_NETCONN_SEM_PER_THREAD
102   apimsg->op_completed_sem = LWIP_NETCONN_THREAD_SEM_GET();
103 #endif /* LWIP_NETCONN_SEM_PER_THREAD */
104 
105   err = tcpip_send_msg_wait_sem(fn, apimsg, LWIP_API_MSG_SEM(apimsg));
106   if (err == ERR_OK) {
107     return apimsg->err;
108   }
109   return err;
110 }
111 
112 /**
113  * Create a new netconn (of a specific type) that has a callback function.
114  * The corresponding pcb is also created.
115  *
116  * @param t the type of 'connection' to create (@see enum netconn_type)
117  * @param proto the IP protocol for RAW IP pcbs
118  * @param callback a function to call on status changes (RX available, TX'ed)
119  * @return a newly allocated struct netconn or
120  *         NULL on memory error
121  */
122 struct netconn*
123 netconn_new_with_proto_and_callback(enum netconn_type t, u8_t proto, netconn_callback callback)
124 {
125   struct netconn *conn;
126   API_MSG_VAR_DECLARE(msg);
127   API_MSG_VAR_ALLOC_RETURN_NULL(msg);
128 
129   conn = netconn_alloc(t, callback);
130   if (conn != NULL) {
131     err_t err;
132 
133     API_MSG_VAR_REF(msg).msg.n.proto = proto;
134     API_MSG_VAR_REF(msg).conn = conn;
135     err = netconn_apimsg(lwip_netconn_do_newconn, &API_MSG_VAR_REF(msg));
136     if (err != ERR_OK) {
137       LWIP_ASSERT("freeing conn without freeing pcb", conn->pcb.tcp == NULL);
138       LWIP_ASSERT("conn has no recvmbox", sys_mbox_valid(&conn->recvmbox));
139 #if LWIP_TCP
140       LWIP_ASSERT("conn->acceptmbox shouldn't exist", !sys_mbox_valid(&conn->acceptmbox));
141 #endif /* LWIP_TCP */
142 #if !LWIP_NETCONN_SEM_PER_THREAD
143       LWIP_ASSERT("conn has no op_completed", sys_sem_valid(&conn->op_completed));
144       sys_sem_free(&conn->op_completed);
145 #endif /* !LWIP_NETCONN_SEM_PER_THREAD */
146       sys_mbox_free(&conn->recvmbox);
147       memp_free(MEMP_NETCONN, conn);
148       API_MSG_VAR_FREE(msg);
149       return NULL;
150     }
151   }
152   API_MSG_VAR_FREE(msg);
153   return conn;
154 }
155 
156 /**
157  * @ingroup netconn_common
158  * Close a netconn 'connection' and free its resources.
159  * UDP and RAW connection are completely closed, TCP pcbs might still be in a waitstate
160  * after this returns.
161  *
162  * @param conn the netconn to delete
163  * @return ERR_OK if the connection was deleted
164  */
165 err_t
166 netconn_delete(struct netconn *conn)
167 {
168   err_t err;
169   API_MSG_VAR_DECLARE(msg);
170 
171   /* No ASSERT here because possible to get a (conn == NULL) if we got an accept error */
172   if (conn == NULL) {
173     return ERR_OK;
174   }
175 
176   API_MSG_VAR_ALLOC(msg);
177   API_MSG_VAR_REF(msg).conn = conn;
178 #if LWIP_SO_SNDTIMEO || LWIP_SO_LINGER
179   /* get the time we started, which is later compared to
180      sys_now() + conn->send_timeout */
181   API_MSG_VAR_REF(msg).msg.sd.time_started = sys_now();
182 #else /* LWIP_SO_SNDTIMEO || LWIP_SO_LINGER */
183 #if LWIP_TCP
184   API_MSG_VAR_REF(msg).msg.sd.polls_left =
185     ((LWIP_TCP_CLOSE_TIMEOUT_MS_DEFAULT + TCP_SLOW_INTERVAL - 1) / TCP_SLOW_INTERVAL) + 1;
186 #endif /* LWIP_TCP */
187 #endif /* LWIP_SO_SNDTIMEO || LWIP_SO_LINGER */
188   err = netconn_apimsg(lwip_netconn_do_delconn, &API_MSG_VAR_REF(msg));
189   API_MSG_VAR_FREE(msg);
190 
191   if (err != ERR_OK) {
192     return err;
193   }
194 
195   netconn_free(conn);
196 
197   return ERR_OK;
198 }
199 
200 /**
201  * Get the local or remote IP address and port of a netconn.
202  * For RAW netconns, this returns the protocol instead of a port!
203  *
204  * @param conn the netconn to query
205  * @param addr a pointer to which to save the IP address
206  * @param port a pointer to which to save the port (or protocol for RAW)
207  * @param local 1 to get the local IP address, 0 to get the remote one
208  * @return ERR_CONN for invalid connections
209  *         ERR_OK if the information was retrieved
210  */
211 err_t
212 netconn_getaddr(struct netconn *conn, ip_addr_t *addr, u16_t *port, u8_t local)
213 {
214   API_MSG_VAR_DECLARE(msg);
215   err_t err;
216 
217   LWIP_ERROR("netconn_getaddr: invalid conn", (conn != NULL), return ERR_ARG;);
218   LWIP_ERROR("netconn_getaddr: invalid addr", (addr != NULL), return ERR_ARG;);
219   LWIP_ERROR("netconn_getaddr: invalid port", (port != NULL), return ERR_ARG;);
220 
221   API_MSG_VAR_ALLOC(msg);
222   API_MSG_VAR_REF(msg).conn = conn;
223   API_MSG_VAR_REF(msg).msg.ad.local = local;
224 #if LWIP_MPU_COMPATIBLE
225   err = netconn_apimsg(lwip_netconn_do_getaddr, &API_MSG_VAR_REF(msg));
226   *addr = msg->msg.ad.ipaddr;
227   *port = msg->msg.ad.port;
228 #else /* LWIP_MPU_COMPATIBLE */
229   msg.msg.ad.ipaddr = addr;
230   msg.msg.ad.port = port;
231   err = netconn_apimsg(lwip_netconn_do_getaddr, &msg);
232 #endif /* LWIP_MPU_COMPATIBLE */
233   API_MSG_VAR_FREE(msg);
234 
235   return err;
236 }
237 
238 /**
239  * @ingroup netconn_common
240  * Bind a netconn to a specific local IP address and port.
241  * Binding one netconn twice might not always be checked correctly!
242  *
243  * @param conn the netconn to bind
244  * @param addr the local IP address to bind the netconn to
245  *             (use IP4_ADDR_ANY/IP6_ADDR_ANY to bind to all addresses)
246  * @param port the local port to bind the netconn to (not used for RAW)
247  * @return ERR_OK if bound, any other err_t on failure
248  */
249 err_t
250 netconn_bind(struct netconn *conn, const ip_addr_t *addr, u16_t port)
251 {
252   API_MSG_VAR_DECLARE(msg);
253   err_t err;
254 
255   LWIP_ERROR("netconn_bind: invalid conn", (conn != NULL), return ERR_ARG;);
256 
257 #if LWIP_IPV4
258   /* Don't propagate NULL pointer (IP_ADDR_ANY alias) to subsequent functions */
259   if (addr == NULL) {
260     addr = IP4_ADDR_ANY;
261   }
262 #endif /* LWIP_IPV4 */
263 
264 #if LWIP_IPV4 && LWIP_IPV6
265   /* "Socket API like" dual-stack support: If IP to bind to is IP6_ADDR_ANY,
266    * and NETCONN_FLAG_IPV6_V6ONLY is 0, use IP_ANY_TYPE to bind
267    */
268   if ((netconn_get_ipv6only(conn) == 0) &&
269      ip_addr_cmp(addr, IP6_ADDR_ANY)) {
270     addr = IP_ANY_TYPE;
271   }
272 #endif /* LWIP_IPV4 && LWIP_IPV6 */
273 
274   API_MSG_VAR_ALLOC(msg);
275   API_MSG_VAR_REF(msg).conn = conn;
276   API_MSG_VAR_REF(msg).msg.bc.ipaddr = API_MSG_VAR_REF(addr);
277   API_MSG_VAR_REF(msg).msg.bc.port = port;
278   err = netconn_apimsg(lwip_netconn_do_bind, &API_MSG_VAR_REF(msg));
279   API_MSG_VAR_FREE(msg);
280 
281   return err;
282 }
283 
284 /**
285  * @ingroup netconn_common
286  * Connect a netconn to a specific remote IP address and port.
287  *
288  * @param conn the netconn to connect
289  * @param addr the remote IP address to connect to
290  * @param port the remote port to connect to (no used for RAW)
291  * @return ERR_OK if connected, return value of tcp_/udp_/raw_connect otherwise
292  */
293 err_t
294 netconn_connect(struct netconn *conn, const ip_addr_t *addr, u16_t port)
295 {
296   API_MSG_VAR_DECLARE(msg);
297   err_t err;
298 
299   LWIP_ERROR("netconn_connect: invalid conn", (conn != NULL), return ERR_ARG;);
300 
301 #if LWIP_IPV4
302   /* Don't propagate NULL pointer (IP_ADDR_ANY alias) to subsequent functions */
303   if (addr == NULL) {
304     addr = IP4_ADDR_ANY;
305   }
306 #endif /* LWIP_IPV4 */
307 
308   API_MSG_VAR_ALLOC(msg);
309   API_MSG_VAR_REF(msg).conn = conn;
310   API_MSG_VAR_REF(msg).msg.bc.ipaddr = API_MSG_VAR_REF(addr);
311   API_MSG_VAR_REF(msg).msg.bc.port = port;
312   err = netconn_apimsg(lwip_netconn_do_connect, &API_MSG_VAR_REF(msg));
313   API_MSG_VAR_FREE(msg);
314 
315   return err;
316 }
317 
318 /**
319  * @ingroup netconn_udp
320  * Disconnect a netconn from its current peer (only valid for UDP netconns).
321  *
322  * @param conn the netconn to disconnect
323  * @return See @ref err_t
324  */
325 err_t
326 netconn_disconnect(struct netconn *conn)
327 {
328   API_MSG_VAR_DECLARE(msg);
329   err_t err;
330 
331   LWIP_ERROR("netconn_disconnect: invalid conn", (conn != NULL), return ERR_ARG;);
332 
333   API_MSG_VAR_ALLOC(msg);
334   API_MSG_VAR_REF(msg).conn = conn;
335   err = netconn_apimsg(lwip_netconn_do_disconnect, &API_MSG_VAR_REF(msg));
336   API_MSG_VAR_FREE(msg);
337 
338   return err;
339 }
340 
341 /**
342  * @ingroup netconn_tcp
343  * Set a TCP netconn into listen mode
344  *
345  * @param conn the tcp netconn to set to listen mode
346  * @param backlog the listen backlog, only used if TCP_LISTEN_BACKLOG==1
347  * @return ERR_OK if the netconn was set to listen (UDP and RAW netconns
348  *         don't return any error (yet?))
349  */
350 err_t
351 netconn_listen_with_backlog(struct netconn *conn, u8_t backlog)
352 {
353 #if LWIP_TCP
354   API_MSG_VAR_DECLARE(msg);
355   err_t err;
356 
357   /* This does no harm. If TCP_LISTEN_BACKLOG is off, backlog is unused. */
358   LWIP_UNUSED_ARG(backlog);
359 
360   LWIP_ERROR("netconn_listen: invalid conn", (conn != NULL), return ERR_ARG;);
361 
362   API_MSG_VAR_ALLOC(msg);
363   API_MSG_VAR_REF(msg).conn = conn;
364 #if TCP_LISTEN_BACKLOG
365   API_MSG_VAR_REF(msg).msg.lb.backlog = backlog;
366 #endif /* TCP_LISTEN_BACKLOG */
367   err = netconn_apimsg(lwip_netconn_do_listen, &API_MSG_VAR_REF(msg));
368   API_MSG_VAR_FREE(msg);
369 
370   return err;
371 #else /* LWIP_TCP */
372   LWIP_UNUSED_ARG(conn);
373   LWIP_UNUSED_ARG(backlog);
374   return ERR_ARG;
375 #endif /* LWIP_TCP */
376 }
377 
378 /**
379  * @ingroup netconn_tcp
380  * Accept a new connection on a TCP listening netconn.
381  *
382  * @param conn the TCP listen netconn
383  * @param new_conn pointer where the new connection is stored
384  * @return ERR_OK if a new connection has been received or an error
385  *                code otherwise
386  */
387 err_t
388 netconn_accept(struct netconn *conn, struct netconn **new_conn)
389 {
390 #if LWIP_TCP
391   void *accept_ptr;
392   struct netconn *newconn;
393 #if TCP_LISTEN_BACKLOG
394   API_MSG_VAR_DECLARE(msg);
395 #endif /* TCP_LISTEN_BACKLOG */
396 
397   LWIP_ERROR("netconn_accept: invalid pointer",    (new_conn != NULL),                  return ERR_ARG;);
398   *new_conn = NULL;
399   LWIP_ERROR("netconn_accept: invalid conn",       (conn != NULL),                      return ERR_ARG;);
400 
401   if (ERR_IS_FATAL(conn->last_err)) {
402     /* don't recv on fatal errors: this might block the application task
403        waiting on acceptmbox forever! */
404     return conn->last_err;
405   }
406   if (!sys_mbox_valid(&conn->acceptmbox)) {
407     return ERR_CLSD;
408   }
409 
410 #if TCP_LISTEN_BACKLOG
411   /* need to allocate API message here so empty message pool does not result in event loss
412    * see bug #47512: MPU_COMPATIBLE may fail on empty pool */
413   API_MSG_VAR_ALLOC(msg);
414 #endif /* TCP_LISTEN_BACKLOG */
415 
416   if (netconn_is_nonblocking(conn)) {
417     if (sys_arch_mbox_tryfetch(&conn->acceptmbox, &accept_ptr) == SYS_ARCH_TIMEOUT) {
418 #if TCP_LISTEN_BACKLOG
419       API_MSG_VAR_FREE(msg);
420 #endif /* TCP_LISTEN_BACKLOG */
421       return ERR_WOULDBLOCK;
422     }
423   } else {
424 #if LWIP_SO_RCVTIMEO
425     if (sys_arch_mbox_fetch(&conn->acceptmbox, &accept_ptr, conn->recv_timeout) == SYS_ARCH_TIMEOUT) {
426 #if TCP_LISTEN_BACKLOG
427       API_MSG_VAR_FREE(msg);
428 #endif /* TCP_LISTEN_BACKLOG */
429       return ERR_TIMEOUT;
430     }
431 #else
432     sys_arch_mbox_fetch(&conn->acceptmbox, &accept_ptr, 0);
433 #endif /* LWIP_SO_RCVTIMEO*/
434   }
435   newconn = (struct netconn *)accept_ptr;
436   /* Register event with callback */
437   API_EVENT(conn, NETCONN_EVT_RCVMINUS, 0);
438 
439   if (accept_ptr == &netconn_aborted) {
440     /* a connection has been aborted: out of pcbs or out of netconns during accept */
441     /* @todo: set netconn error, but this would be fatal and thus block further accepts */
442 #if TCP_LISTEN_BACKLOG
443     API_MSG_VAR_FREE(msg);
444 #endif /* TCP_LISTEN_BACKLOG */
445     return ERR_ABRT;
446   }
447   if (newconn == NULL) {
448     /* connection has been aborted */
449     /* in this special case, we set the netconn error from application thread, as
450        on a ready-to-accept listening netconn, there should not be anything running
451        in tcpip_thread */
452     NETCONN_SET_SAFE_ERR(conn, ERR_CLSD);
453 #if TCP_LISTEN_BACKLOG
454     API_MSG_VAR_FREE(msg);
455 #endif /* TCP_LISTEN_BACKLOG */
456     return ERR_CLSD;
457   }
458 #if TCP_LISTEN_BACKLOG
459   /* Let the stack know that we have accepted the connection. */
460   API_MSG_VAR_REF(msg).conn = newconn;
461   /* don't care for the return value of lwip_netconn_do_recv */
462   netconn_apimsg(lwip_netconn_do_accepted, &API_MSG_VAR_REF(msg));
463   API_MSG_VAR_FREE(msg);
464 #endif /* TCP_LISTEN_BACKLOG */
465 
466   *new_conn = newconn;
467   /* don't set conn->last_err: it's only ERR_OK, anyway */
468   return ERR_OK;
469 #else /* LWIP_TCP */
470   LWIP_UNUSED_ARG(conn);
471   LWIP_UNUSED_ARG(new_conn);
472   return ERR_ARG;
473 #endif /* LWIP_TCP */
474 }
475 
476 /**
477  * @ingroup netconn_common
478  * Receive data: actual implementation that doesn't care whether pbuf or netbuf
479  * is received (this is internal, it's just here for describing common errors)
480  *
481  * @param conn the netconn from which to receive data
482  * @param new_buf pointer where a new pbuf/netbuf is stored when received data
483  * @param apiflags flags that control function behaviour. For now only:
484  * - NETCONN_DONTBLOCK: only read data that is available now, don't wait for more data
485  * @return ERR_OK if data has been received, an error code otherwise (timeout,
486  *                memory error or another error)
487  *         ERR_CONN if not connected
488  *         ERR_CLSD if TCP connection has been closed
489  *         ERR_WOULDBLOCK if the netconn is nonblocking but would block to wait for data
490  *         ERR_TIMEOUT if the netconn has a receive timeout and no data was received
491  */
492 static err_t
493 netconn_recv_data(struct netconn *conn, void **new_buf, u8_t apiflags)
494 {
495   void *buf = NULL;
496   u16_t len;
497 
498   LWIP_ERROR("netconn_recv: invalid pointer", (new_buf != NULL), return ERR_ARG;);
499   *new_buf = NULL;
500   LWIP_ERROR("netconn_recv: invalid conn",    (conn != NULL),    return ERR_ARG;);
501   LWIP_ERROR("netconn_recv: invalid recvmbox", sys_mbox_valid(&conn->recvmbox), return ERR_CONN;);
502 
503   if (ERR_IS_FATAL(conn->last_err)) {
504     /* don't recv on fatal errors: this might block the application task
505        waiting on recvmbox forever! */
506     /* @todo: this does not allow us to fetch data that has been put into recvmbox
507        before the fatal error occurred - is that a problem? */
508     return conn->last_err;
509   }
510 
511   if (netconn_is_nonblocking(conn) || (apiflags & NETCONN_DONTBLOCK)) {
512     if (sys_arch_mbox_tryfetch(&conn->recvmbox, &buf) == SYS_ARCH_TIMEOUT) {
513      return ERR_WOULDBLOCK;
514     }
515   } else {
516 #if LWIP_SO_RCVTIMEO
517     if (sys_arch_mbox_fetch(&conn->recvmbox, &buf, conn->recv_timeout) == SYS_ARCH_TIMEOUT) {
518       return ERR_TIMEOUT;
519     }
520 #else
521     sys_arch_mbox_fetch(&conn->recvmbox, &buf, 0);
522 #endif /* LWIP_SO_RCVTIMEO*/
523   }
524 
525 #if LWIP_TCP
526 #if (LWIP_UDP || LWIP_RAW)
527   if (NETCONNTYPE_GROUP(conn->type) == NETCONN_TCP)
528 #endif /* (LWIP_UDP || LWIP_RAW) */
529   {
530     /* If we received a NULL pointer, we are closed */
531     if (buf == NULL) {
532       /* new_buf has been zeroed above alredy */
533       return ERR_OK;
534     }
535     len = ((struct pbuf *)buf)->tot_len;
536   }
537 #endif /* LWIP_TCP */
538 #if LWIP_TCP && (LWIP_UDP || LWIP_RAW)
539   else
540 #endif /* LWIP_TCP && (LWIP_UDP || LWIP_RAW) */
541 #if (LWIP_UDP || LWIP_RAW)
542   {
543     LWIP_ASSERT("buf != NULL", buf != NULL);
544     len = netbuf_len((struct netbuf*)buf);
545   }
546 #endif /* (LWIP_UDP || LWIP_RAW) */
547 
548 #if LWIP_SO_RCVBUF
549   SYS_ARCH_DEC(conn->recv_avail, len);
550 #endif /* LWIP_SO_RCVBUF */
551   /* Register event with callback */
552   API_EVENT(conn, NETCONN_EVT_RCVMINUS, len);
553 
554   LWIP_DEBUGF(API_LIB_DEBUG, ("netconn_recv_data: received %p, len=%"U16_F"\n", buf, len));
555 
556   *new_buf = buf;
557   /* don't set conn->last_err: it's only ERR_OK, anyway */
558   return ERR_OK;
559 }
560 
561 static err_t
562 netconn_tcp_recvd_msg(struct netconn *conn, u32_t len, struct api_msg* msg)
563 {
564   LWIP_ERROR("netconn_recv_tcp_pbuf: invalid conn", (conn != NULL) &&
565              NETCONNTYPE_GROUP(netconn_type(conn)) == NETCONN_TCP, return ERR_ARG;);
566 
567   msg->conn = conn;
568   msg->msg.r.len = len;
569 
570   return netconn_apimsg(lwip_netconn_do_recv, msg);
571 }
572 
573 err_t
574 netconn_tcp_recvd(struct netconn *conn, u32_t len)
575 {
576   err_t err;
577   API_MSG_VAR_DECLARE(msg);
578   LWIP_ERROR("netconn_recv_tcp_pbuf: invalid conn", (conn != NULL) &&
579              NETCONNTYPE_GROUP(netconn_type(conn)) == NETCONN_TCP, return ERR_ARG;);
580 
581   API_MSG_VAR_ALLOC(msg);
582   err = netconn_tcp_recvd_msg(conn, len, &API_VAR_REF(msg));
583   API_MSG_VAR_FREE(msg);
584   return err;
585 }
586 
587 #if LWIP_TCP
588 static err_t
589 netconn_recv_data_tcp(struct netconn *conn, struct pbuf **new_buf, u8_t apiflags)
590 {
591   err_t err;
592   struct pbuf *buf;
593 #if LWIP_TCP
594   API_MSG_VAR_DECLARE(msg);
595 #if LWIP_MPU_COMPATIBLE
596   msg = NULL;
597 #endif
598 #endif /* LWIP_TCP */
599 
600   if (!sys_mbox_valid(&conn->recvmbox)) {
601     /* This happens when calling this function after receiving FIN */
602     return sys_mbox_valid(&conn->acceptmbox) ? ERR_CONN : ERR_CLSD;
603   }
604 
605   if (!(apiflags & NETCONN_NOAUTORCVD)) {
606     /* need to allocate API message here so empty message pool does not result in event loss
607       * see bug #47512: MPU_COMPATIBLE may fail on empty pool */
608     API_MSG_VAR_ALLOC(msg);
609   }
610 
611   err = netconn_recv_data(conn, (void **)new_buf, apiflags);
612   if (err != ERR_OK) {
613     if (!(apiflags & NETCONN_NOAUTORCVD)) {
614       API_MSG_VAR_FREE(msg);
615     }
616     return err;
617   }
618   buf = *new_buf;
619   if (!(apiflags & NETCONN_NOAUTORCVD)) {
620     /* Let the stack know that we have taken the data. */
621     u16_t len = buf ? buf->tot_len : 1;
622     /* don't care for the return value of lwip_netconn_do_recv */
623     /* @todo: this should really be fixed, e.g. by retrying in poll on error */
624     netconn_tcp_recvd_msg(conn, len,  &API_VAR_REF(msg));
625     API_MSG_VAR_FREE(msg);
626   }
627 
628   /* If we are closed, we indicate that we no longer wish to use the socket */
629   if (buf == NULL) {
630     API_EVENT(conn, NETCONN_EVT_RCVMINUS, 0);
631     if (conn->pcb.ip == NULL) {
632       /* race condition: RST during recv */
633       return conn->last_err == ERR_OK ? ERR_RST : conn->last_err;
634     }
635     /* RX side is closed, so deallocate the recvmbox */
636     netconn_close_shutdown(conn, NETCONN_SHUT_RD);
637     /* Don' store ERR_CLSD as conn->err since we are only half-closed */
638     return ERR_CLSD;
639   }
640   return err;
641 }
642 
643 /**
644  * @ingroup netconn_tcp
645  * Receive data (in form of a pbuf) from a TCP netconn
646  *
647  * @param conn the netconn from which to receive data
648  * @param new_buf pointer where a new pbuf is stored when received data
649  * @return ERR_OK if data has been received, an error code otherwise (timeout,
650  *                memory error or another error, @see netconn_recv_data)
651  *         ERR_ARG if conn is not a TCP netconn
652  */
653 err_t
654 netconn_recv_tcp_pbuf(struct netconn *conn, struct pbuf **new_buf)
655 {
656   LWIP_ERROR("netconn_recv_tcp_pbuf: invalid conn", (conn != NULL) &&
657              NETCONNTYPE_GROUP(netconn_type(conn)) == NETCONN_TCP, return ERR_ARG;);
658 
659   return netconn_recv_data_tcp(conn, new_buf, 0);
660 }
661 
662 /**
663  * @ingroup netconn_tcp
664  * Receive data (in form of a pbuf) from a TCP netconn
665  *
666  * @param conn the netconn from which to receive data
667  * @param new_buf pointer where a new pbuf is stored when received data
668  * @param apiflags flags that control function behaviour. For now only:
669  * - NETCONN_DONTBLOCK: only read data that is available now, don't wait for more data
670  * @return ERR_OK if data has been received, an error code otherwise (timeout,
671  *                memory error or another error, @see netconn_recv_data)
672  *         ERR_ARG if conn is not a TCP netconn
673  */
674 err_t
675 netconn_recv_tcp_pbuf_flags(struct netconn *conn, struct pbuf **new_buf, u8_t apiflags)
676 {
677   LWIP_ERROR("netconn_recv_tcp_pbuf: invalid conn", (conn != NULL) &&
678              NETCONNTYPE_GROUP(netconn_type(conn)) == NETCONN_TCP, return ERR_ARG;);
679 
680   return netconn_recv_data_tcp(conn, new_buf, apiflags);
681 }
682 #endif /* LWIP_TCP */
683 
684 /**
685  * Receive data (in form of a netbuf) from a UDP or RAW netconn
686  *
687  * @param conn the netconn from which to receive data
688  * @param new_buf pointer where a new netbuf is stored when received data
689  * @return ERR_OK if data has been received, an error code otherwise (timeout,
690  *                memory error or another error)
691  *         ERR_ARG if conn is not a UDP/RAW netconn
692  */
693 err_t
694 netconn_recv_udp_raw_netbuf(struct netconn *conn, struct netbuf **new_buf)
695 {
696   LWIP_ERROR("netconn_recv_udp_raw_netbuf: invalid conn", (conn != NULL) &&
697              NETCONNTYPE_GROUP(netconn_type(conn)) != NETCONN_TCP, return ERR_ARG;);
698 
699   return netconn_recv_data(conn, (void **)new_buf, 0);
700 }
701 
702 /**
703  * Receive data (in form of a netbuf) from a UDP or RAW netconn
704  *
705  * @param conn the netconn from which to receive data
706  * @param new_buf pointer where a new netbuf is stored when received data
707  * @param apiflags flags that control function behaviour. For now only:
708  * - NETCONN_DONTBLOCK: only read data that is available now, don't wait for more data
709  * @return ERR_OK if data has been received, an error code otherwise (timeout,
710  *                memory error or another error)
711  *         ERR_ARG if conn is not a UDP/RAW netconn
712  */
713 err_t
714 netconn_recv_udp_raw_netbuf_flags(struct netconn *conn, struct netbuf **new_buf, u8_t apiflags)
715 {
716   LWIP_ERROR("netconn_recv_udp_raw_netbuf: invalid conn", (conn != NULL) &&
717              NETCONNTYPE_GROUP(netconn_type(conn)) != NETCONN_TCP, return ERR_ARG;);
718 
719   return netconn_recv_data(conn, (void **)new_buf, apiflags);
720 }
721 
722 /**
723  * @ingroup netconn_common
724  * Receive data (in form of a netbuf containing a packet buffer) from a netconn
725  *
726  * @param conn the netconn from which to receive data
727  * @param new_buf pointer where a new netbuf is stored when received data
728  * @return ERR_OK if data has been received, an error code otherwise (timeout,
729  *                memory error or another error)
730  */
731 err_t
732 netconn_recv(struct netconn *conn, struct netbuf **new_buf)
733 {
734 #if LWIP_TCP
735   struct netbuf *buf = NULL;
736   err_t err;
737 #endif /* LWIP_TCP */
738 
739   LWIP_ERROR("netconn_recv: invalid pointer", (new_buf != NULL), return ERR_ARG;);
740   *new_buf = NULL;
741   LWIP_ERROR("netconn_recv: invalid conn",    (conn != NULL),    return ERR_ARG;);
742 
743 #if LWIP_TCP
744 #if (LWIP_UDP || LWIP_RAW)
745   if (NETCONNTYPE_GROUP(conn->type) == NETCONN_TCP)
746 #endif /* (LWIP_UDP || LWIP_RAW) */
747   {
748     struct pbuf *p = NULL;
749     /* This is not a listening netconn, since recvmbox is set */
750 
751     buf = (struct netbuf *)memp_malloc(MEMP_NETBUF);
752     if (buf == NULL) {
753       return ERR_MEM;
754     }
755 
756     err = netconn_recv_data_tcp(conn, &p, 0);
757     if (err != ERR_OK) {
758       memp_free(MEMP_NETBUF, buf);
759       return err;
760     }
761     LWIP_ASSERT("p != NULL", p != NULL);
762 
763     buf->p = p;
764     buf->ptr = p;
765     buf->port = 0;
766     ip_addr_set_zero(&buf->addr);
767     *new_buf = buf;
768     /* don't set conn->last_err: it's only ERR_OK, anyway */
769     return ERR_OK;
770   }
771 #endif /* LWIP_TCP */
772 #if LWIP_TCP && (LWIP_UDP || LWIP_RAW)
773   else
774 #endif /* LWIP_TCP && (LWIP_UDP || LWIP_RAW) */
775   {
776 #if (LWIP_UDP || LWIP_RAW)
777     return netconn_recv_data(conn, (void **)new_buf, 0);
778 #endif /* (LWIP_UDP || LWIP_RAW) */
779   }
780 }
781 
782 /**
783  * @ingroup netconn_udp
784  * Send data (in form of a netbuf) to a specific remote IP address and port.
785  * Only to be used for UDP and RAW netconns (not TCP).
786  *
787  * @param conn the netconn over which to send data
788  * @param buf a netbuf containing the data to send
789  * @param addr the remote IP address to which to send the data
790  * @param port the remote port to which to send the data
791  * @return ERR_OK if data was sent, any other err_t on error
792  */
793 err_t
794 netconn_sendto(struct netconn *conn, struct netbuf *buf, const ip_addr_t *addr, u16_t port)
795 {
796   if (buf != NULL) {
797     ip_addr_set(&buf->addr, addr);
798     buf->port = port;
799     return netconn_send(conn, buf);
800   }
801   return ERR_VAL;
802 }
803 
804 /**
805  * @ingroup netconn_udp
806  * Send data over a UDP or RAW netconn (that is already connected).
807  *
808  * @param conn the UDP or RAW netconn over which to send data
809  * @param buf a netbuf containing the data to send
810  * @return ERR_OK if data was sent, any other err_t on error
811  */
812 err_t
813 netconn_send(struct netconn *conn, struct netbuf *buf)
814 {
815   API_MSG_VAR_DECLARE(msg);
816   err_t err;
817 
818   LWIP_ERROR("netconn_send: invalid conn",  (conn != NULL), return ERR_ARG;);
819 
820   LWIP_DEBUGF(API_LIB_DEBUG, ("netconn_send: sending %"U16_F" bytes\n", buf->p->tot_len));
821 
822   API_MSG_VAR_ALLOC(msg);
823   API_MSG_VAR_REF(msg).conn = conn;
824   API_MSG_VAR_REF(msg).msg.b = buf;
825   err = netconn_apimsg(lwip_netconn_do_send, &API_MSG_VAR_REF(msg));
826   API_MSG_VAR_FREE(msg);
827 
828   return err;
829 }
830 
831 /**
832  * @ingroup netconn_tcp
833  * Send data over a TCP netconn.
834  *
835  * @param conn the TCP netconn over which to send data
836  * @param dataptr pointer to the application buffer that contains the data to send
837  * @param size size of the application data to send
838  * @param apiflags combination of following flags :
839  * - NETCONN_COPY: data will be copied into memory belonging to the stack
840  * - NETCONN_MORE: for TCP connection, PSH flag will be set on last segment sent
841  * - NETCONN_DONTBLOCK: only write the data if all data can be written at once
842  * @param bytes_written pointer to a location that receives the number of written bytes
843  * @return ERR_OK if data was sent, any other err_t on error
844  */
845 err_t
846 netconn_write_partly(struct netconn *conn, const void *dataptr, size_t size,
847                      u8_t apiflags, size_t *bytes_written)
848 {
849   struct netvector vector;
850   vector.ptr = dataptr;
851   vector.len = size;
852   return netconn_write_vectors_partly(conn, &vector, 1, apiflags, bytes_written);
853 }
854 
855 /**
856  * Send vectorized data atomically over a TCP netconn.
857  *
858  * @param conn the TCP netconn over which to send data
859  * @param vectors array of vectors containing data to send
860  * @param vectorcnt number of vectors in the array
861  * @param apiflags combination of following flags :
862  * - NETCONN_COPY: data will be copied into memory belonging to the stack
863  * - NETCONN_MORE: for TCP connection, PSH flag will be set on last segment sent
864  * - NETCONN_DONTBLOCK: only write the data if all data can be written at once
865  * @param bytes_written pointer to a location that receives the number of written bytes
866  * @return ERR_OK if data was sent, any other err_t on error
867  */
868 err_t
869 netconn_write_vectors_partly(struct netconn *conn, struct netvector *vectors, u16_t vectorcnt,
870                              u8_t apiflags, size_t *bytes_written)
871 {
872   API_MSG_VAR_DECLARE(msg);
873   err_t err;
874   u8_t dontblock;
875   size_t size;
876   int i;
877 
878   LWIP_ERROR("netconn_write: invalid conn",  (conn != NULL), return ERR_ARG;);
879   LWIP_ERROR("netconn_write: invalid conn->type",  (NETCONNTYPE_GROUP(conn->type)== NETCONN_TCP), return ERR_VAL;);
880   dontblock = netconn_is_nonblocking(conn) || (apiflags & NETCONN_DONTBLOCK);
881 #if LWIP_SO_SNDTIMEO
882   if (conn->send_timeout != 0) {
883     dontblock = 1;
884   }
885 #endif /* LWIP_SO_SNDTIMEO */
886   if (dontblock && !bytes_written) {
887     /* This implies netconn_write() cannot be used for non-blocking send, since
888        it has no way to return the number of bytes written. */
889     return ERR_VAL;
890   }
891 
892   /* sum up the total size */
893   size = 0;
894   for (i = 0; i < vectorcnt; i++) {
895     size += vectors[i].len;
896     if (size < vectors[i].len) {
897       /* overflow */
898       return ERR_VAL;
899     }
900   }
901   if (size == 0) {
902     return ERR_OK;
903   } else if (size > INT_MAX) {
904     /* this is required by the socket layer (cannot send full size_t range) */
905     return ERR_VAL;
906   }
907 
908   API_MSG_VAR_ALLOC(msg);
909   /* non-blocking write sends as much  */
910   API_MSG_VAR_REF(msg).conn = conn;
911   API_MSG_VAR_REF(msg).msg.w.vector = vectors;
912   API_MSG_VAR_REF(msg).msg.w.vector_cnt = vectorcnt;
913   API_MSG_VAR_REF(msg).msg.w.vector_off = 0;
914   API_MSG_VAR_REF(msg).msg.w.apiflags = apiflags;
915   API_MSG_VAR_REF(msg).msg.w.len = size;
916   API_MSG_VAR_REF(msg).msg.w.offset = 0;
917 #if LWIP_SO_SNDTIMEO
918   if (conn->send_timeout != 0) {
919     /* get the time we started, which is later compared to
920         sys_now() + conn->send_timeout */
921     API_MSG_VAR_REF(msg).msg.w.time_started = sys_now();
922   } else {
923     API_MSG_VAR_REF(msg).msg.w.time_started = 0;
924   }
925 #endif /* LWIP_SO_SNDTIMEO */
926 
927   /* For locking the core: this _can_ be delayed on low memory/low send buffer,
928      but if it is, this is done inside api_msg.c:do_write(), so we can use the
929      non-blocking version here. */
930   err = netconn_apimsg(lwip_netconn_do_write, &API_MSG_VAR_REF(msg));
931   if (err == ERR_OK) {
932     if (bytes_written != NULL) {
933       *bytes_written = API_MSG_VAR_REF(msg).msg.w.offset;
934     }
935     /* for blocking, check all requested bytes were written, NOTE: send_timeout is
936        treated as dontblock (see dontblock assignment above) */
937     if (!dontblock) {
938       LWIP_ASSERT("do_write failed to write all bytes", API_MSG_VAR_REF(msg).msg.w.offset == size);
939     }
940   }
941   API_MSG_VAR_FREE(msg);
942 
943   return err;
944 }
945 
946 /**
947  * @ingroup netconn_tcp
948  * Close or shutdown a TCP netconn (doesn't delete it).
949  *
950  * @param conn the TCP netconn to close or shutdown
951  * @param how fully close or only shutdown one side?
952  * @return ERR_OK if the netconn was closed, any other err_t on error
953  */
954 static err_t
955 netconn_close_shutdown(struct netconn *conn, u8_t how)
956 {
957   API_MSG_VAR_DECLARE(msg);
958   err_t err;
959   LWIP_UNUSED_ARG(how);
960 
961   LWIP_ERROR("netconn_close: invalid conn",  (conn != NULL), return ERR_ARG;);
962 
963   API_MSG_VAR_ALLOC(msg);
964   API_MSG_VAR_REF(msg).conn = conn;
965 #if LWIP_TCP
966   /* shutting down both ends is the same as closing */
967   API_MSG_VAR_REF(msg).msg.sd.shut = how;
968 #if LWIP_SO_SNDTIMEO || LWIP_SO_LINGER
969   /* get the time we started, which is later compared to
970      sys_now() + conn->send_timeout */
971   API_MSG_VAR_REF(msg).msg.sd.time_started = sys_now();
972 #else /* LWIP_SO_SNDTIMEO || LWIP_SO_LINGER */
973   API_MSG_VAR_REF(msg).msg.sd.polls_left =
974     ((LWIP_TCP_CLOSE_TIMEOUT_MS_DEFAULT + TCP_SLOW_INTERVAL - 1) / TCP_SLOW_INTERVAL) + 1;
975 #endif /* LWIP_SO_SNDTIMEO || LWIP_SO_LINGER */
976 #endif /* LWIP_TCP */
977   err = netconn_apimsg(lwip_netconn_do_close, &API_MSG_VAR_REF(msg));
978   API_MSG_VAR_FREE(msg);
979 
980   return err;
981 }
982 
983 /**
984  * @ingroup netconn_tcp
985  * Close a TCP netconn (doesn't delete it).
986  *
987  * @param conn the TCP netconn to close
988  * @return ERR_OK if the netconn was closed, any other err_t on error
989  */
990 err_t
991 netconn_close(struct netconn *conn)
992 {
993   /* shutting down both ends is the same as closing */
994   return netconn_close_shutdown(conn, NETCONN_SHUT_RDWR);
995 }
996 
997 /**
998  * @ingroup netconn_tcp
999  * Shut down one or both sides of a TCP netconn (doesn't delete it).
1000  *
1001  * @param conn the TCP netconn to shut down
1002  * @param shut_rx shut down the RX side (no more read possible after this)
1003  * @param shut_tx shut down the TX side (no more write possible after this)
1004  * @return ERR_OK if the netconn was closed, any other err_t on error
1005  */
1006 err_t
1007 netconn_shutdown(struct netconn *conn, u8_t shut_rx, u8_t shut_tx)
1008 {
1009   return netconn_close_shutdown(conn, (shut_rx ? NETCONN_SHUT_RD : 0) | (shut_tx ? NETCONN_SHUT_WR : 0));
1010 }
1011 
1012 #if LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD)
1013 /**
1014  * @ingroup netconn_udp
1015  * Join multicast groups for UDP netconns.
1016  *
1017  * @param conn the UDP netconn for which to change multicast addresses
1018  * @param multiaddr IP address of the multicast group to join or leave
1019  * @param netif_addr the IP address of the network interface on which to send
1020  *                  the igmp message
1021  * @param join_or_leave flag whether to send a join- or leave-message
1022  * @return ERR_OK if the action was taken, any err_t on error
1023  */
1024 err_t
1025 netconn_join_leave_group(struct netconn *conn,
1026                          const ip_addr_t *multiaddr,
1027                          const ip_addr_t *netif_addr,
1028                          enum netconn_igmp join_or_leave)
1029 {
1030   API_MSG_VAR_DECLARE(msg);
1031   err_t err;
1032 
1033   LWIP_ERROR("netconn_join_leave_group: invalid conn",  (conn != NULL), return ERR_ARG;);
1034 
1035   API_MSG_VAR_ALLOC(msg);
1036 
1037 #if LWIP_IPV4
1038   /* Don't propagate NULL pointer (IP_ADDR_ANY alias) to subsequent functions */
1039   if (multiaddr == NULL) {
1040     multiaddr = IP4_ADDR_ANY;
1041   }
1042   if (netif_addr == NULL) {
1043     netif_addr = IP4_ADDR_ANY;
1044   }
1045 #endif /* LWIP_IPV4 */
1046 
1047   API_MSG_VAR_REF(msg).conn = conn;
1048   API_MSG_VAR_REF(msg).msg.jl.multiaddr = API_MSG_VAR_REF(multiaddr);
1049   API_MSG_VAR_REF(msg).msg.jl.netif_addr = API_MSG_VAR_REF(netif_addr);
1050   API_MSG_VAR_REF(msg).msg.jl.join_or_leave = join_or_leave;
1051   err = netconn_apimsg(lwip_netconn_do_join_leave_group, &API_MSG_VAR_REF(msg));
1052   API_MSG_VAR_FREE(msg);
1053 
1054   return err;
1055 }
1056 #endif /* LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD) */
1057 
1058 #if LWIP_DNS
1059 /**
1060  * @ingroup netconn_common
1061  * Execute a DNS query, only one IP address is returned
1062  *
1063  * @param name a string representation of the DNS host name to query
1064  * @param addr a preallocated ip_addr_t where to store the resolved IP address
1065  * @param dns_addrtype IP address type (IPv4 / IPv6)
1066  * @return ERR_OK: resolving succeeded
1067  *         ERR_MEM: memory error, try again later
1068  *         ERR_ARG: dns client not initialized or invalid hostname
1069  *         ERR_VAL: dns server response was invalid
1070  */
1071 #if LWIP_IPV4 && LWIP_IPV6
1072 err_t
1073 netconn_gethostbyname_addrtype(const char *name, ip_addr_t *addr, u8_t dns_addrtype)
1074 #else
1075 err_t
1076 netconn_gethostbyname(const char *name, ip_addr_t *addr)
1077 #endif
1078 {
1079   API_VAR_DECLARE(struct dns_api_msg, msg);
1080 #if !LWIP_MPU_COMPATIBLE
1081   sys_sem_t sem;
1082 #endif /* LWIP_MPU_COMPATIBLE */
1083   err_t err;
1084   err_t cberr;
1085 
1086   LWIP_ERROR("netconn_gethostbyname: invalid name", (name != NULL), return ERR_ARG;);
1087   LWIP_ERROR("netconn_gethostbyname: invalid addr", (addr != NULL), return ERR_ARG;);
1088 #if LWIP_MPU_COMPATIBLE
1089   if (strlen(name) >= DNS_MAX_NAME_LENGTH) {
1090     return ERR_ARG;
1091   }
1092 #endif
1093 
1094   API_VAR_ALLOC(struct dns_api_msg, MEMP_DNS_API_MSG, msg, ERR_MEM);
1095 #if LWIP_MPU_COMPATIBLE
1096   strncpy(API_VAR_REF(msg).name, name, DNS_MAX_NAME_LENGTH-1);
1097   API_VAR_REF(msg).name[DNS_MAX_NAME_LENGTH-1] = 0;
1098 #else /* LWIP_MPU_COMPATIBLE */
1099   msg.err = &err;
1100   msg.sem = &sem;
1101   API_VAR_REF(msg).addr = API_VAR_REF(addr);
1102   API_VAR_REF(msg).name = name;
1103 #endif /* LWIP_MPU_COMPATIBLE */
1104 #if LWIP_IPV4 && LWIP_IPV6
1105   API_VAR_REF(msg).dns_addrtype = dns_addrtype;
1106 #endif /* LWIP_IPV4 && LWIP_IPV6 */
1107 #if LWIP_NETCONN_SEM_PER_THREAD
1108   API_VAR_REF(msg).sem = LWIP_NETCONN_THREAD_SEM_GET();
1109 #else /* LWIP_NETCONN_SEM_PER_THREAD*/
1110   err = sys_sem_new(API_EXPR_REF(API_VAR_REF(msg).sem), 0);
1111   if (err != ERR_OK) {
1112     API_VAR_FREE(MEMP_DNS_API_MSG, msg);
1113     return err;
1114   }
1115 #endif /* LWIP_NETCONN_SEM_PER_THREAD */
1116 
1117   cberr = tcpip_callback(lwip_netconn_do_gethostbyname, &API_VAR_REF(msg));
1118   if (cberr != ERR_OK) {
1119 #if !LWIP_NETCONN_SEM_PER_THREAD
1120     sys_sem_free(API_EXPR_REF(API_VAR_REF(msg).sem));
1121 #endif /* !LWIP_NETCONN_SEM_PER_THREAD */
1122     API_VAR_FREE(MEMP_DNS_API_MSG, msg);
1123     return cberr;
1124   }
1125   sys_sem_wait(API_EXPR_REF_SEM(API_VAR_REF(msg).sem));
1126 #if !LWIP_NETCONN_SEM_PER_THREAD
1127   sys_sem_free(API_EXPR_REF(API_VAR_REF(msg).sem));
1128 #endif /* !LWIP_NETCONN_SEM_PER_THREAD */
1129 
1130 #if LWIP_MPU_COMPATIBLE
1131   *addr = msg->addr;
1132   err = msg->err;
1133 #endif /* LWIP_MPU_COMPATIBLE */
1134 
1135   API_VAR_FREE(MEMP_DNS_API_MSG, msg);
1136   return err;
1137 }
1138 #endif /* LWIP_DNS*/
1139 
1140 #if LWIP_NETCONN_SEM_PER_THREAD
1141 void
1142 netconn_thread_init(void)
1143 {
1144   sys_sem_t *sem = LWIP_NETCONN_THREAD_SEM_GET();
1145   if ((sem == NULL) || !sys_sem_valid(sem)) {
1146     /* call alloc only once */
1147     LWIP_NETCONN_THREAD_SEM_ALLOC();
1148     LWIP_ASSERT("LWIP_NETCONN_THREAD_SEM_ALLOC() failed", sys_sem_valid(LWIP_NETCONN_THREAD_SEM_GET()));
1149   }
1150 }
1151 
1152 void
1153 netconn_thread_cleanup(void)
1154 {
1155   sys_sem_t *sem = LWIP_NETCONN_THREAD_SEM_GET();
1156   if ((sem != NULL) && sys_sem_valid(sem)) {
1157     /* call free only once */
1158     LWIP_NETCONN_THREAD_SEM_FREE();
1159   }
1160 }
1161 #endif /* LWIP_NETCONN_SEM_PER_THREAD */
1162 
1163 #endif /* LWIP_NETCONN */
1164