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