source: rtems-lwip/lwip/src/api/api_msg.c @ 444dc8a

Last change on this file since 444dc8a was 444dc8a, checked in by Kinsey Moore <kinsey.moore@…>, on 08/24/22 at 20:53:12

lwip: Update to 2.1.3

This updates lwIP to the STABLE-2_1_3_RELEASE tag.

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