1 /**
2  * @file
3  * Sequential API Internal module
4  *
5  */
6 
7 /*
8  * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
9  * All rights reserved.
10  *
11  * Redistribution and use in source and binary forms, with or without modification,
12  * are permitted provided that the following conditions are met:
13  *
14  * 1. Redistributions of source code must retain the above copyright notice,
15  *    this list of conditions and the following disclaimer.
16  * 2. Redistributions in binary form must reproduce the above copyright notice,
17  *    this list of conditions and the following disclaimer in the documentation
18  *    and/or other materials provided with the distribution.
19  * 3. The name of the author may not be used to endorse or promote products
20  *    derived from this software without specific prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
23  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
24  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
25  * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
26  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
27  * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
30  * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
31  * OF SUCH DAMAGE.
32  *
33  * This file is part of the lwIP TCP/IP stack.
34  *
35  * Author: Adam Dunkels <adam@sics.se>
36  *
37  */
38 
39 #include "lwip/opt.h"
40 
41 #if LWIP_NETCONN /* don't build if not configured for use in lwipopts.h */
42 
43 #include "lwip/priv/api_msg.h"
44 
45 #include "lwip/ip.h"
46 #include "lwip/ip_addr.h"
47 #include "lwip/udp.h"
48 #include "lwip/tcp.h"
49 #include "lwip/raw.h"
50 
51 #include "lwip/memp.h"
52 #include "lwip/igmp.h"
53 #include "lwip/dns.h"
54 #include "lwip/mld6.h"
55 #include "lwip/priv/tcpip_priv.h"
56 
57 #include <string.h>
58 
59 /* netconns are polled once per second (e.g. continue write on memory error) */
60 #define NETCONN_TCP_POLL_INTERVAL 2
61 
62 #define SET_NONBLOCKING_CONNECT(conn, val)  do { if (val) { \
63   netconn_set_flags(conn, NETCONN_FLAG_IN_NONBLOCKING_CONNECT); \
64 } else { \
65   netconn_clear_flags(conn, NETCONN_FLAG_IN_NONBLOCKING_CONNECT); }} while(0)
66 #define IN_NONBLOCKING_CONNECT(conn) netconn_is_flag_set(conn, NETCONN_FLAG_IN_NONBLOCKING_CONNECT)
67 
68 #if LWIP_NETCONN_FULLDUPLEX
69 #define NETCONN_MBOX_VALID(conn, mbox) (sys_mbox_valid(mbox) && ((conn->flags & NETCONN_FLAG_MBOXINVALID) == 0))
70 #else
71 #define NETCONN_MBOX_VALID(conn, mbox) sys_mbox_valid(mbox)
72 #endif
73 
74 /* forward declarations */
75 #if LWIP_TCP
76 #if LWIP_TCPIP_CORE_LOCKING
77 #define WRITE_DELAYED         , 1
78 #define WRITE_DELAYED_PARAM   , u8_t delayed
79 #else /* LWIP_TCPIP_CORE_LOCKING */
80 #define WRITE_DELAYED
81 #define WRITE_DELAYED_PARAM
82 #endif /* LWIP_TCPIP_CORE_LOCKING */
83 static err_t lwip_netconn_do_writemore(struct netconn *conn  WRITE_DELAYED_PARAM);
84 static err_t lwip_netconn_do_close_internal(struct netconn *conn  WRITE_DELAYED_PARAM);
85 #endif
86 
87 static void netconn_drain(struct netconn *conn);
88 
89 #if LWIP_TCPIP_CORE_LOCKING
90 #define TCPIP_APIMSG_ACK(m)
91 #else /* LWIP_TCPIP_CORE_LOCKING */
92 #define TCPIP_APIMSG_ACK(m)   do { sys_sem_signal(LWIP_API_MSG_SEM(m)); } while(0)
93 #endif /* LWIP_TCPIP_CORE_LOCKING */
94 
95 #if LWIP_NETCONN_FULLDUPLEX
96 static const u8_t netconn_deleted = 0;
97 
98 int
lwip_netconn_is_deallocated_msg(void * msg)99 lwip_netconn_is_deallocated_msg(void *msg)
100 {
101   if (msg == &netconn_deleted) {
102     return 1;
103   }
104   return 0;
105 }
106 #endif /* LWIP_NETCONN_FULLDUPLEX */
107 
108 #if LWIP_TCP
109 static const u8_t netconn_aborted = 0;
110 static const u8_t netconn_reset = 0;
111 static const u8_t netconn_closed = 0;
112 
113 /** Translate an error to a unique void* passed via an mbox */
114 static void *
lwip_netconn_err_to_msg(err_t err)115 lwip_netconn_err_to_msg(err_t err)
116 {
117   switch (err) {
118     case ERR_ABRT:
119       return LWIP_CONST_CAST(void *, &netconn_aborted);
120     case ERR_RST:
121       return LWIP_CONST_CAST(void *, &netconn_reset);
122     case ERR_CLSD:
123       return LWIP_CONST_CAST(void *, &netconn_closed);
124     default:
125       LWIP_ASSERT("unhandled error", err == ERR_OK);
126       return NULL;
127   }
128 }
129 
130 int
lwip_netconn_is_err_msg(void * msg,err_t * err)131 lwip_netconn_is_err_msg(void *msg, err_t *err)
132 {
133   LWIP_ASSERT("err != NULL", err != NULL);
134 
135   if (msg == &netconn_aborted) {
136     *err = ERR_ABRT;
137     return 1;
138   } else if (msg == &netconn_reset) {
139     *err = ERR_RST;
140     return 1;
141   } else if (msg == &netconn_closed) {
142     *err = ERR_CLSD;
143     return 1;
144   }
145   return 0;
146 }
147 #endif /* LWIP_TCP */
148 
149 
150 #if LWIP_RAW
151 /**
152  * Receive callback function for RAW netconns.
153  * Doesn't 'eat' the packet, only copies it and sends it to
154  * conn->recvmbox
155  *
156  * @see raw.h (struct raw_pcb.recv) for parameters and return value
157  */
158 static u8_t
recv_raw(void * arg,struct raw_pcb * pcb,struct pbuf * p,const ip_addr_t * addr)159 recv_raw(void *arg, struct raw_pcb *pcb, struct pbuf *p,
160          const ip_addr_t *addr)
161 {
162   struct pbuf *q;
163   struct netbuf *buf;
164   struct netconn *conn;
165 
166   LWIP_UNUSED_ARG(addr);
167   conn = (struct netconn *)arg;
168 
169   if ((conn != NULL) && NETCONN_MBOX_VALID(conn, &conn->recvmbox)) {
170 #if LWIP_SO_RCVBUF
171     int recv_avail;
172     SYS_ARCH_GET(conn->recv_avail, recv_avail);
173     if ((recv_avail + (int)(p->tot_len)) > conn->recv_bufsize) {
174       return 0;
175     }
176 #endif /* LWIP_SO_RCVBUF */
177     /* copy the whole packet into new pbufs */
178     q = pbuf_clone(PBUF_RAW, PBUF_RAM, p);
179     if (q != NULL) {
180       u16_t len;
181       buf = (struct netbuf *)memp_malloc(MEMP_NETBUF);
182       if (buf == NULL) {
183         pbuf_free(q);
184         return 0;
185       }
186 
187       buf->p = q;
188       buf->ptr = q;
189       ip_addr_copy(buf->addr, *ip_current_src_addr());
190       buf->port = pcb->protocol;
191 
192       len = q->tot_len;
193       if (sys_mbox_trypost(&conn->recvmbox, buf) != ERR_OK) {
194         netbuf_delete(buf);
195         return 0;
196       } else {
197 #if LWIP_SO_RCVBUF
198         SYS_ARCH_INC(conn->recv_avail, len);
199 #endif /* LWIP_SO_RCVBUF */
200         /* Register event with callback */
201         API_EVENT(conn, NETCONN_EVT_RCVPLUS, len);
202       }
203     }
204   }
205 
206   return 0; /* do not eat the packet */
207 }
208 #endif /* LWIP_RAW*/
209 
210 #if LWIP_UDP
211 /**
212  * Receive callback function for UDP netconns.
213  * Posts the packet to conn->recvmbox or deletes it on memory error.
214  *
215  * @see udp.h (struct udp_pcb.recv) for parameters
216  */
217 static void
recv_udp(void * arg,struct udp_pcb * pcb,struct pbuf * p,const ip_addr_t * addr,u16_t port)218 recv_udp(void *arg, struct udp_pcb *pcb, struct pbuf *p,
219          const ip_addr_t *addr, u16_t port)
220 {
221   struct netbuf *buf;
222   struct netconn *conn;
223   u16_t len;
224   err_t err;
225 #if LWIP_SO_RCVBUF
226   int recv_avail;
227 #endif /* LWIP_SO_RCVBUF */
228 
229   LWIP_UNUSED_ARG(pcb); /* only used for asserts... */
230   LWIP_ASSERT("recv_udp must have a pcb argument", pcb != NULL);
231   LWIP_ASSERT("recv_udp must have an argument", arg != NULL);
232   conn = (struct netconn *)arg;
233 
234   if (conn == NULL) {
235     pbuf_free(p);
236     return;
237   }
238 
239   LWIP_ASSERT("recv_udp: recv for wrong pcb!", conn->pcb.udp == pcb);
240 
241 #if LWIP_SO_RCVBUF
242   SYS_ARCH_GET(conn->recv_avail, recv_avail);
243   if (!NETCONN_MBOX_VALID(conn, &conn->recvmbox) ||
244       ((recv_avail + (int)(p->tot_len)) > conn->recv_bufsize)) {
245 #else  /* LWIP_SO_RCVBUF */
246   if (!NETCONN_MBOX_VALID(conn, &conn->recvmbox)) {
247 #endif /* LWIP_SO_RCVBUF */
248     pbuf_free(p);
249     return;
250   }
251 
252   buf = (struct netbuf *)memp_malloc(MEMP_NETBUF);
253   if (buf == NULL) {
254     pbuf_free(p);
255     return;
256   } else {
257     buf->p = p;
258     buf->ptr = p;
259     ip_addr_set(&buf->addr, addr);
260     buf->port = port;
261 #if LWIP_NETBUF_RECVINFO
262     if (conn->flags & NETCONN_FLAG_PKTINFO) {
263       /* get the UDP header - always in the first pbuf, ensured by udp_input */
264       const struct udp_hdr *udphdr = (const struct udp_hdr *)ip_next_header_ptr();
265       buf->flags = NETBUF_FLAG_DESTADDR;
266       ip_addr_set(&buf->toaddr, ip_current_dest_addr());
267       buf->toport_chksum = udphdr->dest;
268     }
269 #endif /* LWIP_NETBUF_RECVINFO */
270   }
271 
272   len = p->tot_len;
273   err = sys_mbox_trypost(&conn->recvmbox, buf);
274   if (err != ERR_OK) {
275     netbuf_delete(buf);
276     LWIP_DEBUGF(API_MSG_DEBUG, ("recv_udp: sys_mbox_trypost failed, err=%d\n", err));
277     return;
278   } else {
279 #if LWIP_SO_RCVBUF
280     SYS_ARCH_INC(conn->recv_avail, len);
281 #endif /* LWIP_SO_RCVBUF */
282     /* Register event with callback */
283     API_EVENT(conn, NETCONN_EVT_RCVPLUS, len);
284   }
285 }
286 #endif /* LWIP_UDP */
287 
288 #if LWIP_TCP
289 /**
290  * Receive callback function for TCP netconns.
291  * Posts the packet to conn->recvmbox, but doesn't delete it on errors.
292  *
293  * @see tcp.h (struct tcp_pcb.recv) for parameters and return value
294  */
295 static err_t
296 recv_tcp(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err)
297 {
298   struct netconn *conn;
299   u16_t len;
300   void *msg;
301 
302   LWIP_UNUSED_ARG(pcb);
303   LWIP_ASSERT("recv_tcp must have a pcb argument", pcb != NULL);
304   LWIP_ASSERT("recv_tcp must have an argument", arg != NULL);
305   LWIP_ASSERT("err != ERR_OK unhandled", err == ERR_OK);
306   LWIP_UNUSED_ARG(err); /* for LWIP_NOASSERT */
307   conn = (struct netconn *)arg;
308 
309   if (conn == NULL) {
310     return ERR_VAL;
311   }
312   LWIP_ASSERT("recv_tcp: recv for wrong pcb!", conn->pcb.tcp == pcb);
313 
314   if (!NETCONN_MBOX_VALID(conn, &conn->recvmbox)) {
315     /* recvmbox already deleted */
316     if (p != NULL) {
317       tcp_recved(pcb, p->tot_len);
318       pbuf_free(p);
319     }
320     return ERR_OK;
321   }
322   /* Unlike for UDP or RAW pcbs, don't check for available space
323      using recv_avail since that could break the connection
324      (data is already ACKed) */
325 
326   if (p != NULL) {
327     msg = p;
328     len = p->tot_len;
329   } else {
330     msg = LWIP_CONST_CAST(void *, &netconn_closed);
331     len = 0;
332   }
333 
334   if (sys_mbox_trypost(&conn->recvmbox, msg) != ERR_OK) {
335     /* don't deallocate p: it is presented to us later again from tcp_fasttmr! */
336     return ERR_MEM;
337   } else {
338 #if LWIP_SO_RCVBUF
339     SYS_ARCH_INC(conn->recv_avail, len);
340 #endif /* LWIP_SO_RCVBUF */
341     /* Register event with callback */
342     API_EVENT(conn, NETCONN_EVT_RCVPLUS, len);
343   }
344 
345   return ERR_OK;
346 }
347 
348 /**
349  * Poll callback function for TCP netconns.
350  * Wakes up an application thread that waits for a connection to close
351  * or data to be sent. The application thread then takes the
352  * appropriate action to go on.
353  *
354  * Signals the conn->sem.
355  * netconn_close waits for conn->sem if closing failed.
356  *
357  * @see tcp.h (struct tcp_pcb.poll) for parameters and return value
358  */
359 static err_t
360 poll_tcp(void *arg, struct tcp_pcb *pcb)
361 {
362   struct netconn *conn = (struct netconn *)arg;
363 
364   LWIP_UNUSED_ARG(pcb);
365   LWIP_ASSERT("conn != NULL", (conn != NULL));
366 
367   if (conn->state == NETCONN_WRITE) {
368     lwip_netconn_do_writemore(conn  WRITE_DELAYED);
369   } else if (conn->state == NETCONN_CLOSE) {
370 #if !LWIP_SO_SNDTIMEO && !LWIP_SO_LINGER
371     if (conn->current_msg && conn->current_msg->msg.sd.polls_left) {
372       conn->current_msg->msg.sd.polls_left--;
373     }
374 #endif /* !LWIP_SO_SNDTIMEO && !LWIP_SO_LINGER */
375     lwip_netconn_do_close_internal(conn  WRITE_DELAYED);
376   }
377   /* @todo: implement connect timeout here? */
378 
379   /* Did a nonblocking write fail before? Then check available write-space. */
380   if (conn->flags & NETCONN_FLAG_CHECK_WRITESPACE) {
381     /* If the queued byte- or pbuf-count drops below the configured low-water limit,
382        let select mark this pcb as writable again. */
383     if ((conn->pcb.tcp != NULL) && (tcp_sndbuf(conn->pcb.tcp) > TCP_SNDLOWAT) &&
384         (tcp_sndqueuelen(conn->pcb.tcp) < TCP_SNDQUEUELOWAT)) {
385       netconn_clear_flags(conn, NETCONN_FLAG_CHECK_WRITESPACE);
386       API_EVENT(conn, NETCONN_EVT_SENDPLUS, 0);
387     }
388   }
389 
390   return ERR_OK;
391 }
392 
393 /**
394  * Sent callback function for TCP netconns.
395  * Signals the conn->sem and calls API_EVENT.
396  * netconn_write waits for conn->sem if send buffer is low.
397  *
398  * @see tcp.h (struct tcp_pcb.sent) for parameters and return value
399  */
400 static err_t
401 sent_tcp(void *arg, struct tcp_pcb *pcb, u16_t len)
402 {
403   struct netconn *conn = (struct netconn *)arg;
404 
405   LWIP_UNUSED_ARG(pcb);
406   LWIP_ASSERT("conn != NULL", (conn != NULL));
407 
408   if (conn) {
409     if (conn->state == NETCONN_WRITE) {
410       lwip_netconn_do_writemore(conn  WRITE_DELAYED);
411     } else if (conn->state == NETCONN_CLOSE) {
412       lwip_netconn_do_close_internal(conn  WRITE_DELAYED);
413     }
414 
415     /* If the queued byte- or pbuf-count drops below the configured low-water limit,
416        let select mark this pcb as writable again. */
417     if ((conn->pcb.tcp != NULL) && (tcp_sndbuf(conn->pcb.tcp) > TCP_SNDLOWAT) &&
418         (tcp_sndqueuelen(conn->pcb.tcp) < TCP_SNDQUEUELOWAT)) {
419       netconn_clear_flags(conn, NETCONN_FLAG_CHECK_WRITESPACE);
420       API_EVENT(conn, NETCONN_EVT_SENDPLUS, len);
421     }
422   }
423 
424   return ERR_OK;
425 }
426 
427 /**
428  * Error callback function for TCP netconns.
429  * Signals conn->sem, posts to all conn mboxes and calls API_EVENT.
430  * The application thread has then to decide what to do.
431  *
432  * @see tcp.h (struct tcp_pcb.err) for parameters
433  */
434 static void
435 err_tcp(void *arg, err_t err)
436 {
437   struct netconn *conn;
438   enum netconn_state old_state;
439   void *mbox_msg;
440   SYS_ARCH_DECL_PROTECT(lev);
441 
442   conn = (struct netconn *)arg;
443   LWIP_ASSERT("conn != NULL", (conn != NULL));
444 
445   SYS_ARCH_PROTECT(lev);
446 
447   /* when err is called, the pcb is deallocated, so delete the reference */
448   conn->pcb.tcp = NULL;
449   /* store pending error */
450   conn->pending_err = err;
451   /* prevent application threads from blocking on 'recvmbox'/'acceptmbox' */
452   conn->flags |= NETCONN_FLAG_MBOXCLOSED;
453 
454   /* reset conn->state now before waking up other threads */
455   old_state = conn->state;
456   conn->state = NETCONN_NONE;
457 
458   SYS_ARCH_UNPROTECT(lev);
459 
460   /* Notify the user layer about a connection error. Used to signal select. */
461   API_EVENT(conn, NETCONN_EVT_ERROR, 0);
462   /* Try to release selects pending on 'read' or 'write', too.
463      They will get an error if they actually try to read or write. */
464   API_EVENT(conn, NETCONN_EVT_RCVPLUS, 0);
465   API_EVENT(conn, NETCONN_EVT_SENDPLUS, 0);
466 
467   mbox_msg = lwip_netconn_err_to_msg(err);
468   /* pass error message to recvmbox to wake up pending recv */
469   if (NETCONN_MBOX_VALID(conn, &conn->recvmbox)) {
470     /* use trypost to prevent deadlock */
471     sys_mbox_trypost(&conn->recvmbox, mbox_msg);
472   }
473   /* pass error message to acceptmbox to wake up pending accept */
474   if (NETCONN_MBOX_VALID(conn, &conn->acceptmbox)) {
475     /* use trypost to prevent deadlock */
476     sys_mbox_trypost(&conn->acceptmbox, mbox_msg);
477   }
478 
479   if ((old_state == NETCONN_WRITE) || (old_state == NETCONN_CLOSE) ||
480       (old_state == NETCONN_CONNECT)) {
481     /* calling lwip_netconn_do_writemore/lwip_netconn_do_close_internal is not necessary
482        since the pcb has already been deleted! */
483     int was_nonblocking_connect = IN_NONBLOCKING_CONNECT(conn);
484     SET_NONBLOCKING_CONNECT(conn, 0);
485 
486     if (!was_nonblocking_connect) {
487       sys_sem_t *op_completed_sem;
488       /* set error return code */
489       LWIP_ASSERT("conn->current_msg != NULL", conn->current_msg != NULL);
490       if (old_state == NETCONN_CLOSE) {
491         /* let close succeed: the connection is closed after all... */
492         conn->current_msg->err = ERR_OK;
493       } else {
494         /* Write and connect fail */
495         conn->current_msg->err = err;
496       }
497       op_completed_sem = LWIP_API_MSG_SEM(conn->current_msg);
498       LWIP_ASSERT("invalid op_completed_sem", sys_sem_valid(op_completed_sem));
499       conn->current_msg = NULL;
500       /* wake up the waiting task */
501       sys_sem_signal(op_completed_sem);
502     } else {
503       /* @todo: test what happens for error on nonblocking connect */
504     }
505   } else {
506     LWIP_ASSERT("conn->current_msg == NULL", conn->current_msg == NULL);
507   }
508 }
509 
510 /**
511  * Setup a tcp_pcb with the correct callback function pointers
512  * and their arguments.
513  *
514  * @param conn the TCP netconn to setup
515  */
516 static void
517 setup_tcp(struct netconn *conn)
518 {
519   struct tcp_pcb *pcb;
520 
521   pcb = conn->pcb.tcp;
522   tcp_arg(pcb, conn);
523   tcp_recv(pcb, recv_tcp);
524   tcp_sent(pcb, sent_tcp);
525   tcp_poll(pcb, poll_tcp, NETCONN_TCP_POLL_INTERVAL);
526   tcp_err(pcb, err_tcp);
527 }
528 
529 /**
530  * Accept callback function for TCP netconns.
531  * Allocates a new netconn and posts that to conn->acceptmbox.
532  *
533  * @see tcp.h (struct tcp_pcb_listen.accept) for parameters and return value
534  */
535 static err_t
536 accept_function(void *arg, struct tcp_pcb *newpcb, err_t err)
537 {
538   struct netconn *newconn;
539   struct netconn *conn = (struct netconn *)arg;
540 
541   if (conn == NULL) {
542     return ERR_VAL;
543   }
544   if (!NETCONN_MBOX_VALID(conn, &conn->acceptmbox)) {
545     LWIP_DEBUGF(API_MSG_DEBUG, ("accept_function: acceptmbox already deleted\n"));
546     return ERR_VAL;
547   }
548 
549   if (newpcb == NULL) {
550     /* out-of-pcbs during connect: pass on this error to the application */
551     if (sys_mbox_trypost(&conn->acceptmbox, lwip_netconn_err_to_msg(ERR_ABRT)) == ERR_OK) {
552       /* Register event with callback */
553       API_EVENT(conn, NETCONN_EVT_RCVPLUS, 0);
554     }
555     return ERR_VAL;
556   }
557   LWIP_ASSERT("expect newpcb == NULL or err == ERR_OK", err == ERR_OK);
558   LWIP_UNUSED_ARG(err); /* for LWIP_NOASSERT */
559 
560   LWIP_DEBUGF(API_MSG_DEBUG, ("accept_function: newpcb->state: %s\n", tcp_debug_state_str(newpcb->state)));
561 
562   /* We have to set the callback here even though
563    * the new socket is unknown. newconn->socket is marked as -1. */
564   newconn = netconn_alloc(conn->type, conn->callback);
565   if (newconn == NULL) {
566     /* outof netconns: pass on this error to the application */
567     if (sys_mbox_trypost(&conn->acceptmbox, lwip_netconn_err_to_msg(ERR_ABRT)) == ERR_OK) {
568       /* Register event with callback */
569       API_EVENT(conn, NETCONN_EVT_RCVPLUS, 0);
570     }
571     return ERR_MEM;
572   }
573   newconn->pcb.tcp = newpcb;
574   setup_tcp(newconn);
575 
576   /* handle backlog counter */
577   tcp_backlog_delayed(newpcb);
578 
579   if (sys_mbox_trypost(&conn->acceptmbox, newconn) != ERR_OK) {
580     /* When returning != ERR_OK, the pcb is aborted in tcp_process(),
581        so do nothing here! */
582     /* remove all references to this netconn from the pcb */
583     struct tcp_pcb *pcb = newconn->pcb.tcp;
584     tcp_arg(pcb, NULL);
585     tcp_recv(pcb, NULL);
586     tcp_sent(pcb, NULL);
587     tcp_poll(pcb, NULL, 0);
588     tcp_err(pcb, NULL);
589     /* remove reference from to the pcb from this netconn */
590     newconn->pcb.tcp = NULL;
591     /* no need to drain since we know the recvmbox is empty. */
592     sys_mbox_free(&newconn->recvmbox);
593     sys_mbox_set_invalid(&newconn->recvmbox);
594     netconn_free(newconn);
595     return ERR_MEM;
596   } else {
597     /* Register event with callback */
598     API_EVENT(conn, NETCONN_EVT_RCVPLUS, 0);
599   }
600 
601   return ERR_OK;
602 }
603 #endif /* LWIP_TCP */
604 
605 /**
606  * Create a new pcb of a specific type.
607  * Called from lwip_netconn_do_newconn().
608  *
609  * @param msg the api_msg describing the connection type
610  */
611 static void
612 pcb_new(struct api_msg *msg)
613 {
614   enum lwip_ip_addr_type iptype = IPADDR_TYPE_V4;
615 
616   LWIP_ASSERT("pcb_new: pcb already allocated", msg->conn->pcb.tcp == NULL);
617 
618 #if LWIP_IPV6 && LWIP_IPV4
619   /* IPv6: Dual-stack by default, unless netconn_set_ipv6only() is called */
620   if (NETCONNTYPE_ISIPV6(netconn_type(msg->conn))) {
621     iptype = IPADDR_TYPE_ANY;
622   }
623 #endif
624 
625   /* Allocate a PCB for this connection */
626   switch (NETCONNTYPE_GROUP(msg->conn->type)) {
627 #if LWIP_RAW
628     case NETCONN_RAW:
629       msg->conn->pcb.raw = raw_new_ip_type(iptype, msg->msg.n.proto);
630       if (msg->conn->pcb.raw != NULL) {
631 #if LWIP_IPV6
632         /* ICMPv6 packets should always have checksum calculated by the stack as per RFC 3542 chapter 3.1 */
633         if (NETCONNTYPE_ISIPV6(msg->conn->type) && msg->conn->pcb.raw->protocol == IP6_NEXTH_ICMP6) {
634           msg->conn->pcb.raw->chksum_reqd = 1;
635           msg->conn->pcb.raw->chksum_offset = 2;
636         }
637 #endif /* LWIP_IPV6 */
638         raw_recv(msg->conn->pcb.raw, recv_raw, msg->conn);
639       }
640       break;
641 #endif /* LWIP_RAW */
642 #if LWIP_UDP
643     case NETCONN_UDP:
644       msg->conn->pcb.udp = udp_new_ip_type(iptype);
645       if (msg->conn->pcb.udp != NULL) {
646 #if LWIP_UDPLITE
647         if (NETCONNTYPE_ISUDPLITE(msg->conn->type)) {
648           udp_setflags(msg->conn->pcb.udp, UDP_FLAGS_UDPLITE);
649         }
650 #endif /* LWIP_UDPLITE */
651         if (NETCONNTYPE_ISUDPNOCHKSUM(msg->conn->type)) {
652           udp_setflags(msg->conn->pcb.udp, UDP_FLAGS_NOCHKSUM);
653         }
654         udp_recv(msg->conn->pcb.udp, recv_udp, msg->conn);
655       }
656       break;
657 #endif /* LWIP_UDP */
658 #if LWIP_TCP
659     case NETCONN_TCP:
660       msg->conn->pcb.tcp = tcp_new_ip_type(iptype);
661       if (msg->conn->pcb.tcp != NULL) {
662         setup_tcp(msg->conn);
663       }
664       break;
665 #endif /* LWIP_TCP */
666     default:
667       /* Unsupported netconn type, e.g. protocol disabled */
668       msg->err = ERR_VAL;
669       return;
670   }
671   if (msg->conn->pcb.ip == NULL) {
672     msg->err = ERR_MEM;
673   }
674 }
675 
676 /**
677  * Create a new pcb of a specific type inside a netconn.
678  * Called from netconn_new_with_proto_and_callback.
679  *
680  * @param m the api_msg describing the connection type
681  */
682 void
683 lwip_netconn_do_newconn(void *m)
684 {
685   struct api_msg *msg = (struct api_msg *)m;
686 
687   msg->err = ERR_OK;
688   if (msg->conn->pcb.tcp == NULL) {
689     pcb_new(msg);
690   }
691   /* Else? This "new" connection already has a PCB allocated. */
692   /* Is this an error condition? Should it be deleted? */
693   /* We currently just are happy and return. */
694 
695   TCPIP_APIMSG_ACK(msg);
696 }
697 
698 /**
699  * Create a new netconn (of a specific type) that has a callback function.
700  * The corresponding pcb is NOT created!
701  *
702  * @param t the type of 'connection' to create (@see enum netconn_type)
703  * @param callback a function to call on status changes (RX available, TX'ed)
704  * @return a newly allocated struct netconn or
705  *         NULL on memory error
706  */
707 struct netconn *
708 netconn_alloc(enum netconn_type t, netconn_callback callback)
709 {
710   struct netconn *conn;
711   int size;
712   u8_t init_flags = 0;
713 
714   conn = (struct netconn *)memp_malloc(MEMP_NETCONN);
715   if (conn == NULL) {
716     return NULL;
717   }
718 
719   conn->pending_err = ERR_OK;
720   conn->type = t;
721   conn->pcb.tcp = NULL;
722 #if LWIP_NETCONN_FULLDUPLEX
723   conn->mbox_threads_waiting = 0;
724 #endif
725 
726   /* If all sizes are the same, every compiler should optimize this switch to nothing */
727   switch (NETCONNTYPE_GROUP(t)) {
728 #if LWIP_RAW
729     case NETCONN_RAW:
730       size = DEFAULT_RAW_RECVMBOX_SIZE;
731       break;
732 #endif /* LWIP_RAW */
733 #if LWIP_UDP
734     case NETCONN_UDP:
735       size = DEFAULT_UDP_RECVMBOX_SIZE;
736 #if LWIP_NETBUF_RECVINFO
737       init_flags |= NETCONN_FLAG_PKTINFO;
738 #endif /* LWIP_NETBUF_RECVINFO */
739       break;
740 #endif /* LWIP_UDP */
741 #if LWIP_TCP
742     case NETCONN_TCP:
743       size = DEFAULT_TCP_RECVMBOX_SIZE;
744       break;
745 #endif /* LWIP_TCP */
746     default:
747       LWIP_ASSERT("netconn_alloc: undefined netconn_type", 0);
748       goto free_and_return;
749   }
750 
751   if (sys_mbox_new(&conn->recvmbox, size) != ERR_OK) {
752     goto free_and_return;
753   }
754 #if !LWIP_NETCONN_SEM_PER_THREAD
755   if (sys_sem_new(&conn->op_completed, 0) != ERR_OK) {
756     sys_mbox_free(&conn->recvmbox);
757     goto free_and_return;
758   }
759 #endif
760 
761 #if LWIP_TCP
762   sys_mbox_set_invalid(&conn->acceptmbox);
763 #endif
764   conn->state        = NETCONN_NONE;
765   /* initialize socket to -1 since 0 is a valid socket */
766   conn->callback_arg.socket = -1;
767   conn->callback     = callback;
768 #if LWIP_TCP
769   conn->current_msg  = NULL;
770 #endif /* LWIP_TCP */
771 #if LWIP_SO_SNDTIMEO
772   conn->send_timeout = 0;
773 #endif /* LWIP_SO_SNDTIMEO */
774 #if LWIP_SO_RCVTIMEO
775   conn->recv_timeout = 0;
776 #endif /* LWIP_SO_RCVTIMEO */
777 #if LWIP_SO_RCVBUF
778   conn->recv_bufsize = RECV_BUFSIZE_DEFAULT;
779   conn->recv_avail   = 0;
780 #endif /* LWIP_SO_RCVBUF */
781 #if LWIP_SO_LINGER
782   conn->linger = -1;
783 #endif /* LWIP_SO_LINGER */
784   conn->flags = init_flags;
785   return conn;
786 free_and_return:
787   memp_free(MEMP_NETCONN, conn);
788   return NULL;
789 }
790 
791 /**
792  * Delete a netconn and all its resources.
793  * The pcb is NOT freed (since we might not be in the right thread context do this).
794  *
795  * @param conn the netconn to free
796  */
797 void
798 netconn_free(struct netconn *conn)
799 {
800   LWIP_ASSERT("PCB must be deallocated outside this function", conn->pcb.tcp == NULL);
801 
802 #if LWIP_NETCONN_FULLDUPLEX
803   /* in fullduplex, netconn is drained here */
804   netconn_drain(conn);
805 #endif /* LWIP_NETCONN_FULLDUPLEX */
806 
807   LWIP_ASSERT("recvmbox must be deallocated before calling this function",
808               !sys_mbox_valid(&conn->recvmbox));
809 #if LWIP_TCP
810   LWIP_ASSERT("acceptmbox must be deallocated before calling this function",
811               !sys_mbox_valid(&conn->acceptmbox));
812 #endif /* LWIP_TCP */
813 
814 #if !LWIP_NETCONN_SEM_PER_THREAD
815   sys_sem_free(&conn->op_completed);
816   sys_sem_set_invalid(&conn->op_completed);
817 #endif
818 
819   memp_free(MEMP_NETCONN, conn);
820 }
821 
822 /**
823  * Delete rcvmbox and acceptmbox of a netconn and free the left-over data in
824  * these mboxes
825  *
826  * @param conn the netconn to free
827  * @bytes_drained bytes drained from recvmbox
828  * @accepts_drained pending connections drained from acceptmbox
829  */
830 static void
831 netconn_drain(struct netconn *conn)
832 {
833   void *mem;
834 
835   /* This runs when mbox and netconn are marked as closed,
836      so we don't need to lock against rx packets */
837 #if LWIP_NETCONN_FULLDUPLEX
838   LWIP_ASSERT("netconn marked closed", conn->flags & NETCONN_FLAG_MBOXINVALID);
839 #endif /* LWIP_NETCONN_FULLDUPLEX */
840 
841   /* Delete and drain the recvmbox. */
842   if (sys_mbox_valid(&conn->recvmbox)) {
843     while (sys_mbox_tryfetch(&conn->recvmbox, &mem) != SYS_MBOX_EMPTY) {
844 #if LWIP_NETCONN_FULLDUPLEX
845       if (!lwip_netconn_is_deallocated_msg(mem))
846 #endif /* LWIP_NETCONN_FULLDUPLEX */
847       {
848 #if LWIP_TCP
849         if (NETCONNTYPE_GROUP(conn->type) == NETCONN_TCP) {
850           err_t err;
851           if (!lwip_netconn_is_err_msg(mem, &err)) {
852             pbuf_free((struct pbuf *)mem);
853           }
854         } else
855 #endif /* LWIP_TCP */
856         {
857           netbuf_delete((struct netbuf *)mem);
858         }
859       }
860     }
861     sys_mbox_free(&conn->recvmbox);
862     sys_mbox_set_invalid(&conn->recvmbox);
863   }
864 
865   /* Delete and drain the acceptmbox. */
866 #if LWIP_TCP
867   if (sys_mbox_valid(&conn->acceptmbox)) {
868     while (sys_mbox_tryfetch(&conn->acceptmbox, &mem) != SYS_MBOX_EMPTY) {
869 #if LWIP_NETCONN_FULLDUPLEX
870       if (!lwip_netconn_is_deallocated_msg(mem))
871 #endif /* LWIP_NETCONN_FULLDUPLEX */
872       {
873         err_t err;
874         if (!lwip_netconn_is_err_msg(mem, &err)) {
875           struct netconn *newconn = (struct netconn *)mem;
876           /* Only tcp pcbs have an acceptmbox, so no need to check conn->type */
877           /* pcb might be set to NULL already by err_tcp() */
878           /* drain recvmbox */
879           netconn_drain(newconn);
880           if (newconn->pcb.tcp != NULL) {
881             tcp_abort(newconn->pcb.tcp);
882             newconn->pcb.tcp = NULL;
883           }
884           netconn_free(newconn);
885         }
886       }
887     }
888     sys_mbox_free(&conn->acceptmbox);
889     sys_mbox_set_invalid(&conn->acceptmbox);
890   }
891 #endif /* LWIP_TCP */
892 }
893 
894 #if LWIP_NETCONN_FULLDUPLEX
895 static void
896 netconn_mark_mbox_invalid(struct netconn *conn)
897 {
898   int i, num_waiting;
899   void *msg = LWIP_CONST_CAST(void *, &netconn_deleted);
900 
901   /* Prevent new calls/threads from reading from the mbox */
902   conn->flags |= NETCONN_FLAG_MBOXINVALID;
903 
904   SYS_ARCH_LOCKED(num_waiting = conn->mbox_threads_waiting);
905   for (i = 0; i < num_waiting; i++) {
906     if (sys_mbox_valid_val(conn->recvmbox)) {
907       sys_mbox_trypost(&conn->recvmbox, msg);
908     } else {
909       sys_mbox_trypost(&conn->acceptmbox, msg);
910     }
911   }
912 }
913 #endif /* LWIP_NETCONN_FULLDUPLEX */
914 
915 #if LWIP_TCP
916 /**
917  * Internal helper function to close a TCP netconn: since this sometimes
918  * doesn't work at the first attempt, this function is called from multiple
919  * places.
920  *
921  * @param conn the TCP netconn to close
922  */
923 static err_t
924 lwip_netconn_do_close_internal(struct netconn *conn  WRITE_DELAYED_PARAM)
925 {
926   err_t err;
927   u8_t shut, shut_rx, shut_tx, shut_close;
928   u8_t close_finished = 0;
929   struct tcp_pcb *tpcb;
930 #if LWIP_SO_LINGER
931   u8_t linger_wait_required = 0;
932 #endif /* LWIP_SO_LINGER */
933 
934   LWIP_ASSERT("invalid conn", (conn != NULL));
935   LWIP_ASSERT("this is for tcp netconns only", (NETCONNTYPE_GROUP(conn->type) == NETCONN_TCP));
936   LWIP_ASSERT("conn must be in state NETCONN_CLOSE", (conn->state == NETCONN_CLOSE));
937   LWIP_ASSERT("pcb already closed", (conn->pcb.tcp != NULL));
938   LWIP_ASSERT("conn->current_msg != NULL", conn->current_msg != NULL);
939 
940   tpcb = conn->pcb.tcp;
941   shut = conn->current_msg->msg.sd.shut;
942   shut_rx = shut & NETCONN_SHUT_RD;
943   shut_tx = shut & NETCONN_SHUT_WR;
944   /* shutting down both ends is the same as closing
945      (also if RD or WR side was shut down before already) */
946   if (shut == NETCONN_SHUT_RDWR) {
947     shut_close = 1;
948   } else if (shut_rx &&
949              ((tpcb->state == FIN_WAIT_1) ||
950               (tpcb->state == FIN_WAIT_2) ||
951               (tpcb->state == CLOSING))) {
952     shut_close = 1;
953   } else if (shut_tx && ((tpcb->flags & TF_RXCLOSED) != 0)) {
954     shut_close = 1;
955   } else {
956     shut_close = 0;
957   }
958 
959   /* Set back some callback pointers */
960   if (shut_close) {
961     tcp_arg(tpcb, NULL);
962   }
963   if (tpcb->state == LISTEN) {
964     tcp_accept(tpcb, NULL);
965   } else {
966     /* some callbacks have to be reset if tcp_close is not successful */
967     if (shut_rx) {
968       tcp_recv(tpcb, NULL);
969       tcp_accept(tpcb, NULL);
970     }
971     if (shut_tx) {
972       tcp_sent(tpcb, NULL);
973     }
974     if (shut_close) {
975       tcp_poll(tpcb, NULL, 0);
976       tcp_err(tpcb, NULL);
977     }
978   }
979   /* Try to close the connection */
980   if (shut_close) {
981 #if LWIP_SO_LINGER
982     /* check linger possibilities before calling tcp_close */
983     err = ERR_OK;
984     /* linger enabled/required at all? (i.e. is there untransmitted data left?) */
985     if ((conn->linger >= 0) && (conn->pcb.tcp->unsent || conn->pcb.tcp->unacked)) {
986       if ((conn->linger == 0)) {
987         /* data left but linger prevents waiting */
988         tcp_abort(tpcb);
989         tpcb = NULL;
990       } else if (conn->linger > 0) {
991         /* data left and linger says we should wait */
992         if (netconn_is_nonblocking(conn)) {
993           /* data left on a nonblocking netconn -> cannot linger */
994           err = ERR_WOULDBLOCK;
995         } else if ((s32_t)(sys_now() - conn->current_msg->msg.sd.time_started) >=
996                    (conn->linger * 1000)) {
997           /* data left but linger timeout has expired (this happens on further
998              calls to this function through poll_tcp */
999           tcp_abort(tpcb);
1000           tpcb = NULL;
1001         } else {
1002           /* data left -> need to wait for ACK after successful close */
1003           linger_wait_required = 1;
1004         }
1005       }
1006     }
1007     if ((err == ERR_OK) && (tpcb != NULL))
1008 #endif /* LWIP_SO_LINGER */
1009     {
1010       err = tcp_close(tpcb);
1011     }
1012   } else {
1013     err = tcp_shutdown(tpcb, shut_rx, shut_tx);
1014   }
1015   if (err == ERR_OK) {
1016     close_finished = 1;
1017 #if LWIP_SO_LINGER
1018     if (linger_wait_required) {
1019       /* wait for ACK of all unsent/unacked data by just getting called again */
1020       close_finished = 0;
1021       err = ERR_INPROGRESS;
1022     }
1023 #endif /* LWIP_SO_LINGER */
1024   } else {
1025     if (err == ERR_MEM) {
1026       /* Closing failed because of memory shortage, try again later. Even for
1027          nonblocking netconns, we have to wait since no standard socket application
1028          is prepared for close failing because of resource shortage.
1029          Check the timeout: this is kind of an lwip addition to the standard sockets:
1030          we wait for some time when failing to allocate a segment for the FIN */
1031 #if LWIP_SO_SNDTIMEO || LWIP_SO_LINGER
1032       s32_t close_timeout = LWIP_TCP_CLOSE_TIMEOUT_MS_DEFAULT;
1033 #if LWIP_SO_SNDTIMEO
1034       if (conn->send_timeout > 0) {
1035         close_timeout = conn->send_timeout;
1036       }
1037 #endif /* LWIP_SO_SNDTIMEO */
1038 #if LWIP_SO_LINGER
1039       if (conn->linger >= 0) {
1040         /* use linger timeout (seconds) */
1041         close_timeout = conn->linger * 1000U;
1042       }
1043 #endif
1044       if ((s32_t)(sys_now() - conn->current_msg->msg.sd.time_started) >= close_timeout) {
1045 #else /* LWIP_SO_SNDTIMEO || LWIP_SO_LINGER */
1046       if (conn->current_msg->msg.sd.polls_left == 0) {
1047 #endif /* LWIP_SO_SNDTIMEO || LWIP_SO_LINGER */
1048         close_finished = 1;
1049         if (shut_close) {
1050           /* in this case, we want to RST the connection */
1051           tcp_abort(tpcb);
1052           err = ERR_OK;
1053         }
1054       }
1055     } else {
1056       /* Closing failed for a non-memory error: give up */
1057       close_finished = 1;
1058     }
1059   }
1060   if (close_finished) {
1061     /* Closing done (succeeded, non-memory error, nonblocking error or timeout) */
1062     sys_sem_t *op_completed_sem = LWIP_API_MSG_SEM(conn->current_msg);
1063     conn->current_msg->err = err;
1064     conn->current_msg = NULL;
1065     conn->state = NETCONN_NONE;
1066     if (err == ERR_OK) {
1067       if (shut_close) {
1068         /* Set back some callback pointers as conn is going away */
1069         conn->pcb.tcp = NULL;
1070         /* Trigger select() in socket layer. Make sure everybody notices activity
1071          on the connection, error first! */
1072         API_EVENT(conn, NETCONN_EVT_ERROR, 0);
1073       }
1074       if (shut_rx) {
1075         API_EVENT(conn, NETCONN_EVT_RCVPLUS, 0);
1076       }
1077       if (shut_tx) {
1078         API_EVENT(conn, NETCONN_EVT_SENDPLUS, 0);
1079       }
1080     }
1081 #if LWIP_TCPIP_CORE_LOCKING
1082     if (delayed)
1083 #endif
1084     {
1085       /* wake up the application task */
1086       sys_sem_signal(op_completed_sem);
1087     }
1088     return ERR_OK;
1089   }
1090   if (!close_finished) {
1091     /* Closing failed and we want to wait: restore some of the callbacks */
1092     /* Closing of listen pcb will never fail! */
1093     LWIP_ASSERT("Closing a listen pcb may not fail!", (tpcb->state != LISTEN));
1094     if (shut_tx) {
1095       tcp_sent(tpcb, sent_tcp);
1096     }
1097     /* when waiting for close, set up poll interval to 500ms */
1098     tcp_poll(tpcb, poll_tcp, 1);
1099     tcp_err(tpcb, err_tcp);
1100     tcp_arg(tpcb, conn);
1101     /* don't restore recv callback: we don't want to receive any more data */
1102   }
1103   /* If closing didn't succeed, we get called again either
1104      from poll_tcp or from sent_tcp */
1105   LWIP_ASSERT("err != ERR_OK", err != ERR_OK);
1106   return err;
1107 }
1108 #endif /* LWIP_TCP */
1109 
1110 /**
1111  * Delete the pcb inside a netconn.
1112  * Called from netconn_delete.
1113  *
1114  * @param m the api_msg pointing to the connection
1115  */
1116 void
1117 lwip_netconn_do_delconn(void *m)
1118 {
1119   struct api_msg *msg = (struct api_msg *)m;
1120 
1121   enum netconn_state state = msg->conn->state;
1122   LWIP_ASSERT("netconn state error", /* this only happens for TCP netconns */
1123               (state == NETCONN_NONE) || (NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_TCP));
1124 #if LWIP_NETCONN_FULLDUPLEX
1125   /* In full duplex mode, blocking write/connect is aborted with ERR_CLSD */
1126   if (state != NETCONN_NONE) {
1127     if ((state == NETCONN_WRITE) ||
1128         ((state == NETCONN_CONNECT) && !IN_NONBLOCKING_CONNECT(msg->conn))) {
1129       /* close requested, abort running write/connect */
1130       sys_sem_t *op_completed_sem;
1131       LWIP_ASSERT("msg->conn->current_msg != NULL", msg->conn->current_msg != NULL);
1132       op_completed_sem = LWIP_API_MSG_SEM(msg->conn->current_msg);
1133       msg->conn->current_msg->err = ERR_CLSD;
1134       msg->conn->current_msg = NULL;
1135       msg->conn->state = NETCONN_NONE;
1136       sys_sem_signal(op_completed_sem);
1137     }
1138   }
1139 #else /* LWIP_NETCONN_FULLDUPLEX */
1140   if (((state != NETCONN_NONE) &&
1141        (state != NETCONN_LISTEN) &&
1142        (state != NETCONN_CONNECT)) ||
1143       ((state == NETCONN_CONNECT) && !IN_NONBLOCKING_CONNECT(msg->conn))) {
1144     /* This means either a blocking write or blocking connect is running
1145        (nonblocking write returns and sets state to NONE) */
1146     msg->err = ERR_INPROGRESS;
1147   } else
1148 #endif /* LWIP_NETCONN_FULLDUPLEX */
1149   {
1150     LWIP_ASSERT("blocking connect in progress",
1151                 (state != NETCONN_CONNECT) || IN_NONBLOCKING_CONNECT(msg->conn));
1152     msg->err = ERR_OK;
1153 #if LWIP_NETCONN_FULLDUPLEX
1154     /* Mark mboxes invalid */
1155     netconn_mark_mbox_invalid(msg->conn);
1156 #else /* LWIP_NETCONN_FULLDUPLEX */
1157     netconn_drain(msg->conn);
1158 #endif /* LWIP_NETCONN_FULLDUPLEX */
1159 
1160     if (msg->conn->pcb.tcp != NULL) {
1161 
1162       switch (NETCONNTYPE_GROUP(msg->conn->type)) {
1163 #if LWIP_RAW
1164         case NETCONN_RAW:
1165           raw_remove(msg->conn->pcb.raw);
1166           break;
1167 #endif /* LWIP_RAW */
1168 #if LWIP_UDP
1169         case NETCONN_UDP:
1170           msg->conn->pcb.udp->recv_arg = NULL;
1171           udp_remove(msg->conn->pcb.udp);
1172           break;
1173 #endif /* LWIP_UDP */
1174 #if LWIP_TCP
1175         case NETCONN_TCP:
1176           LWIP_ASSERT("already writing or closing", msg->conn->current_msg == NULL);
1177           msg->conn->state = NETCONN_CLOSE;
1178           msg->msg.sd.shut = NETCONN_SHUT_RDWR;
1179           msg->conn->current_msg = msg;
1180 #if LWIP_TCPIP_CORE_LOCKING
1181           if (lwip_netconn_do_close_internal(msg->conn, 0) != ERR_OK) {
1182             LWIP_ASSERT("state!", msg->conn->state == NETCONN_CLOSE);
1183             UNLOCK_TCPIP_CORE();
1184             sys_arch_sem_wait(LWIP_API_MSG_SEM(msg), 0);
1185             LOCK_TCPIP_CORE();
1186             LWIP_ASSERT("state!", msg->conn->state == NETCONN_NONE);
1187           }
1188 #else /* LWIP_TCPIP_CORE_LOCKING */
1189           lwip_netconn_do_close_internal(msg->conn);
1190 #endif /* LWIP_TCPIP_CORE_LOCKING */
1191           /* API_EVENT is called inside lwip_netconn_do_close_internal, before releasing
1192              the application thread, so we can return at this point! */
1193           return;
1194 #endif /* LWIP_TCP */
1195         default:
1196           break;
1197       }
1198       msg->conn->pcb.tcp = NULL;
1199     }
1200     /* tcp netconns don't come here! */
1201 
1202     /* @todo: this lets select make the socket readable and writable,
1203        which is wrong! errfd instead? */
1204     API_EVENT(msg->conn, NETCONN_EVT_RCVPLUS, 0);
1205     API_EVENT(msg->conn, NETCONN_EVT_SENDPLUS, 0);
1206   }
1207   if (sys_sem_valid(LWIP_API_MSG_SEM(msg))) {
1208     TCPIP_APIMSG_ACK(msg);
1209   }
1210 }
1211 
1212 /**
1213  * Bind a pcb contained in a netconn
1214  * Called from netconn_bind.
1215  *
1216  * @param m the api_msg pointing to the connection and containing
1217  *          the IP address and port to bind to
1218  */
1219 void
1220 lwip_netconn_do_bind(void *m)
1221 {
1222   struct api_msg *msg = (struct api_msg *)m;
1223   err_t err;
1224 
1225   if (msg->conn->pcb.tcp != NULL) {
1226     switch (NETCONNTYPE_GROUP(msg->conn->type)) {
1227 #if LWIP_RAW
1228       case NETCONN_RAW:
1229         err = raw_bind(msg->conn->pcb.raw, API_EXPR_REF(msg->msg.bc.ipaddr));
1230         break;
1231 #endif /* LWIP_RAW */
1232 #if LWIP_UDP
1233       case NETCONN_UDP:
1234         err = udp_bind(msg->conn->pcb.udp, API_EXPR_REF(msg->msg.bc.ipaddr), msg->msg.bc.port);
1235         break;
1236 #endif /* LWIP_UDP */
1237 #if LWIP_TCP
1238       case NETCONN_TCP:
1239         err = tcp_bind(msg->conn->pcb.tcp, API_EXPR_REF(msg->msg.bc.ipaddr), msg->msg.bc.port);
1240         break;
1241 #endif /* LWIP_TCP */
1242       default:
1243         err = ERR_VAL;
1244         break;
1245     }
1246   } else {
1247     err = ERR_VAL;
1248   }
1249   msg->err = err;
1250   TCPIP_APIMSG_ACK(msg);
1251 }
1252 /**
1253  * Bind a pcb contained in a netconn to an interface
1254  * Called from netconn_bind_if.
1255  *
1256  * @param m the api_msg pointing to the connection and containing
1257  *          the IP address and port to bind to
1258  */
1259 void
1260 lwip_netconn_do_bind_if(void *m)
1261 {
1262   struct netif *netif;
1263   struct api_msg *msg = (struct api_msg *)m;
1264   err_t err;
1265 
1266   netif = netif_get_by_index(msg->msg.bc.if_idx);
1267 
1268   if ((netif != NULL) && (msg->conn->pcb.tcp != NULL)) {
1269     err = ERR_OK;
1270     switch (NETCONNTYPE_GROUP(msg->conn->type)) {
1271 #if LWIP_RAW
1272       case NETCONN_RAW:
1273         raw_bind_netif(msg->conn->pcb.raw, netif);
1274         break;
1275 #endif /* LWIP_RAW */
1276 #if LWIP_UDP
1277       case NETCONN_UDP:
1278         udp_bind_netif(msg->conn->pcb.udp, netif);
1279         break;
1280 #endif /* LWIP_UDP */
1281 #if LWIP_TCP
1282       case NETCONN_TCP:
1283         tcp_bind_netif(msg->conn->pcb.tcp, netif);
1284         break;
1285 #endif /* LWIP_TCP */
1286       default:
1287         err = ERR_VAL;
1288         break;
1289     }
1290   } else {
1291     err = ERR_VAL;
1292   }
1293   msg->err = err;
1294   TCPIP_APIMSG_ACK(msg);
1295 }
1296 
1297 #if LWIP_TCP
1298 /**
1299  * TCP callback function if a connection (opened by tcp_connect/lwip_netconn_do_connect) has
1300  * been established (or reset by the remote host).
1301  *
1302  * @see tcp.h (struct tcp_pcb.connected) for parameters and return values
1303  */
1304 static err_t
1305 lwip_netconn_do_connected(void *arg, struct tcp_pcb *pcb, err_t err)
1306 {
1307   struct netconn *conn;
1308   int was_blocking;
1309   sys_sem_t *op_completed_sem = NULL;
1310 
1311   LWIP_UNUSED_ARG(pcb);
1312 
1313   conn = (struct netconn *)arg;
1314 
1315   if (conn == NULL) {
1316     return ERR_VAL;
1317   }
1318 
1319   LWIP_ASSERT("conn->state == NETCONN_CONNECT", conn->state == NETCONN_CONNECT);
1320   LWIP_ASSERT("(conn->current_msg != NULL) || conn->in_non_blocking_connect",
1321               (conn->current_msg != NULL) || IN_NONBLOCKING_CONNECT(conn));
1322 
1323   if (conn->current_msg != NULL) {
1324     conn->current_msg->err = err;
1325     op_completed_sem = LWIP_API_MSG_SEM(conn->current_msg);
1326   }
1327   if ((NETCONNTYPE_GROUP(conn->type) == NETCONN_TCP) && (err == ERR_OK)) {
1328     setup_tcp(conn);
1329   }
1330   was_blocking = !IN_NONBLOCKING_CONNECT(conn);
1331   SET_NONBLOCKING_CONNECT(conn, 0);
1332   LWIP_ASSERT("blocking connect state error",
1333               (was_blocking && op_completed_sem != NULL) ||
1334               (!was_blocking && op_completed_sem == NULL));
1335   conn->current_msg = NULL;
1336   conn->state = NETCONN_NONE;
1337   API_EVENT(conn, NETCONN_EVT_SENDPLUS, 0);
1338 
1339   if (was_blocking) {
1340     sys_sem_signal(op_completed_sem);
1341   }
1342   return ERR_OK;
1343 }
1344 #endif /* LWIP_TCP */
1345 
1346 /**
1347  * Connect a pcb contained inside a netconn
1348  * Called from netconn_connect.
1349  *
1350  * @param m the api_msg pointing to the connection and containing
1351  *          the IP address and port to connect to
1352  */
1353 void
1354 lwip_netconn_do_connect(void *m)
1355 {
1356   struct api_msg *msg = (struct api_msg *)m;
1357   err_t err;
1358 
1359   if (msg->conn->pcb.tcp == NULL) {
1360     /* This may happen when calling netconn_connect() a second time */
1361     err = ERR_CLSD;
1362   } else {
1363     switch (NETCONNTYPE_GROUP(msg->conn->type)) {
1364 #if LWIP_RAW
1365       case NETCONN_RAW:
1366         err = raw_connect(msg->conn->pcb.raw, API_EXPR_REF(msg->msg.bc.ipaddr));
1367         break;
1368 #endif /* LWIP_RAW */
1369 #if LWIP_UDP
1370       case NETCONN_UDP:
1371         err = udp_connect(msg->conn->pcb.udp, API_EXPR_REF(msg->msg.bc.ipaddr), msg->msg.bc.port);
1372         break;
1373 #endif /* LWIP_UDP */
1374 #if LWIP_TCP
1375       case NETCONN_TCP:
1376         /* Prevent connect while doing any other action. */
1377         if (msg->conn->state == NETCONN_CONNECT) {
1378           err = ERR_ALREADY;
1379         } else if (msg->conn->state != NETCONN_NONE) {
1380           err = ERR_ISCONN;
1381         } else {
1382           setup_tcp(msg->conn);
1383           err = tcp_connect(msg->conn->pcb.tcp, API_EXPR_REF(msg->msg.bc.ipaddr),
1384                             msg->msg.bc.port, lwip_netconn_do_connected);
1385           if (err == ERR_OK) {
1386             u8_t non_blocking = netconn_is_nonblocking(msg->conn);
1387             msg->conn->state = NETCONN_CONNECT;
1388             SET_NONBLOCKING_CONNECT(msg->conn, non_blocking);
1389             if (non_blocking) {
1390               err = ERR_INPROGRESS;
1391             } else {
1392               msg->conn->current_msg = msg;
1393               /* sys_sem_signal() is called from lwip_netconn_do_connected (or err_tcp()),
1394                  when the connection is established! */
1395 #if LWIP_TCPIP_CORE_LOCKING
1396               LWIP_ASSERT("state!", msg->conn->state == NETCONN_CONNECT);
1397               UNLOCK_TCPIP_CORE();
1398               sys_arch_sem_wait(LWIP_API_MSG_SEM(msg), 0);
1399               LOCK_TCPIP_CORE();
1400               LWIP_ASSERT("state!", msg->conn->state != NETCONN_CONNECT);
1401 #endif /* LWIP_TCPIP_CORE_LOCKING */
1402               return;
1403             }
1404           }
1405         }
1406         break;
1407 #endif /* LWIP_TCP */
1408       default:
1409         LWIP_ERROR("Invalid netconn type", 0, do {
1410           err = ERR_VAL;
1411         } while (0));
1412         break;
1413     }
1414   }
1415   msg->err = err;
1416   /* For all other protocols, netconn_connect() calls netconn_apimsg(),
1417      so use TCPIP_APIMSG_ACK() here. */
1418   TCPIP_APIMSG_ACK(msg);
1419 }
1420 
1421 /**
1422  * Disconnect a pcb contained inside a netconn
1423  * Only used for UDP netconns.
1424  * Called from netconn_disconnect.
1425  *
1426  * @param m the api_msg pointing to the connection to disconnect
1427  */
1428 void
1429 lwip_netconn_do_disconnect(void *m)
1430 {
1431   struct api_msg *msg = (struct api_msg *)m;
1432 
1433 #if LWIP_UDP
1434   if (NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_UDP) {
1435     udp_disconnect(msg->conn->pcb.udp);
1436     msg->err = ERR_OK;
1437   } else
1438 #endif /* LWIP_UDP */
1439   {
1440     msg->err = ERR_VAL;
1441   }
1442   TCPIP_APIMSG_ACK(msg);
1443 }
1444 
1445 #if LWIP_TCP
1446 /**
1447  * Set a TCP pcb contained in a netconn into listen mode
1448  * Called from netconn_listen.
1449  *
1450  * @param m the api_msg pointing to the connection
1451  */
1452 void
1453 lwip_netconn_do_listen(void *m)
1454 {
1455   struct api_msg *msg = (struct api_msg *)m;
1456   err_t err;
1457 
1458   if (msg->conn->pcb.tcp != NULL) {
1459     if (NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_TCP) {
1460       if (msg->conn->state == NETCONN_NONE) {
1461         struct tcp_pcb *lpcb;
1462         if (msg->conn->pcb.tcp->state != CLOSED) {
1463           /* connection is not closed, cannot listen */
1464           err = ERR_VAL;
1465         } else {
1466           u8_t backlog;
1467 #if TCP_LISTEN_BACKLOG
1468           backlog = msg->msg.lb.backlog;
1469 #else  /* TCP_LISTEN_BACKLOG */
1470           backlog = TCP_DEFAULT_LISTEN_BACKLOG;
1471 #endif /* TCP_LISTEN_BACKLOG */
1472 #if LWIP_IPV4 && LWIP_IPV6
1473           /* "Socket API like" dual-stack support: If IP to listen to is IP6_ADDR_ANY,
1474             * and NETCONN_FLAG_IPV6_V6ONLY is NOT set, use IP_ANY_TYPE to listen
1475             */
1476           if (ip_addr_eq(&msg->conn->pcb.ip->local_ip, IP6_ADDR_ANY) &&
1477               (netconn_get_ipv6only(msg->conn) == 0)) {
1478             /* change PCB type to IPADDR_TYPE_ANY */
1479             IP_SET_TYPE_VAL(msg->conn->pcb.tcp->local_ip,  IPADDR_TYPE_ANY);
1480             IP_SET_TYPE_VAL(msg->conn->pcb.tcp->remote_ip, IPADDR_TYPE_ANY);
1481           }
1482 #endif /* LWIP_IPV4 && LWIP_IPV6 */
1483 
1484           lpcb = tcp_listen_with_backlog_and_err(msg->conn->pcb.tcp, backlog, &err);
1485 
1486           if (lpcb == NULL) {
1487             /* in this case, the old pcb is still allocated */
1488           } else {
1489             /* delete the recvmbox and allocate the acceptmbox */
1490             if (sys_mbox_valid(&msg->conn->recvmbox)) {
1491               /** @todo: should we drain the recvmbox here? */
1492               sys_mbox_free(&msg->conn->recvmbox);
1493               sys_mbox_set_invalid(&msg->conn->recvmbox);
1494             }
1495             err = ERR_OK;
1496             if (!sys_mbox_valid(&msg->conn->acceptmbox)) {
1497               err = sys_mbox_new(&msg->conn->acceptmbox, DEFAULT_ACCEPTMBOX_SIZE);
1498             }
1499             if (err == ERR_OK) {
1500               msg->conn->state = NETCONN_LISTEN;
1501               msg->conn->pcb.tcp = lpcb;
1502               tcp_arg(msg->conn->pcb.tcp, msg->conn);
1503               tcp_accept(msg->conn->pcb.tcp, accept_function);
1504             } else {
1505               /* since the old pcb is already deallocated, free lpcb now */
1506               tcp_close(lpcb);
1507               msg->conn->pcb.tcp = NULL;
1508             }
1509           }
1510         }
1511       } else if (msg->conn->state == NETCONN_LISTEN) {
1512         /* already listening, allow updating of the backlog */
1513         err = ERR_OK;
1514         tcp_backlog_set(msg->conn->pcb.tcp, msg->msg.lb.backlog);
1515       } else {
1516         err = ERR_CONN;
1517       }
1518     } else {
1519       err = ERR_ARG;
1520     }
1521   } else {
1522     err = ERR_CONN;
1523   }
1524   msg->err = err;
1525   TCPIP_APIMSG_ACK(msg);
1526 }
1527 #endif /* LWIP_TCP */
1528 
1529 /**
1530  * Send some data on a RAW or UDP pcb contained in a netconn
1531  * Called from netconn_send
1532  *
1533  * @param m the api_msg pointing to the connection
1534  */
1535 void
1536 lwip_netconn_do_send(void *m)
1537 {
1538   struct api_msg *msg = (struct api_msg *)m;
1539 
1540   err_t err = netconn_err(msg->conn);
1541   if (err == ERR_OK) {
1542     if (msg->conn->pcb.tcp != NULL) {
1543       switch (NETCONNTYPE_GROUP(msg->conn->type)) {
1544 #if LWIP_RAW
1545         case NETCONN_RAW:
1546           if (ip_addr_isany(&msg->msg.b->addr) || IP_IS_ANY_TYPE_VAL(msg->msg.b->addr)) {
1547             err = raw_send(msg->conn->pcb.raw, msg->msg.b->p);
1548           } else {
1549             err = raw_sendto(msg->conn->pcb.raw, msg->msg.b->p, &msg->msg.b->addr);
1550           }
1551           break;
1552 #endif
1553 #if LWIP_UDP
1554         case NETCONN_UDP:
1555 #if LWIP_CHECKSUM_ON_COPY
1556           if (ip_addr_isany(&msg->msg.b->addr) || IP_IS_ANY_TYPE_VAL(msg->msg.b->addr)) {
1557             err = udp_send_chksum(msg->conn->pcb.udp, msg->msg.b->p,
1558                                   msg->msg.b->flags & NETBUF_FLAG_CHKSUM, msg->msg.b->toport_chksum);
1559           } else {
1560             err = udp_sendto_chksum(msg->conn->pcb.udp, msg->msg.b->p,
1561                                     &msg->msg.b->addr, msg->msg.b->port,
1562                                     msg->msg.b->flags & NETBUF_FLAG_CHKSUM, msg->msg.b->toport_chksum);
1563           }
1564 #else /* LWIP_CHECKSUM_ON_COPY */
1565           if (ip_addr_isany_val(msg->msg.b->addr) || IP_IS_ANY_TYPE_VAL(msg->msg.b->addr)) {
1566             err = udp_send(msg->conn->pcb.udp, msg->msg.b->p);
1567           } else {
1568             err = udp_sendto(msg->conn->pcb.udp, msg->msg.b->p, &msg->msg.b->addr, msg->msg.b->port);
1569           }
1570 #endif /* LWIP_CHECKSUM_ON_COPY */
1571           break;
1572 #endif /* LWIP_UDP */
1573         default:
1574           err = ERR_CONN;
1575           break;
1576       }
1577     } else {
1578       err = ERR_CONN;
1579     }
1580   }
1581   msg->err = err;
1582   TCPIP_APIMSG_ACK(msg);
1583 }
1584 
1585 #if LWIP_TCP
1586 /**
1587  * Indicate data has been received from a TCP pcb contained in a netconn
1588  * Called from netconn_recv
1589  *
1590  * @param m the api_msg pointing to the connection
1591  */
1592 void
1593 lwip_netconn_do_recv(void *m)
1594 {
1595   struct api_msg *msg = (struct api_msg *)m;
1596 
1597   msg->err = ERR_OK;
1598   if (msg->conn->pcb.tcp != NULL) {
1599     if (NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_TCP) {
1600       size_t remaining = msg->msg.r.len;
1601       do {
1602         u16_t recved = (u16_t)((remaining > 0xffff) ? 0xffff : remaining);
1603         tcp_recved(msg->conn->pcb.tcp, recved);
1604         remaining -= recved;
1605       } while (remaining != 0);
1606     }
1607   }
1608   TCPIP_APIMSG_ACK(msg);
1609 }
1610 
1611 #if TCP_LISTEN_BACKLOG
1612 /** Indicate that a TCP pcb has been accepted
1613  * Called from netconn_accept
1614  *
1615  * @param m the api_msg pointing to the connection
1616  */
1617 void
1618 lwip_netconn_do_accepted(void *m)
1619 {
1620   struct api_msg *msg = (struct api_msg *)m;
1621 
1622   msg->err = ERR_OK;
1623   if (msg->conn->pcb.tcp != NULL) {
1624     if (NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_TCP) {
1625       tcp_backlog_accepted(msg->conn->pcb.tcp);
1626     }
1627   }
1628   TCPIP_APIMSG_ACK(msg);
1629 }
1630 #endif /* TCP_LISTEN_BACKLOG */
1631 
1632 /**
1633  * See if more data needs to be written from a previous call to netconn_write.
1634  * Called initially from lwip_netconn_do_write. If the first call can't send all data
1635  * (because of low memory or empty send-buffer), this function is called again
1636  * from sent_tcp() or poll_tcp() to send more data. If all data is sent, the
1637  * blocking application thread (waiting in netconn_write) is released.
1638  *
1639  * @param conn netconn (that is currently in state NETCONN_WRITE) to process
1640  * @return ERR_OK
1641  *         ERR_MEM if LWIP_TCPIP_CORE_LOCKING=1 and sending hasn't yet finished
1642  */
1643 static err_t
1644 lwip_netconn_do_writemore(struct netconn *conn  WRITE_DELAYED_PARAM)
1645 {
1646   err_t err;
1647   const void *dataptr;
1648   u16_t len, available;
1649   u8_t write_finished = 0;
1650   size_t diff;
1651   u8_t dontblock;
1652   u8_t apiflags;
1653   u8_t write_more;
1654 
1655   LWIP_ASSERT("conn != NULL", conn != NULL);
1656   LWIP_ASSERT("conn->state == NETCONN_WRITE", (conn->state == NETCONN_WRITE));
1657   LWIP_ASSERT("conn->current_msg != NULL", conn->current_msg != NULL);
1658   LWIP_ASSERT("conn->pcb.tcp != NULL", conn->pcb.tcp != NULL);
1659   LWIP_ASSERT("conn->current_msg->msg.w.offset < conn->current_msg->msg.w.len",
1660               conn->current_msg->msg.w.offset < conn->current_msg->msg.w.len);
1661   LWIP_ASSERT("conn->current_msg->msg.w.vector_cnt > 0", conn->current_msg->msg.w.vector_cnt > 0);
1662 
1663   apiflags = conn->current_msg->msg.w.apiflags;
1664   dontblock = netconn_is_nonblocking(conn) || (apiflags & NETCONN_DONTBLOCK);
1665 
1666 #if LWIP_SO_SNDTIMEO
1667   if ((conn->send_timeout != 0) &&
1668       ((s32_t)(sys_now() - conn->current_msg->msg.w.time_started) >= conn->send_timeout)) {
1669     write_finished = 1;
1670     if (conn->current_msg->msg.w.offset == 0) {
1671       /* nothing has been written */
1672       err = ERR_WOULDBLOCK;
1673     } else {
1674       /* partial write */
1675       err = ERR_OK;
1676     }
1677   } else
1678 #endif /* LWIP_SO_SNDTIMEO */
1679   {
1680     do {
1681       dataptr = (const u8_t *)conn->current_msg->msg.w.vector->ptr + conn->current_msg->msg.w.vector_off;
1682       diff = conn->current_msg->msg.w.vector->len - conn->current_msg->msg.w.vector_off;
1683       if (diff > 0xffffUL) { /* max_u16_t */
1684         len = 0xffff;
1685         apiflags |= TCP_WRITE_FLAG_MORE;
1686       } else {
1687         len = (u16_t)diff;
1688       }
1689       available = tcp_sndbuf(conn->pcb.tcp);
1690       if (available < len) {
1691         /* don't try to write more than sendbuf */
1692         len = available;
1693         if (dontblock) {
1694           if (!len) {
1695             /* set error according to partial write or not */
1696             err = (conn->current_msg->msg.w.offset == 0) ? ERR_WOULDBLOCK : ERR_OK;
1697             goto err_mem;
1698           }
1699         } else {
1700           apiflags |= TCP_WRITE_FLAG_MORE;
1701         }
1702       }
1703       LWIP_ASSERT("lwip_netconn_do_writemore: invalid length!",
1704                   ((conn->current_msg->msg.w.vector_off + len) <= conn->current_msg->msg.w.vector->len));
1705       /* we should loop around for more sending in the following cases:
1706            1) We couldn't finish the current vector because of 16-bit size limitations.
1707               tcp_write() and tcp_sndbuf() both are limited to 16-bit sizes
1708            2) We are sending the remainder of the current vector and have more */
1709       if ((len == 0xffff && diff > 0xffffUL) ||
1710           (len == (u16_t)diff && conn->current_msg->msg.w.vector_cnt > 1)) {
1711         write_more = 1;
1712         apiflags |= TCP_WRITE_FLAG_MORE;
1713       } else {
1714         write_more = 0;
1715       }
1716       err = tcp_write(conn->pcb.tcp, dataptr, len, apiflags);
1717       if (err == ERR_OK) {
1718         conn->current_msg->msg.w.offset += len;
1719         conn->current_msg->msg.w.vector_off += len;
1720         /* check if current vector is finished */
1721         if (conn->current_msg->msg.w.vector_off == conn->current_msg->msg.w.vector->len) {
1722           conn->current_msg->msg.w.vector_cnt--;
1723           /* if we have additional vectors, move on to them */
1724           if (conn->current_msg->msg.w.vector_cnt > 0) {
1725             conn->current_msg->msg.w.vector++;
1726             conn->current_msg->msg.w.vector_off = 0;
1727           }
1728         }
1729       }
1730     } while (write_more && err == ERR_OK);
1731     /* if OK or memory error, check available space */
1732     if ((err == ERR_OK) || (err == ERR_MEM)) {
1733 err_mem:
1734       if (dontblock && (conn->current_msg->msg.w.offset < conn->current_msg->msg.w.len)) {
1735         /* non-blocking write did not write everything: mark the pcb non-writable
1736            and let poll_tcp check writable space to mark the pcb writable again */
1737         API_EVENT(conn, NETCONN_EVT_SENDMINUS, 0);
1738         conn->flags |= NETCONN_FLAG_CHECK_WRITESPACE;
1739       } else if ((tcp_sndbuf(conn->pcb.tcp) <= TCP_SNDLOWAT) ||
1740                  (tcp_sndqueuelen(conn->pcb.tcp) >= TCP_SNDQUEUELOWAT)) {
1741         /* The queued byte- or pbuf-count exceeds the configured low-water limit,
1742            let select mark this pcb as non-writable. */
1743         API_EVENT(conn, NETCONN_EVT_SENDMINUS, 0);
1744       }
1745     }
1746 
1747     if (err == ERR_OK) {
1748       err_t out_err;
1749       if ((conn->current_msg->msg.w.offset == conn->current_msg->msg.w.len) || dontblock) {
1750         /* return sent length (caller reads length from msg.w.offset) */
1751         write_finished = 1;
1752       }
1753       out_err = tcp_output(conn->pcb.tcp);
1754       if (out_err == ERR_RTE) {
1755         /* If tcp_output fails because no route is found,
1756            don't try writing any more but return the error
1757            to the application thread. */
1758         err = out_err;
1759         write_finished = 1;
1760       }
1761     } else if (err == ERR_MEM) {
1762       /* If ERR_MEM, we wait for sent_tcp or poll_tcp to be called.
1763          For blocking sockets, we do NOT return to the application
1764          thread, since ERR_MEM is only a temporary error! Non-blocking
1765          will remain non-writable until sent_tcp/poll_tcp is called */
1766 
1767       /* tcp_write returned ERR_MEM, try tcp_output anyway */
1768       err_t out_err = tcp_output(conn->pcb.tcp);
1769       if (out_err == ERR_RTE) {
1770         /* If tcp_output fails because no route is found,
1771            don't try writing any more but return the error
1772            to the application thread. */
1773         err = out_err;
1774         write_finished = 1;
1775       } else if (dontblock) {
1776         /* non-blocking write is done on ERR_MEM, set error according
1777            to partial write or not */
1778         err = (conn->current_msg->msg.w.offset == 0) ? ERR_WOULDBLOCK : ERR_OK;
1779         write_finished = 1;
1780       }
1781     } else {
1782       /* On errors != ERR_MEM, we don't try writing any more but return
1783          the error to the application thread. */
1784       write_finished = 1;
1785     }
1786   }
1787   if (write_finished) {
1788     /* everything was written: set back connection state
1789        and back to application task */
1790     sys_sem_t *op_completed_sem = LWIP_API_MSG_SEM(conn->current_msg);
1791     conn->current_msg->err = err;
1792     conn->current_msg = NULL;
1793     conn->state = NETCONN_NONE;
1794 #if LWIP_TCPIP_CORE_LOCKING
1795     if (delayed)
1796 #endif
1797     {
1798       sys_sem_signal(op_completed_sem);
1799     }
1800   }
1801 #if LWIP_TCPIP_CORE_LOCKING
1802   else {
1803     return ERR_MEM;
1804   }
1805 #endif
1806   return ERR_OK;
1807 }
1808 #endif /* LWIP_TCP */
1809 
1810 /**
1811  * Send some data on a TCP pcb contained in a netconn
1812  * Called from netconn_write
1813  *
1814  * @param m the api_msg pointing to the connection
1815  */
1816 void
1817 lwip_netconn_do_write(void *m)
1818 {
1819   struct api_msg *msg = (struct api_msg *)m;
1820 
1821   err_t err = netconn_err(msg->conn);
1822   if (err == ERR_OK) {
1823     if (NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_TCP) {
1824 #if LWIP_TCP
1825       if (msg->conn->state != NETCONN_NONE) {
1826         /* netconn is connecting, closing or in blocking write */
1827         err = ERR_INPROGRESS;
1828       } else if (msg->conn->pcb.tcp != NULL) {
1829         msg->conn->state = NETCONN_WRITE;
1830         /* set all the variables used by lwip_netconn_do_writemore */
1831         LWIP_ASSERT("already writing or closing", msg->conn->current_msg == NULL);
1832         LWIP_ASSERT("msg->msg.w.len != 0", msg->msg.w.len != 0);
1833         msg->conn->current_msg = msg;
1834 #if LWIP_TCPIP_CORE_LOCKING
1835         if (lwip_netconn_do_writemore(msg->conn, 0) != ERR_OK) {
1836           LWIP_ASSERT("state!", msg->conn->state == NETCONN_WRITE);
1837           UNLOCK_TCPIP_CORE();
1838           sys_arch_sem_wait(LWIP_API_MSG_SEM(msg), 0);
1839           LOCK_TCPIP_CORE();
1840           LWIP_ASSERT("state!", msg->conn->state != NETCONN_WRITE);
1841         }
1842 #else /* LWIP_TCPIP_CORE_LOCKING */
1843         lwip_netconn_do_writemore(msg->conn);
1844 #endif /* LWIP_TCPIP_CORE_LOCKING */
1845         /* for both cases: if lwip_netconn_do_writemore was called, don't ACK the APIMSG
1846            since lwip_netconn_do_writemore ACKs it! */
1847         return;
1848       } else {
1849         err = ERR_CONN;
1850       }
1851 #else /* LWIP_TCP */
1852       err = ERR_VAL;
1853 #endif /* LWIP_TCP */
1854 #if (LWIP_UDP || LWIP_RAW)
1855     } else {
1856       err = ERR_VAL;
1857 #endif /* (LWIP_UDP || LWIP_RAW) */
1858     }
1859   }
1860   msg->err = err;
1861   TCPIP_APIMSG_ACK(msg);
1862 }
1863 
1864 /**
1865  * Return a connection's local or remote address
1866  * Called from netconn_getaddr
1867  *
1868  * @param m the api_msg pointing to the connection
1869  */
1870 void
1871 lwip_netconn_do_getaddr(void *m)
1872 {
1873   struct api_msg *msg = (struct api_msg *)m;
1874 
1875   if (msg->conn->pcb.ip != NULL) {
1876     if (msg->msg.ad.local) {
1877       ip_addr_copy(API_EXPR_DEREF(msg->msg.ad.ipaddr),
1878                    msg->conn->pcb.ip->local_ip);
1879     } else {
1880       ip_addr_copy(API_EXPR_DEREF(msg->msg.ad.ipaddr),
1881                    msg->conn->pcb.ip->remote_ip);
1882     }
1883 
1884     msg->err = ERR_OK;
1885     switch (NETCONNTYPE_GROUP(msg->conn->type)) {
1886 #if LWIP_RAW
1887       case NETCONN_RAW:
1888         if (msg->msg.ad.local) {
1889           API_EXPR_DEREF(msg->msg.ad.port) = msg->conn->pcb.raw->protocol;
1890         } else {
1891           /* return an error as connecting is only a helper for upper layers */
1892           msg->err = ERR_CONN;
1893         }
1894         break;
1895 #endif /* LWIP_RAW */
1896 #if LWIP_UDP
1897       case NETCONN_UDP:
1898         if (msg->msg.ad.local) {
1899           API_EXPR_DEREF(msg->msg.ad.port) = msg->conn->pcb.udp->local_port;
1900         } else {
1901           if ((msg->conn->pcb.udp->flags & UDP_FLAGS_CONNECTED) == 0) {
1902             msg->err = ERR_CONN;
1903           } else {
1904             API_EXPR_DEREF(msg->msg.ad.port) = msg->conn->pcb.udp->remote_port;
1905           }
1906         }
1907         break;
1908 #endif /* LWIP_UDP */
1909 #if LWIP_TCP
1910       case NETCONN_TCP:
1911         if ((msg->msg.ad.local == 0) &&
1912             ((msg->conn->pcb.tcp->state == CLOSED) || (msg->conn->pcb.tcp->state == LISTEN))) {
1913           /* pcb is not connected and remote name is requested */
1914           msg->err = ERR_CONN;
1915         } else {
1916           API_EXPR_DEREF(msg->msg.ad.port) = (msg->msg.ad.local ? msg->conn->pcb.tcp->local_port : msg->conn->pcb.tcp->remote_port);
1917         }
1918         break;
1919 #endif /* LWIP_TCP */
1920       default:
1921         LWIP_ASSERT("invalid netconn_type", 0);
1922         break;
1923     }
1924   } else {
1925     msg->err = ERR_CONN;
1926   }
1927   TCPIP_APIMSG_ACK(msg);
1928 }
1929 
1930 /**
1931  * Close or half-shutdown a TCP pcb contained in a netconn
1932  * Called from netconn_close
1933  * In contrast to closing sockets, the netconn is not deallocated.
1934  *
1935  * @param m the api_msg pointing to the connection
1936  */
1937 void
1938 lwip_netconn_do_close(void *m)
1939 {
1940   struct api_msg *msg = (struct api_msg *)m;
1941 
1942 #if LWIP_TCP
1943   enum netconn_state state = msg->conn->state;
1944   /* First check if this is a TCP netconn and if it is in a correct state
1945       (LISTEN doesn't support half shutdown) */
1946   if ((msg->conn->pcb.tcp != NULL) &&
1947       (NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_TCP) &&
1948       ((msg->msg.sd.shut == NETCONN_SHUT_RDWR) || (state != NETCONN_LISTEN))) {
1949     /* Check if we are in a connected state */
1950     if (state == NETCONN_CONNECT) {
1951       /* TCP connect in progress: cannot shutdown */
1952       msg->err = ERR_CONN;
1953     } else if (state == NETCONN_WRITE) {
1954 #if LWIP_NETCONN_FULLDUPLEX
1955       if (msg->msg.sd.shut & NETCONN_SHUT_WR) {
1956         /* close requested, abort running write */
1957         sys_sem_t *write_completed_sem;
1958         LWIP_ASSERT("msg->conn->current_msg != NULL", msg->conn->current_msg != NULL);
1959         write_completed_sem = LWIP_API_MSG_SEM(msg->conn->current_msg);
1960         msg->conn->current_msg->err = ERR_CLSD;
1961         msg->conn->current_msg = NULL;
1962         msg->conn->state = NETCONN_NONE;
1963         state = NETCONN_NONE;
1964         sys_sem_signal(write_completed_sem);
1965       } else {
1966         LWIP_ASSERT("msg->msg.sd.shut == NETCONN_SHUT_RD", msg->msg.sd.shut == NETCONN_SHUT_RD);
1967         /* In this case, let the write continue and do not interfere with
1968            conn->current_msg or conn->state! */
1969         msg->err = tcp_shutdown(msg->conn->pcb.tcp, 1, 0);
1970       }
1971     }
1972     if (state == NETCONN_NONE) {
1973 #else /* LWIP_NETCONN_FULLDUPLEX */
1974       msg->err = ERR_INPROGRESS;
1975     } else {
1976 #endif /* LWIP_NETCONN_FULLDUPLEX */
1977       if (msg->msg.sd.shut & NETCONN_SHUT_RD) {
1978 #if LWIP_NETCONN_FULLDUPLEX
1979         /* Mark mboxes invalid */
1980         netconn_mark_mbox_invalid(msg->conn);
1981 #else /* LWIP_NETCONN_FULLDUPLEX */
1982         netconn_drain(msg->conn);
1983 #endif /* LWIP_NETCONN_FULLDUPLEX */
1984       }
1985       LWIP_ASSERT("already writing or closing", msg->conn->current_msg == NULL);
1986       msg->conn->state = NETCONN_CLOSE;
1987       msg->conn->current_msg = msg;
1988 #if LWIP_TCPIP_CORE_LOCKING
1989       if (lwip_netconn_do_close_internal(msg->conn, 0) != ERR_OK) {
1990         LWIP_ASSERT("state!", msg->conn->state == NETCONN_CLOSE);
1991         UNLOCK_TCPIP_CORE();
1992         sys_arch_sem_wait(LWIP_API_MSG_SEM(msg), 0);
1993         LOCK_TCPIP_CORE();
1994         LWIP_ASSERT("state!", msg->conn->state == NETCONN_NONE);
1995       }
1996 #else /* LWIP_TCPIP_CORE_LOCKING */
1997       lwip_netconn_do_close_internal(msg->conn);
1998 #endif /* LWIP_TCPIP_CORE_LOCKING */
1999       /* for tcp netconns, lwip_netconn_do_close_internal ACKs the message */
2000       return;
2001     }
2002   } else
2003 #endif /* LWIP_TCP */
2004   {
2005     msg->err = ERR_CONN;
2006   }
2007   TCPIP_APIMSG_ACK(msg);
2008 }
2009 
2010 #if LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD)
2011 /**
2012  * Join multicast groups for UDP netconns.
2013  * Called from netconn_join_leave_group
2014  *
2015  * @param m the api_msg pointing to the connection
2016  */
2017 void
2018 lwip_netconn_do_join_leave_group(void *m)
2019 {
2020   struct api_msg *msg = (struct api_msg *)m;
2021 
2022   msg->err = ERR_CONN;
2023   if (msg->conn->pcb.tcp != NULL) {
2024     if (NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_UDP) {
2025 #if LWIP_UDP
2026 #if LWIP_IPV6 && LWIP_IPV6_MLD
2027       if (NETCONNTYPE_ISIPV6(msg->conn->type)) {
2028         if (msg->msg.jl.join_or_leave == NETCONN_JOIN) {
2029           msg->err = mld6_joingroup(ip_2_ip6(API_EXPR_REF(msg->msg.jl.netif_addr)),
2030                                     ip_2_ip6(API_EXPR_REF(msg->msg.jl.multiaddr)));
2031         } else {
2032           msg->err = mld6_leavegroup(ip_2_ip6(API_EXPR_REF(msg->msg.jl.netif_addr)),
2033                                      ip_2_ip6(API_EXPR_REF(msg->msg.jl.multiaddr)));
2034         }
2035       } else
2036 #endif /* LWIP_IPV6 && LWIP_IPV6_MLD */
2037       {
2038 #if LWIP_IGMP
2039         if (msg->msg.jl.join_or_leave == NETCONN_JOIN) {
2040           msg->err = igmp_joingroup(ip_2_ip4(API_EXPR_REF(msg->msg.jl.netif_addr)),
2041                                     ip_2_ip4(API_EXPR_REF(msg->msg.jl.multiaddr)));
2042         } else {
2043           msg->err = igmp_leavegroup(ip_2_ip4(API_EXPR_REF(msg->msg.jl.netif_addr)),
2044                                      ip_2_ip4(API_EXPR_REF(msg->msg.jl.multiaddr)));
2045         }
2046 #endif /* LWIP_IGMP */
2047       }
2048 #endif /* LWIP_UDP */
2049 #if (LWIP_TCP || LWIP_RAW)
2050     } else {
2051       msg->err = ERR_VAL;
2052 #endif /* (LWIP_TCP || LWIP_RAW) */
2053     }
2054   }
2055   TCPIP_APIMSG_ACK(msg);
2056 }
2057 /**
2058  * Join multicast groups for UDP netconns.
2059  * Called from netconn_join_leave_group_netif
2060  *
2061  * @param m the api_msg pointing to the connection
2062  */
2063 void
2064 lwip_netconn_do_join_leave_group_netif(void *m)
2065 {
2066   struct api_msg *msg = (struct api_msg *)m;
2067   struct netif *netif;
2068 
2069   netif = netif_get_by_index(msg->msg.jl.if_idx);
2070   if (netif == NULL) {
2071     msg->err = ERR_IF;
2072     goto done;
2073   }
2074 
2075   msg->err = ERR_CONN;
2076   if (msg->conn->pcb.tcp != NULL) {
2077     if (NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_UDP) {
2078 #if LWIP_UDP
2079 #if LWIP_IPV6 && LWIP_IPV6_MLD
2080       if (NETCONNTYPE_ISIPV6(msg->conn->type)) {
2081         if (msg->msg.jl.join_or_leave == NETCONN_JOIN) {
2082           msg->err = mld6_joingroup_netif(netif,
2083                                           ip_2_ip6(API_EXPR_REF(msg->msg.jl.multiaddr)));
2084         } else {
2085           msg->err = mld6_leavegroup_netif(netif,
2086                                            ip_2_ip6(API_EXPR_REF(msg->msg.jl.multiaddr)));
2087         }
2088       } else
2089 #endif /* LWIP_IPV6 && LWIP_IPV6_MLD */
2090       {
2091 #if LWIP_IGMP
2092         if (msg->msg.jl.join_or_leave == NETCONN_JOIN) {
2093           msg->err = igmp_joingroup_netif(netif,
2094                                           ip_2_ip4(API_EXPR_REF(msg->msg.jl.multiaddr)));
2095         } else {
2096           msg->err = igmp_leavegroup_netif(netif,
2097                                            ip_2_ip4(API_EXPR_REF(msg->msg.jl.multiaddr)));
2098         }
2099 #endif /* LWIP_IGMP */
2100       }
2101 #endif /* LWIP_UDP */
2102 #if (LWIP_TCP || LWIP_RAW)
2103     } else {
2104       msg->err = ERR_VAL;
2105 #endif /* (LWIP_TCP || LWIP_RAW) */
2106     }
2107   }
2108 
2109 done:
2110   TCPIP_APIMSG_ACK(msg);
2111 }
2112 #endif /* LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD) */
2113 
2114 #if LWIP_DNS
2115 /**
2116  * Callback function that is called when DNS name is resolved
2117  * (or on timeout). A waiting application thread is waked up by
2118  * signaling the semaphore.
2119  */
2120 static void
2121 lwip_netconn_do_dns_found(const char *name, const ip_addr_t *ipaddr, void *arg)
2122 {
2123   struct dns_api_msg *msg = (struct dns_api_msg *)arg;
2124 
2125   /* we trust the internal implementation to be correct :-) */
2126   LWIP_UNUSED_ARG(name);
2127 
2128   if (ipaddr == NULL) {
2129     /* timeout or memory error */
2130     API_EXPR_DEREF(msg->err) = ERR_VAL;
2131   } else {
2132     /* address was resolved */
2133     API_EXPR_DEREF(msg->err) = ERR_OK;
2134     API_EXPR_DEREF(msg->addr) = *ipaddr;
2135   }
2136   /* wake up the application task waiting in netconn_gethostbyname */
2137   sys_sem_signal(API_EXPR_REF_SEM(msg->sem));
2138 }
2139 
2140 /**
2141  * Execute a DNS query
2142  * Called from netconn_gethostbyname
2143  *
2144  * @param arg the dns_api_msg pointing to the query
2145  */
2146 void
2147 lwip_netconn_do_gethostbyname(void *arg)
2148 {
2149   struct dns_api_msg *msg = (struct dns_api_msg *)arg;
2150   u8_t addrtype =
2151 #if LWIP_IPV4 && LWIP_IPV6
2152     msg->dns_addrtype;
2153 #else
2154     LWIP_DNS_ADDRTYPE_DEFAULT;
2155 #endif
2156 
2157   API_EXPR_DEREF(msg->err) = dns_gethostbyname_addrtype(msg->name,
2158                              API_EXPR_REF(msg->addr), lwip_netconn_do_dns_found, msg, addrtype);
2159 #if LWIP_TCPIP_CORE_LOCKING
2160   /* For core locking, only block if we need to wait for answer/timeout */
2161   if (API_EXPR_DEREF(msg->err) == ERR_INPROGRESS) {
2162     UNLOCK_TCPIP_CORE();
2163     sys_sem_wait(API_EXPR_REF_SEM(msg->sem));
2164     LOCK_TCPIP_CORE();
2165     LWIP_ASSERT("do_gethostbyname still in progress!!", API_EXPR_DEREF(msg->err) != ERR_INPROGRESS);
2166   }
2167 #else /* LWIP_TCPIP_CORE_LOCKING */
2168   if (API_EXPR_DEREF(msg->err) != ERR_INPROGRESS) {
2169     /* on error or immediate success, wake up the application
2170      * task waiting in netconn_gethostbyname */
2171     sys_sem_signal(API_EXPR_REF_SEM(msg->sem));
2172   }
2173 #endif /* LWIP_TCPIP_CORE_LOCKING */
2174 }
2175 #endif /* LWIP_DNS */
2176 
2177 #endif /* LWIP_NETCONN */
2178