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