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