You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mynewt.apache.org by ma...@apache.org on 2016/09/30 00:31:18 UTC

[35/51] [abbrv] [partial] incubator-mynewt-core git commit: net/ip/lwip_base; LwIP.

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/f5a0f2a0/net/ip/lwip_base/src/api/api_msg.c
----------------------------------------------------------------------
diff --git a/net/ip/lwip_base/src/api/api_msg.c b/net/ip/lwip_base/src/api/api_msg.c
new file mode 100644
index 0000000..9dfea64
--- /dev/null
+++ b/net/ip/lwip_base/src/api/api_msg.c
@@ -0,0 +1,1946 @@
+/**
+ * @file
+ * Sequential API Internal module
+ *
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <ad...@sics.se>
+ *
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_NETCONN /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/priv/api_msg.h"
+
+#include "lwip/ip.h"
+#include "lwip/udp.h"
+#include "lwip/tcp.h"
+#include "lwip/raw.h"
+
+#include "lwip/memp.h"
+#include "lwip/igmp.h"
+#include "lwip/dns.h"
+#include "lwip/mld6.h"
+#include "lwip/priv/tcpip_priv.h"
+
+#include <string.h>
+
+/* netconns are polled once per second (e.g. continue write on memory error) */
+#define NETCONN_TCP_POLL_INTERVAL 2
+
+#define SET_NONBLOCKING_CONNECT(conn, val)  do { if (val) { \
+  (conn)->flags |= NETCONN_FLAG_IN_NONBLOCKING_CONNECT; \
+} else { \
+  (conn)->flags &= ~ NETCONN_FLAG_IN_NONBLOCKING_CONNECT; }} while(0)
+#define IN_NONBLOCKING_CONNECT(conn) (((conn)->flags & NETCONN_FLAG_IN_NONBLOCKING_CONNECT) != 0)
+
+/* forward declarations */
+#if LWIP_TCP
+#if LWIP_TCPIP_CORE_LOCKING
+#define WRITE_DELAYED         , 1
+#define WRITE_DELAYED_PARAM   , u8_t delayed
+#else /* LWIP_TCPIP_CORE_LOCKING */
+#define WRITE_DELAYED
+#define WRITE_DELAYED_PARAM
+#endif /* LWIP_TCPIP_CORE_LOCKING */
+static err_t lwip_netconn_do_writemore(struct netconn *conn  WRITE_DELAYED_PARAM);
+static err_t lwip_netconn_do_close_internal(struct netconn *conn  WRITE_DELAYED_PARAM);
+#endif
+
+#if LWIP_TCPIP_CORE_LOCKING
+#define TCPIP_APIMSG_ACK(m)   NETCONN_SET_SAFE_ERR((m)->conn, (m)->err)
+#else /* LWIP_TCPIP_CORE_LOCKING */
+#define TCPIP_APIMSG_ACK(m)   do { NETCONN_SET_SAFE_ERR((m)->conn, (m)->err); sys_sem_signal(LWIP_API_MSG_SEM(m)); } while(0)
+#endif /* LWIP_TCPIP_CORE_LOCKING */
+
+#if LWIP_TCP
+u8_t netconn_aborted;
+#endif /* LWIP_TCP */
+
+#if LWIP_RAW
+/**
+ * Receive callback function for RAW netconns.
+ * Doesn't 'eat' the packet, only copies it and sends it to
+ * conn->recvmbox
+ *
+ * @see raw.h (struct raw_pcb.recv) for parameters and return value
+ */
+static u8_t
+recv_raw(void *arg, struct raw_pcb *pcb, struct pbuf *p,
+    const ip_addr_t *addr)
+{
+  struct pbuf *q;
+  struct netbuf *buf;
+  struct netconn *conn;
+
+  LWIP_UNUSED_ARG(addr);
+  conn = (struct netconn *)arg;
+
+  if ((conn != NULL) && sys_mbox_valid(&conn->recvmbox)) {
+#if LWIP_SO_RCVBUF
+    int recv_avail;
+    SYS_ARCH_GET(conn->recv_avail, recv_avail);
+    if ((recv_avail + (int)(p->tot_len)) > conn->recv_bufsize) {
+      return 0;
+    }
+#endif /* LWIP_SO_RCVBUF */
+    /* copy the whole packet into new pbufs */
+    q = pbuf_alloc(PBUF_RAW, p->tot_len, PBUF_RAM);
+    if (q != NULL) {
+      if (pbuf_copy(q, p) != ERR_OK) {
+        pbuf_free(q);
+        q = NULL;
+      }
+    }
+
+    if (q != NULL) {
+      u16_t len;
+      buf = (struct netbuf *)memp_malloc(MEMP_NETBUF);
+      if (buf == NULL) {
+        pbuf_free(q);
+        return 0;
+      }
+
+      buf->p = q;
+      buf->ptr = q;
+      ip_addr_copy(buf->addr, *ip_current_src_addr());
+      buf->port = pcb->protocol;
+
+      len = q->tot_len;
+      if (sys_mbox_trypost(&conn->recvmbox, buf) != ERR_OK) {
+        netbuf_delete(buf);
+        return 0;
+      } else {
+#if LWIP_SO_RCVBUF
+        SYS_ARCH_INC(conn->recv_avail, len);
+#endif /* LWIP_SO_RCVBUF */
+        /* Register event with callback */
+        API_EVENT(conn, NETCONN_EVT_RCVPLUS, len);
+      }
+    }
+  }
+
+  return 0; /* do not eat the packet */
+}
+#endif /* LWIP_RAW*/
+
+#if LWIP_UDP
+/**
+ * Receive callback function for UDP netconns.
+ * Posts the packet to conn->recvmbox or deletes it on memory error.
+ *
+ * @see udp.h (struct udp_pcb.recv) for parameters
+ */
+static void
+recv_udp(void *arg, struct udp_pcb *pcb, struct pbuf *p,
+   const ip_addr_t *addr, u16_t port)
+{
+  struct netbuf *buf;
+  struct netconn *conn;
+  u16_t len;
+#if LWIP_SO_RCVBUF
+  int recv_avail;
+#endif /* LWIP_SO_RCVBUF */
+
+  LWIP_UNUSED_ARG(pcb); /* only used for asserts... */
+  LWIP_ASSERT("recv_udp must have a pcb argument", pcb != NULL);
+  LWIP_ASSERT("recv_udp must have an argument", arg != NULL);
+  conn = (struct netconn *)arg;
+  LWIP_ASSERT("recv_udp: recv for wrong pcb!", conn->pcb.udp == pcb);
+
+#if LWIP_SO_RCVBUF
+  SYS_ARCH_GET(conn->recv_avail, recv_avail);
+  if ((conn == NULL) || !sys_mbox_valid(&conn->recvmbox) ||
+      ((recv_avail + (int)(p->tot_len)) > conn->recv_bufsize)) {
+#else  /* LWIP_SO_RCVBUF */
+  if ((conn == NULL) || !sys_mbox_valid(&conn->recvmbox)) {
+#endif /* LWIP_SO_RCVBUF */
+    pbuf_free(p);
+    return;
+  }
+
+  buf = (struct netbuf *)memp_malloc(MEMP_NETBUF);
+  if (buf == NULL) {
+    pbuf_free(p);
+    return;
+  } else {
+    buf->p = p;
+    buf->ptr = p;
+    ip_addr_set(&buf->addr, addr);
+    buf->port = port;
+#if LWIP_NETBUF_RECVINFO
+    {
+      /* get the UDP header - always in the first pbuf, ensured by udp_input */
+      const struct udp_hdr* udphdr = (const struct udp_hdr*)ip_next_header_ptr();
+#if LWIP_CHECKSUM_ON_COPY
+      buf->flags = NETBUF_FLAG_DESTADDR;
+#endif /* LWIP_CHECKSUM_ON_COPY */
+      ip_addr_set(&buf->toaddr, ip_current_dest_addr());
+      buf->toport_chksum = udphdr->dest;
+    }
+#endif /* LWIP_NETBUF_RECVINFO */
+  }
+
+  len = p->tot_len;
+  if (sys_mbox_trypost(&conn->recvmbox, buf) != ERR_OK) {
+    netbuf_delete(buf);
+    return;
+  } else {
+#if LWIP_SO_RCVBUF
+    SYS_ARCH_INC(conn->recv_avail, len);
+#endif /* LWIP_SO_RCVBUF */
+    /* Register event with callback */
+    API_EVENT(conn, NETCONN_EVT_RCVPLUS, len);
+  }
+}
+#endif /* LWIP_UDP */
+
+#if LWIP_TCP
+/**
+ * Receive callback function for TCP netconns.
+ * Posts the packet to conn->recvmbox, but doesn't delete it on errors.
+ *
+ * @see tcp.h (struct tcp_pcb.recv) for parameters and return value
+ */
+static err_t
+recv_tcp(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err)
+{
+  struct netconn *conn;
+  u16_t len;
+
+  LWIP_UNUSED_ARG(pcb);
+  LWIP_ASSERT("recv_tcp must have a pcb argument", pcb != NULL);
+  LWIP_ASSERT("recv_tcp must have an argument", arg != NULL);
+  conn = (struct netconn *)arg;
+
+  if (conn == NULL) {
+    return ERR_VAL;
+  }
+  LWIP_ASSERT("recv_tcp: recv for wrong pcb!", conn->pcb.tcp == pcb);
+
+  if (!sys_mbox_valid(&conn->recvmbox)) {
+    /* recvmbox already deleted */
+    if (p != NULL) {
+      tcp_recved(pcb, p->tot_len);
+      pbuf_free(p);
+    }
+    return ERR_OK;
+  }
+  /* Unlike for UDP or RAW pcbs, don't check for available space
+     using recv_avail since that could break the connection
+     (data is already ACKed) */
+
+  /* don't overwrite fatal errors! */
+  if (err != ERR_OK) {
+    NETCONN_SET_SAFE_ERR(conn, err);
+  }
+
+  if (p != NULL) {
+    len = p->tot_len;
+  } else {
+    len = 0;
+  }
+
+  if (sys_mbox_trypost(&conn->recvmbox, p) != ERR_OK) {
+    /* don't deallocate p: it is presented to us later again from tcp_fasttmr! */
+    return ERR_MEM;
+  } else {
+#if LWIP_SO_RCVBUF
+    SYS_ARCH_INC(conn->recv_avail, len);
+#endif /* LWIP_SO_RCVBUF */
+    /* Register event with callback */
+    API_EVENT(conn, NETCONN_EVT_RCVPLUS, len);
+  }
+
+  return ERR_OK;
+}
+
+/**
+ * Poll callback function for TCP netconns.
+ * Wakes up an application thread that waits for a connection to close
+ * or data to be sent. The application thread then takes the
+ * appropriate action to go on.
+ *
+ * Signals the conn->sem.
+ * netconn_close waits for conn->sem if closing failed.
+ *
+ * @see tcp.h (struct tcp_pcb.poll) for parameters and return value
+ */
+static err_t
+poll_tcp(void *arg, struct tcp_pcb *pcb)
+{
+  struct netconn *conn = (struct netconn *)arg;
+
+  LWIP_UNUSED_ARG(pcb);
+  LWIP_ASSERT("conn != NULL", (conn != NULL));
+
+  if (conn->state == NETCONN_WRITE) {
+    lwip_netconn_do_writemore(conn  WRITE_DELAYED);
+  } else if (conn->state == NETCONN_CLOSE) {
+#if !LWIP_SO_SNDTIMEO && !LWIP_SO_LINGER
+    if (conn->current_msg && conn->current_msg->msg.sd.polls_left) {
+      conn->current_msg->msg.sd.polls_left--;
+    }
+#endif /* !LWIP_SO_SNDTIMEO && !LWIP_SO_LINGER */
+    lwip_netconn_do_close_internal(conn  WRITE_DELAYED);
+  }
+  /* @todo: implement connect timeout here? */
+
+  /* Did a nonblocking write fail before? Then check available write-space. */
+  if (conn->flags & NETCONN_FLAG_CHECK_WRITESPACE) {
+    /* If the queued byte- or pbuf-count drops below the configured low-water limit,
+       let select mark this pcb as writable again. */
+    if ((conn->pcb.tcp != NULL) && (tcp_sndbuf(conn->pcb.tcp) > TCP_SNDLOWAT) &&
+      (tcp_sndqueuelen(conn->pcb.tcp) < TCP_SNDQUEUELOWAT)) {
+      conn->flags &= ~NETCONN_FLAG_CHECK_WRITESPACE;
+      API_EVENT(conn, NETCONN_EVT_SENDPLUS, 0);
+    }
+  }
+
+  return ERR_OK;
+}
+
+/**
+ * Sent callback function for TCP netconns.
+ * Signals the conn->sem and calls API_EVENT.
+ * netconn_write waits for conn->sem if send buffer is low.
+ *
+ * @see tcp.h (struct tcp_pcb.sent) for parameters and return value
+ */
+static err_t
+sent_tcp(void *arg, struct tcp_pcb *pcb, u16_t len)
+{
+  struct netconn *conn = (struct netconn *)arg;
+
+  LWIP_UNUSED_ARG(pcb);
+  LWIP_ASSERT("conn != NULL", (conn != NULL));
+
+  if (conn) {
+    if (conn->state == NETCONN_WRITE) {
+      lwip_netconn_do_writemore(conn  WRITE_DELAYED);
+    } else if (conn->state == NETCONN_CLOSE) {
+      lwip_netconn_do_close_internal(conn  WRITE_DELAYED);
+    }
+
+    /* If the queued byte- or pbuf-count drops below the configured low-water limit,
+       let select mark this pcb as writable again. */
+    if ((conn->pcb.tcp != NULL) && (tcp_sndbuf(conn->pcb.tcp) > TCP_SNDLOWAT) &&
+      (tcp_sndqueuelen(conn->pcb.tcp) < TCP_SNDQUEUELOWAT)) {
+      conn->flags &= ~NETCONN_FLAG_CHECK_WRITESPACE;
+      API_EVENT(conn, NETCONN_EVT_SENDPLUS, len);
+    }
+  }
+
+  return ERR_OK;
+}
+
+/**
+ * Error callback function for TCP netconns.
+ * Signals conn->sem, posts to all conn mboxes and calls API_EVENT.
+ * The application thread has then to decide what to do.
+ *
+ * @see tcp.h (struct tcp_pcb.err) for parameters
+ */
+static void
+err_tcp(void *arg, err_t err)
+{
+  struct netconn *conn;
+  enum netconn_state old_state;
+
+  conn = (struct netconn *)arg;
+  LWIP_ASSERT("conn != NULL", (conn != NULL));
+
+  conn->pcb.tcp = NULL;
+
+  /* reset conn->state now before waking up other threads */
+  old_state = conn->state;
+  conn->state = NETCONN_NONE;
+
+  if (old_state == NETCONN_CLOSE) {
+    /* RST during close: let close return success & dealloc the netconn */
+    err = ERR_OK;
+    NETCONN_SET_SAFE_ERR(conn, ERR_OK);
+  } else {
+    /* no check since this is always fatal! */
+    SYS_ARCH_SET(conn->last_err, err);
+  }
+
+  /* @todo: the type of NETCONN_EVT created should depend on 'old_state' */
+
+  /* Notify the user layer about a connection error. Used to signal select. */
+  API_EVENT(conn, NETCONN_EVT_ERROR, 0);
+  /* Try to release selects pending on 'read' or 'write', too.
+     They will get an error if they actually try to read or write. */
+  API_EVENT(conn, NETCONN_EVT_RCVPLUS, 0);
+  API_EVENT(conn, NETCONN_EVT_SENDPLUS, 0);
+
+  /* pass NULL-message to recvmbox to wake up pending recv */
+  if (sys_mbox_valid(&conn->recvmbox)) {
+    /* use trypost to prevent deadlock */
+    sys_mbox_trypost(&conn->recvmbox, NULL);
+  }
+  /* pass NULL-message to acceptmbox to wake up pending accept */
+  if (sys_mbox_valid(&conn->acceptmbox)) {
+    /* use trypost to preven deadlock */
+    sys_mbox_trypost(&conn->acceptmbox, NULL);
+  }
+
+  if ((old_state == NETCONN_WRITE) || (old_state == NETCONN_CLOSE) ||
+      (old_state == NETCONN_CONNECT)) {
+    /* calling lwip_netconn_do_writemore/lwip_netconn_do_close_internal is not necessary
+       since the pcb has already been deleted! */
+    int was_nonblocking_connect = IN_NONBLOCKING_CONNECT(conn);
+    SET_NONBLOCKING_CONNECT(conn, 0);
+
+    if (!was_nonblocking_connect) {
+      sys_sem_t* op_completed_sem;
+      /* set error return code */
+      LWIP_ASSERT("conn->current_msg != NULL", conn->current_msg != NULL);
+      conn->current_msg->err = err;
+      op_completed_sem = LWIP_API_MSG_SEM(conn->current_msg);
+      LWIP_ASSERT("inavlid op_completed_sem", sys_sem_valid(op_completed_sem));
+      conn->current_msg = NULL;
+      /* wake up the waiting task */
+      NETCONN_SET_SAFE_ERR(conn, err);
+      sys_sem_signal(op_completed_sem);
+    }
+  } else {
+    LWIP_ASSERT("conn->current_msg == NULL", conn->current_msg == NULL);
+  }
+}
+
+/**
+ * Setup a tcp_pcb with the correct callback function pointers
+ * and their arguments.
+ *
+ * @param conn the TCP netconn to setup
+ */
+static void
+setup_tcp(struct netconn *conn)
+{
+  struct tcp_pcb *pcb;
+
+  pcb = conn->pcb.tcp;
+  tcp_arg(pcb, conn);
+  tcp_recv(pcb, recv_tcp);
+  tcp_sent(pcb, sent_tcp);
+  tcp_poll(pcb, poll_tcp, NETCONN_TCP_POLL_INTERVAL);
+  tcp_err(pcb, err_tcp);
+}
+
+/**
+ * Accept callback function for TCP netconns.
+ * Allocates a new netconn and posts that to conn->acceptmbox.
+ *
+ * @see tcp.h (struct tcp_pcb_listen.accept) for parameters and return value
+ */
+static err_t
+accept_function(void *arg, struct tcp_pcb *newpcb, err_t err)
+{
+  struct netconn *newconn;
+  struct netconn *conn = (struct netconn *)arg;
+
+  LWIP_DEBUGF(API_MSG_DEBUG, ("accept_function: newpcb->tate: %s\n", tcp_debug_state_str(newpcb->state)));
+
+  if (conn == NULL) {
+    return ERR_VAL;
+  }
+  if (!sys_mbox_valid(&conn->acceptmbox)) {
+    LWIP_DEBUGF(API_MSG_DEBUG, ("accept_function: acceptmbox already deleted\n"));
+    return ERR_VAL;
+  }
+
+  if (newpcb == NULL) {
+    /* out-of-pcbs during connect: pass on this error to the application */
+    if (sys_mbox_trypost(&conn->acceptmbox, &netconn_aborted) == ERR_OK) {
+      /* Register event with callback */
+      API_EVENT(conn, NETCONN_EVT_RCVPLUS, 0);
+    }
+    return ERR_VAL;
+  }
+
+  /* We have to set the callback here even though
+   * the new socket is unknown. newconn->socket is marked as -1. */
+  newconn = netconn_alloc(conn->type, conn->callback);
+  if (newconn == NULL) {
+    /* outof netconns: pass on this error to the application */
+    if (sys_mbox_trypost(&conn->acceptmbox, &netconn_aborted) == ERR_OK) {
+      /* Register event with callback */
+      API_EVENT(conn, NETCONN_EVT_RCVPLUS, 0);
+    }
+    return ERR_MEM;
+  }
+  newconn->pcb.tcp = newpcb;
+  setup_tcp(newconn);
+  /* no protection: when creating the pcb, the netconn is not yet known
+     to the application thread */
+  newconn->last_err = err;
+
+  /* handle backlog counter */
+  tcp_backlog_delayed(newpcb);
+
+  if (sys_mbox_trypost(&conn->acceptmbox, newconn) != ERR_OK) {
+    /* When returning != ERR_OK, the pcb is aborted in tcp_process(),
+       so do nothing here! */
+    /* remove all references to this netconn from the pcb */
+    struct tcp_pcb* pcb = newconn->pcb.tcp;
+    tcp_arg(pcb, NULL);
+    tcp_recv(pcb, NULL);
+    tcp_sent(pcb, NULL);
+    tcp_poll(pcb, NULL, 0);
+    tcp_err(pcb, NULL);
+    /* remove reference from to the pcb from this netconn */
+    newconn->pcb.tcp = NULL;
+    /* no need to drain since we know the recvmbox is empty. */
+    sys_mbox_free(&newconn->recvmbox);
+    sys_mbox_set_invalid(&newconn->recvmbox);
+    netconn_free(newconn);
+    return ERR_MEM;
+  } else {
+    /* Register event with callback */
+    API_EVENT(conn, NETCONN_EVT_RCVPLUS, 0);
+  }
+
+  return ERR_OK;
+}
+#endif /* LWIP_TCP */
+
+/**
+ * Create a new pcb of a specific type.
+ * Called from lwip_netconn_do_newconn().
+ *
+ * @param msg the api_msg_msg describing the connection type
+ * @return msg->conn->err, but the return value is currently ignored
+ */
+static void
+pcb_new(struct api_msg *msg)
+{
+  LWIP_ASSERT("pcb_new: pcb already allocated", msg->conn->pcb.tcp == NULL);
+
+  /* Allocate a PCB for this connection */
+  switch(NETCONNTYPE_GROUP(msg->conn->type)) {
+#if LWIP_RAW
+  case NETCONN_RAW:
+    msg->conn->pcb.raw = raw_new(msg->msg.n.proto);
+    if (msg->conn->pcb.raw != NULL) {
+      raw_recv(msg->conn->pcb.raw, recv_raw, msg->conn);
+    }
+    break;
+#endif /* LWIP_RAW */
+#if LWIP_UDP
+  case NETCONN_UDP:
+    msg->conn->pcb.udp = udp_new();
+    if (msg->conn->pcb.udp != NULL) {
+#if LWIP_UDPLITE
+      if (NETCONNTYPE_ISUDPLITE(msg->conn->type)) {
+        udp_setflags(msg->conn->pcb.udp, UDP_FLAGS_UDPLITE);
+      }
+#endif /* LWIP_UDPLITE */
+      if (NETCONNTYPE_ISUDPNOCHKSUM(msg->conn->type)) {
+        udp_setflags(msg->conn->pcb.udp, UDP_FLAGS_NOCHKSUM);
+      }
+      udp_recv(msg->conn->pcb.udp, recv_udp, msg->conn);
+    }
+    break;
+#endif /* LWIP_UDP */
+#if LWIP_TCP
+  case NETCONN_TCP:
+    msg->conn->pcb.tcp = tcp_new();
+    if (msg->conn->pcb.tcp != NULL) {
+      setup_tcp(msg->conn);
+    }
+    break;
+#endif /* LWIP_TCP */
+  default:
+    /* Unsupported netconn type, e.g. protocol disabled */
+    msg->err = ERR_VAL;
+    return;
+  }
+  if (msg->conn->pcb.ip == NULL) {
+    msg->err = ERR_MEM;
+  }
+#if LWIP_IPV4 && LWIP_IPV6
+  else {
+    if (NETCONNTYPE_ISIPV6(msg->conn->type)) {
+      /* Convert IPv4 PCB manually to an IPv6 PCB */
+      IP_SET_TYPE_VAL(msg->conn->pcb.ip->local_ip,  IPADDR_TYPE_V6);
+      IP_SET_TYPE_VAL(msg->conn->pcb.ip->remote_ip, IPADDR_TYPE_V6);
+    }
+  }
+#endif /* LWIP_IPV4 && LWIP_IPV6 */
+}
+
+/**
+ * Create a new pcb of a specific type inside a netconn.
+ * Called from netconn_new_with_proto_and_callback.
+ *
+ * @param m the api_msg_msg describing the connection type
+ */
+void
+lwip_netconn_do_newconn(void *m)
+{
+  struct api_msg *msg = (struct api_msg*)m;
+
+  msg->err = ERR_OK;
+  if (msg->conn->pcb.tcp == NULL) {
+    pcb_new(msg);
+  }
+  /* Else? This "new" connection already has a PCB allocated. */
+  /* Is this an error condition? Should it be deleted? */
+  /* We currently just are happy and return. */
+
+  TCPIP_APIMSG_ACK(msg);
+}
+
+/**
+ * Create a new netconn (of a specific type) that has a callback function.
+ * The corresponding pcb is NOT created!
+ *
+ * @param t the type of 'connection' to create (@see enum netconn_type)
+ * @param callback a function to call on status changes (RX available, TX'ed)
+ * @return a newly allocated struct netconn or
+ *         NULL on memory error
+ */
+struct netconn*
+netconn_alloc(enum netconn_type t, netconn_callback callback)
+{
+  struct netconn *conn;
+  int size;
+
+  conn = (struct netconn *)memp_malloc(MEMP_NETCONN);
+  if (conn == NULL) {
+    return NULL;
+  }
+
+  conn->last_err = ERR_OK;
+  conn->type = t;
+  conn->pcb.tcp = NULL;
+
+  /* If all sizes are the same, every compiler should optimize this switch to nothing */
+  switch(NETCONNTYPE_GROUP(t)) {
+#if LWIP_RAW
+  case NETCONN_RAW:
+    size = DEFAULT_RAW_RECVMBOX_SIZE;
+    break;
+#endif /* LWIP_RAW */
+#if LWIP_UDP
+  case NETCONN_UDP:
+    size = DEFAULT_UDP_RECVMBOX_SIZE;
+    break;
+#endif /* LWIP_UDP */
+#if LWIP_TCP
+  case NETCONN_TCP:
+    size = DEFAULT_TCP_RECVMBOX_SIZE;
+    break;
+#endif /* LWIP_TCP */
+  default:
+    LWIP_ASSERT("netconn_alloc: undefined netconn_type", 0);
+    goto free_and_return;
+  }
+
+  if (sys_mbox_new(&conn->recvmbox, size) != ERR_OK) {
+    goto free_and_return;
+  }
+#if !LWIP_NETCONN_SEM_PER_THREAD
+  if (sys_sem_new(&conn->op_completed, 0) != ERR_OK) {
+    sys_mbox_free(&conn->recvmbox);
+    goto free_and_return;
+  }
+#endif
+
+#if LWIP_TCP
+  sys_mbox_set_invalid(&conn->acceptmbox);
+#endif
+  conn->state        = NETCONN_NONE;
+#if LWIP_SOCKET
+  /* initialize socket to -1 since 0 is a valid socket */
+  conn->socket       = -1;
+#endif /* LWIP_SOCKET */
+  conn->callback     = callback;
+#if LWIP_TCP
+  conn->current_msg  = NULL;
+  conn->write_offset = 0;
+#endif /* LWIP_TCP */
+#if LWIP_SO_SNDTIMEO
+  conn->send_timeout = 0;
+#endif /* LWIP_SO_SNDTIMEO */
+#if LWIP_SO_RCVTIMEO
+  conn->recv_timeout = 0;
+#endif /* LWIP_SO_RCVTIMEO */
+#if LWIP_SO_RCVBUF
+  conn->recv_bufsize = RECV_BUFSIZE_DEFAULT;
+  conn->recv_avail   = 0;
+#endif /* LWIP_SO_RCVBUF */
+#if LWIP_SO_LINGER
+  conn->linger = -1;
+#endif /* LWIP_SO_LINGER */
+  conn->flags = 0;
+  return conn;
+free_and_return:
+  memp_free(MEMP_NETCONN, conn);
+  return NULL;
+}
+
+/**
+ * Delete a netconn and all its resources.
+ * The pcb is NOT freed (since we might not be in the right thread context do this).
+ *
+ * @param conn the netconn to free
+ */
+void
+netconn_free(struct netconn *conn)
+{
+  LWIP_ASSERT("PCB must be deallocated outside this function", conn->pcb.tcp == NULL);
+  LWIP_ASSERT("recvmbox must be deallocated before calling this function",
+    !sys_mbox_valid(&conn->recvmbox));
+#if LWIP_TCP
+  LWIP_ASSERT("acceptmbox must be deallocated before calling this function",
+    !sys_mbox_valid(&conn->acceptmbox));
+#endif /* LWIP_TCP */
+
+#if !LWIP_NETCONN_SEM_PER_THREAD
+  sys_sem_free(&conn->op_completed);
+  sys_sem_set_invalid(&conn->op_completed);
+#endif
+
+  memp_free(MEMP_NETCONN, conn);
+}
+
+/**
+ * Delete rcvmbox and acceptmbox of a netconn and free the left-over data in
+ * these mboxes
+ *
+ * @param conn the netconn to free
+ * @bytes_drained bytes drained from recvmbox
+ * @accepts_drained pending connections drained from acceptmbox
+ */
+static void
+netconn_drain(struct netconn *conn)
+{
+  void *mem;
+#if LWIP_TCP
+  struct pbuf *p;
+#endif /* LWIP_TCP */
+
+  /* This runs in tcpip_thread, so we don't need to lock against rx packets */
+
+  /* Delete and drain the recvmbox. */
+  if (sys_mbox_valid(&conn->recvmbox)) {
+    while (sys_mbox_tryfetch(&conn->recvmbox, &mem) != SYS_MBOX_EMPTY) {
+#if LWIP_TCP
+      if (NETCONNTYPE_GROUP(conn->type) == NETCONN_TCP) {
+        if (mem != NULL) {
+          p = (struct pbuf*)mem;
+          /* pcb might be set to NULL already by err_tcp() */
+          if (conn->pcb.tcp != NULL) {
+            tcp_recved(conn->pcb.tcp, p->tot_len);
+          }
+          pbuf_free(p);
+        }
+      } else
+#endif /* LWIP_TCP */
+      {
+        netbuf_delete((struct netbuf *)mem);
+      }
+    }
+    sys_mbox_free(&conn->recvmbox);
+    sys_mbox_set_invalid(&conn->recvmbox);
+  }
+
+  /* Delete and drain the acceptmbox. */
+#if LWIP_TCP
+  if (sys_mbox_valid(&conn->acceptmbox)) {
+    while (sys_mbox_tryfetch(&conn->acceptmbox, &mem) != SYS_MBOX_EMPTY) {
+      struct netconn *newconn = (struct netconn *)mem;
+      /* Only tcp pcbs have an acceptmbox, so no need to check conn->type */
+      /* pcb might be set to NULL already by err_tcp() */
+      /* drain recvmbox */
+      netconn_drain(newconn);
+      if (newconn->pcb.tcp != NULL) {
+        tcp_abort(newconn->pcb.tcp);
+        newconn->pcb.tcp = NULL;
+      }
+      netconn_free(newconn);
+    }
+    sys_mbox_free(&conn->acceptmbox);
+    sys_mbox_set_invalid(&conn->acceptmbox);
+  }
+#endif /* LWIP_TCP */
+}
+
+#if LWIP_TCP
+/**
+ * Internal helper function to close a TCP netconn: since this sometimes
+ * doesn't work at the first attempt, this function is called from multiple
+ * places.
+ *
+ * @param conn the TCP netconn to close
+ * [@param delay 1 if called from sent/poll (wake up calling thread on end)]
+ */
+static err_t
+lwip_netconn_do_close_internal(struct netconn *conn  WRITE_DELAYED_PARAM)
+{
+  err_t err;
+  u8_t shut, shut_rx, shut_tx, close;
+  u8_t close_finished = 0;
+  struct tcp_pcb* tpcb;
+#if LWIP_SO_LINGER
+  u8_t linger_wait_required = 0;
+#endif /* LWIP_SO_LINGER */
+
+  LWIP_ASSERT("invalid conn", (conn != NULL));
+  LWIP_ASSERT("this is for tcp netconns only", (NETCONNTYPE_GROUP(conn->type) == NETCONN_TCP));
+  LWIP_ASSERT("conn must be in state NETCONN_CLOSE", (conn->state == NETCONN_CLOSE));
+  LWIP_ASSERT("pcb already closed", (conn->pcb.tcp != NULL));
+  LWIP_ASSERT("conn->current_msg != NULL", conn->current_msg != NULL);
+
+  tpcb = conn->pcb.tcp;
+  shut = conn->current_msg->msg.sd.shut;
+  shut_rx = shut & NETCONN_SHUT_RD;
+  shut_tx = shut & NETCONN_SHUT_WR;
+  /* shutting down both ends is the same as closing
+     (also if RD or WR side was shut down before already) */
+  if (shut == NETCONN_SHUT_RDWR) {
+    close = 1;
+  } else if (shut_rx &&
+             ((tpcb->state == FIN_WAIT_1) ||
+              (tpcb->state == FIN_WAIT_2) ||
+              (tpcb->state == CLOSING))) {
+    close = 1;
+  } else if (shut_tx && ((tpcb->flags & TF_RXCLOSED) != 0)) {
+    close = 1;
+  } else {
+    close = 0;
+  }
+
+  /* Set back some callback pointers */
+  if (close) {
+    tcp_arg(tpcb, NULL);
+  }
+  if (tpcb->state == LISTEN) {
+    tcp_accept(tpcb, NULL);
+  } else {
+    /* some callbacks have to be reset if tcp_close is not successful */
+    if (shut_rx) {
+      tcp_recv(tpcb, NULL);
+      tcp_accept(tpcb, NULL);
+    }
+    if (shut_tx) {
+      tcp_sent(tpcb, NULL);
+    }
+    if (close) {
+      tcp_poll(tpcb, NULL, 0);
+      tcp_err(tpcb, NULL);
+    }
+  }
+  /* Try to close the connection */
+  if (close) {
+#if LWIP_SO_LINGER
+    /* check linger possibilites before calling tcp_close */
+    err = ERR_OK;
+    /* linger enabled/required at all? (i.e. is there untransmitted data left?) */
+    if ((conn->linger >= 0) && (conn->pcb.tcp->unsent || conn->pcb.tcp->unacked)) {
+      if ((conn->linger == 0)) {
+        /* data left but linger prevents waiting */
+        tcp_abort(tpcb);
+        tpcb = NULL;
+      } else if (conn->linger > 0) {
+        /* data left and linger says we should wait */
+        if (netconn_is_nonblocking(conn)) {
+          /* data left on a nonblocking netconn -> cannot linger */
+          err = ERR_WOULDBLOCK;
+        } else if ((s32_t)(sys_now() - conn->current_msg->msg.sd.time_started) >=
+          (conn->linger * 1000)) {
+          /* data left but linger timeout has expired (this happens on further
+             calls to this function through poll_tcp */
+          tcp_abort(tpcb);
+          tpcb = NULL;
+        } else {
+          /* data left -> need to wait for ACK after successful close */
+          linger_wait_required = 1;
+        }
+      }
+    }
+    if ((err == ERR_OK) && (tpcb != NULL))
+#endif /* LWIP_SO_LINGER */
+    {
+      err = tcp_close(tpcb);
+    }
+  } else {
+    err = tcp_shutdown(tpcb, shut_rx, shut_tx);
+  }
+  if (err == ERR_OK) {
+    close_finished = 1;
+#if LWIP_SO_LINGER
+    if (linger_wait_required) {
+      /* wait for ACK of all unsent/unacked data by just getting called again */
+      close_finished = 0;
+      err = ERR_INPROGRESS;
+    }
+#endif /* LWIP_SO_LINGER */
+  } else {
+    if (err == ERR_MEM) {
+      /* Closing failed because of memory shortage, try again later. Even for
+         nonblocking netconns, we have to wait since no standard socket application
+         is prepared for close failing because of resource shortage.
+         Check the timeout: this is kind of an lwip addition to the standard sockets:
+         we wait for some time when failing to allocate a segment for the FIN */
+#if LWIP_SO_SNDTIMEO || LWIP_SO_LINGER
+      s32_t close_timeout = LWIP_TCP_CLOSE_TIMEOUT_MS_DEFAULT;
+#if LWIP_SO_SNDTIMEO
+      if (conn->send_timeout > 0) {
+        close_timeout = conn->send_timeout;
+      }
+#endif /* LWIP_SO_SNDTIMEO */
+#if LWIP_SO_LINGER
+      if (conn->linger >= 0) {
+        /* use linger timeout (seconds) */
+        close_timeout = conn->linger * 1000U;
+      }
+#endif
+      if ((s32_t)(sys_now() - conn->current_msg->msg.sd.time_started) >= close_timeout) {
+#else /* LWIP_SO_SNDTIMEO || LWIP_SO_LINGER */
+      if (conn->current_msg->msg.sd.polls_left == 0) {
+#endif /* LWIP_SO_SNDTIMEO || LWIP_SO_LINGER */
+        close_finished = 1;
+        if (close) {
+          /* in this case, we want to RST the connection */
+          tcp_abort(tpcb);
+          err = ERR_OK;
+        }
+      }
+    } else {
+      /* Closing failed for a non-memory error: give up */
+      close_finished = 1;
+    }
+  }
+  if (close_finished) {
+    /* Closing done (succeeded, non-memory error, nonblocking error or timeout) */
+    sys_sem_t* op_completed_sem = LWIP_API_MSG_SEM(conn->current_msg);
+    conn->current_msg->err = err;
+    conn->current_msg = NULL;
+    conn->state = NETCONN_NONE;
+    if (err == ERR_OK) {
+      if (close) {
+        /* Set back some callback pointers as conn is going away */
+        conn->pcb.tcp = NULL;
+        /* Trigger select() in socket layer. Make sure everybody notices activity
+         on the connection, error first! */
+        API_EVENT(conn, NETCONN_EVT_ERROR, 0);
+      }
+      if (shut_rx) {
+        API_EVENT(conn, NETCONN_EVT_RCVPLUS, 0);
+      }
+      if (shut_tx) {
+        API_EVENT(conn, NETCONN_EVT_SENDPLUS, 0);
+      }
+    }
+    NETCONN_SET_SAFE_ERR(conn, err);
+#if LWIP_TCPIP_CORE_LOCKING
+    if (delayed)
+#endif
+    {
+      /* wake up the application task */
+      sys_sem_signal(op_completed_sem);
+    }
+    return ERR_OK;
+  }
+  if (!close_finished) {
+    /* Closing failed and we want to wait: restore some of the callbacks */
+    /* Closing of listen pcb will never fail! */
+    LWIP_ASSERT("Closing a listen pcb may not fail!", (tpcb->state != LISTEN));
+    if (shut_tx) {
+      tcp_sent(tpcb, sent_tcp);
+    }
+    /* when waiting for close, set up poll interval to 500ms */
+    tcp_poll(tpcb, poll_tcp, 1);
+    tcp_err(tpcb, err_tcp);
+    tcp_arg(tpcb, conn);
+    /* don't restore recv callback: we don't want to receive any more data */
+  }
+  /* If closing didn't succeed, we get called again either
+     from poll_tcp or from sent_tcp */
+  LWIP_ASSERT("err != ERR_OK", err != ERR_OK);
+  return err;
+}
+#endif /* LWIP_TCP */
+
+/**
+ * Delete the pcb inside a netconn.
+ * Called from netconn_delete.
+ *
+ * @param m the api_msg_msg pointing to the connection
+ */
+void
+lwip_netconn_do_delconn(void *m)
+{
+  struct api_msg *msg = (struct api_msg*)m;
+
+  enum netconn_state state = msg->conn->state;
+  LWIP_ASSERT("netconn state error", /* this only happens for TCP netconns */
+    (state == NETCONN_NONE) || (NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_TCP));
+#if LWIP_NETCONN_FULLDUPLEX
+  /* In full duplex mode, blocking write/connect is aborted with ERR_CLSD */
+  if (state != NETCONN_NONE) {
+    if ((state == NETCONN_WRITE) ||
+        ((state == NETCONN_CONNECT) && !IN_NONBLOCKING_CONNECT(msg->conn))) {
+      /* close requested, abort running write/connect */
+      sys_sem_t* op_completed_sem;
+      LWIP_ASSERT("msg->conn->current_msg != NULL", msg->conn->current_msg != NULL);
+      op_completed_sem = LWIP_API_MSG_SEM(msg->conn->current_msg);
+      msg->conn->current_msg->err = ERR_CLSD;
+      msg->conn->current_msg = NULL;
+      msg->conn->write_offset = 0;
+      msg->conn->state = NETCONN_NONE;
+      NETCONN_SET_SAFE_ERR(msg->conn, ERR_CLSD);
+      sys_sem_signal(op_completed_sem);
+    }
+  }
+#else /* LWIP_NETCONN_FULLDUPLEX */
+  if (((state != NETCONN_NONE) &&
+       (state != NETCONN_LISTEN) &&
+       (state != NETCONN_CONNECT)) ||
+      ((state == NETCONN_CONNECT) && !IN_NONBLOCKING_CONNECT(msg->conn))) {
+    /* This means either a blocking write or blocking connect is running
+       (nonblocking write returns and sets state to NONE) */
+    msg->err = ERR_INPROGRESS;
+  } else
+#endif /* LWIP_NETCONN_FULLDUPLEX */
+  {
+    LWIP_ASSERT("blocking connect in progress",
+      (state != NETCONN_CONNECT) || IN_NONBLOCKING_CONNECT(msg->conn));
+    msg->err = ERR_OK;
+    /* Drain and delete mboxes */
+    netconn_drain(msg->conn);
+
+    if (msg->conn->pcb.tcp != NULL) {
+
+      switch (NETCONNTYPE_GROUP(msg->conn->type)) {
+#if LWIP_RAW
+      case NETCONN_RAW:
+        raw_remove(msg->conn->pcb.raw);
+        break;
+#endif /* LWIP_RAW */
+#if LWIP_UDP
+      case NETCONN_UDP:
+        msg->conn->pcb.udp->recv_arg = NULL;
+        udp_remove(msg->conn->pcb.udp);
+        break;
+#endif /* LWIP_UDP */
+#if LWIP_TCP
+      case NETCONN_TCP:
+        LWIP_ASSERT("already writing or closing", msg->conn->current_msg == NULL &&
+          msg->conn->write_offset == 0);
+        msg->conn->state = NETCONN_CLOSE;
+        msg->msg.sd.shut = NETCONN_SHUT_RDWR;
+        msg->conn->current_msg = msg;
+#if LWIP_TCPIP_CORE_LOCKING
+        if (lwip_netconn_do_close_internal(msg->conn, 0) != ERR_OK) {
+          LWIP_ASSERT("state!", msg->conn->state == NETCONN_CLOSE);
+          UNLOCK_TCPIP_CORE();
+          sys_arch_sem_wait(LWIP_API_MSG_SEM(msg), 0);
+          LOCK_TCPIP_CORE();
+          LWIP_ASSERT("state!", msg->conn->state == NETCONN_NONE);
+        }
+#else /* LWIP_TCPIP_CORE_LOCKING */
+        lwip_netconn_do_close_internal(msg->conn);
+#endif /* LWIP_TCPIP_CORE_LOCKING */
+        /* API_EVENT is called inside lwip_netconn_do_close_internal, before releasing
+           the application thread, so we can return at this point! */
+        return;
+#endif /* LWIP_TCP */
+      default:
+        break;
+      }
+      msg->conn->pcb.tcp = NULL;
+    }
+    /* tcp netconns don't come here! */
+
+    /* @todo: this lets select make the socket readable and writable,
+       which is wrong! errfd instead? */
+    API_EVENT(msg->conn, NETCONN_EVT_RCVPLUS, 0);
+    API_EVENT(msg->conn, NETCONN_EVT_SENDPLUS, 0);
+  }
+  if (sys_sem_valid(LWIP_API_MSG_SEM(msg))) {
+    TCPIP_APIMSG_ACK(msg);
+  }
+}
+
+/**
+ * Bind a pcb contained in a netconn
+ * Called from netconn_bind.
+ *
+ * @param m the api_msg_msg pointing to the connection and containing
+ *          the IP address and port to bind to
+ */
+void
+lwip_netconn_do_bind(void *m)
+{
+  struct api_msg *msg = (struct api_msg*)m;
+
+  if (ERR_IS_FATAL(msg->conn->last_err)) {
+    msg->err = msg->conn->last_err;
+  } else {
+    msg->err = ERR_VAL;
+    if (msg->conn->pcb.tcp != NULL) {
+      const ip_addr_t *ipaddr = API_EXPR_REF(msg->msg.bc.ipaddr);
+
+#if LWIP_IPV4 && LWIP_IPV6
+      /* "Socket API like" dual-stack support: If IP to bind to is IP6_ADDR_ANY,
+       * and NETCONN_FLAG_IPV6_V6ONLY is NOT set, use IP_ANY_TYPE to bind
+       */
+      if (ip_addr_cmp(ipaddr, IP6_ADDR_ANY) &&
+          (netconn_get_ipv6only(msg->conn) == 0)) {
+        /* change PCB type to IPADDR_TYPE_ANY */
+        IP_SET_TYPE_VAL(msg->conn->pcb.ip->local_ip,  IPADDR_TYPE_ANY);
+        IP_SET_TYPE_VAL(msg->conn->pcb.ip->remote_ip, IPADDR_TYPE_ANY);
+        
+        /* bind to IPADDR_TYPE_ANY */
+        ipaddr = IP_ANY_TYPE;
+      }
+#endif /* LWIP_IPV4 && LWIP_IPV6 */
+
+      switch (NETCONNTYPE_GROUP(msg->conn->type)) {
+#if LWIP_RAW
+      case NETCONN_RAW:
+        msg->err = raw_bind(msg->conn->pcb.raw, ipaddr);
+        break;
+#endif /* LWIP_RAW */
+#if LWIP_UDP
+      case NETCONN_UDP:
+        msg->err = udp_bind(msg->conn->pcb.udp, ipaddr, msg->msg.bc.port);
+        break;
+#endif /* LWIP_UDP */
+#if LWIP_TCP
+      case NETCONN_TCP:
+        msg->err = tcp_bind(msg->conn->pcb.tcp, ipaddr, msg->msg.bc.port);
+        break;
+#endif /* LWIP_TCP */
+      default:
+        break;
+      }
+    }
+  }
+  TCPIP_APIMSG_ACK(msg);
+}
+
+#if LWIP_TCP
+/**
+ * TCP callback function if a connection (opened by tcp_connect/lwip_netconn_do_connect) has
+ * been established (or reset by the remote host).
+ *
+ * @see tcp.h (struct tcp_pcb.connected) for parameters and return values
+ */
+static err_t
+lwip_netconn_do_connected(void *arg, struct tcp_pcb *pcb, err_t err)
+{
+  struct netconn *conn;
+  int was_blocking;
+  sys_sem_t* op_completed_sem = NULL;
+
+  LWIP_UNUSED_ARG(pcb);
+
+  conn = (struct netconn *)arg;
+
+  if (conn == NULL) {
+    return ERR_VAL;
+  }
+
+  LWIP_ASSERT("conn->state == NETCONN_CONNECT", conn->state == NETCONN_CONNECT);
+  LWIP_ASSERT("(conn->current_msg != NULL) || conn->in_non_blocking_connect",
+    (conn->current_msg != NULL) || IN_NONBLOCKING_CONNECT(conn));
+
+  if (conn->current_msg != NULL) {
+    conn->current_msg->err = err;
+    op_completed_sem = LWIP_API_MSG_SEM(conn->current_msg);
+  }
+  if ((NETCONNTYPE_GROUP(conn->type) == NETCONN_TCP) && (err == ERR_OK)) {
+    setup_tcp(conn);
+  }
+  was_blocking = !IN_NONBLOCKING_CONNECT(conn);
+  SET_NONBLOCKING_CONNECT(conn, 0);
+  LWIP_ASSERT("blocking connect state error",
+    (was_blocking && op_completed_sem != NULL) ||
+    (!was_blocking && op_completed_sem == NULL));
+  conn->current_msg = NULL;
+  conn->state = NETCONN_NONE;
+  NETCONN_SET_SAFE_ERR(conn, ERR_OK);
+  API_EVENT(conn, NETCONN_EVT_SENDPLUS, 0);
+
+  if (was_blocking) {
+    sys_sem_signal(op_completed_sem);
+  }
+  return ERR_OK;
+}
+#endif /* LWIP_TCP */
+
+/**
+ * Connect a pcb contained inside a netconn
+ * Called from netconn_connect.
+ *
+ * @param m the api_msg_msg pointing to the connection and containing
+ *          the IP address and port to connect to
+ */
+void
+lwip_netconn_do_connect(void *m)
+{
+  struct api_msg *msg = (struct api_msg*)m;
+
+  if (msg->conn->pcb.tcp == NULL) {
+    /* This may happen when calling netconn_connect() a second time */
+    msg->err = ERR_CLSD;
+  } else {
+    switch (NETCONNTYPE_GROUP(msg->conn->type)) {
+#if LWIP_RAW
+    case NETCONN_RAW:
+      msg->err = raw_connect(msg->conn->pcb.raw, API_EXPR_REF(msg->msg.bc.ipaddr));
+      break;
+#endif /* LWIP_RAW */
+#if LWIP_UDP
+    case NETCONN_UDP:
+      msg->err = udp_connect(msg->conn->pcb.udp, API_EXPR_REF(msg->msg.bc.ipaddr), msg->msg.bc.port);
+      break;
+#endif /* LWIP_UDP */
+#if LWIP_TCP
+    case NETCONN_TCP:
+      /* Prevent connect while doing any other action. */
+      if (msg->conn->state == NETCONN_CONNECT) {
+        msg->err = ERR_ALREADY;
+      } else if (msg->conn->state != NETCONN_NONE) {
+        msg->err = ERR_ISCONN;
+      } else {
+        setup_tcp(msg->conn);
+        msg->err = tcp_connect(msg->conn->pcb.tcp, API_EXPR_REF(msg->msg.bc.ipaddr),
+          msg->msg.bc.port, lwip_netconn_do_connected);
+        if (msg->err == ERR_OK) {
+          u8_t non_blocking = netconn_is_nonblocking(msg->conn);
+          msg->conn->state = NETCONN_CONNECT;
+          SET_NONBLOCKING_CONNECT(msg->conn, non_blocking);
+          if (non_blocking) {
+            msg->err = ERR_INPROGRESS;
+          } else {
+            msg->conn->current_msg = msg;
+            /* sys_sem_signal() is called from lwip_netconn_do_connected (or err_tcp()),
+               when the connection is established! */
+#if LWIP_TCPIP_CORE_LOCKING
+            LWIP_ASSERT("state!", msg->conn->state == NETCONN_CONNECT);
+            UNLOCK_TCPIP_CORE();
+            sys_arch_sem_wait(LWIP_API_MSG_SEM(msg), 0);
+            LOCK_TCPIP_CORE();
+            LWIP_ASSERT("state!", msg->conn->state != NETCONN_CONNECT);
+#endif /* LWIP_TCPIP_CORE_LOCKING */
+            return;
+          }
+        }
+      }
+      break;
+#endif /* LWIP_TCP */
+    default:
+      LWIP_ERROR("Invalid netconn type", 0, do{ msg->err = ERR_VAL; }while(0));
+      break;
+    }
+  }
+  /* For all other protocols, netconn_connect() calls TCPIP_APIMSG(),
+     so use TCPIP_APIMSG_ACK() here. */
+  TCPIP_APIMSG_ACK(msg);
+}
+
+/**
+ * Disconnect a pcb contained inside a netconn
+ * Only used for UDP netconns.
+ * Called from netconn_disconnect.
+ *
+ * @param m the api_msg_msg pointing to the connection to disconnect
+ */
+void
+lwip_netconn_do_disconnect(void *m)
+{
+  struct api_msg *msg = (struct api_msg*)m;
+
+#if LWIP_UDP
+  if (NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_UDP) {
+    udp_disconnect(msg->conn->pcb.udp);
+    msg->err = ERR_OK;
+  } else
+#endif /* LWIP_UDP */
+  {
+    msg->err = ERR_VAL;
+  }
+  TCPIP_APIMSG_ACK(msg);
+}
+
+#if LWIP_TCP
+/**
+ * Set a TCP pcb contained in a netconn into listen mode
+ * Called from netconn_listen.
+ *
+ * @param m the api_msg_msg pointing to the connection
+ */
+void
+lwip_netconn_do_listen(void *m)
+{
+  struct api_msg *msg = (struct api_msg*)m;
+
+  if (ERR_IS_FATAL(msg->conn->last_err)) {
+    msg->err = msg->conn->last_err;
+  } else {
+    msg->err = ERR_CONN;
+    if (msg->conn->pcb.tcp != NULL) {
+      if (NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_TCP) {
+        if (msg->conn->state == NETCONN_NONE) {
+          struct tcp_pcb* lpcb;
+          if (msg->conn->pcb.tcp->state != CLOSED) {
+            /* connection is not closed, cannot listen */
+            msg->err = ERR_VAL;
+          } else {
+#if LWIP_IPV4 && LWIP_IPV6
+            /* "Socket API like" dual-stack support: If IP to listen to is IP6_ADDR_ANY,
+             * and NETCONN_FLAG_IPV6_V6ONLY is NOT set, use IP_ANY_TYPE to listen
+             */
+            if (ip_addr_cmp(&msg->conn->pcb.ip->local_ip, IP6_ADDR_ANY) &&
+                (netconn_get_ipv6only(msg->conn) == 0)) {
+              /* change PCB type to IPADDR_TYPE_ANY */
+              IP_SET_TYPE_VAL(msg->conn->pcb.tcp->local_ip,  IPADDR_TYPE_ANY);
+              IP_SET_TYPE_VAL(msg->conn->pcb.tcp->remote_ip, IPADDR_TYPE_ANY);
+            }
+#endif /* LWIP_IPV4 && LWIP_IPV6 */
+
+#if TCP_LISTEN_BACKLOG
+            lpcb = tcp_listen_with_backlog(msg->conn->pcb.tcp, msg->msg.lb.backlog);
+#else  /* TCP_LISTEN_BACKLOG */
+            lpcb = tcp_listen(msg->conn->pcb.tcp);
+#endif /* TCP_LISTEN_BACKLOG */
+
+            if (lpcb == NULL) {
+              /* in this case, the old pcb is still allocated */
+              msg->err = ERR_MEM;
+            } else {
+              /* delete the recvmbox and allocate the acceptmbox */
+              if (sys_mbox_valid(&msg->conn->recvmbox)) {
+                /** @todo: should we drain the recvmbox here? */
+                sys_mbox_free(&msg->conn->recvmbox);
+                sys_mbox_set_invalid(&msg->conn->recvmbox);
+              }
+              msg->err = ERR_OK;
+              if (!sys_mbox_valid(&msg->conn->acceptmbox)) {
+                msg->err = sys_mbox_new(&msg->conn->acceptmbox, DEFAULT_ACCEPTMBOX_SIZE);
+              }
+              if (msg->err == ERR_OK) {
+                msg->conn->state = NETCONN_LISTEN;
+                msg->conn->pcb.tcp = lpcb;
+                tcp_arg(msg->conn->pcb.tcp, msg->conn);
+                tcp_accept(msg->conn->pcb.tcp, accept_function);
+              } else {
+                /* since the old pcb is already deallocated, free lpcb now */
+                tcp_close(lpcb);
+                msg->conn->pcb.tcp = NULL;
+              }
+            }
+          }
+        } else if (msg->conn->state == NETCONN_LISTEN) {
+          /* already listening, allow updating of the backlog */
+          msg->err = ERR_OK;
+          tcp_backlog_set(msg->conn->pcb.tcp, msg->msg.lb.backlog);
+        }
+      } else {
+        msg->err = ERR_ARG;
+      }
+    }
+  }
+  TCPIP_APIMSG_ACK(msg);
+}
+#endif /* LWIP_TCP */
+
+/**
+ * Send some data on a RAW or UDP pcb contained in a netconn
+ * Called from netconn_send
+ *
+ * @param m the api_msg_msg pointing to the connection
+ */
+void
+lwip_netconn_do_send(void *m)
+{
+  struct api_msg *msg = (struct api_msg*)m;
+
+  if (ERR_IS_FATAL(msg->conn->last_err)) {
+    msg->err = msg->conn->last_err;
+  } else {
+    msg->err = ERR_CONN;
+    if (msg->conn->pcb.tcp != NULL) {
+      switch (NETCONNTYPE_GROUP(msg->conn->type)) {
+#if LWIP_RAW
+      case NETCONN_RAW:
+        if (ip_addr_isany(&msg->msg.b->addr)) {
+          msg->err = raw_send(msg->conn->pcb.raw, msg->msg.b->p);
+        } else {
+          msg->err = raw_sendto(msg->conn->pcb.raw, msg->msg.b->p, &msg->msg.b->addr);
+        }
+        break;
+#endif
+#if LWIP_UDP
+      case NETCONN_UDP:
+#if LWIP_CHECKSUM_ON_COPY
+        if (ip_addr_isany(&msg->msg.b->addr) || IP_IS_ANY_TYPE_VAL(msg->msg.b->addr)) {
+          msg->err = udp_send_chksum(msg->conn->pcb.udp, msg->msg.b->p,
+            msg->msg.b->flags & NETBUF_FLAG_CHKSUM, msg->msg.b->toport_chksum);
+        } else {
+          msg->err = udp_sendto_chksum(msg->conn->pcb.udp, msg->msg.b->p,
+            &msg->msg.b->addr, msg->msg.b->port,
+            msg->msg.b->flags & NETBUF_FLAG_CHKSUM, msg->msg.b->toport_chksum);
+        }
+#else /* LWIP_CHECKSUM_ON_COPY */
+        if (ip_addr_isany_val(msg->msg.b->addr) || IP_IS_ANY_TYPE_VAL(msg->msg.b->addr)) {
+          msg->err = udp_send(msg->conn->pcb.udp, msg->msg.b->p);
+        } else {
+          msg->err = udp_sendto(msg->conn->pcb.udp, msg->msg.b->p, &msg->msg.b->addr, msg->msg.b->port);
+        }
+#endif /* LWIP_CHECKSUM_ON_COPY */
+        break;
+#endif /* LWIP_UDP */
+      default:
+        break;
+      }
+    }
+  }
+  TCPIP_APIMSG_ACK(msg);
+}
+
+#if LWIP_TCP
+/**
+ * Indicate data has been received from a TCP pcb contained in a netconn
+ * Called from netconn_recv
+ *
+ * @param m the api_msg_msg pointing to the connection
+ */
+void
+lwip_netconn_do_recv(void *m)
+{
+  struct api_msg *msg = (struct api_msg*)m;
+
+  msg->err = ERR_OK;
+  if (msg->conn->pcb.tcp != NULL) {
+    if (NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_TCP) {
+      u32_t remaining = msg->msg.r.len;
+      do {
+        u16_t recved = (remaining > 0xffff) ? 0xffff : (u16_t)remaining;
+        tcp_recved(msg->conn->pcb.tcp, recved);
+        remaining -= recved;
+      } while (remaining != 0);
+    }
+  }
+  TCPIP_APIMSG_ACK(msg);
+}
+
+#if TCP_LISTEN_BACKLOG
+/** Indicate that a TCP pcb has been accepted
+ * Called from netconn_accept
+ *
+ * @param m the api_msg_msg pointing to the connection
+ */
+void
+lwip_netconn_do_accepted(void *m)
+{
+  struct api_msg *msg = (struct api_msg*)m;
+
+  msg->err = ERR_OK;
+  if (msg->conn->pcb.tcp != NULL) {
+    if (NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_TCP) {
+      tcp_backlog_accepted(msg->conn->pcb.tcp);
+    }
+  }
+  TCPIP_APIMSG_ACK(msg);
+}
+#endif /* TCP_LISTEN_BACKLOG */
+
+/**
+ * See if more data needs to be written from a previous call to netconn_write.
+ * Called initially from lwip_netconn_do_write. If the first call can't send all data
+ * (because of low memory or empty send-buffer), this function is called again
+ * from sent_tcp() or poll_tcp() to send more data. If all data is sent, the
+ * blocking application thread (waiting in netconn_write) is released.
+ *
+ * @param conn netconn (that is currently in state NETCONN_WRITE) to process
+ * [@param delay 1 if called from sent/poll (wake up calling thread on end)]
+ * @return ERR_OK
+ *         ERR_MEM if LWIP_TCPIP_CORE_LOCKING=1 and sending hasn't yet finished
+ */
+static err_t
+lwip_netconn_do_writemore(struct netconn *conn  WRITE_DELAYED_PARAM)
+{
+  err_t err;
+  const void *dataptr;
+  u16_t len, available;
+  u8_t write_finished = 0;
+  size_t diff;
+  u8_t dontblock;
+  u8_t apiflags;
+
+  LWIP_ASSERT("conn != NULL", conn != NULL);
+  LWIP_ASSERT("conn->state == NETCONN_WRITE", (conn->state == NETCONN_WRITE));
+  LWIP_ASSERT("conn->current_msg != NULL", conn->current_msg != NULL);
+  LWIP_ASSERT("conn->pcb.tcp != NULL", conn->pcb.tcp != NULL);
+  LWIP_ASSERT("conn->write_offset < conn->current_msg->msg.w.len",
+    conn->write_offset < conn->current_msg->msg.w.len);
+
+  dontblock = netconn_is_nonblocking(conn) ||
+       (conn->current_msg->msg.w.apiflags & NETCONN_DONTBLOCK);
+  apiflags = conn->current_msg->msg.w.apiflags;
+
+#if LWIP_SO_SNDTIMEO
+  if ((conn->send_timeout != 0) &&
+      ((s32_t)(sys_now() - conn->current_msg->msg.w.time_started) >= conn->send_timeout)) {
+    write_finished = 1;
+    if (conn->write_offset == 0) {
+      /* nothing has been written */
+      err = ERR_WOULDBLOCK;
+      conn->current_msg->msg.w.len = 0;
+    } else {
+      /* partial write */
+      err = ERR_OK;
+      conn->current_msg->msg.w.len = conn->write_offset;
+      conn->write_offset = 0;
+    }
+  } else
+#endif /* LWIP_SO_SNDTIMEO */
+  {
+    dataptr = (const u8_t*)conn->current_msg->msg.w.dataptr + conn->write_offset;
+    diff = conn->current_msg->msg.w.len - conn->write_offset;
+    if (diff > 0xffffUL) { /* max_u16_t */
+      len = 0xffff;
+      apiflags |= TCP_WRITE_FLAG_MORE;
+    } else {
+      len = (u16_t)diff;
+    }
+    available = tcp_sndbuf(conn->pcb.tcp);
+    if (available < len) {
+      /* don't try to write more than sendbuf */
+      len = available;
+      if (dontblock) {
+        if (!len) {
+          err = ERR_WOULDBLOCK;
+          goto err_mem;
+        }
+      } else {
+        apiflags |= TCP_WRITE_FLAG_MORE;
+      }
+    }
+    LWIP_ASSERT("lwip_netconn_do_writemore: invalid length!", ((conn->write_offset + len) <= conn->current_msg->msg.w.len));
+    err = tcp_write(conn->pcb.tcp, dataptr, len, apiflags);
+    /* if OK or memory error, check available space */
+    if ((err == ERR_OK) || (err == ERR_MEM)) {
+err_mem:
+      if (dontblock && (len < conn->current_msg->msg.w.len)) {
+        /* non-blocking write did not write everything: mark the pcb non-writable
+           and let poll_tcp check writable space to mark the pcb writable again */
+        API_EVENT(conn, NETCONN_EVT_SENDMINUS, len);
+        conn->flags |= NETCONN_FLAG_CHECK_WRITESPACE;
+      } else if ((tcp_sndbuf(conn->pcb.tcp) <= TCP_SNDLOWAT) ||
+                 (tcp_sndqueuelen(conn->pcb.tcp) >= TCP_SNDQUEUELOWAT)) {
+        /* The queued byte- or pbuf-count exceeds the configured low-water limit,
+           let select mark this pcb as non-writable. */
+        API_EVENT(conn, NETCONN_EVT_SENDMINUS, len);
+      }
+    }
+
+    if (err == ERR_OK) {
+      err_t out_err;
+      conn->write_offset += len;
+      if ((conn->write_offset == conn->current_msg->msg.w.len) || dontblock) {
+        /* return sent length */
+        conn->current_msg->msg.w.len = conn->write_offset;
+        /* everything was written */
+        write_finished = 1;
+      }
+      out_err = tcp_output(conn->pcb.tcp);
+      if (ERR_IS_FATAL(out_err) || (out_err == ERR_RTE)) {
+        /* If tcp_output fails with fatal error or no route is found,
+           don't try writing any more but return the error
+           to the application thread. */
+        err = out_err;
+        write_finished = 1;
+        conn->current_msg->msg.w.len = 0;
+      }
+    } else if ((err == ERR_MEM) && !dontblock) {
+      /* If ERR_MEM, we wait for sent_tcp or poll_tcp to be called
+         we do NOT return to the application thread, since ERR_MEM is
+         only a temporary error! */
+
+      /* tcp_write returned ERR_MEM, try tcp_output anyway */
+      err_t out_err = tcp_output(conn->pcb.tcp);
+      if (ERR_IS_FATAL(out_err) || (out_err == ERR_RTE)) {
+        /* If tcp_output fails with fatal error or no route is found,
+           don't try writing any more but return the error
+           to the application thread. */
+        err = out_err;
+        write_finished = 1;
+        conn->current_msg->msg.w.len = 0;
+      } else {
+      }
+    } else {
+      /* On errors != ERR_MEM, we don't try writing any more but return
+         the error to the application thread. */
+      write_finished = 1;
+      conn->current_msg->msg.w.len = 0;
+    }
+  }
+  if (write_finished) {
+    /* everything was written: set back connection state
+       and back to application task */
+    sys_sem_t* op_completed_sem = LWIP_API_MSG_SEM(conn->current_msg);
+    conn->current_msg->err = err;
+    conn->current_msg = NULL;
+    conn->write_offset = 0;
+    conn->state = NETCONN_NONE;
+    NETCONN_SET_SAFE_ERR(conn, err);
+#if LWIP_TCPIP_CORE_LOCKING
+    if (delayed)
+#endif
+    {
+      sys_sem_signal(op_completed_sem);
+    }
+  }
+#if LWIP_TCPIP_CORE_LOCKING
+  else {
+    return ERR_MEM;
+  }
+#endif
+  return ERR_OK;
+}
+#endif /* LWIP_TCP */
+
+/**
+ * Send some data on a TCP pcb contained in a netconn
+ * Called from netconn_write
+ *
+ * @param m the api_msg_msg pointing to the connection
+ */
+void
+lwip_netconn_do_write(void *m)
+{
+  struct api_msg *msg = (struct api_msg*)m;
+
+  if (ERR_IS_FATAL(msg->conn->last_err)) {
+    msg->err = msg->conn->last_err;
+  } else {
+    if (NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_TCP) {
+#if LWIP_TCP
+      if (msg->conn->state != NETCONN_NONE) {
+        /* netconn is connecting, closing or in blocking write */
+        msg->err = ERR_INPROGRESS;
+      } else if (msg->conn->pcb.tcp != NULL) {
+        msg->conn->state = NETCONN_WRITE;
+        /* set all the variables used by lwip_netconn_do_writemore */
+        LWIP_ASSERT("already writing or closing", msg->conn->current_msg == NULL &&
+          msg->conn->write_offset == 0);
+        LWIP_ASSERT("msg->msg.w.len != 0", msg->msg.w.len != 0);
+        msg->conn->current_msg = msg;
+        msg->conn->write_offset = 0;
+#if LWIP_TCPIP_CORE_LOCKING
+        if (lwip_netconn_do_writemore(msg->conn, 0) != ERR_OK) {
+          LWIP_ASSERT("state!", msg->conn->state == NETCONN_WRITE);
+          UNLOCK_TCPIP_CORE();
+          sys_arch_sem_wait(LWIP_API_MSG_SEM(msg), 0);
+          LOCK_TCPIP_CORE();
+          LWIP_ASSERT("state!", msg->conn->state != NETCONN_WRITE);
+        }
+#else /* LWIP_TCPIP_CORE_LOCKING */
+        lwip_netconn_do_writemore(msg->conn);
+#endif /* LWIP_TCPIP_CORE_LOCKING */
+        /* for both cases: if lwip_netconn_do_writemore was called, don't ACK the APIMSG
+           since lwip_netconn_do_writemore ACKs it! */
+        return;
+      } else {
+        msg->err = ERR_CONN;
+      }
+#else /* LWIP_TCP */
+      msg->err = ERR_VAL;
+#endif /* LWIP_TCP */
+#if (LWIP_UDP || LWIP_RAW)
+    } else {
+      msg->err = ERR_VAL;
+#endif /* (LWIP_UDP || LWIP_RAW) */
+    }
+  }
+  TCPIP_APIMSG_ACK(msg);
+}
+
+/**
+ * Return a connection's local or remote address
+ * Called from netconn_getaddr
+ *
+ * @param m the api_msg_msg pointing to the connection
+ */
+void
+lwip_netconn_do_getaddr(void *m)
+{
+  struct api_msg *msg = (struct api_msg*)m;
+
+  if (msg->conn->pcb.ip != NULL) {
+    if (msg->msg.ad.local) {
+      ip_addr_copy(API_EXPR_DEREF(msg->msg.ad.ipaddr),
+        msg->conn->pcb.ip->local_ip);
+    } else {
+      ip_addr_copy(API_EXPR_DEREF(msg->msg.ad.ipaddr),
+        msg->conn->pcb.ip->remote_ip);
+    }
+    msg->err = ERR_OK;
+    switch (NETCONNTYPE_GROUP(msg->conn->type)) {
+#if LWIP_RAW
+    case NETCONN_RAW:
+      if (msg->msg.ad.local) {
+        API_EXPR_DEREF(msg->msg.ad.port) = msg->conn->pcb.raw->protocol;
+      } else {
+        /* return an error as connecting is only a helper for upper layers */
+        msg->err = ERR_CONN;
+      }
+      break;
+#endif /* LWIP_RAW */
+#if LWIP_UDP
+    case NETCONN_UDP:
+      if (msg->msg.ad.local) {
+        API_EXPR_DEREF(msg->msg.ad.port) = msg->conn->pcb.udp->local_port;
+      } else {
+        if ((msg->conn->pcb.udp->flags & UDP_FLAGS_CONNECTED) == 0) {
+          msg->err = ERR_CONN;
+        } else {
+          API_EXPR_DEREF(msg->msg.ad.port) = msg->conn->pcb.udp->remote_port;
+        }
+      }
+      break;
+#endif /* LWIP_UDP */
+#if LWIP_TCP
+    case NETCONN_TCP:
+      if ((msg->msg.ad.local == 0) &&
+          ((msg->conn->pcb.tcp->state == CLOSED) || (msg->conn->pcb.tcp->state == LISTEN))) {
+        /* pcb is not connected and remote name is requested */
+        msg->err = ERR_CONN;
+      } else {
+        API_EXPR_DEREF(msg->msg.ad.port) = (msg->msg.ad.local ? msg->conn->pcb.tcp->local_port : msg->conn->pcb.tcp->remote_port);
+      }
+      break;
+#endif /* LWIP_TCP */
+    default:
+      LWIP_ASSERT("invalid netconn_type", 0);
+      break;
+    }
+  } else {
+    msg->err = ERR_CONN;
+  }
+  TCPIP_APIMSG_ACK(msg);
+}
+
+/**
+ * Close or half-shutdown a TCP pcb contained in a netconn
+ * Called from netconn_close
+ * In contrast to closing sockets, the netconn is not deallocated.
+ *
+ * @param m the api_msg_msg pointing to the connection
+ */
+void
+lwip_netconn_do_close(void *m)
+{
+  struct api_msg *msg = (struct api_msg*)m;
+
+#if LWIP_TCP
+  enum netconn_state state = msg->conn->state;
+  /* First check if this is a TCP netconn and if it is in a correct state
+      (LISTEN doesn't support half shutdown) */
+  if ((msg->conn->pcb.tcp != NULL) &&
+      (NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_TCP) &&
+      ((msg->msg.sd.shut == NETCONN_SHUT_RDWR) || (state != NETCONN_LISTEN))) {
+    /* Check if we are in a connected state */
+    if (state == NETCONN_CONNECT) {
+      /* TCP connect in progress: cannot shutdown */
+      msg->err = ERR_CONN;
+    } else if (state == NETCONN_WRITE) {
+#if LWIP_NETCONN_FULLDUPLEX
+      if (msg->msg.sd.shut & NETCONN_SHUT_WR) {
+        /* close requested, abort running write */
+        sys_sem_t* op_completed_sem;
+        LWIP_ASSERT("msg->conn->current_msg != NULL", msg->conn->current_msg != NULL);
+        op_completed_sem = LWIP_API_MSG_SEM(msg->conn->current_msg);
+        msg->conn->current_msg->err = ERR_CLSD;
+        msg->conn->current_msg = NULL;
+        msg->conn->write_offset = 0;
+        msg->conn->state = NETCONN_NONE;
+        NETCONN_SET_SAFE_ERR(msg->conn, ERR_CLSD);
+        sys_sem_signal(op_completed_sem);
+      } else {
+        LWIP_ASSERT("msg->msg.sd.shut == NETCONN_SHUT_RD", msg->msg.sd.shut == NETCONN_SHUT_RD);
+        /* In this case, let the write continue and do not interfere with
+           conn->current_msg or conn->state! */
+        msg->err = tcp_shutdown(msg->conn->pcb.tcp, 1, 0);
+      }
+#else /* LWIP_NETCONN_FULLDUPLEX */
+      msg->err = ERR_INPROGRESS;
+#endif /* LWIP_NETCONN_FULLDUPLEX */
+    } else {
+      if (msg->msg.sd.shut & NETCONN_SHUT_RD) {
+        /* Drain and delete mboxes */
+        netconn_drain(msg->conn);
+      }
+      LWIP_ASSERT("already writing or closing", msg->conn->current_msg == NULL &&
+        msg->conn->write_offset == 0);
+      msg->conn->state = NETCONN_CLOSE;
+      msg->conn->current_msg = msg;
+#if LWIP_TCPIP_CORE_LOCKING
+      if (lwip_netconn_do_close_internal(msg->conn, 0) != ERR_OK) {
+        LWIP_ASSERT("state!", msg->conn->state == NETCONN_CLOSE);
+        UNLOCK_TCPIP_CORE();
+        sys_arch_sem_wait(LWIP_API_MSG_SEM(msg), 0);
+        LOCK_TCPIP_CORE();
+        LWIP_ASSERT("state!", msg->conn->state == NETCONN_NONE);
+      }
+#else /* LWIP_TCPIP_CORE_LOCKING */
+      lwip_netconn_do_close_internal(msg->conn);
+#endif /* LWIP_TCPIP_CORE_LOCKING */
+      /* for tcp netconns, lwip_netconn_do_close_internal ACKs the message */
+      return;
+    }
+  } else
+#endif /* LWIP_TCP */
+  {
+    msg->err = ERR_CONN;
+  }
+  TCPIP_APIMSG_ACK(msg);
+}
+
+#if LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD)
+/**
+ * Join multicast groups for UDP netconns.
+ * Called from netconn_join_leave_group
+ *
+ * @param m the api_msg_msg pointing to the connection
+ */
+void
+lwip_netconn_do_join_leave_group(void *m)
+{
+  struct api_msg *msg = (struct api_msg*)m;
+
+  if (ERR_IS_FATAL(msg->conn->last_err)) {
+    msg->err = msg->conn->last_err;
+  } else {
+    if (msg->conn->pcb.tcp != NULL) {
+      if (NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_UDP) {
+#if LWIP_UDP
+#if LWIP_IPV6 && LWIP_IPV6_MLD
+        if (NETCONNTYPE_ISIPV6(msg->conn->type)) {
+          if (msg->msg.jl.join_or_leave == NETCONN_JOIN) {
+            msg->err = mld6_joingroup(ip_2_ip6(API_EXPR_REF(msg->msg.jl.netif_addr)),
+              ip_2_ip6(API_EXPR_REF(msg->msg.jl.multiaddr)));
+          } else {
+            msg->err = mld6_leavegroup(ip_2_ip6(API_EXPR_REF(msg->msg.jl.netif_addr)),
+              ip_2_ip6(API_EXPR_REF(msg->msg.jl.multiaddr)));
+          }
+        }
+        else
+#endif /* LWIP_IPV6 && LWIP_IPV6_MLD */
+        {
+#if LWIP_IGMP
+          if (msg->msg.jl.join_or_leave == NETCONN_JOIN) {
+            msg->err = igmp_joingroup(ip_2_ip4(API_EXPR_REF(msg->msg.jl.netif_addr)),
+              ip_2_ip4(API_EXPR_REF(msg->msg.jl.multiaddr)));
+          } else {
+            msg->err = igmp_leavegroup(ip_2_ip4(API_EXPR_REF(msg->msg.jl.netif_addr)),
+              ip_2_ip4(API_EXPR_REF(msg->msg.jl.multiaddr)));
+          }
+#endif /* LWIP_IGMP */
+        }
+#endif /* LWIP_UDP */
+#if (LWIP_TCP || LWIP_RAW)
+      } else {
+        msg->err = ERR_VAL;
+#endif /* (LWIP_TCP || LWIP_RAW) */
+      }
+    } else {
+      msg->err = ERR_CONN;
+    }
+  }
+  TCPIP_APIMSG_ACK(msg);
+}
+#endif /* LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD) */
+
+#if LWIP_DNS
+/**
+ * Callback function that is called when DNS name is resolved
+ * (or on timeout). A waiting application thread is waked up by
+ * signaling the semaphore.
+ */
+static void
+lwip_netconn_do_dns_found(const char *name, const ip_addr_t *ipaddr, void *arg)
+{
+  struct dns_api_msg *msg = (struct dns_api_msg*)arg;
+
+  /* we trust the internal implementation to be correct :-) */
+  LWIP_UNUSED_ARG(name);
+
+  if (ipaddr == NULL) {
+    /* timeout or memory error */
+    API_EXPR_DEREF(msg->err) = ERR_VAL;
+  } else {
+    /* address was resolved */
+    API_EXPR_DEREF(msg->err) = ERR_OK;
+    API_EXPR_DEREF(msg->addr) = *ipaddr;
+  }
+  /* wake up the application task waiting in netconn_gethostbyname */
+  sys_sem_signal(API_EXPR_REF_SEM(msg->sem));
+}
+
+/**
+ * Execute a DNS query
+ * Called from netconn_gethostbyname
+ *
+ * @param arg the dns_api_msg pointing to the query
+ */
+void
+lwip_netconn_do_gethostbyname(void *arg)
+{
+  struct dns_api_msg *msg = (struct dns_api_msg*)arg;
+  u8_t addrtype =
+#if LWIP_IPV4 && LWIP_IPV6
+    msg->dns_addrtype;
+#else
+    LWIP_DNS_ADDRTYPE_DEFAULT;
+#endif
+
+  API_EXPR_DEREF(msg->err) = dns_gethostbyname_addrtype(msg->name,
+    API_EXPR_REF(msg->addr), lwip_netconn_do_dns_found, msg, addrtype);
+  if (API_EXPR_DEREF(msg->err) != ERR_INPROGRESS) {
+    /* on error or immediate success, wake up the application
+     * task waiting in netconn_gethostbyname */
+    sys_sem_signal(API_EXPR_REF_SEM(msg->sem));
+  }
+}
+#endif /* LWIP_DNS */
+
+#endif /* LWIP_NETCONN */

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/f5a0f2a0/net/ip/lwip_base/src/api/err.c
----------------------------------------------------------------------
diff --git a/net/ip/lwip_base/src/api/err.c b/net/ip/lwip_base/src/api/err.c
new file mode 100644
index 0000000..f3650f4
--- /dev/null
+++ b/net/ip/lwip_base/src/api/err.c
@@ -0,0 +1,75 @@
+/**
+ * @file
+ * Error Management module
+ *
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <ad...@sics.se>
+ *
+ */
+
+#include "lwip/err.h"
+
+#ifdef LWIP_DEBUG
+
+static const char *err_strerr[] = {
+           "Ok.",                    /* ERR_OK          0  */
+           "Out of memory error.",   /* ERR_MEM        -1  */
+           "Buffer error.",          /* ERR_BUF        -2  */
+           "Timeout.",               /* ERR_TIMEOUT    -3  */
+           "Routing problem.",       /* ERR_RTE        -4  */
+           "Operation in progress.", /* ERR_INPROGRESS -5  */
+           "Illegal value.",         /* ERR_VAL        -6  */
+           "Operation would block.", /* ERR_WOULDBLOCK -7  */
+           "Address in use.",        /* ERR_USE        -8  */
+           "Already connecting.",    /* ERR_ALREADY    -9  */
+           "Already connected.",     /* ERR_ISCONN     -10 */
+           "Not connected.",         /* ERR_CONN       -11 */
+           "Low-level netif error.", /* ERR_IF         -12 */
+           "Connection aborted.",    /* ERR_ABRT       -13 */
+           "Connection reset.",      /* ERR_RST        -14 */
+           "Connection closed.",     /* ERR_CLSD       -15 */
+           "Illegal argument."       /* ERR_ARG        -16 */
+};
+
+/**
+ * Convert an lwip internal error to a string representation.
+ *
+ * @param err an lwip internal err_t
+ * @return a string representation for err
+ */
+const char *
+lwip_strerr(err_t err)
+{
+  return err_strerr[-err];
+}
+
+#endif /* LWIP_DEBUG */

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/f5a0f2a0/net/ip/lwip_base/src/api/netbuf.c
----------------------------------------------------------------------
diff --git a/net/ip/lwip_base/src/api/netbuf.c b/net/ip/lwip_base/src/api/netbuf.c
new file mode 100644
index 0000000..eb25011
--- /dev/null
+++ b/net/ip/lwip_base/src/api/netbuf.c
@@ -0,0 +1,246 @@
+/**
+ * @file
+ * Network buffer management
+ *
+ * @defgroup netbuf Network buffers
+ * @ingroup netconn
+ * Network buffer descriptor for @ref netconn. Based on @ref pbuf internally
+ * to avoid copying data around.\n
+ * Buffers must not be shared accross multiple threads, all functions except
+ * netbuf_new() and netbuf_delete() are not thread-safe.
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <ad...@sics.se>
+ *
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_NETCONN /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/netbuf.h"
+#include "lwip/memp.h"
+
+#include <string.h>
+
+/**
+ * @ingroup netbuf
+ * Create (allocate) and initialize a new netbuf.
+ * The netbuf doesn't yet contain a packet buffer!
+ *
+ * @return a pointer to a new netbuf
+ *         NULL on lack of memory
+ */
+struct
+netbuf *netbuf_new(void)
+{
+  struct netbuf *buf;
+
+  buf = (struct netbuf *)memp_malloc(MEMP_NETBUF);
+  if (buf != NULL) {
+    memset(buf, 0, sizeof(struct netbuf));
+  }
+  return buf;
+}
+
+/**
+ * @ingroup netbuf
+ * Deallocate a netbuf allocated by netbuf_new().
+ *
+ * @param buf pointer to a netbuf allocated by netbuf_new()
+ */
+void
+netbuf_delete(struct netbuf *buf)
+{
+  if (buf != NULL) {
+    if (buf->p != NULL) {
+      pbuf_free(buf->p);
+      buf->p = buf->ptr = NULL;
+    }
+    memp_free(MEMP_NETBUF, buf);
+  }
+}
+
+/**
+ * @ingroup netbuf
+ * Allocate memory for a packet buffer for a given netbuf.
+ *
+ * @param buf the netbuf for which to allocate a packet buffer
+ * @param size the size of the packet buffer to allocate
+ * @return pointer to the allocated memory
+ *         NULL if no memory could be allocated
+ */
+void *
+netbuf_alloc(struct netbuf *buf, u16_t size)
+{
+  LWIP_ERROR("netbuf_alloc: invalid buf", (buf != NULL), return NULL;);
+
+  /* Deallocate any previously allocated memory. */
+  if (buf->p != NULL) {
+    pbuf_free(buf->p);
+  }
+  buf->p = pbuf_alloc(PBUF_TRANSPORT, size, PBUF_RAM);
+  if (buf->p == NULL) {
+     return NULL;
+  }
+  LWIP_ASSERT("check that first pbuf can hold size",
+             (buf->p->len >= size));
+  buf->ptr = buf->p;
+  return buf->p->payload;
+}
+
+/**
+ * @ingroup netbuf
+ * Free the packet buffer included in a netbuf
+ *
+ * @param buf pointer to the netbuf which contains the packet buffer to free
+ */
+void
+netbuf_free(struct netbuf *buf)
+{
+  LWIP_ERROR("netbuf_free: invalid buf", (buf != NULL), return;);
+  if (buf->p != NULL) {
+    pbuf_free(buf->p);
+  }
+  buf->p = buf->ptr = NULL;
+}
+
+/**
+ * @ingroup netbuf
+ * Let a netbuf reference existing (non-volatile) data.
+ *
+ * @param buf netbuf which should reference the data
+ * @param dataptr pointer to the data to reference
+ * @param size size of the data
+ * @return ERR_OK if data is referenced
+ *         ERR_MEM if data couldn't be referenced due to lack of memory
+ */
+err_t
+netbuf_ref(struct netbuf *buf, const void *dataptr, u16_t size)
+{
+  LWIP_ERROR("netbuf_ref: invalid buf", (buf != NULL), return ERR_ARG;);
+  if (buf->p != NULL) {
+    pbuf_free(buf->p);
+  }
+  buf->p = pbuf_alloc(PBUF_TRANSPORT, 0, PBUF_REF);
+  if (buf->p == NULL) {
+    buf->ptr = NULL;
+    return ERR_MEM;
+  }
+  ((struct pbuf_rom*)buf->p)->payload = dataptr;
+  buf->p->len = buf->p->tot_len = size;
+  buf->ptr = buf->p;
+  return ERR_OK;
+}
+
+/**
+ * @ingroup netbuf
+ * Chain one netbuf to another (@see pbuf_chain)
+ *
+ * @param head the first netbuf
+ * @param tail netbuf to chain after head, freed by this function, may not be reference after returning
+ */
+void
+netbuf_chain(struct netbuf *head, struct netbuf *tail)
+{
+  LWIP_ERROR("netbuf_chain: invalid head", (head != NULL), return;);
+  LWIP_ERROR("netbuf_chain: invalid tail", (tail != NULL), return;);
+  pbuf_cat(head->p, tail->p);
+  head->ptr = head->p;
+  memp_free(MEMP_NETBUF, tail);
+}
+
+/**
+ * @ingroup netbuf
+ * Get the data pointer and length of the data inside a netbuf.
+ *
+ * @param buf netbuf to get the data from
+ * @param dataptr pointer to a void pointer where to store the data pointer
+ * @param len pointer to an u16_t where the length of the data is stored
+ * @return ERR_OK if the information was retrieved,
+ *         ERR_BUF on error.
+ */
+err_t
+netbuf_data(struct netbuf *buf, void **dataptr, u16_t *len)
+{
+  LWIP_ERROR("netbuf_data: invalid buf", (buf != NULL), return ERR_ARG;);
+  LWIP_ERROR("netbuf_data: invalid dataptr", (dataptr != NULL), return ERR_ARG;);
+  LWIP_ERROR("netbuf_data: invalid len", (len != NULL), return ERR_ARG;);
+
+  if (buf->ptr == NULL) {
+    return ERR_BUF;
+  }
+  *dataptr = buf->ptr->payload;
+  *len = buf->ptr->len;
+  return ERR_OK;
+}
+
+/**
+ * @ingroup netbuf
+ * Move the current data pointer of a packet buffer contained in a netbuf
+ * to the next part.
+ * The packet buffer itself is not modified.
+ *
+ * @param buf the netbuf to modify
+ * @return -1 if there is no next part
+ *         1  if moved to the next part but now there is no next part
+ *         0  if moved to the next part and there are still more parts
+ */
+s8_t
+netbuf_next(struct netbuf *buf)
+{
+  LWIP_ERROR("netbuf_next: invalid buf", (buf != NULL), return -1;);
+  if (buf->ptr->next == NULL) {
+    return -1;
+  }
+  buf->ptr = buf->ptr->next;
+  if (buf->ptr->next == NULL) {
+    return 1;
+  }
+  return 0;
+}
+
+/**
+ * @ingroup netbuf
+ * Move the current data pointer of a packet buffer contained in a netbuf
+ * to the beginning of the packet.
+ * The packet buffer itself is not modified.
+ *
+ * @param buf the netbuf to modify
+ */
+void
+netbuf_first(struct netbuf *buf)
+{
+  LWIP_ERROR("netbuf_first: invalid buf", (buf != NULL), return;);
+  buf->ptr = buf->p;
+}
+
+#endif /* LWIP_NETCONN */

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/f5a0f2a0/net/ip/lwip_base/src/api/netdb.c
----------------------------------------------------------------------
diff --git a/net/ip/lwip_base/src/api/netdb.c b/net/ip/lwip_base/src/api/netdb.c
new file mode 100644
index 0000000..670f6b9
--- /dev/null
+++ b/net/ip/lwip_base/src/api/netdb.c
@@ -0,0 +1,413 @@
+/**
+ * @file
+ * API functions for name resolving
+ *
+ * @defgroup netdbapi NETDB API
+ * @ingroup socket
+ */
+
+/*
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Simon Goldschmidt
+ *
+ */
+
+#include "lwip/netdb.h"
+
+#if LWIP_DNS && LWIP_SOCKET
+
+#include "lwip/err.h"
+#include "lwip/mem.h"
+#include "lwip/memp.h"
+#include "lwip/ip_addr.h"
+#include "lwip/api.h"
+#include "lwip/dns.h"
+
+#include <string.h>
+#include <stdlib.h>
+
+/** helper struct for gethostbyname_r to access the char* buffer */
+struct gethostbyname_r_helper {
+  ip_addr_t *addr_list[2];
+  ip_addr_t addr;
+  char *aliases;
+};
+
+/** h_errno is exported in netdb.h for access by applications. */
+#if LWIP_DNS_API_DECLARE_H_ERRNO
+int h_errno;
+#endif /* LWIP_DNS_API_DECLARE_H_ERRNO */
+
+/** define "hostent" variables storage: 0 if we use a static (but unprotected)
+ * set of variables for lwip_gethostbyname, 1 if we use a local storage */
+#ifndef LWIP_DNS_API_HOSTENT_STORAGE
+#define LWIP_DNS_API_HOSTENT_STORAGE 0
+#endif
+
+/** define "hostent" variables storage */
+#if LWIP_DNS_API_HOSTENT_STORAGE
+#define HOSTENT_STORAGE
+#else
+#define HOSTENT_STORAGE static
+#endif /* LWIP_DNS_API_STATIC_HOSTENT */
+
+/**
+ * Returns an entry containing addresses of address family AF_INET
+ * for the host with name name.
+ * Due to dns_gethostbyname limitations, only one address is returned.
+ *
+ * @param name the hostname to resolve
+ * @return an entry containing addresses of address family AF_INET
+ *         for the host with name name
+ */
+struct hostent*
+lwip_gethostbyname(const char *name)
+{
+  err_t err;
+  ip_addr_t addr;
+
+  /* buffer variables for lwip_gethostbyname() */
+  HOSTENT_STORAGE struct hostent s_hostent;
+  HOSTENT_STORAGE char *s_aliases;
+  HOSTENT_STORAGE ip_addr_t s_hostent_addr;
+  HOSTENT_STORAGE ip_addr_t *s_phostent_addr[2];
+  HOSTENT_STORAGE char s_hostname[DNS_MAX_NAME_LENGTH + 1];
+
+  /* query host IP address */
+  err = netconn_gethostbyname(name, &addr);
+  if (err != ERR_OK) {
+    LWIP_DEBUGF(DNS_DEBUG, ("lwip_gethostbyname(%s) failed, err=%d\n", name, err));
+    h_errno = HOST_NOT_FOUND;
+    return NULL;
+  }
+
+  /* fill hostent */
+  s_hostent_addr = addr;
+  s_phostent_addr[0] = &s_hostent_addr;
+  s_phostent_addr[1] = NULL;
+  strncpy(s_hostname, name, DNS_MAX_NAME_LENGTH);
+  s_hostname[DNS_MAX_NAME_LENGTH] = 0;
+  s_hostent.h_name = s_hostname;
+  s_aliases = NULL;
+  s_hostent.h_aliases = &s_aliases;
+  s_hostent.h_addrtype = AF_INET;
+  s_hostent.h_length = sizeof(ip_addr_t);
+  s_hostent.h_addr_list = (char**)&s_phostent_addr;
+
+#if DNS_DEBUG
+  /* dump hostent */
+  LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_name           == %s\n", s_hostent.h_name));
+  LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_aliases        == %p\n", (void*)s_hostent.h_aliases));
+  /* h_aliases are always empty */
+  LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_addrtype       == %d\n", s_hostent.h_addrtype));
+  LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_length         == %d\n", s_hostent.h_length));
+  LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_addr_list      == %p\n", (void*)s_hostent.h_addr_list));
+  if (s_hostent.h_addr_list != NULL) {
+    u8_t idx;
+    for (idx=0; s_hostent.h_addr_list[idx]; idx++) {
+      LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_addr_list[%i]   == %p\n", idx, s_hostent.h_addr_list[idx]));
+      LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_addr_list[%i]-> == %s\n", idx, ipaddr_ntoa((ip_addr_t*)s_hostent.h_addr_list[idx])));
+    }
+  }
+#endif /* DNS_DEBUG */
+
+#if LWIP_DNS_API_HOSTENT_STORAGE
+  /* this function should return the "per-thread" hostent after copy from s_hostent */
+  return sys_thread_hostent(&s_hostent);
+#else
+  return &s_hostent;
+#endif /* LWIP_DNS_API_HOSTENT_STORAGE */
+}
+
+/**
+ * Thread-safe variant of lwip_gethostbyname: instead of using a static
+ * buffer, this function takes buffer and errno pointers as arguments
+ * and uses these for the result.
+ *
+ * @param name the hostname to resolve
+ * @param ret pre-allocated struct where to store the result
+ * @param buf pre-allocated buffer where to store additional data
+ * @param buflen the size of buf
+ * @param result pointer to a hostent pointer that is set to ret on success
+ *               and set to zero on error
+ * @param h_errnop pointer to an int where to store errors (instead of modifying
+ *                 the global h_errno)
+ * @return 0 on success, non-zero on error, additional error information
+ *         is stored in *h_errnop instead of h_errno to be thread-safe
+ */
+int
+lwip_gethostbyname_r(const char *name, struct hostent *ret, char *buf,
+                size_t buflen, struct hostent **result, int *h_errnop)
+{
+  err_t err;
+  struct gethostbyname_r_helper *h;
+  char *hostname;
+  size_t namelen;
+  int lh_errno;
+
+  if (h_errnop == NULL) {
+    /* ensure h_errnop is never NULL */
+    h_errnop = &lh_errno;
+  }
+
+  if (result == NULL) {
+    /* not all arguments given */
+    *h_errnop = EINVAL;
+    return -1;
+  }
+  /* first thing to do: set *result to nothing */
+  *result = NULL;
+  if ((name == NULL) || (ret == NULL) || (buf == NULL)) {
+    /* not all arguments given */
+    *h_errnop = EINVAL;
+    return -1;
+  }
+
+  namelen = strlen(name);
+  if (buflen < (sizeof(struct gethostbyname_r_helper) + namelen + 1 + (MEM_ALIGNMENT - 1))) {
+    /* buf can't hold the data needed + a copy of name */
+    *h_errnop = ERANGE;
+    return -1;
+  }
+
+  h = (struct gethostbyname_r_helper*)LWIP_MEM_ALIGN(buf);
+  hostname = ((char*)h) + sizeof(struct gethostbyname_r_helper);
+
+  /* query host IP address */
+  err = netconn_gethostbyname(name, &h->addr);
+  if (err != ERR_OK) {
+    LWIP_DEBUGF(DNS_DEBUG, ("lwip_gethostbyname(%s) failed, err=%d\n", name, err));
+    *h_errnop = HOST_NOT_FOUND;
+    return -1;
+  }
+
+  /* copy the hostname into buf */
+  MEMCPY(hostname, name, namelen);
+  hostname[namelen] = 0;
+
+  /* fill hostent */
+  h->addr_list[0] = &h->addr;
+  h->addr_list[1] = NULL;
+  h->aliases = NULL;
+  ret->h_name = hostname;
+  ret->h_aliases = &h->aliases;
+  ret->h_addrtype = AF_INET;
+  ret->h_length = sizeof(ip_addr_t);
+  ret->h_addr_list = (char**)&h->addr_list;
+
+  /* set result != NULL */
+  *result = ret;
+
+  /* return success */
+  return 0;
+}
+
+/**
+ * Frees one or more addrinfo structures returned by getaddrinfo(), along with
+ * any additional storage associated with those structures. If the ai_next field
+ * of the structure is not null, the entire list of structures is freed.
+ *
+ * @param ai struct addrinfo to free
+ */
+void
+lwip_freeaddrinfo(struct addrinfo *ai)
+{
+  struct addrinfo *next;
+
+  while (ai != NULL) {
+    next = ai->ai_next;
+    memp_free(MEMP_NETDB, ai);
+    ai = next;
+  }
+}
+
+/**
+ * Translates the name of a service location (for example, a host name) and/or
+ * a service name and returns a set of socket addresses and associated
+ * information to be used in creating a socket with which to address the
+ * specified service.
+ * Memory for the result is allocated internally and must be freed by calling
+ * lwip_freeaddrinfo()!
+ *
+ * Due to a limitation in dns_gethostbyname, only the first address of a
+ * host is returned.
+ * Also, service names are not supported (only port numbers)!
+ *
+ * @param nodename descriptive name or address string of the host
+ *                 (may be NULL -> local address)
+ * @param servname port number as string of NULL
+ * @param hints structure containing input values that set socktype and protocol
+ * @param res pointer to a pointer where to store the result (set to NULL on failure)
+ * @return 0 on success, non-zero on failure
+ *
+ * @todo: implement AI_V4MAPPED, AI_ADDRCONFIG
+ */
+int
+lwip_getaddrinfo(const char *nodename, const char *servname,
+       const struct addrinfo *hints, struct addrinfo **res)
+{
+  err_t err;
+  ip_addr_t addr;
+  struct addrinfo *ai;
+  struct sockaddr_storage *sa = NULL;
+  int port_nr = 0;
+  size_t total_size;
+  size_t namelen = 0;
+  int ai_family;
+
+  if (res == NULL) {
+    return EAI_FAIL;
+  }
+  *res = NULL;
+  if ((nodename == NULL) && (servname == NULL)) {
+    return EAI_NONAME;
+  }
+
+  if (hints != NULL) {
+    ai_family = hints->ai_family;
+    if ((ai_family != AF_UNSPEC)
+#if LWIP_IPV4
+      && (ai_family != AF_INET)
+#endif /* LWIP_IPV4 */
+#if LWIP_IPV6
+      && (ai_family != AF_INET6)
+#endif /* LWIP_IPV6 */
+      ) {
+      return EAI_FAMILY;
+    }
+  } else {
+    ai_family = AF_UNSPEC;
+  }
+
+  if (servname != NULL) {
+    /* service name specified: convert to port number
+     * @todo?: currently, only ASCII integers (port numbers) are supported (AI_NUMERICSERV)! */
+    port_nr = atoi(servname);
+    if ((port_nr <= 0) || (port_nr > 0xffff)) {
+      return EAI_SERVICE;
+    }
+  }
+
+  if (nodename != NULL) {
+    /* service location specified, try to resolve */
+    if ((hints != NULL) && (hints->ai_flags & AI_NUMERICHOST)) {
+      /* no DNS lookup, just parse for an address string */
+      if (!ipaddr_aton(nodename, &addr)) {
+        return EAI_NONAME;
+      }
+#if LWIP_IPV4 && LWIP_IPV6
+      if ((IP_IS_V6_VAL(addr) && ai_family == AF_INET) ||
+          (IP_IS_V4_VAL(addr) && ai_family == AF_INET6)) {
+        return EAI_NONAME;
+      }
+#endif /* LWIP_IPV4 && LWIP_IPV6 */
+    } else {
+#if LWIP_IPV4 && LWIP_IPV6
+      /* AF_UNSPEC: prefer IPv4 */
+      u8_t type = NETCONN_DNS_IPV4_IPV6;
+      if (ai_family == AF_INET) {
+        type = NETCONN_DNS_IPV4;
+      } else if (ai_family == AF_INET6) {
+        type = NETCONN_DNS_IPV6;
+      }
+#endif /* LWIP_IPV4 && LWIP_IPV6 */
+      err = netconn_gethostbyname_addrtype(nodename, &addr, type);
+      if (err != ERR_OK) {
+        return EAI_FAIL;
+      }
+    }
+  } else {
+    /* service location specified, use loopback address */
+    if ((hints != NULL) && (hints->ai_flags & AI_PASSIVE)) {
+      ip_addr_set_any(ai_family == AF_INET6, &addr);
+    } else {
+      ip_addr_set_loopback(ai_family == AF_INET6, &addr);
+    }
+  }
+
+  total_size = sizeof(struct addrinfo) + sizeof(struct sockaddr_storage);
+  if (nodename != NULL) {
+    namelen = strlen(nodename);
+    if (namelen > DNS_MAX_NAME_LENGTH) {
+      /* invalid name length */
+      return EAI_FAIL;
+    }
+    LWIP_ASSERT("namelen is too long", total_size + namelen + 1 > total_size);
+    total_size += namelen + 1;
+  }
+  /* If this fails, please report to lwip-devel! :-) */
+  LWIP_ASSERT("total_size <= NETDB_ELEM_SIZE: please report this!",
+    total_size <= NETDB_ELEM_SIZE);
+  ai = (struct addrinfo *)memp_malloc(MEMP_NETDB);
+  if (ai == NULL) {
+    return EAI_MEMORY;
+  }
+  memset(ai, 0, total_size);
+  /* cast through void* to get rid of alignment warnings */
+  sa = (struct sockaddr_storage *)(void*)((u8_t*)ai + sizeof(struct addrinfo));
+  if (IP_IS_V6_VAL(addr)) {
+#if LWIP_IPV6
+    struct sockaddr_in6 *sa6 = (struct sockaddr_in6*)sa;
+    /* set up sockaddr */
+    inet6_addr_from_ip6addr(&sa6->sin6_addr, ip_2_ip6(&addr));
+    sa6->sin6_family = AF_INET6;
+    sa6->sin6_len = sizeof(struct sockaddr_in6);
+    sa6->sin6_port = htons((u16_t)port_nr);
+    ai->ai_family = AF_INET6;
+#endif /* LWIP_IPV6 */
+  } else {
+#if LWIP_IPV4
+    struct sockaddr_in *sa4 = (struct sockaddr_in*)sa;
+    /* set up sockaddr */
+    inet_addr_from_ipaddr(&sa4->sin_addr, ip_2_ip4(&addr));
+    sa4->sin_family = AF_INET;
+    sa4->sin_len = sizeof(struct sockaddr_in);
+    sa4->sin_port = htons((u16_t)port_nr);
+    ai->ai_family = AF_INET;
+#endif /* LWIP_IPV4 */
+  }
+
+  /* set up addrinfo */
+  if (hints != NULL) {
+    /* copy socktype & protocol from hints if specified */
+    ai->ai_socktype = hints->ai_socktype;
+    ai->ai_protocol = hints->ai_protocol;
+  }
+  if (nodename != NULL) {
+    /* copy nodename to canonname if specified */
+    ai->ai_canonname = ((char*)ai + sizeof(struct addrinfo) + sizeof(struct sockaddr_storage));
+    MEMCPY(ai->ai_canonname, nodename, namelen);
+    ai->ai_canonname[namelen] = 0;
+  }
+  ai->ai_addrlen = sizeof(struct sockaddr_storage);
+  ai->ai_addr = (struct sockaddr*)sa;
+
+  *res = ai;
+
+  return 0;
+}
+
+#endif /* LWIP_DNS && LWIP_SOCKET */