Home | History | Annotate | Download | only in api
      1 /**
      2  * @file
      3  * Sequential API External 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 (at) sics.se>
     36  *
     37  */
     38 
     39 /* This is the part of the API that is linked with
     40    the application */
     41 
     42 #include "lwip/opt.h"
     43 
     44 #if LWIP_NETCONN /* don't build if not configured for use in lwipopts.h */
     45 
     46 #include "lwip/api.h"
     47 #include "lwip/tcpip.h"
     48 #include "lwip/memp.h"
     49 
     50 #include "lwip/ip.h"
     51 #include "lwip/raw.h"
     52 #include "lwip/udp.h"
     53 #include "lwip/tcp.h"
     54 
     55 #include <string.h>
     56 
     57 /**
     58  * Create a new netconn (of a specific type) that has a callback function.
     59  * The corresponding pcb is also created.
     60  *
     61  * @param t the type of 'connection' to create (@see enum netconn_type)
     62  * @param proto the IP protocol for RAW IP pcbs
     63  * @param callback a function to call on status changes (RX available, TX'ed)
     64  * @return a newly allocated struct netconn or
     65  *         NULL on memory error
     66  */
     67 struct netconn*
     68 netconn_new_with_proto_and_callback(enum netconn_type t, u8_t proto, netconn_callback callback)
     69 {
     70   struct netconn *conn;
     71   struct api_msg msg;
     72 
     73   conn = netconn_alloc(t, callback);
     74   if (conn != NULL) {
     75     msg.function = do_newconn;
     76     msg.msg.msg.n.proto = proto;
     77     msg.msg.conn = conn;
     78     if (TCPIP_APIMSG(&msg) != ERR_OK) {
     79       LWIP_ASSERT("freeing conn without freeing pcb", conn->pcb.tcp == NULL);
     80       LWIP_ASSERT("conn has no op_completed", sys_sem_valid(&conn->op_completed));
     81       LWIP_ASSERT("conn has no recvmbox", sys_mbox_valid(&conn->recvmbox));
     82 #if LWIP_TCP
     83       LWIP_ASSERT("conn->acceptmbox shouldn't exist", !sys_mbox_valid(&conn->acceptmbox));
     84 #endif /* LWIP_TCP */
     85       sys_sem_free(&conn->op_completed);
     86       sys_mbox_free(&conn->recvmbox);
     87       memp_free(MEMP_NETCONN, conn);
     88       return NULL;
     89     }
     90   }
     91   return conn;
     92 }
     93 
     94 /**
     95  * Close a netconn 'connection' and free its resources.
     96  * UDP and RAW connection are completely closed, TCP pcbs might still be in a waitstate
     97  * after this returns.
     98  *
     99  * @param conn the netconn to delete
    100  * @return ERR_OK if the connection was deleted
    101  */
    102 err_t
    103 netconn_delete(struct netconn *conn)
    104 {
    105   struct api_msg msg;
    106 
    107   /* No ASSERT here because possible to get a (conn == NULL) if we got an accept error */
    108   if (conn == NULL) {
    109     return ERR_OK;
    110   }
    111 
    112   msg.function = do_delconn;
    113   msg.msg.conn = conn;
    114   tcpip_apimsg(&msg);
    115 
    116   netconn_free(conn);
    117 
    118   /* don't care for return value of do_delconn since it only calls void functions */
    119 
    120   return ERR_OK;
    121 }
    122 
    123 /**
    124  * Get the local or remote IP address and port of a netconn.
    125  * For RAW netconns, this returns the protocol instead of a port!
    126  *
    127  * @param conn the netconn to query
    128  * @param addr a pointer to which to save the IP address
    129  * @param port a pointer to which to save the port (or protocol for RAW)
    130  * @param local 1 to get the local IP address, 0 to get the remote one
    131  * @return ERR_CONN for invalid connections
    132  *         ERR_OK if the information was retrieved
    133  */
    134 err_t
    135 netconn_getaddr(struct netconn *conn, ip_addr_t *addr, u16_t *port, u8_t local)
    136 {
    137   struct api_msg msg;
    138   err_t err;
    139 
    140   LWIP_ERROR("netconn_getaddr: invalid conn", (conn != NULL), return ERR_ARG;);
    141   LWIP_ERROR("netconn_getaddr: invalid addr", (addr != NULL), return ERR_ARG;);
    142   LWIP_ERROR("netconn_getaddr: invalid port", (port != NULL), return ERR_ARG;);
    143 
    144   msg.function = do_getaddr;
    145   msg.msg.conn = conn;
    146   msg.msg.msg.ad.ipaddr = addr;
    147   msg.msg.msg.ad.port = port;
    148   msg.msg.msg.ad.local = local;
    149   err = TCPIP_APIMSG(&msg);
    150 
    151   NETCONN_SET_SAFE_ERR(conn, err);
    152   return err;
    153 }
    154 
    155 /**
    156  * Bind a netconn to a specific local IP address and port.
    157  * Binding one netconn twice might not always be checked correctly!
    158  *
    159  * @param conn the netconn to bind
    160  * @param addr the local IP address to bind the netconn to (use IP_ADDR_ANY
    161  *             to bind to all addresses)
    162  * @param port the local port to bind the netconn to (not used for RAW)
    163  * @return ERR_OK if bound, any other err_t on failure
    164  */
    165 err_t
    166 netconn_bind(struct netconn *conn, ip_addr_t *addr, u16_t port)
    167 {
    168   struct api_msg msg;
    169   err_t err;
    170 
    171   LWIP_ERROR("netconn_bind: invalid conn", (conn != NULL), return ERR_ARG;);
    172 
    173   msg.function = do_bind;
    174   msg.msg.conn = conn;
    175   msg.msg.msg.bc.ipaddr = addr;
    176   msg.msg.msg.bc.port = port;
    177   err = TCPIP_APIMSG(&msg);
    178 
    179   NETCONN_SET_SAFE_ERR(conn, err);
    180   return err;
    181 }
    182 
    183 /**
    184  * Connect a netconn to a specific remote IP address and port.
    185  *
    186  * @param conn the netconn to connect
    187  * @param addr the remote IP address to connect to
    188  * @param port the remote port to connect to (no used for RAW)
    189  * @return ERR_OK if connected, return value of tcp_/udp_/raw_connect otherwise
    190  */
    191 err_t
    192 netconn_connect(struct netconn *conn, ip_addr_t *addr, u16_t port)
    193 {
    194   struct api_msg msg;
    195   err_t err;
    196 
    197   LWIP_ERROR("netconn_connect: invalid conn", (conn != NULL), return ERR_ARG;);
    198 
    199   msg.function = do_connect;
    200   msg.msg.conn = conn;
    201   msg.msg.msg.bc.ipaddr = addr;
    202   msg.msg.msg.bc.port = port;
    203   /* This is the only function which need to not block tcpip_thread */
    204   err = tcpip_apimsg(&msg);
    205 
    206   NETCONN_SET_SAFE_ERR(conn, err);
    207   return err;
    208 }
    209 
    210 /**
    211  * Disconnect a netconn from its current peer (only valid for UDP netconns).
    212  *
    213  * @param conn the netconn to disconnect
    214  * @return TODO: return value is not set here...
    215  */
    216 err_t
    217 netconn_disconnect(struct netconn *conn)
    218 {
    219   struct api_msg msg;
    220   err_t err;
    221 
    222   LWIP_ERROR("netconn_disconnect: invalid conn", (conn != NULL), return ERR_ARG;);
    223 
    224   msg.function = do_disconnect;
    225   msg.msg.conn = conn;
    226   err = TCPIP_APIMSG(&msg);
    227 
    228   NETCONN_SET_SAFE_ERR(conn, err);
    229   return err;
    230 }
    231 
    232 /**
    233  * Set a TCP netconn into listen mode
    234  *
    235  * @param conn the tcp netconn to set to listen mode
    236  * @param backlog the listen backlog, only used if TCP_LISTEN_BACKLOG==1
    237  * @return ERR_OK if the netconn was set to listen (UDP and RAW netconns
    238  *         don't return any error (yet?))
    239  */
    240 err_t
    241 netconn_listen_with_backlog(struct netconn *conn, u8_t backlog)
    242 {
    243 #if LWIP_TCP
    244   struct api_msg msg;
    245   err_t err;
    246 
    247   /* This does no harm. If TCP_LISTEN_BACKLOG is off, backlog is unused. */
    248   LWIP_UNUSED_ARG(backlog);
    249 
    250   LWIP_ERROR("netconn_listen: invalid conn", (conn != NULL), return ERR_ARG;);
    251 
    252   msg.function = do_listen;
    253   msg.msg.conn = conn;
    254 #if TCP_LISTEN_BACKLOG
    255   msg.msg.msg.lb.backlog = backlog;
    256 #endif /* TCP_LISTEN_BACKLOG */
    257   err = TCPIP_APIMSG(&msg);
    258 
    259   NETCONN_SET_SAFE_ERR(conn, err);
    260   return err;
    261 #else /* LWIP_TCP */
    262   LWIP_UNUSED_ARG(conn);
    263   LWIP_UNUSED_ARG(backlog);
    264   return ERR_ARG;
    265 #endif /* LWIP_TCP */
    266 }
    267 
    268 /**
    269  * Accept a new connection on a TCP listening netconn.
    270  *
    271  * @param conn the TCP listen netconn
    272  * @param new_conn pointer where the new connection is stored
    273  * @return ERR_OK if a new connection has been received or an error
    274  *                code otherwise
    275  */
    276 err_t
    277 netconn_accept(struct netconn *conn, struct netconn **new_conn)
    278 {
    279 #if LWIP_TCP
    280   struct netconn *newconn;
    281   err_t err;
    282 #if TCP_LISTEN_BACKLOG
    283   struct api_msg msg;
    284 #endif /* TCP_LISTEN_BACKLOG */
    285 
    286   LWIP_ERROR("netconn_accept: invalid pointer",    (new_conn != NULL),                  return ERR_ARG;);
    287   *new_conn = NULL;
    288   LWIP_ERROR("netconn_accept: invalid conn",       (conn != NULL),                      return ERR_ARG;);
    289   LWIP_ERROR("netconn_accept: invalid acceptmbox", sys_mbox_valid(&conn->acceptmbox),   return ERR_ARG;);
    290 
    291   err = conn->last_err;
    292   if (ERR_IS_FATAL(err)) {
    293     /* don't recv on fatal errors: this might block the application task
    294        waiting on acceptmbox forever! */
    295     return err;
    296   }
    297 
    298 #if LWIP_SO_RCVTIMEO
    299   if (sys_arch_mbox_fetch(&conn->acceptmbox, (void **)&newconn, conn->recv_timeout) == SYS_ARCH_TIMEOUT) {
    300     NETCONN_SET_SAFE_ERR(conn, ERR_TIMEOUT);
    301     return ERR_TIMEOUT;
    302   }
    303 #else
    304   sys_arch_mbox_fetch(&conn->acceptmbox, (void **)&newconn, 0);
    305 #endif /* LWIP_SO_RCVTIMEO*/
    306   /* Register event with callback */
    307   API_EVENT(conn, NETCONN_EVT_RCVMINUS, 0);
    308 
    309   if (newconn == NULL) {
    310     /* connection has been aborted */
    311     NETCONN_SET_SAFE_ERR(conn, ERR_ABRT);
    312     return ERR_ABRT;
    313   }
    314 #if TCP_LISTEN_BACKLOG
    315   /* Let the stack know that we have accepted the connection. */
    316   msg.function = do_recv;
    317   msg.msg.conn = conn;
    318   /* don't care for the return value of do_recv */
    319   TCPIP_APIMSG(&msg);
    320 #endif /* TCP_LISTEN_BACKLOG */
    321 
    322   *new_conn = newconn;
    323   /* don't set conn->last_err: it's only ERR_OK, anyway */
    324   return ERR_OK;
    325 #else /* LWIP_TCP */
    326   LWIP_UNUSED_ARG(conn);
    327   LWIP_UNUSED_ARG(new_conn);
    328   return ERR_ARG;
    329 #endif /* LWIP_TCP */
    330 }
    331 
    332 /**
    333  * Receive data: actual implementation that doesn't care whether pbuf or netbuf
    334  * is received
    335  *
    336  * @param conn the netconn from which to receive data
    337  * @param new_buf pointer where a new pbuf/netbuf is stored when received data
    338  * @return ERR_OK if data has been received, an error code otherwise (timeout,
    339  *                memory error or another error)
    340  */
    341 static err_t
    342 netconn_recv_data(struct netconn *conn, void **new_buf)
    343 {
    344   void *buf = NULL;
    345   u16_t len;
    346   err_t err;
    347 #if LWIP_TCP
    348   struct api_msg msg;
    349 #endif /* LWIP_TCP */
    350 
    351   LWIP_ERROR("netconn_recv: invalid pointer", (new_buf != NULL), return ERR_ARG;);
    352   *new_buf = NULL;
    353   LWIP_ERROR("netconn_recv: invalid conn",    (conn != NULL),    return ERR_ARG;);
    354   LWIP_ERROR("netconn_accept: invalid recvmbox", sys_mbox_valid(&conn->recvmbox), return ERR_CONN;);
    355 
    356   err = conn->last_err;
    357   if (ERR_IS_FATAL(err)) {
    358     /* don't recv on fatal errors: this might block the application task
    359        waiting on recvmbox forever! */
    360     /* @todo: this does not allow us to fetch data that has been put into recvmbox
    361        before the fatal error occurred - is that a problem? */
    362     return err;
    363   }
    364 
    365 #if LWIP_SO_RCVTIMEO
    366   if (sys_arch_mbox_fetch(&conn->recvmbox, &buf, conn->recv_timeout) == SYS_ARCH_TIMEOUT) {
    367     NETCONN_SET_SAFE_ERR(conn, ERR_TIMEOUT);
    368     return ERR_TIMEOUT;
    369   }
    370 #else
    371   sys_arch_mbox_fetch(&conn->recvmbox, &buf, 0);
    372 #endif /* LWIP_SO_RCVTIMEO*/
    373 
    374 #if LWIP_TCP
    375   if (conn->type == NETCONN_TCP) {
    376     if (!netconn_get_noautorecved(conn) || (buf == NULL)) {
    377       /* Let the stack know that we have taken the data. */
    378       /* TODO: Speedup: Don't block and wait for the answer here
    379          (to prevent multiple thread-switches). */
    380       msg.function = do_recv;
    381       msg.msg.conn = conn;
    382       if (buf != NULL) {
    383         msg.msg.msg.r.len = ((struct pbuf *)buf)->tot_len;
    384       } else {
    385         msg.msg.msg.r.len = 1;
    386       }
    387       /* don't care for the return value of do_recv */
    388       TCPIP_APIMSG(&msg);
    389     }
    390 
    391     /* If we are closed, we indicate that we no longer wish to use the socket */
    392     if (buf == NULL) {
    393       API_EVENT(conn, NETCONN_EVT_RCVMINUS, 0);
    394       /* Avoid to lose any previous error code */
    395       NETCONN_SET_SAFE_ERR(conn, ERR_CLSD);
    396       return ERR_CLSD;
    397     }
    398     len = ((struct pbuf *)buf)->tot_len;
    399   }
    400 #endif /* LWIP_TCP */
    401 #if LWIP_TCP && (LWIP_UDP || LWIP_RAW)
    402   else
    403 #endif /* LWIP_TCP && (LWIP_UDP || LWIP_RAW) */
    404 #if (LWIP_UDP || LWIP_RAW)
    405   {
    406     LWIP_ASSERT("buf != NULL", buf != NULL);
    407     len = netbuf_len((struct netbuf *)buf);
    408   }
    409 #endif /* (LWIP_UDP || LWIP_RAW) */
    410 
    411 #if LWIP_SO_RCVBUF
    412   SYS_ARCH_DEC(conn->recv_avail, len);
    413 #endif /* LWIP_SO_RCVBUF */
    414   /* Register event with callback */
    415   API_EVENT(conn, NETCONN_EVT_RCVMINUS, len);
    416 
    417   LWIP_DEBUGF(API_LIB_DEBUG, ("netconn_recv_data: received %p, len=%"U16_F"\n", buf, len));
    418 
    419   *new_buf = buf;
    420   /* don't set conn->last_err: it's only ERR_OK, anyway */
    421   return ERR_OK;
    422 }
    423 
    424 /**
    425  * Receive data (in form of a pbuf) from a TCP netconn
    426  *
    427  * @param conn the netconn from which to receive data
    428  * @param new_buf pointer where a new pbuf is stored when received data
    429  * @return ERR_OK if data has been received, an error code otherwise (timeout,
    430  *                memory error or another error)
    431  *         ERR_ARG if conn is not a TCP netconn
    432  */
    433 err_t
    434 netconn_recv_tcp_pbuf(struct netconn *conn, struct pbuf **new_buf)
    435 {
    436   LWIP_ERROR("netconn_recv: invalid conn", (conn != NULL) &&
    437              netconn_type(conn) == NETCONN_TCP, return ERR_ARG;);
    438 
    439   return netconn_recv_data(conn, (void **)new_buf);
    440 }
    441 
    442 /**
    443  * Receive data (in form of a netbuf containing a packet buffer) from a netconn
    444  *
    445  * @param conn the netconn from which to receive data
    446  * @param new_buf pointer where a new netbuf is stored when received data
    447  * @return ERR_OK if data has been received, an error code otherwise (timeout,
    448  *                memory error or another error)
    449  */
    450 err_t
    451 netconn_recv(struct netconn *conn, struct netbuf **new_buf)
    452 {
    453 #if LWIP_TCP
    454   struct netbuf *buf = NULL;
    455   err_t err;
    456 #endif /* LWIP_TCP */
    457 
    458   LWIP_ERROR("netconn_recv: invalid pointer", (new_buf != NULL), return ERR_ARG;);
    459   *new_buf = NULL;
    460   LWIP_ERROR("netconn_recv: invalid conn",    (conn != NULL),    return ERR_ARG;);
    461   LWIP_ERROR("netconn_accept: invalid recvmbox", sys_mbox_valid(&conn->recvmbox), return ERR_CONN;);
    462 
    463 #if LWIP_TCP
    464   if (conn->type == NETCONN_TCP) {
    465     struct pbuf *p = NULL;
    466     /* This is not a listening netconn, since recvmbox is set */
    467 
    468     buf = (struct netbuf *)memp_malloc(MEMP_NETBUF);
    469     if (buf == NULL) {
    470       NETCONN_SET_SAFE_ERR(conn, ERR_MEM);
    471       return ERR_MEM;
    472     }
    473 
    474     err = netconn_recv_data(conn, (void **)&p);
    475     if (err != ERR_OK) {
    476       memp_free(MEMP_NETBUF, buf);
    477       return err;
    478     }
    479     LWIP_ASSERT("p != NULL", p != NULL);
    480 
    481     buf->p = p;
    482     buf->ptr = p;
    483     buf->port = 0;
    484     ip_addr_set_any(&buf->addr);
    485     *new_buf = buf;
    486     /* don't set conn->last_err: it's only ERR_OK, anyway */
    487     return ERR_OK;
    488   } else
    489 #endif /* LWIP_TCP */
    490   {
    491 #if (LWIP_UDP || LWIP_RAW)
    492     return netconn_recv_data(conn, (void **)new_buf);
    493 #endif /* (LWIP_UDP || LWIP_RAW) */
    494   }
    495 }
    496 
    497 /**
    498  * TCP: update the receive window: by calling this, the application
    499  * tells the stack that it has processed data and is able to accept
    500  * new data.
    501  * ATTENTION: use with care, this is mainly used for sockets!
    502  * Can only be used when calling netconn_set_noautorecved(conn, 1) before.
    503  *
    504  * @param conn the netconn for which to update the receive window
    505  * @param length amount of data processed (ATTENTION: this must be accurate!)
    506  */
    507 void
    508 netconn_recved(struct netconn *conn, u32_t length)
    509 {
    510 #if LWIP_TCP
    511   if ((conn != NULL) && (conn->type == NETCONN_TCP) &&
    512       (netconn_get_noautorecved(conn))) {
    513     struct api_msg msg;
    514     /* Let the stack know that we have taken the data. */
    515     /* TODO: Speedup: Don't block and wait for the answer here
    516        (to prevent multiple thread-switches). */
    517     msg.function = do_recv;
    518     msg.msg.conn = conn;
    519     msg.msg.msg.r.len = length;
    520     /* don't care for the return value of do_recv */
    521     TCPIP_APIMSG(&msg);
    522   }
    523 #else /* LWIP_TCP */
    524   LWIP_UNUSED_ARG(conn);
    525   LWIP_UNUSED_ARG(length);
    526 #endif /* LWIP_TCP */
    527 }
    528 
    529 /**
    530  * Send data (in form of a netbuf) to a specific remote IP address and port.
    531  * Only to be used for UDP and RAW netconns (not TCP).
    532  *
    533  * @param conn the netconn over which to send data
    534  * @param buf a netbuf containing the data to send
    535  * @param addr the remote IP address to which to send the data
    536  * @param port the remote port to which to send the data
    537  * @return ERR_OK if data was sent, any other err_t on error
    538  */
    539 err_t
    540 netconn_sendto(struct netconn *conn, struct netbuf *buf, ip_addr_t *addr, u16_t port)
    541 {
    542   if (buf != NULL) {
    543     ip_addr_set(&buf->addr, addr);
    544     buf->port = port;
    545     return netconn_send(conn, buf);
    546   }
    547   return ERR_VAL;
    548 }
    549 
    550 /**
    551  * Send data over a UDP or RAW netconn (that is already connected).
    552  *
    553  * @param conn the UDP or RAW netconn over which to send data
    554  * @param buf a netbuf containing the data to send
    555  * @return ERR_OK if data was sent, any other err_t on error
    556  */
    557 err_t
    558 netconn_send(struct netconn *conn, struct netbuf *buf)
    559 {
    560   struct api_msg msg;
    561   err_t err;
    562 
    563   LWIP_ERROR("netconn_send: invalid conn",  (conn != NULL), return ERR_ARG;);
    564 
    565   LWIP_DEBUGF(API_LIB_DEBUG, ("netconn_send: sending %"U16_F" bytes\n", buf->p->tot_len));
    566   msg.function = do_send;
    567   msg.msg.conn = conn;
    568   msg.msg.msg.b = buf;
    569   err = TCPIP_APIMSG(&msg);
    570 
    571   NETCONN_SET_SAFE_ERR(conn, err);
    572   return err;
    573 }
    574 
    575 /**
    576  * Send data over a TCP netconn.
    577  *
    578  * @param conn the TCP netconn over which to send data
    579  * @param dataptr pointer to the application buffer that contains the data to send
    580  * @param size size of the application data to send
    581  * @param apiflags combination of following flags :
    582  * - NETCONN_COPY: data will be copied into memory belonging to the stack
    583  * - NETCONN_MORE: for TCP connection, PSH flag will be set on last segment sent
    584  * - NETCONN_DONTBLOCK: only write the data if all dat can be written at once
    585  * @return ERR_OK if data was sent, any other err_t on error
    586  */
    587 err_t
    588 netconn_write(struct netconn *conn, const void *dataptr, size_t size, u8_t apiflags)
    589 {
    590   struct api_msg msg;
    591   err_t err;
    592 
    593   LWIP_ERROR("netconn_write: invalid conn",  (conn != NULL), return ERR_ARG;);
    594   LWIP_ERROR("netconn_write: invalid conn->type",  (conn->type == NETCONN_TCP), return ERR_VAL;);
    595   if (size == 0) {
    596     return ERR_OK;
    597   }
    598 
    599   /* @todo: for non-blocking write, check if 'size' would ever fit into
    600             snd_queue or snd_buf */
    601   msg.function = do_write;
    602   msg.msg.conn = conn;
    603   msg.msg.msg.w.dataptr = dataptr;
    604   msg.msg.msg.w.apiflags = apiflags;
    605   msg.msg.msg.w.len = size;
    606   /* For locking the core: this _can_ be delayed on low memory/low send buffer,
    607      but if it is, this is done inside api_msg.c:do_write(), so we can use the
    608      non-blocking version here. */
    609   err = TCPIP_APIMSG(&msg);
    610 
    611   NETCONN_SET_SAFE_ERR(conn, err);
    612   return err;
    613 }
    614 
    615 /**
    616  * Close ot shutdown a TCP netconn (doesn't delete it).
    617  *
    618  * @param conn the TCP netconn to close or shutdown
    619  * @param how fully close or only shutdown one side?
    620  * @return ERR_OK if the netconn was closed, any other err_t on error
    621  */
    622 static err_t
    623 netconn_close_shutdown(struct netconn *conn, u8_t how)
    624 {
    625   struct api_msg msg;
    626   err_t err;
    627 
    628   LWIP_ERROR("netconn_close: invalid conn",  (conn != NULL), return ERR_ARG;);
    629 
    630   msg.function = do_close;
    631   msg.msg.conn = conn;
    632   /* shutting down both ends is the same as closing */
    633   msg.msg.msg.sd.shut = how;
    634   /* because of the LWIP_TCPIP_CORE_LOCKING implementation of do_close,
    635      don't use TCPIP_APIMSG here */
    636   err = tcpip_apimsg(&msg);
    637 
    638   NETCONN_SET_SAFE_ERR(conn, err);
    639   return err;
    640 }
    641 
    642 /**
    643  * Close a TCP netconn (doesn't delete it).
    644  *
    645  * @param conn the TCP netconn to close
    646  * @return ERR_OK if the netconn was closed, any other err_t on error
    647  */
    648 err_t
    649 netconn_close(struct netconn *conn)
    650 {
    651   /* shutting down both ends is the same as closing */
    652   return netconn_close_shutdown(conn, NETCONN_SHUT_RDWR);
    653 }
    654 
    655 /**
    656  * Shut down one or both sides of a TCP netconn (doesn't delete it).
    657  *
    658  * @param conn the TCP netconn to shut down
    659  * @return ERR_OK if the netconn was closed, any other err_t on error
    660  */
    661 err_t
    662 netconn_shutdown(struct netconn *conn, u8_t shut_rx, u8_t shut_tx)
    663 {
    664   return netconn_close_shutdown(conn, (shut_rx ? NETCONN_SHUT_RD : 0) | (shut_tx ? NETCONN_SHUT_WR : 0));
    665 }
    666 
    667 #if LWIP_IGMP
    668 /**
    669  * Join multicast groups for UDP netconns.
    670  *
    671  * @param conn the UDP netconn for which to change multicast addresses
    672  * @param multiaddr IP address of the multicast group to join or leave
    673  * @param netif_addr the IP address of the network interface on which to send
    674  *                  the igmp message
    675  * @param join_or_leave flag whether to send a join- or leave-message
    676  * @return ERR_OK if the action was taken, any err_t on error
    677  */
    678 err_t
    679 netconn_join_leave_group(struct netconn *conn,
    680                          ip_addr_t *multiaddr,
    681                          ip_addr_t *netif_addr,
    682                          enum netconn_igmp join_or_leave)
    683 {
    684   struct api_msg msg;
    685   err_t err;
    686 
    687   LWIP_ERROR("netconn_join_leave_group: invalid conn",  (conn != NULL), return ERR_ARG;);
    688 
    689   msg.function = do_join_leave_group;
    690   msg.msg.conn = conn;
    691   msg.msg.msg.jl.multiaddr = multiaddr;
    692   msg.msg.msg.jl.netif_addr = netif_addr;
    693   msg.msg.msg.jl.join_or_leave = join_or_leave;
    694   err = TCPIP_APIMSG(&msg);
    695 
    696   NETCONN_SET_SAFE_ERR(conn, err);
    697   return err;
    698 }
    699 #endif /* LWIP_IGMP */
    700 
    701 #if LWIP_DNS
    702 /**
    703  * Execute a DNS query, only one IP address is returned
    704  *
    705  * @param name a string representation of the DNS host name to query
    706  * @param addr a preallocated ip_addr_t where to store the resolved IP address
    707  * @return ERR_OK: resolving succeeded
    708  *         ERR_MEM: memory error, try again later
    709  *         ERR_ARG: dns client not initialized or invalid hostname
    710  *         ERR_VAL: dns server response was invalid
    711  */
    712 err_t
    713 netconn_gethostbyname(const char *name, ip_addr_t *addr)
    714 {
    715   struct dns_api_msg msg;
    716   err_t err;
    717   sys_sem_t sem;
    718 
    719   LWIP_ERROR("netconn_gethostbyname: invalid name", (name != NULL), return ERR_ARG;);
    720   LWIP_ERROR("netconn_gethostbyname: invalid addr", (addr != NULL), return ERR_ARG;);
    721 
    722   err = sys_sem_new(&sem, 0);
    723   if (err != ERR_OK) {
    724     return err;
    725   }
    726 
    727   msg.name = name;
    728   msg.addr = addr;
    729   msg.err = &err;
    730   msg.sem = &sem;
    731 
    732   tcpip_callback(do_gethostbyname, &msg);
    733   sys_sem_wait(&sem);
    734   sys_sem_free(&sem);
    735 
    736   return err;
    737 }
    738 #endif /* LWIP_DNS*/
    739 
    740 #endif /* LWIP_NETCONN */
    741