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