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