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