• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 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 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 *
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 #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 #if LWIP_NETCONN_FULLDUPLEX
720   conn->mbox_threads_waiting = 0;
721 #endif
722 
723   /* If all sizes are the same, every compiler should optimize this switch to nothing */
724   switch (NETCONNTYPE_GROUP(t)) {
725 #if LWIP_RAW
726     case NETCONN_RAW:
727       size = DEFAULT_RAW_RECVMBOX_SIZE;
728       break;
729 #endif /* LWIP_RAW */
730 #if LWIP_UDP
731     case NETCONN_UDP:
732       size = DEFAULT_UDP_RECVMBOX_SIZE;
733 #if LWIP_NETBUF_RECVINFO
734       init_flags |= NETCONN_FLAG_PKTINFO;
735 #endif /* LWIP_NETBUF_RECVINFO */
736       break;
737 #endif /* LWIP_UDP */
738 #if LWIP_TCP
739     case NETCONN_TCP:
740       size = DEFAULT_TCP_RECVMBOX_SIZE;
741       break;
742 #endif /* LWIP_TCP */
743     default:
744       LWIP_ASSERT("netconn_alloc: undefined netconn_type", 0);
745       goto free_and_return;
746   }
747 
748   if (sys_mbox_new(&conn->recvmbox, size) != ERR_OK) {
749     goto free_and_return;
750   }
751 #if !LWIP_NETCONN_SEM_PER_THREAD
752   if (sys_sem_new(&conn->op_completed, 0) != ERR_OK) {
753     sys_mbox_free(&conn->recvmbox);
754     goto free_and_return;
755   }
756 #endif
757 
758 #if LWIP_TCP
759   sys_mbox_set_invalid(&conn->acceptmbox);
760 #endif
761   conn->state        = NETCONN_NONE;
762 #if LWIP_SOCKET
763   /* initialize socket to -1 since 0 is a valid socket */
764   conn->socket       = -1;
765 #endif /* LWIP_SOCKET */
766   conn->callback     = callback;
767 #if LWIP_TCP
768   conn->current_msg  = NULL;
769 #endif /* LWIP_TCP */
770 #if LWIP_SO_SNDTIMEO
771   conn->send_timeout = 0;
772 #endif /* LWIP_SO_SNDTIMEO */
773 #if LWIP_SO_RCVTIMEO
774   conn->recv_timeout = 0;
775 #endif /* LWIP_SO_RCVTIMEO */
776 #if LWIP_SO_RCVBUF
777   conn->recv_bufsize = RECV_BUFSIZE_DEFAULT;
778   conn->recv_avail   = 0;
779 #endif /* LWIP_SO_RCVBUF */
780 #if LWIP_SO_LINGER
781   conn->linger = -1;
782 #endif /* LWIP_SO_LINGER */
783   conn->flags = init_flags;
784   return conn;
785 free_and_return:
786   memp_free(MEMP_NETCONN, conn);
787   return NULL;
788 }
789 
790 /**
791  * Delete a netconn and all its resources.
792  * The pcb is NOT freed (since we might not be in the right thread context do this).
793  *
794  * @param conn the netconn to free
795  */
796 void
797 netconn_free(struct netconn *conn)
798 {
799   LWIP_ASSERT("PCB must be deallocated outside this function", conn->pcb.tcp == NULL);
800 
801 #if LWIP_NETCONN_FULLDUPLEX
802   /* in fullduplex, netconn is drained here */
803   netconn_drain(conn);
804 #endif /* LWIP_NETCONN_FULLDUPLEX */
805 
806   LWIP_ASSERT("recvmbox must be deallocated before calling this function",
807               !sys_mbox_valid(&conn->recvmbox));
808 #if LWIP_TCP
809   LWIP_ASSERT("acceptmbox must be deallocated before calling this function",
810               !sys_mbox_valid(&conn->acceptmbox));
811 #endif /* LWIP_TCP */
812 
813 #if !LWIP_NETCONN_SEM_PER_THREAD
814   sys_sem_free(&conn->op_completed);
815   sys_sem_set_invalid(&conn->op_completed);
816 #endif
817 
818   memp_free(MEMP_NETCONN, conn);
819 }
820 
821 /**
822  * Delete rcvmbox and acceptmbox of a netconn and free the left-over data in
823  * these mboxes
824  *
825  * @param conn the netconn to free
826  * @bytes_drained bytes drained from recvmbox
827  * @accepts_drained pending connections drained from acceptmbox
828  */
829 static void
830 netconn_drain(struct netconn *conn)
831 {
832   void *mem;
833 
834   /* This runs when mbox and netconn are marked as closed,
835      so we don't need to lock against rx packets */
836 #if LWIP_NETCONN_FULLDUPLEX
837   LWIP_ASSERT("netconn marked closed", conn->flags & NETCONN_FLAG_MBOXINVALID);
838 #endif /* LWIP_NETCONN_FULLDUPLEX */
839 
840   /* Delete and drain the recvmbox. */
841   if (sys_mbox_valid(&conn->recvmbox)) {
842     while (sys_mbox_tryfetch(&conn->recvmbox, &mem) != SYS_MBOX_EMPTY) {
843 #if LWIP_NETCONN_FULLDUPLEX
844       if (!lwip_netconn_is_deallocated_msg(mem))
845 #endif /* LWIP_NETCONN_FULLDUPLEX */
846       {
847 #if LWIP_TCP
848         if (NETCONNTYPE_GROUP(conn->type) == NETCONN_TCP) {
849           err_t err;
850           if (!lwip_netconn_is_err_msg(mem, &err)) {
851             pbuf_free((struct pbuf *)mem);
852           }
853         } else
854 #endif /* LWIP_TCP */
855         {
856           netbuf_delete((struct netbuf *)mem);
857         }
858       }
859     }
860     sys_mbox_free(&conn->recvmbox);
861     sys_mbox_set_invalid(&conn->recvmbox);
862   }
863 
864   /* Delete and drain the acceptmbox. */
865 #if LWIP_TCP
866   if (sys_mbox_valid(&conn->acceptmbox)) {
867     while (sys_mbox_tryfetch(&conn->acceptmbox, &mem) != SYS_MBOX_EMPTY) {
868 #if LWIP_NETCONN_FULLDUPLEX
869       if (!lwip_netconn_is_deallocated_msg(mem))
870 #endif /* LWIP_NETCONN_FULLDUPLEX */
871       {
872         err_t err;
873         if (!lwip_netconn_is_err_msg(mem, &err)) {
874           struct netconn *newconn = (struct netconn *)mem;
875           /* Only tcp pcbs have an acceptmbox, so no need to check conn->type */
876           /* pcb might be set to NULL already by err_tcp() */
877           /* drain recvmbox */
878           netconn_drain(newconn);
879           if (newconn->pcb.tcp != NULL) {
880             tcp_abort(newconn->pcb.tcp);
881             newconn->pcb.tcp = NULL;
882           }
883           netconn_free(newconn);
884         }
885       }
886     }
887     sys_mbox_free(&conn->acceptmbox);
888     sys_mbox_set_invalid(&conn->acceptmbox);
889   }
890 #endif /* LWIP_TCP */
891 }
892 
893 #if LWIP_NETCONN_FULLDUPLEX
894 static void
895 netconn_mark_mbox_invalid(struct netconn *conn)
896 {
897   int i, num_waiting;
898   void *msg = LWIP_CONST_CAST(void *, &netconn_deleted);
899 
900   /* Prevent new calls/threads from reading from the mbox */
901   conn->flags |= NETCONN_FLAG_MBOXINVALID;
902 
903   SYS_ARCH_LOCKED(num_waiting = conn->mbox_threads_waiting);
904   for (i = 0; i < num_waiting; i++) {
905     if (sys_mbox_valid_val(conn->recvmbox)) {
906       sys_mbox_trypost(&conn->recvmbox, msg);
907     } else {
908       sys_mbox_trypost(&conn->acceptmbox, msg);
909     }
910   }
911 }
912 #endif /* LWIP_NETCONN_FULLDUPLEX */
913 
914 #if LWIP_TCP
915 /**
916  * Internal helper function to close a TCP netconn: since this sometimes
917  * doesn't work at the first attempt, this function is called from multiple
918  * places.
919  *
920  * @param conn the TCP netconn to close
921  */
922 static err_t
923 lwip_netconn_do_close_internal(struct netconn *conn  WRITE_DELAYED_PARAM)
924 {
925   err_t err;
926   u8_t shut, shut_rx, shut_tx, shut_close;
927   u8_t close_finished = 0;
928   struct tcp_pcb *tpcb;
929 #if LWIP_SO_LINGER
930   u8_t linger_wait_required = 0;
931 #endif /* LWIP_SO_LINGER */
932 
933   LWIP_ASSERT("invalid conn", (conn != NULL));
934   LWIP_ASSERT("this is for tcp netconns only", (NETCONNTYPE_GROUP(conn->type) == NETCONN_TCP));
935   LWIP_ASSERT("conn must be in state NETCONN_CLOSE", (conn->state == NETCONN_CLOSE));
936   LWIP_ASSERT("pcb already closed", (conn->pcb.tcp != NULL));
937   LWIP_ASSERT("conn->current_msg != NULL", conn->current_msg != NULL);
938 
939   tpcb = conn->pcb.tcp;
940   shut = conn->current_msg->msg.sd.shut;
941   shut_rx = shut & NETCONN_SHUT_RD;
942   shut_tx = shut & NETCONN_SHUT_WR;
943   /* shutting down both ends is the same as closing
944      (also if RD or WR side was shut down before already) */
945   if (shut == NETCONN_SHUT_RDWR) {
946     shut_close = 1;
947   } else if (shut_rx &&
948              ((tpcb->state == FIN_WAIT_1) ||
949               (tpcb->state == FIN_WAIT_2) ||
950               (tpcb->state == CLOSING))) {
951     shut_close = 1;
952   } else if (shut_tx && ((tpcb->flags & TF_RXCLOSED) != 0)) {
953     shut_close = 1;
954   } else {
955     shut_close = 0;
956   }
957 
958   /* Set back some callback pointers */
959   if (shut_close) {
960     tcp_arg(tpcb, NULL);
961   }
962   if (tpcb->state == LISTEN) {
963     tcp_accept(tpcb, NULL);
964   } else {
965     /* some callbacks have to be reset if tcp_close is not successful */
966     if (shut_rx) {
967       tcp_recv(tpcb, NULL);
968       tcp_accept(tpcb, NULL);
969     }
970     if (shut_tx) {
971       tcp_sent(tpcb, NULL);
972     }
973     if (shut_close) {
974       tcp_poll(tpcb, NULL, 0);
975       tcp_err(tpcb, NULL);
976     }
977   }
978   /* Try to close the connection */
979   if (shut_close) {
980 #if LWIP_SO_LINGER
981     /* check linger possibilites before calling tcp_close */
982     err = ERR_OK;
983     /* linger enabled/required at all? (i.e. is there untransmitted data left?) */
984     if ((conn->linger >= 0) && (conn->pcb.tcp->unsent || conn->pcb.tcp->unacked)) {
985       if ((conn->linger == 0)) {
986         /* data left but linger prevents waiting */
987         tcp_abort(tpcb);
988         tpcb = NULL;
989       } else if (conn->linger > 0) {
990         /* data left and linger says we should wait */
991         if (netconn_is_nonblocking(conn)) {
992           /* data left on a nonblocking netconn -> cannot linger */
993           err = ERR_WOULDBLOCK;
994         } else if ((s32_t)(sys_now() - conn->current_msg->msg.sd.time_started) >=
995                    (conn->linger * 1000)) {
996           /* data left but linger timeout has expired (this happens on further
997              calls to this function through poll_tcp */
998           tcp_abort(tpcb);
999           tpcb = NULL;
1000         } else {
1001           /* data left -> need to wait for ACK after successful close */
1002           linger_wait_required = 1;
1003         }
1004       }
1005     }
1006     if ((err == ERR_OK) && (tpcb != NULL))
1007 #endif /* LWIP_SO_LINGER */
1008     {
1009       err = tcp_close(tpcb);
1010     }
1011   } else {
1012     err = tcp_shutdown(tpcb, shut_rx, shut_tx);
1013   }
1014   if (err == ERR_OK) {
1015     close_finished = 1;
1016 #if LWIP_SO_LINGER
1017     if (linger_wait_required) {
1018       /* wait for ACK of all unsent/unacked data by just getting called again */
1019       close_finished = 0;
1020       err = ERR_INPROGRESS;
1021     }
1022 #endif /* LWIP_SO_LINGER */
1023   } else {
1024     if (err == ERR_MEM) {
1025       /* Closing failed because of memory shortage, try again later. Even for
1026          nonblocking netconns, we have to wait since no standard socket application
1027          is prepared for close failing because of resource shortage.
1028          Check the timeout: this is kind of an lwip addition to the standard sockets:
1029          we wait for some time when failing to allocate a segment for the FIN */
1030 #if LWIP_SO_SNDTIMEO || LWIP_SO_LINGER
1031       s32_t close_timeout = LWIP_TCP_CLOSE_TIMEOUT_MS_DEFAULT;
1032 #if LWIP_SO_SNDTIMEO
1033       if (conn->send_timeout > 0) {
1034         close_timeout = conn->send_timeout;
1035       }
1036 #endif /* LWIP_SO_SNDTIMEO */
1037 #if LWIP_SO_LINGER
1038       if (conn->linger >= 0) {
1039         /* use linger timeout (seconds) */
1040         close_timeout = conn->linger * 1000U;
1041       }
1042 #endif
1043       if ((s32_t)(sys_now() - conn->current_msg->msg.sd.time_started) >= close_timeout) {
1044 #else /* LWIP_SO_SNDTIMEO || LWIP_SO_LINGER */
1045       if (conn->current_msg->msg.sd.polls_left == 0) {
1046 #endif /* LWIP_SO_SNDTIMEO || LWIP_SO_LINGER */
1047         close_finished = 1;
1048         if (shut_close) {
1049           /* in this case, we want to RST the connection */
1050           tcp_abort(tpcb);
1051           err = ERR_OK;
1052         }
1053       }
1054     } else {
1055       /* Closing failed for a non-memory error: give up */
1056       close_finished = 1;
1057     }
1058   }
1059   if (close_finished) {
1060     /* Closing done (succeeded, non-memory error, nonblocking error or timeout) */
1061     sys_sem_t *op_completed_sem = LWIP_API_MSG_SEM(conn->current_msg);
1062     conn->current_msg->err = err;
1063     conn->current_msg = NULL;
1064     conn->state = NETCONN_NONE;
1065     if (err == ERR_OK) {
1066       if (shut_close) {
1067         /* Set back some callback pointers as conn is going away */
1068         conn->pcb.tcp = NULL;
1069         /* Trigger select() in socket layer. Make sure everybody notices activity
1070          on the connection, error first! */
1071         API_EVENT(conn, NETCONN_EVT_ERROR, 0);
1072       }
1073       if (shut_rx) {
1074         API_EVENT(conn, NETCONN_EVT_RCVPLUS, 0);
1075       }
1076       if (shut_tx) {
1077         API_EVENT(conn, NETCONN_EVT_SENDPLUS, 0);
1078       }
1079     }
1080 #if LWIP_TCPIP_CORE_LOCKING
1081     if (delayed)
1082 #endif
1083     {
1084       /* wake up the application task */
1085       sys_sem_signal(op_completed_sem);
1086     }
1087     return ERR_OK;
1088   }
1089   if (!close_finished) {
1090     /* Closing failed and we want to wait: restore some of the callbacks */
1091     /* Closing of listen pcb will never fail! */
1092     LWIP_ASSERT("Closing a listen pcb may not fail!", (tpcb->state != LISTEN));
1093     if (shut_tx) {
1094       tcp_sent(tpcb, sent_tcp);
1095     }
1096     /* when waiting for close, set up poll interval to 500ms */
1097     tcp_poll(tpcb, poll_tcp, 1);
1098     tcp_err(tpcb, err_tcp);
1099     tcp_arg(tpcb, conn);
1100     /* don't restore recv callback: we don't want to receive any more data */
1101   }
1102   /* If closing didn't succeed, we get called again either
1103      from poll_tcp or from sent_tcp */
1104   LWIP_ASSERT("err != ERR_OK", err != ERR_OK);
1105   return err;
1106 }
1107 #endif /* LWIP_TCP */
1108 
1109 /**
1110  * Delete the pcb inside a netconn.
1111  * Called from netconn_delete.
1112  *
1113  * @param m the api_msg pointing to the connection
1114  */
1115 void
1116 lwip_netconn_do_delconn(void *m)
1117 {
1118   struct api_msg *msg = (struct api_msg *)m;
1119 
1120   enum netconn_state state = msg->conn->state;
1121   LWIP_ASSERT("netconn state error", /* this only happens for TCP netconns */
1122               (state == NETCONN_NONE) || (NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_TCP));
1123 #if LWIP_NETCONN_FULLDUPLEX
1124   /* In full duplex mode, blocking write/connect is aborted with ERR_CLSD */
1125   if (state != NETCONN_NONE) {
1126     if ((state == NETCONN_WRITE) ||
1127         ((state == NETCONN_CONNECT) && !IN_NONBLOCKING_CONNECT(msg->conn))) {
1128       /* close requested, abort running write/connect */
1129       sys_sem_t *op_completed_sem;
1130       LWIP_ASSERT("msg->conn->current_msg != NULL", msg->conn->current_msg != NULL);
1131       op_completed_sem = LWIP_API_MSG_SEM(msg->conn->current_msg);
1132       msg->conn->current_msg->err = ERR_CLSD;
1133       msg->conn->current_msg = NULL;
1134       msg->conn->state = NETCONN_NONE;
1135       sys_sem_signal(op_completed_sem);
1136     }
1137   }
1138 #else /* LWIP_NETCONN_FULLDUPLEX */
1139   if (((state != NETCONN_NONE) &&
1140        (state != NETCONN_LISTEN) &&
1141        (state != NETCONN_CONNECT)) ||
1142       ((state == NETCONN_CONNECT) && !IN_NONBLOCKING_CONNECT(msg->conn))) {
1143     /* This means either a blocking write or blocking connect is running
1144        (nonblocking write returns and sets state to NONE) */
1145     msg->err = ERR_INPROGRESS;
1146   } else
1147 #endif /* LWIP_NETCONN_FULLDUPLEX */
1148   {
1149     LWIP_ASSERT("blocking connect in progress",
1150                 (state != NETCONN_CONNECT) || IN_NONBLOCKING_CONNECT(msg->conn));
1151     msg->err = ERR_OK;
1152 #if LWIP_NETCONN_FULLDUPLEX
1153     /* Mark mboxes invalid */
1154     netconn_mark_mbox_invalid(msg->conn);
1155 #else /* LWIP_NETCONN_FULLDUPLEX */
1156     netconn_drain(msg->conn);
1157 #endif /* LWIP_NETCONN_FULLDUPLEX */
1158 
1159     if (msg->conn->pcb.tcp != NULL) {
1160 
1161       switch (NETCONNTYPE_GROUP(msg->conn->type)) {
1162 #if LWIP_RAW
1163         case NETCONN_RAW:
1164           raw_remove(msg->conn->pcb.raw);
1165           break;
1166 #endif /* LWIP_RAW */
1167 #if LWIP_UDP
1168         case NETCONN_UDP:
1169           msg->conn->pcb.udp->recv_arg = NULL;
1170           udp_remove(msg->conn->pcb.udp);
1171           break;
1172 #endif /* LWIP_UDP */
1173 #if LWIP_TCP
1174         case NETCONN_TCP:
1175           LWIP_ASSERT("already writing or closing", msg->conn->current_msg == NULL);
1176           msg->conn->state = NETCONN_CLOSE;
1177           msg->msg.sd.shut = NETCONN_SHUT_RDWR;
1178           msg->conn->current_msg = msg;
1179 #if LWIP_TCPIP_CORE_LOCKING
1180           if (lwip_netconn_do_close_internal(msg->conn, 0) != ERR_OK) {
1181             LWIP_ASSERT("state!", msg->conn->state == NETCONN_CLOSE);
1182             UNLOCK_TCPIP_CORE();
1183             sys_arch_sem_wait(LWIP_API_MSG_SEM(msg), 0);
1184             LOCK_TCPIP_CORE();
1185             LWIP_ASSERT("state!", msg->conn->state == NETCONN_NONE);
1186           }
1187 #else /* LWIP_TCPIP_CORE_LOCKING */
1188           lwip_netconn_do_close_internal(msg->conn);
1189 #endif /* LWIP_TCPIP_CORE_LOCKING */
1190           /* API_EVENT is called inside lwip_netconn_do_close_internal, before releasing
1191              the application thread, so we can return at this point! */
1192           return;
1193 #endif /* LWIP_TCP */
1194         default:
1195           break;
1196       }
1197       msg->conn->pcb.tcp = NULL;
1198     }
1199     /* tcp netconns don't come here! */
1200 
1201     /* @todo: this lets select make the socket readable and writable,
1202        which is wrong! errfd instead? */
1203     API_EVENT(msg->conn, NETCONN_EVT_RCVPLUS, 0);
1204     API_EVENT(msg->conn, NETCONN_EVT_SENDPLUS, 0);
1205   }
1206   if (sys_sem_valid(LWIP_API_MSG_SEM(msg))) {
1207     TCPIP_APIMSG_ACK(msg);
1208   }
1209 }
1210 
1211 /**
1212  * Bind a pcb contained in a netconn
1213  * Called from netconn_bind.
1214  *
1215  * @param m the api_msg pointing to the connection and containing
1216  *          the IP address and port to bind to
1217  */
1218 void
1219 lwip_netconn_do_bind(void *m)
1220 {
1221   struct api_msg *msg = (struct api_msg *)m;
1222   err_t err;
1223 
1224   if (msg->conn->pcb.tcp != NULL) {
1225     switch (NETCONNTYPE_GROUP(msg->conn->type)) {
1226 #if LWIP_RAW
1227       case NETCONN_RAW:
1228         err = raw_bind(msg->conn->pcb.raw, API_EXPR_REF(msg->msg.bc.ipaddr));
1229         break;
1230 #endif /* LWIP_RAW */
1231 #if LWIP_UDP
1232       case NETCONN_UDP:
1233         err = udp_bind(msg->conn->pcb.udp, API_EXPR_REF(msg->msg.bc.ipaddr), msg->msg.bc.port);
1234         break;
1235 #endif /* LWIP_UDP */
1236 #if LWIP_TCP
1237       case NETCONN_TCP:
1238         err = tcp_bind(msg->conn->pcb.tcp, API_EXPR_REF(msg->msg.bc.ipaddr), msg->msg.bc.port);
1239         break;
1240 #endif /* LWIP_TCP */
1241       default:
1242         err = ERR_VAL;
1243         break;
1244     }
1245   } else {
1246     err = ERR_VAL;
1247   }
1248   msg->err = err;
1249   TCPIP_APIMSG_ACK(msg);
1250 }
1251 /**
1252  * Bind a pcb contained in a netconn to an interface
1253  * Called from netconn_bind_if.
1254  *
1255  * @param m the api_msg pointing to the connection and containing
1256  *          the IP address and port to bind to
1257  */
1258 void
1259 lwip_netconn_do_bind_if(void *m)
1260 {
1261   struct netif *netif;
1262   struct api_msg *msg = (struct api_msg *)m;
1263   err_t err;
1264 
1265   netif = netif_get_by_index(msg->msg.bc.if_idx);
1266 
1267   if ((netif != NULL) && (msg->conn->pcb.tcp != NULL)) {
1268     err = ERR_OK;
1269     switch (NETCONNTYPE_GROUP(msg->conn->type)) {
1270 #if LWIP_RAW
1271       case NETCONN_RAW:
1272         raw_bind_netif(msg->conn->pcb.raw, netif);
1273         break;
1274 #endif /* LWIP_RAW */
1275 #if LWIP_UDP
1276       case NETCONN_UDP:
1277         udp_bind_netif(msg->conn->pcb.udp, netif);
1278         break;
1279 #endif /* LWIP_UDP */
1280 #if LWIP_TCP
1281       case NETCONN_TCP:
1282         tcp_bind_netif(msg->conn->pcb.tcp, netif);
1283         break;
1284 #endif /* LWIP_TCP */
1285       default:
1286         err = ERR_VAL;
1287         break;
1288     }
1289   } else {
1290     err = ERR_VAL;
1291   }
1292   msg->err = err;
1293   TCPIP_APIMSG_ACK(msg);
1294 }
1295 
1296 #if LWIP_TCP
1297 /**
1298  * TCP callback function if a connection (opened by tcp_connect/lwip_netconn_do_connect) has
1299  * been established (or reset by the remote host).
1300  *
1301  * @see tcp.h (struct tcp_pcb.connected) for parameters and return values
1302  */
1303 static err_t
1304 lwip_netconn_do_connected(void *arg, struct tcp_pcb *pcb, err_t err)
1305 {
1306   struct netconn *conn;
1307   int was_blocking;
1308   sys_sem_t *op_completed_sem = NULL;
1309 
1310   LWIP_UNUSED_ARG(pcb);
1311 
1312   conn = (struct netconn *)arg;
1313 
1314   if (conn == NULL) {
1315     return ERR_VAL;
1316   }
1317 
1318   LWIP_ASSERT("conn->state == NETCONN_CONNECT", conn->state == NETCONN_CONNECT);
1319   LWIP_ASSERT("(conn->current_msg != NULL) || conn->in_non_blocking_connect",
1320               (conn->current_msg != NULL) || IN_NONBLOCKING_CONNECT(conn));
1321 
1322   if (conn->current_msg != NULL) {
1323     conn->current_msg->err = err;
1324     op_completed_sem = LWIP_API_MSG_SEM(conn->current_msg);
1325   }
1326   if ((NETCONNTYPE_GROUP(conn->type) == NETCONN_TCP) && (err == ERR_OK)) {
1327     setup_tcp(conn);
1328   }
1329   was_blocking = !IN_NONBLOCKING_CONNECT(conn);
1330   SET_NONBLOCKING_CONNECT(conn, 0);
1331   LWIP_ASSERT("blocking connect state error",
1332               (was_blocking && op_completed_sem != NULL) ||
1333               (!was_blocking && op_completed_sem == NULL));
1334   conn->current_msg = NULL;
1335   conn->state = NETCONN_NONE;
1336   API_EVENT(conn, NETCONN_EVT_SENDPLUS, 0);
1337 
1338   if (was_blocking) {
1339     sys_sem_signal(op_completed_sem);
1340   }
1341   return ERR_OK;
1342 }
1343 #endif /* LWIP_TCP */
1344 
1345 /**
1346  * Connect a pcb contained inside a netconn
1347  * Called from netconn_connect.
1348  *
1349  * @param m the api_msg pointing to the connection and containing
1350  *          the IP address and port to connect to
1351  */
1352 void
1353 lwip_netconn_do_connect(void *m)
1354 {
1355   struct api_msg *msg = (struct api_msg *)m;
1356   err_t err;
1357 
1358   if (msg->conn->pcb.tcp == NULL) {
1359     /* This may happen when calling netconn_connect() a second time */
1360     err = ERR_CLSD;
1361   } else {
1362     switch (NETCONNTYPE_GROUP(msg->conn->type)) {
1363 #if LWIP_RAW
1364       case NETCONN_RAW:
1365         err = raw_connect(msg->conn->pcb.raw, API_EXPR_REF(msg->msg.bc.ipaddr));
1366         break;
1367 #endif /* LWIP_RAW */
1368 #if LWIP_UDP
1369       case NETCONN_UDP:
1370         err = udp_connect(msg->conn->pcb.udp, API_EXPR_REF(msg->msg.bc.ipaddr), msg->msg.bc.port);
1371         break;
1372 #endif /* LWIP_UDP */
1373 #if LWIP_TCP
1374       case NETCONN_TCP:
1375         /* Prevent connect while doing any other action. */
1376         if (msg->conn->state == NETCONN_CONNECT) {
1377           err = ERR_ALREADY;
1378         } else if (msg->conn->state != NETCONN_NONE) {
1379           err = ERR_ISCONN;
1380         } else {
1381           setup_tcp(msg->conn);
1382           err = tcp_connect(msg->conn->pcb.tcp, API_EXPR_REF(msg->msg.bc.ipaddr),
1383                             msg->msg.bc.port, lwip_netconn_do_connected);
1384           if (err == ERR_OK) {
1385             u8_t non_blocking = netconn_is_nonblocking(msg->conn);
1386             msg->conn->state = NETCONN_CONNECT;
1387             SET_NONBLOCKING_CONNECT(msg->conn, non_blocking);
1388             if (non_blocking) {
1389               err = ERR_INPROGRESS;
1390             } else {
1391               msg->conn->current_msg = msg;
1392               /* sys_sem_signal() is called from lwip_netconn_do_connected (or err_tcp()),
1393                  when the connection is established! */
1394 #if LWIP_TCPIP_CORE_LOCKING
1395               LWIP_ASSERT("state!", msg->conn->state == NETCONN_CONNECT);
1396               UNLOCK_TCPIP_CORE();
1397               sys_arch_sem_wait(LWIP_API_MSG_SEM(msg), 0);
1398               LOCK_TCPIP_CORE();
1399               LWIP_ASSERT("state!", msg->conn->state != NETCONN_CONNECT);
1400 #endif /* LWIP_TCPIP_CORE_LOCKING */
1401               return;
1402             }
1403           }
1404         }
1405         break;
1406 #endif /* LWIP_TCP */
1407       default:
1408         LWIP_ERROR("Invalid netconn type", 0, do {
1409           err = ERR_VAL;
1410         } while (0));
1411         break;
1412     }
1413   }
1414   msg->err = err;
1415   /* For all other protocols, netconn_connect() calls netconn_apimsg(),
1416      so use TCPIP_APIMSG_ACK() here. */
1417   TCPIP_APIMSG_ACK(msg);
1418 }
1419 
1420 /**
1421  * Disconnect a pcb contained inside a netconn
1422  * Only used for UDP netconns.
1423  * Called from netconn_disconnect.
1424  *
1425  * @param m the api_msg pointing to the connection to disconnect
1426  */
1427 void
1428 lwip_netconn_do_disconnect(void *m)
1429 {
1430   struct api_msg *msg = (struct api_msg *)m;
1431 
1432 #if LWIP_UDP
1433   if (NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_UDP) {
1434     udp_disconnect(msg->conn->pcb.udp);
1435     msg->err = ERR_OK;
1436   } else
1437 #endif /* LWIP_UDP */
1438   {
1439     msg->err = ERR_VAL;
1440   }
1441   TCPIP_APIMSG_ACK(msg);
1442 }
1443 
1444 #if LWIP_TCP
1445 /**
1446  * Set a TCP pcb contained in a netconn into listen mode
1447  * Called from netconn_listen.
1448  *
1449  * @param m the api_msg pointing to the connection
1450  */
1451 void
1452 lwip_netconn_do_listen(void *m)
1453 {
1454   struct api_msg *msg = (struct api_msg *)m;
1455   err_t err;
1456 
1457   if (msg->conn->pcb.tcp != NULL) {
1458     if (NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_TCP) {
1459       if (msg->conn->state == NETCONN_NONE) {
1460         struct tcp_pcb *lpcb;
1461         if (msg->conn->pcb.tcp->state != CLOSED) {
1462           /* connection is not closed, cannot listen */
1463           err = ERR_VAL;
1464         } else {
1465           u8_t backlog;
1466 #if TCP_LISTEN_BACKLOG
1467           backlog = msg->msg.lb.backlog;
1468 #else  /* TCP_LISTEN_BACKLOG */
1469           backlog = TCP_DEFAULT_LISTEN_BACKLOG;
1470 #endif /* TCP_LISTEN_BACKLOG */
1471 #if LWIP_IPV4 && LWIP_IPV6
1472           /* "Socket API like" dual-stack support: If IP to listen to is IP6_ADDR_ANY,
1473             * and NETCONN_FLAG_IPV6_V6ONLY is NOT set, use IP_ANY_TYPE to listen
1474             */
1475           if (ip_addr_cmp(&msg->conn->pcb.ip->local_ip, IP6_ADDR_ANY) &&
1476               (netconn_get_ipv6only(msg->conn) == 0)) {
1477             /* change PCB type to IPADDR_TYPE_ANY */
1478             IP_SET_TYPE_VAL(msg->conn->pcb.tcp->local_ip,  IPADDR_TYPE_ANY);
1479             IP_SET_TYPE_VAL(msg->conn->pcb.tcp->remote_ip, IPADDR_TYPE_ANY);
1480           }
1481 #endif /* LWIP_IPV4 && LWIP_IPV6 */
1482 
1483           lpcb = tcp_listen_with_backlog_and_err(msg->conn->pcb.tcp, backlog, &err);
1484 
1485           if (lpcb == NULL) {
1486             /* in this case, the old pcb is still allocated */
1487           } else {
1488             /* delete the recvmbox and allocate the acceptmbox */
1489             if (sys_mbox_valid(&msg->conn->recvmbox)) {
1490               /** @todo: should we drain the recvmbox here? */
1491               sys_mbox_free(&msg->conn->recvmbox);
1492               sys_mbox_set_invalid(&msg->conn->recvmbox);
1493             }
1494             err = ERR_OK;
1495             if (!sys_mbox_valid(&msg->conn->acceptmbox)) {
1496               err = sys_mbox_new(&msg->conn->acceptmbox, DEFAULT_ACCEPTMBOX_SIZE);
1497             }
1498             if (err == ERR_OK) {
1499               msg->conn->state = NETCONN_LISTEN;
1500               msg->conn->pcb.tcp = lpcb;
1501               tcp_arg(msg->conn->pcb.tcp, msg->conn);
1502               tcp_accept(msg->conn->pcb.tcp, accept_function);
1503             } else {
1504               /* since the old pcb is already deallocated, free lpcb now */
1505               tcp_close(lpcb);
1506               msg->conn->pcb.tcp = NULL;
1507             }
1508           }
1509         }
1510       } else if (msg->conn->state == NETCONN_LISTEN) {
1511         /* already listening, allow updating of the backlog */
1512         err = ERR_OK;
1513         tcp_backlog_set(msg->conn->pcb.tcp, msg->msg.lb.backlog);
1514       } else {
1515         err = ERR_CONN;
1516       }
1517     } else {
1518       err = ERR_ARG;
1519     }
1520   } else {
1521     err = ERR_CONN;
1522   }
1523   msg->err = err;
1524   TCPIP_APIMSG_ACK(msg);
1525 }
1526 #endif /* LWIP_TCP */
1527 
1528 /**
1529  * Send some data on a RAW or UDP pcb contained in a netconn
1530  * Called from netconn_send
1531  *
1532  * @param m the api_msg pointing to the connection
1533  */
1534 void
1535 lwip_netconn_do_send(void *m)
1536 {
1537   struct api_msg *msg = (struct api_msg *)m;
1538 
1539   err_t err = netconn_err(msg->conn);
1540   if (err == ERR_OK) {
1541     if (msg->conn->pcb.tcp != NULL) {
1542       switch (NETCONNTYPE_GROUP(msg->conn->type)) {
1543 #if LWIP_RAW
1544         case NETCONN_RAW:
1545           if (ip_addr_isany(&msg->msg.b->addr) || IP_IS_ANY_TYPE_VAL(msg->msg.b->addr)) {
1546             err = raw_send(msg->conn->pcb.raw, msg->msg.b->p);
1547           } else {
1548             err = raw_sendto(msg->conn->pcb.raw, msg->msg.b->p, &msg->msg.b->addr);
1549           }
1550           break;
1551 #endif
1552 #if LWIP_UDP
1553         case NETCONN_UDP:
1554 #if LWIP_CHECKSUM_ON_COPY
1555           if (ip_addr_isany(&msg->msg.b->addr) || IP_IS_ANY_TYPE_VAL(msg->msg.b->addr)) {
1556             err = udp_send_chksum(msg->conn->pcb.udp, msg->msg.b->p,
1557                                   msg->msg.b->flags & NETBUF_FLAG_CHKSUM, msg->msg.b->toport_chksum);
1558           } else {
1559             err = udp_sendto_chksum(msg->conn->pcb.udp, msg->msg.b->p,
1560                                     &msg->msg.b->addr, msg->msg.b->port,
1561                                     msg->msg.b->flags & NETBUF_FLAG_CHKSUM, msg->msg.b->toport_chksum);
1562           }
1563 #else /* LWIP_CHECKSUM_ON_COPY */
1564           if (ip_addr_isany_val(msg->msg.b->addr) || IP_IS_ANY_TYPE_VAL(msg->msg.b->addr)) {
1565             err = udp_send(msg->conn->pcb.udp, msg->msg.b->p);
1566           } else {
1567             err = udp_sendto(msg->conn->pcb.udp, msg->msg.b->p, &msg->msg.b->addr, msg->msg.b->port);
1568           }
1569 #endif /* LWIP_CHECKSUM_ON_COPY */
1570           break;
1571 #endif /* LWIP_UDP */
1572         default:
1573           err = ERR_CONN;
1574           break;
1575       }
1576     } else {
1577       err = ERR_CONN;
1578     }
1579   }
1580   msg->err = err;
1581   TCPIP_APIMSG_ACK(msg);
1582 }
1583 
1584 #if LWIP_TCP
1585 /**
1586  * Indicate data has been received from a TCP pcb contained in a netconn
1587  * Called from netconn_recv
1588  *
1589  * @param m the api_msg pointing to the connection
1590  */
1591 void
1592 lwip_netconn_do_recv(void *m)
1593 {
1594   struct api_msg *msg = (struct api_msg *)m;
1595 
1596   msg->err = ERR_OK;
1597   if (msg->conn->pcb.tcp != NULL) {
1598     if (NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_TCP) {
1599       size_t remaining = msg->msg.r.len;
1600       do {
1601         u16_t recved = (u16_t)((remaining > 0xffff) ? 0xffff : remaining);
1602         tcp_recved(msg->conn->pcb.tcp, recved);
1603         remaining -= recved;
1604       } while (remaining != 0);
1605     }
1606   }
1607   TCPIP_APIMSG_ACK(msg);
1608 }
1609 
1610 #if TCP_LISTEN_BACKLOG
1611 /** Indicate that a TCP pcb has been accepted
1612  * Called from netconn_accept
1613  *
1614  * @param m the api_msg pointing to the connection
1615  */
1616 void
1617 lwip_netconn_do_accepted(void *m)
1618 {
1619   struct api_msg *msg = (struct api_msg *)m;
1620 
1621   msg->err = ERR_OK;
1622   if (msg->conn->pcb.tcp != NULL) {
1623     if (NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_TCP) {
1624       tcp_backlog_accepted(msg->conn->pcb.tcp);
1625     }
1626   }
1627   TCPIP_APIMSG_ACK(msg);
1628 }
1629 #endif /* TCP_LISTEN_BACKLOG */
1630 
1631 /**
1632  * See if more data needs to be written from a previous call to netconn_write.
1633  * Called initially from lwip_netconn_do_write. If the first call can't send all data
1634  * (because of low memory or empty send-buffer), this function is called again
1635  * from sent_tcp() or poll_tcp() to send more data. If all data is sent, the
1636  * blocking application thread (waiting in netconn_write) is released.
1637  *
1638  * @param conn netconn (that is currently in state NETCONN_WRITE) to process
1639  * @return ERR_OK
1640  *         ERR_MEM if LWIP_TCPIP_CORE_LOCKING=1 and sending hasn't yet finished
1641  */
1642 static err_t
1643 lwip_netconn_do_writemore(struct netconn *conn  WRITE_DELAYED_PARAM)
1644 {
1645   err_t err;
1646   const void *dataptr;
1647   u16_t len, available;
1648   u8_t write_finished = 0;
1649   size_t diff;
1650   u8_t dontblock;
1651   u8_t apiflags;
1652   u8_t write_more;
1653 
1654   LWIP_ASSERT("conn != NULL", conn != NULL);
1655   LWIP_ASSERT("conn->state == NETCONN_WRITE", (conn->state == NETCONN_WRITE));
1656   LWIP_ASSERT("conn->current_msg != NULL", conn->current_msg != NULL);
1657   LWIP_ASSERT("conn->pcb.tcp != NULL", conn->pcb.tcp != NULL);
1658   LWIP_ASSERT("conn->current_msg->msg.w.offset < conn->current_msg->msg.w.len",
1659               conn->current_msg->msg.w.offset < conn->current_msg->msg.w.len);
1660   LWIP_ASSERT("conn->current_msg->msg.w.vector_cnt > 0", conn->current_msg->msg.w.vector_cnt > 0);
1661 
1662   apiflags = conn->current_msg->msg.w.apiflags;
1663   dontblock = netconn_is_nonblocking(conn) || (apiflags & NETCONN_DONTBLOCK);
1664 
1665 #if LWIP_SO_SNDTIMEO
1666   if ((conn->send_timeout != 0) &&
1667       ((s32_t)(sys_now() - conn->current_msg->msg.w.time_started) >= conn->send_timeout)) {
1668     write_finished = 1;
1669     if (conn->current_msg->msg.w.offset == 0) {
1670       /* nothing has been written */
1671       err = ERR_WOULDBLOCK;
1672     } else {
1673       /* partial write */
1674       err = ERR_OK;
1675     }
1676   } else
1677 #endif /* LWIP_SO_SNDTIMEO */
1678   {
1679     do {
1680       dataptr = (const u8_t *)conn->current_msg->msg.w.vector->ptr + conn->current_msg->msg.w.vector_off;
1681       diff = conn->current_msg->msg.w.vector->len - conn->current_msg->msg.w.vector_off;
1682       if (diff > 0xffffUL) { /* max_u16_t */
1683         len = 0xffff;
1684         apiflags |= TCP_WRITE_FLAG_MORE;
1685       } else {
1686         len = (u16_t)diff;
1687       }
1688       available = tcp_sndbuf(conn->pcb.tcp);
1689       if (available < len) {
1690         /* don't try to write more than sendbuf */
1691         len = available;
1692         if (dontblock) {
1693           if (!len) {
1694             /* set error according to partial write or not */
1695             err = (conn->current_msg->msg.w.offset == 0) ? ERR_WOULDBLOCK : ERR_OK;
1696             goto err_mem;
1697           }
1698         } else {
1699           apiflags |= TCP_WRITE_FLAG_MORE;
1700         }
1701       }
1702       LWIP_ASSERT("lwip_netconn_do_writemore: invalid length!",
1703                   ((conn->current_msg->msg.w.vector_off + len) <= conn->current_msg->msg.w.vector->len));
1704       /* we should loop around for more sending in the following cases:
1705            1) We couldn't finish the current vector because of 16-bit size limitations.
1706               tcp_write() and tcp_sndbuf() both are limited to 16-bit sizes
1707            2) We are sending the remainder of the current vector and have more */
1708       if ((len == 0xffff && diff > 0xffffUL) ||
1709           (len == (u16_t)diff && conn->current_msg->msg.w.vector_cnt > 1)) {
1710         write_more = 1;
1711         apiflags |= TCP_WRITE_FLAG_MORE;
1712       } else {
1713         write_more = 0;
1714       }
1715       err = tcp_write(conn->pcb.tcp, dataptr, len, apiflags);
1716       if (err == ERR_OK) {
1717         conn->current_msg->msg.w.offset += len;
1718         conn->current_msg->msg.w.vector_off += len;
1719         /* check if current vector is finished */
1720         if (conn->current_msg->msg.w.vector_off == conn->current_msg->msg.w.vector->len) {
1721           conn->current_msg->msg.w.vector_cnt--;
1722           /* if we have additional vectors, move on to them */
1723           if (conn->current_msg->msg.w.vector_cnt > 0) {
1724             conn->current_msg->msg.w.vector++;
1725             conn->current_msg->msg.w.vector_off = 0;
1726           }
1727         }
1728       }
1729     } while (write_more && err == ERR_OK);
1730     /* if OK or memory error, check available space */
1731     if ((err == ERR_OK) || (err == ERR_MEM)) {
1732 err_mem:
1733       if (dontblock && (conn->current_msg->msg.w.offset < conn->current_msg->msg.w.len)) {
1734         /* non-blocking write did not write everything: mark the pcb non-writable
1735            and let poll_tcp check writable space to mark the pcb writable again */
1736         API_EVENT(conn, NETCONN_EVT_SENDMINUS, 0);
1737         conn->flags |= NETCONN_FLAG_CHECK_WRITESPACE;
1738       } else if ((tcp_sndbuf(conn->pcb.tcp) <= TCP_SNDLOWAT) ||
1739                  (tcp_sndqueuelen(conn->pcb.tcp) >= TCP_SNDQUEUELOWAT)) {
1740         /* The queued byte- or pbuf-count exceeds the configured low-water limit,
1741            let select mark this pcb as non-writable. */
1742         API_EVENT(conn, NETCONN_EVT_SENDMINUS, 0);
1743       }
1744     }
1745 
1746     if (err == ERR_OK) {
1747       err_t out_err;
1748       if ((conn->current_msg->msg.w.offset == conn->current_msg->msg.w.len) || dontblock) {
1749         /* return sent length (caller reads length from msg.w.offset) */
1750         write_finished = 1;
1751       }
1752       out_err = tcp_output(conn->pcb.tcp);
1753       if (out_err == ERR_RTE) {
1754         /* If tcp_output fails because no route is found,
1755            don't try writing any more but return the error
1756            to the application thread. */
1757         err = out_err;
1758         write_finished = 1;
1759       }
1760     } else if (err == ERR_MEM) {
1761       /* If ERR_MEM, we wait for sent_tcp or poll_tcp to be called.
1762          For blocking sockets, we do NOT return to the application
1763          thread, since ERR_MEM is only a temporary error! Non-blocking
1764          will remain non-writable until sent_tcp/poll_tcp is called */
1765 
1766       /* tcp_write returned ERR_MEM, try tcp_output anyway */
1767       err_t out_err = tcp_output(conn->pcb.tcp);
1768       if (out_err == ERR_RTE) {
1769         /* If tcp_output fails because no route is found,
1770            don't try writing any more but return the error
1771            to the application thread. */
1772         err = out_err;
1773         write_finished = 1;
1774       } else if (dontblock) {
1775         /* non-blocking write is done on ERR_MEM, set error according
1776            to partial write or not */
1777         err = (conn->current_msg->msg.w.offset == 0) ? ERR_WOULDBLOCK : ERR_OK;
1778         write_finished = 1;
1779       }
1780     } else {
1781       /* On errors != ERR_MEM, we don't try writing any more but return
1782          the error to the application thread. */
1783       write_finished = 1;
1784     }
1785   }
1786   if (write_finished) {
1787     /* everything was written: set back connection state
1788        and back to application task */
1789     sys_sem_t *op_completed_sem = LWIP_API_MSG_SEM(conn->current_msg);
1790     conn->current_msg->err = err;
1791     conn->current_msg = NULL;
1792     conn->state = NETCONN_NONE;
1793 #if LWIP_TCPIP_CORE_LOCKING
1794     if (delayed)
1795 #endif
1796     {
1797       sys_sem_signal(op_completed_sem);
1798     }
1799   }
1800 #if LWIP_TCPIP_CORE_LOCKING
1801   else {
1802     return ERR_MEM;
1803   }
1804 #endif
1805   return ERR_OK;
1806 }
1807 #endif /* LWIP_TCP */
1808 
1809 /**
1810  * Send some data on a TCP pcb contained in a netconn
1811  * Called from netconn_write
1812  *
1813  * @param m the api_msg pointing to the connection
1814  */
1815 void
1816 lwip_netconn_do_write(void *m)
1817 {
1818   struct api_msg *msg = (struct api_msg *)m;
1819 
1820   err_t err = netconn_err(msg->conn);
1821   if (err == ERR_OK) {
1822     if (NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_TCP) {
1823 #if LWIP_TCP
1824       if (msg->conn->state != NETCONN_NONE) {
1825         /* netconn is connecting, closing or in blocking write */
1826         err = ERR_INPROGRESS;
1827       } else if (msg->conn->pcb.tcp != NULL) {
1828         msg->conn->state = NETCONN_WRITE;
1829         /* set all the variables used by lwip_netconn_do_writemore */
1830         LWIP_ASSERT("already writing or closing", msg->conn->current_msg == NULL);
1831         LWIP_ASSERT("msg->msg.w.len != 0", msg->msg.w.len != 0);
1832         msg->conn->current_msg = msg;
1833 #if LWIP_TCPIP_CORE_LOCKING
1834         if (lwip_netconn_do_writemore(msg->conn, 0) != ERR_OK) {
1835           LWIP_ASSERT("state!", msg->conn->state == NETCONN_WRITE);
1836           UNLOCK_TCPIP_CORE();
1837           sys_arch_sem_wait(LWIP_API_MSG_SEM(msg), 0);
1838           LOCK_TCPIP_CORE();
1839           LWIP_ASSERT("state!", msg->conn->state != NETCONN_WRITE);
1840         }
1841 #else /* LWIP_TCPIP_CORE_LOCKING */
1842         lwip_netconn_do_writemore(msg->conn);
1843 #endif /* LWIP_TCPIP_CORE_LOCKING */
1844         /* for both cases: if lwip_netconn_do_writemore was called, don't ACK the APIMSG
1845            since lwip_netconn_do_writemore ACKs it! */
1846         return;
1847       } else {
1848         err = ERR_CONN;
1849       }
1850 #else /* LWIP_TCP */
1851       err = ERR_VAL;
1852 #endif /* LWIP_TCP */
1853 #if (LWIP_UDP || LWIP_RAW)
1854     } else {
1855       err = ERR_VAL;
1856 #endif /* (LWIP_UDP || LWIP_RAW) */
1857     }
1858   }
1859   msg->err = err;
1860   TCPIP_APIMSG_ACK(msg);
1861 }
1862 
1863 /**
1864  * Return a connection's local or remote address
1865  * Called from netconn_getaddr
1866  *
1867  * @param m the api_msg pointing to the connection
1868  */
1869 void
1870 lwip_netconn_do_getaddr(void *m)
1871 {
1872   struct api_msg *msg = (struct api_msg *)m;
1873 
1874   if (msg->conn->pcb.ip != NULL) {
1875     if (msg->msg.ad.local) {
1876       ip_addr_copy(API_EXPR_DEREF(msg->msg.ad.ipaddr),
1877                    msg->conn->pcb.ip->local_ip);
1878     } else {
1879       ip_addr_copy(API_EXPR_DEREF(msg->msg.ad.ipaddr),
1880                    msg->conn->pcb.ip->remote_ip);
1881     }
1882 
1883     msg->err = ERR_OK;
1884     switch (NETCONNTYPE_GROUP(msg->conn->type)) {
1885 #if LWIP_RAW
1886       case NETCONN_RAW:
1887         if (msg->msg.ad.local) {
1888           API_EXPR_DEREF(msg->msg.ad.port) = msg->conn->pcb.raw->protocol;
1889         } else {
1890           /* return an error as connecting is only a helper for upper layers */
1891           msg->err = ERR_CONN;
1892         }
1893         break;
1894 #endif /* LWIP_RAW */
1895 #if LWIP_UDP
1896       case NETCONN_UDP:
1897         if (msg->msg.ad.local) {
1898           API_EXPR_DEREF(msg->msg.ad.port) = msg->conn->pcb.udp->local_port;
1899         } else {
1900           if ((msg->conn->pcb.udp->flags & UDP_FLAGS_CONNECTED) == 0) {
1901             msg->err = ERR_CONN;
1902           } else {
1903             API_EXPR_DEREF(msg->msg.ad.port) = msg->conn->pcb.udp->remote_port;
1904           }
1905         }
1906         break;
1907 #endif /* LWIP_UDP */
1908 #if LWIP_TCP
1909       case NETCONN_TCP:
1910         if ((msg->msg.ad.local == 0) &&
1911             ((msg->conn->pcb.tcp->state == CLOSED) || (msg->conn->pcb.tcp->state == LISTEN))) {
1912           /* pcb is not connected and remote name is requested */
1913           msg->err = ERR_CONN;
1914         } else {
1915           API_EXPR_DEREF(msg->msg.ad.port) = (msg->msg.ad.local ? msg->conn->pcb.tcp->local_port : msg->conn->pcb.tcp->remote_port);
1916         }
1917         break;
1918 #endif /* LWIP_TCP */
1919       default:
1920         LWIP_ASSERT("invalid netconn_type", 0);
1921         break;
1922     }
1923   } else {
1924     msg->err = ERR_CONN;
1925   }
1926   TCPIP_APIMSG_ACK(msg);
1927 }
1928 
1929 /**
1930  * Close or half-shutdown a TCP pcb contained in a netconn
1931  * Called from netconn_close
1932  * In contrast to closing sockets, the netconn is not deallocated.
1933  *
1934  * @param m the api_msg pointing to the connection
1935  */
1936 void
1937 lwip_netconn_do_close(void *m)
1938 {
1939   struct api_msg *msg = (struct api_msg *)m;
1940 
1941 #if LWIP_TCP
1942   enum netconn_state state = msg->conn->state;
1943   /* First check if this is a TCP netconn and if it is in a correct state
1944       (LISTEN doesn't support half shutdown) */
1945   if ((msg->conn->pcb.tcp != NULL) &&
1946       (NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_TCP) &&
1947       ((msg->msg.sd.shut == NETCONN_SHUT_RDWR) || (state != NETCONN_LISTEN))) {
1948     /* Check if we are in a connected state */
1949     if (state == NETCONN_CONNECT) {
1950       /* TCP connect in progress: cannot shutdown */
1951       msg->err = ERR_CONN;
1952     } else if (state == NETCONN_WRITE) {
1953 #if LWIP_NETCONN_FULLDUPLEX
1954       if (msg->msg.sd.shut & NETCONN_SHUT_WR) {
1955         /* close requested, abort running write */
1956         sys_sem_t *write_completed_sem;
1957         LWIP_ASSERT("msg->conn->current_msg != NULL", msg->conn->current_msg != NULL);
1958         write_completed_sem = LWIP_API_MSG_SEM(msg->conn->current_msg);
1959         msg->conn->current_msg->err = ERR_CLSD;
1960         msg->conn->current_msg = NULL;
1961         msg->conn->state = NETCONN_NONE;
1962         state = NETCONN_NONE;
1963         sys_sem_signal(write_completed_sem);
1964       } else {
1965         LWIP_ASSERT("msg->msg.sd.shut == NETCONN_SHUT_RD", msg->msg.sd.shut == NETCONN_SHUT_RD);
1966         /* In this case, let the write continue and do not interfere with
1967            conn->current_msg or conn->state! */
1968         msg->err = tcp_shutdown(msg->conn->pcb.tcp, 1, 0);
1969       }
1970     }
1971     if (state == NETCONN_NONE) {
1972 #else /* LWIP_NETCONN_FULLDUPLEX */
1973       msg->err = ERR_INPROGRESS;
1974     } else {
1975 #endif /* LWIP_NETCONN_FULLDUPLEX */
1976       if (msg->msg.sd.shut & NETCONN_SHUT_RD) {
1977 #if LWIP_NETCONN_FULLDUPLEX
1978         /* Mark mboxes invalid */
1979         netconn_mark_mbox_invalid(msg->conn);
1980 #else /* LWIP_NETCONN_FULLDUPLEX */
1981         netconn_drain(msg->conn);
1982 #endif /* LWIP_NETCONN_FULLDUPLEX */
1983       }
1984       LWIP_ASSERT("already writing or closing", msg->conn->current_msg == NULL);
1985       msg->conn->state = NETCONN_CLOSE;
1986       msg->conn->current_msg = msg;
1987 #if LWIP_TCPIP_CORE_LOCKING
1988       if (lwip_netconn_do_close_internal(msg->conn, 0) != ERR_OK) {
1989         LWIP_ASSERT("state!", msg->conn->state == NETCONN_CLOSE);
1990         UNLOCK_TCPIP_CORE();
1991         sys_arch_sem_wait(LWIP_API_MSG_SEM(msg), 0);
1992         LOCK_TCPIP_CORE();
1993         LWIP_ASSERT("state!", msg->conn->state == NETCONN_NONE);
1994       }
1995 #else /* LWIP_TCPIP_CORE_LOCKING */
1996       lwip_netconn_do_close_internal(msg->conn);
1997 #endif /* LWIP_TCPIP_CORE_LOCKING */
1998       /* for tcp netconns, lwip_netconn_do_close_internal ACKs the message */
1999       return;
2000     }
2001   } else
2002 #endif /* LWIP_TCP */
2003   {
2004     msg->err = ERR_CONN;
2005   }
2006   TCPIP_APIMSG_ACK(msg);
2007 }
2008 
2009 #if LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD)
2010 /**
2011  * Join multicast groups for UDP netconns.
2012  * Called from netconn_join_leave_group
2013  *
2014  * @param m the api_msg pointing to the connection
2015  */
2016 void
2017 lwip_netconn_do_join_leave_group(void *m)
2018 {
2019   struct api_msg *msg = (struct api_msg *)m;
2020 
2021   msg->err = ERR_CONN;
2022   if (msg->conn->pcb.tcp != NULL) {
2023     if (NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_UDP) {
2024 #if LWIP_UDP
2025 #if LWIP_IPV6 && LWIP_IPV6_MLD
2026       if (NETCONNTYPE_ISIPV6(msg->conn->type)) {
2027         if (msg->msg.jl.join_or_leave == NETCONN_JOIN) {
2028           msg->err = mld6_joingroup(ip_2_ip6(API_EXPR_REF(msg->msg.jl.netif_addr)),
2029                                     ip_2_ip6(API_EXPR_REF(msg->msg.jl.multiaddr)));
2030         } else {
2031           msg->err = mld6_leavegroup(ip_2_ip6(API_EXPR_REF(msg->msg.jl.netif_addr)),
2032                                      ip_2_ip6(API_EXPR_REF(msg->msg.jl.multiaddr)));
2033         }
2034       } else
2035 #endif /* LWIP_IPV6 && LWIP_IPV6_MLD */
2036       {
2037 #if LWIP_IGMP
2038         if (msg->msg.jl.join_or_leave == NETCONN_JOIN) {
2039           msg->err = igmp_joingroup(ip_2_ip4(API_EXPR_REF(msg->msg.jl.netif_addr)),
2040                                     ip_2_ip4(API_EXPR_REF(msg->msg.jl.multiaddr)));
2041         } else {
2042           msg->err = igmp_leavegroup(ip_2_ip4(API_EXPR_REF(msg->msg.jl.netif_addr)),
2043                                      ip_2_ip4(API_EXPR_REF(msg->msg.jl.multiaddr)));
2044         }
2045 #endif /* LWIP_IGMP */
2046       }
2047 #endif /* LWIP_UDP */
2048 #if (LWIP_TCP || LWIP_RAW)
2049     } else {
2050       msg->err = ERR_VAL;
2051 #endif /* (LWIP_TCP || LWIP_RAW) */
2052     }
2053   }
2054   TCPIP_APIMSG_ACK(msg);
2055 }
2056 /**
2057  * Join multicast groups for UDP netconns.
2058  * Called from netconn_join_leave_group_netif
2059  *
2060  * @param m the api_msg pointing to the connection
2061  */
2062 void
2063 lwip_netconn_do_join_leave_group_netif(void *m)
2064 {
2065   struct api_msg *msg = (struct api_msg *)m;
2066   struct netif *netif;
2067 
2068   netif = netif_get_by_index(msg->msg.jl.if_idx);
2069   if (netif == NULL) {
2070     msg->err = ERR_IF;
2071     goto done;
2072   }
2073 
2074   msg->err = ERR_CONN;
2075   if (msg->conn->pcb.tcp != NULL) {
2076     if (NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_UDP) {
2077 #if LWIP_UDP
2078 #if LWIP_IPV6 && LWIP_IPV6_MLD
2079       if (NETCONNTYPE_ISIPV6(msg->conn->type)) {
2080         if (msg->msg.jl.join_or_leave == NETCONN_JOIN) {
2081           msg->err = mld6_joingroup_netif(netif,
2082                                           ip_2_ip6(API_EXPR_REF(msg->msg.jl.multiaddr)));
2083         } else {
2084           msg->err = mld6_leavegroup_netif(netif,
2085                                            ip_2_ip6(API_EXPR_REF(msg->msg.jl.multiaddr)));
2086         }
2087       } else
2088 #endif /* LWIP_IPV6 && LWIP_IPV6_MLD */
2089       {
2090 #if LWIP_IGMP
2091         if (msg->msg.jl.join_or_leave == NETCONN_JOIN) {
2092           msg->err = igmp_joingroup_netif(netif,
2093                                           ip_2_ip4(API_EXPR_REF(msg->msg.jl.multiaddr)));
2094         } else {
2095           msg->err = igmp_leavegroup_netif(netif,
2096                                            ip_2_ip4(API_EXPR_REF(msg->msg.jl.multiaddr)));
2097         }
2098 #endif /* LWIP_IGMP */
2099       }
2100 #endif /* LWIP_UDP */
2101 #if (LWIP_TCP || LWIP_RAW)
2102     } else {
2103       msg->err = ERR_VAL;
2104 #endif /* (LWIP_TCP || LWIP_RAW) */
2105     }
2106   }
2107 
2108 done:
2109   TCPIP_APIMSG_ACK(msg);
2110 }
2111 #endif /* LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD) */
2112 
2113 #if LWIP_DNS
2114 /**
2115  * Callback function that is called when DNS name is resolved
2116  * (or on timeout). A waiting application thread is waked up by
2117  * signaling the semaphore.
2118  */
2119 static void
2120 lwip_netconn_do_dns_found(const char *name, const ip_addr_t *ipaddr, void *arg)
2121 {
2122   struct dns_api_msg *msg = (struct dns_api_msg *)arg;
2123 
2124   /* we trust the internal implementation to be correct :-) */
2125   LWIP_UNUSED_ARG(name);
2126 
2127   if (ipaddr == NULL) {
2128     /* timeout or memory error */
2129     API_EXPR_DEREF(msg->err) = ERR_VAL;
2130   } else {
2131     /* address was resolved */
2132     API_EXPR_DEREF(msg->err) = ERR_OK;
2133     API_EXPR_DEREF(msg->addr) = *ipaddr;
2134   }
2135   /* wake up the application task waiting in netconn_gethostbyname */
2136   sys_sem_signal(API_EXPR_REF_SEM(msg->sem));
2137 }
2138 
2139 /**
2140  * Execute a DNS query
2141  * Called from netconn_gethostbyname
2142  *
2143  * @param arg the dns_api_msg pointing to the query
2144  */
2145 void
2146 lwip_netconn_do_gethostbyname(void *arg)
2147 {
2148   struct dns_api_msg *msg = (struct dns_api_msg *)arg;
2149   u8_t addrtype =
2150 #if LWIP_IPV4 && LWIP_IPV6
2151     msg->dns_addrtype;
2152 #else
2153     LWIP_DNS_ADDRTYPE_DEFAULT;
2154 #endif
2155 
2156   API_EXPR_DEREF(msg->err) = dns_gethostbyname_addrtype(msg->name,
2157                              API_EXPR_REF(msg->addr), lwip_netconn_do_dns_found, msg, addrtype);
2158 #if LWIP_TCPIP_CORE_LOCKING
2159   /* For core locking, only block if we need to wait for answer/timeout */
2160   if (API_EXPR_DEREF(msg->err) == ERR_INPROGRESS) {
2161     UNLOCK_TCPIP_CORE();
2162     sys_sem_wait(API_EXPR_REF_SEM(msg->sem));
2163     LOCK_TCPIP_CORE();
2164     LWIP_ASSERT("do_gethostbyname still in progress!!", API_EXPR_DEREF(msg->err) != ERR_INPROGRESS);
2165   }
2166 #else /* LWIP_TCPIP_CORE_LOCKING */
2167   if (API_EXPR_DEREF(msg->err) != ERR_INPROGRESS) {
2168     /* on error or immediate success, wake up the application
2169      * task waiting in netconn_gethostbyname */
2170     sys_sem_signal(API_EXPR_REF_SEM(msg->sem));
2171   }
2172 #endif /* LWIP_TCPIP_CORE_LOCKING */
2173 }
2174 #endif /* LWIP_DNS */
2175 
2176 #endif /* LWIP_NETCONN */
2177