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