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