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