1/**
2 * @file
3 * Sequential API External module
4 *
5 * @defgroup netconn Netconn API
6 * @ingroup sequential_api
7 * Thread-safe, to be called from non-TCPIP threads only.
8 * TX/RX handling based on @ref netbuf (containing @ref pbuf)
9 * to avoid copying data around.
10 *
11 * @defgroup netconn_common Common functions
12 * @ingroup netconn
13 * For use with TCP and UDP
14 *
15 * @defgroup netconn_tcp TCP only
16 * @ingroup netconn
17 * TCP only functions
18 *
19 * @defgroup netconn_udp UDP only
20 * @ingroup netconn
21 * UDP only functions
22 */
23
24/*
25 * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
26 * All rights reserved.
27 *
28 * Redistribution and use in source and binary forms, with or without modification,
29 * are permitted provided that the following conditions are met:
30 *
31 * 1. Redistributions of source code must retain the above copyright notice,
32 *    this list of conditions and the following disclaimer.
33 * 2. Redistributions in binary form must reproduce the above copyright notice,
34 *    this list of conditions and the following disclaimer in the documentation
35 *    and/or other materials provided with the distribution.
36 * 3. The name of the author may not be used to endorse or promote products
37 *    derived from this software without specific prior written permission.
38 *
39 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
40 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
41 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
42 * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
43 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
44 * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
45 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
46 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
47 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
48 * OF SUCH DAMAGE.
49 *
50 * This file is part of the lwIP TCP/IP stack.
51 *
52 * Author: Adam Dunkels <adam@sics.se>
53 */
54
55/* This is the part of the API that is linked with
56   the application */
57
58#include "lwip/opt.h"
59
60#if LWIP_NETCONN /* don't build if not configured for use in lwipopts.h */
61
62#include "lwip/api.h"
63#include "lwip/memp.h"
64
65#include "lwip/ip.h"
66#include "lwip/raw.h"
67#include "lwip/udp.h"
68#include "lwip/priv/api_msg.h"
69#include "lwip/priv/tcp_priv.h"
70#include "lwip/priv/tcpip_priv.h"
71
72#ifdef LWIP_HOOK_FILENAME
73#include LWIP_HOOK_FILENAME
74#endif
75
76#include <string.h>
77
78#define API_MSG_VAR_REF(name)               API_VAR_REF(name)
79#define API_MSG_VAR_DECLARE(name)           API_VAR_DECLARE(struct api_msg, name)
80#define API_MSG_VAR_ALLOC(name)             API_VAR_ALLOC(struct api_msg, MEMP_API_MSG, name, ERR_MEM)
81#define API_MSG_VAR_ALLOC_RETURN_NULL(name) API_VAR_ALLOC(struct api_msg, MEMP_API_MSG, name, NULL)
82#define API_MSG_VAR_FREE(name)              API_VAR_FREE(MEMP_API_MSG, name)
83
84#if TCP_LISTEN_BACKLOG
85/* need to allocate API message for accept so empty message pool does not result in event loss
86 * see bug #47512: MPU_COMPATIBLE may fail on empty pool */
87#define API_MSG_VAR_ALLOC_ACCEPT(msg) API_MSG_VAR_ALLOC(msg)
88#define API_MSG_VAR_FREE_ACCEPT(msg) API_MSG_VAR_FREE(msg)
89#else /* TCP_LISTEN_BACKLOG */
90#define API_MSG_VAR_ALLOC_ACCEPT(msg)
91#define API_MSG_VAR_FREE_ACCEPT(msg)
92#endif /* TCP_LISTEN_BACKLOG */
93
94#if LWIP_NETCONN_FULLDUPLEX
95#define NETCONN_RECVMBOX_WAITABLE(conn) (sys_mbox_valid(&(conn)->recvmbox) && (((conn)->flags & NETCONN_FLAG_MBOXINVALID) == 0))
96#define NETCONN_ACCEPTMBOX_WAITABLE(conn) (sys_mbox_valid(&(conn)->acceptmbox) && (((conn)->flags & (NETCONN_FLAG_MBOXCLOSED|NETCONN_FLAG_MBOXINVALID)) == 0))
97#define NETCONN_MBOX_WAITING_INC(conn) SYS_ARCH_INC(conn->mbox_threads_waiting, 1)
98#define NETCONN_MBOX_WAITING_DEC(conn) SYS_ARCH_DEC(conn->mbox_threads_waiting, 1)
99#else /* LWIP_NETCONN_FULLDUPLEX */
100#define NETCONN_RECVMBOX_WAITABLE(conn)   sys_mbox_valid(&(conn)->recvmbox)
101#define NETCONN_ACCEPTMBOX_WAITABLE(conn) (sys_mbox_valid(&(conn)->acceptmbox) && (((conn)->flags & NETCONN_FLAG_MBOXCLOSED) == 0))
102#define NETCONN_MBOX_WAITING_INC(conn)
103#define NETCONN_MBOX_WAITING_DEC(conn)
104#endif /* LWIP_NETCONN_FULLDUPLEX */
105
106static err_t netconn_close_shutdown(struct netconn *conn, u8_t how);
107
108/**
109 * Call the lower part of a netconn_* function
110 * This function is then running in the thread context
111 * of tcpip_thread and has exclusive access to lwIP core code.
112 *
113 * @param fn function to call
114 * @param apimsg a struct containing the function to call and its parameters
115 * @return ERR_OK if the function was called, another err_t if not
116 */
117static err_t
118netconn_apimsg(tcpip_callback_fn fn, struct api_msg *apimsg)
119{
120  err_t err;
121
122#ifdef LWIP_DEBUG
123  /* catch functions that don't set err */
124  apimsg->err = ERR_VAL;
125#endif /* LWIP_DEBUG */
126
127#if LWIP_NETCONN_SEM_PER_THREAD
128  apimsg->op_completed_sem = LWIP_NETCONN_THREAD_SEM_GET();
129#endif /* LWIP_NETCONN_SEM_PER_THREAD */
130
131  err = tcpip_send_msg_wait_sem(fn, apimsg, LWIP_API_MSG_SEM(apimsg));
132  if (err == ERR_OK) {
133    return apimsg->err;
134  }
135  return err;
136}
137
138/**
139 * Create a new netconn (of a specific type) that has a callback function.
140 * The corresponding pcb is also created.
141 *
142 * @param t the type of 'connection' to create (@see enum netconn_type)
143 * @param proto the IP protocol for RAW IP pcbs
144 * @param callback a function to call on status changes (RX available, TX'ed)
145 * @return a newly allocated struct netconn or
146 *         NULL on memory error
147 */
148struct netconn *
149netconn_new_with_proto_and_callback(enum netconn_type t, u8_t proto, netconn_callback callback)
150{
151  struct netconn *conn;
152  API_MSG_VAR_DECLARE(msg);
153  API_MSG_VAR_ALLOC_RETURN_NULL(msg);
154
155  conn = netconn_alloc(t, callback);
156  if (conn != NULL) {
157    err_t err;
158
159    API_MSG_VAR_REF(msg).msg.n.proto = proto;
160    API_MSG_VAR_REF(msg).conn = conn;
161    err = netconn_apimsg(lwip_netconn_do_newconn, &API_MSG_VAR_REF(msg));
162    if (err != ERR_OK) {
163      LWIP_ASSERT("freeing conn without freeing pcb", conn->pcb.tcp == NULL);
164      LWIP_ASSERT("conn has no recvmbox", sys_mbox_valid(&conn->recvmbox));
165#if LWIP_TCP
166      LWIP_ASSERT("conn->acceptmbox shouldn't exist", !sys_mbox_valid(&conn->acceptmbox));
167#endif /* LWIP_TCP */
168#if !LWIP_NETCONN_SEM_PER_THREAD
169      LWIP_ASSERT("conn has no op_completed", sys_sem_valid(&conn->op_completed));
170      sys_sem_free(&conn->op_completed);
171#endif /* !LWIP_NETCONN_SEM_PER_THREAD */
172      sys_mbox_free(&conn->recvmbox);
173      memp_free(MEMP_NETCONN, conn);
174      API_MSG_VAR_FREE(msg);
175      return NULL;
176    }
177  }
178  API_MSG_VAR_FREE(msg);
179  return conn;
180}
181
182/**
183 * @ingroup netconn_common
184 * Close a netconn 'connection' and free all its resources but not the netconn itself.
185 * UDP and RAW connection are completely closed, TCP pcbs might still be in a waitstate
186 * after this returns.
187 *
188 * @param conn the netconn to delete
189 * @return ERR_OK if the connection was deleted
190 */
191err_t
192netconn_prepare_delete(struct netconn *conn)
193{
194  err_t err;
195  API_MSG_VAR_DECLARE(msg);
196
197  /* No ASSERT here because possible to get a (conn == NULL) if we got an accept error */
198  if (conn == NULL) {
199    return ERR_OK;
200  }
201
202  API_MSG_VAR_ALLOC(msg);
203  API_MSG_VAR_REF(msg).conn = conn;
204#if LWIP_SO_SNDTIMEO || LWIP_SO_LINGER
205  /* get the time we started, which is later compared to
206     sys_now() + conn->send_timeout */
207  API_MSG_VAR_REF(msg).msg.sd.time_started = sys_now();
208#else /* LWIP_SO_SNDTIMEO || LWIP_SO_LINGER */
209#if LWIP_TCP
210  API_MSG_VAR_REF(msg).msg.sd.polls_left =
211    ((LWIP_TCP_CLOSE_TIMEOUT_MS_DEFAULT + TCP_SLOW_INTERVAL - 1) / TCP_SLOW_INTERVAL) + 1;
212#endif /* LWIP_TCP */
213#endif /* LWIP_SO_SNDTIMEO || LWIP_SO_LINGER */
214  err = netconn_apimsg(lwip_netconn_do_delconn, &API_MSG_VAR_REF(msg));
215  API_MSG_VAR_FREE(msg);
216
217  if (err != ERR_OK) {
218    return err;
219  }
220  return ERR_OK;
221}
222
223/**
224 * @ingroup netconn_common
225 * Close a netconn 'connection' and free its resources.
226 * UDP and RAW connection are completely closed, TCP pcbs might still be in a waitstate
227 * after this returns.
228 *
229 * @param conn the netconn to delete
230 * @return ERR_OK if the connection was deleted
231 */
232err_t
233netconn_delete(struct netconn *conn)
234{
235  err_t err;
236
237  /* No ASSERT here because possible to get a (conn == NULL) if we got an accept error */
238  if (conn == NULL) {
239    return ERR_OK;
240  }
241
242#if LWIP_NETCONN_FULLDUPLEX
243  if (conn->flags & NETCONN_FLAG_MBOXINVALID) {
244    /* Already called netconn_prepare_delete() before */
245    err = ERR_OK;
246  } else
247#endif /* LWIP_NETCONN_FULLDUPLEX */
248  {
249    err = netconn_prepare_delete(conn);
250  }
251  if (err == ERR_OK) {
252    netconn_free(conn);
253  }
254  return err;
255}
256
257/**
258 * Get the local or remote IP address and port of a netconn.
259 * For RAW netconns, this returns the protocol instead of a port!
260 *
261 * @param conn the netconn to query
262 * @param addr a pointer to which to save the IP address
263 * @param port a pointer to which to save the port (or protocol for RAW)
264 * @param local 1 to get the local IP address, 0 to get the remote one
265 * @return ERR_CONN for invalid connections
266 *         ERR_OK if the information was retrieved
267 */
268err_t
269netconn_getaddr(struct netconn *conn, ip_addr_t *addr, u16_t *port, u8_t local)
270{
271  API_MSG_VAR_DECLARE(msg);
272  err_t err;
273
274  LWIP_ERROR("netconn_getaddr: invalid conn", (conn != NULL), return ERR_ARG;);
275  LWIP_ERROR("netconn_getaddr: invalid addr", (addr != NULL), return ERR_ARG;);
276  LWIP_ERROR("netconn_getaddr: invalid port", (port != NULL), return ERR_ARG;);
277
278  API_MSG_VAR_ALLOC(msg);
279  API_MSG_VAR_REF(msg).conn = conn;
280  API_MSG_VAR_REF(msg).msg.ad.local = local;
281#if LWIP_MPU_COMPATIBLE
282  err = netconn_apimsg(lwip_netconn_do_getaddr, &API_MSG_VAR_REF(msg));
283  *addr = msg->msg.ad.ipaddr;
284  *port = msg->msg.ad.port;
285#else /* LWIP_MPU_COMPATIBLE */
286  msg.msg.ad.ipaddr = addr;
287  msg.msg.ad.port = port;
288  err = netconn_apimsg(lwip_netconn_do_getaddr, &msg);
289#endif /* LWIP_MPU_COMPATIBLE */
290  API_MSG_VAR_FREE(msg);
291
292  return err;
293}
294
295/**
296 * @ingroup netconn_common
297 * Bind a netconn to a specific local IP address and port.
298 * Binding one netconn twice might not always be checked correctly!
299 *
300 * @param conn the netconn to bind
301 * @param addr the local IP address to bind the netconn to
302 *             (use IP4_ADDR_ANY/IP6_ADDR_ANY to bind to all addresses)
303 * @param port the local port to bind the netconn to (not used for RAW)
304 * @return ERR_OK if bound, any other err_t on failure
305 */
306err_t
307netconn_bind(struct netconn *conn, const ip_addr_t *addr, u16_t port)
308{
309  API_MSG_VAR_DECLARE(msg);
310  err_t err;
311
312  LWIP_ERROR("netconn_bind: invalid conn", (conn != NULL), return ERR_ARG;);
313
314#if LWIP_IPV4
315  /* Don't propagate NULL pointer (IP_ADDR_ANY alias) to subsequent functions */
316  if (addr == NULL) {
317    addr = IP4_ADDR_ANY;
318  }
319#endif /* LWIP_IPV4 */
320
321#if LWIP_IPV4 && LWIP_IPV6
322  /* "Socket API like" dual-stack support: If IP to bind to is IP6_ADDR_ANY,
323   * and NETCONN_FLAG_IPV6_V6ONLY is 0, use IP_ANY_TYPE to bind
324   */
325  if ((netconn_get_ipv6only(conn) == 0) &&
326      ip_addr_cmp(addr, IP6_ADDR_ANY)) {
327    addr = IP_ANY_TYPE;
328  }
329#endif /* LWIP_IPV4 && LWIP_IPV6 */
330
331  API_MSG_VAR_ALLOC(msg);
332  API_MSG_VAR_REF(msg).conn = conn;
333  API_MSG_VAR_REF(msg).msg.bc.ipaddr = API_MSG_VAR_REF(addr);
334  API_MSG_VAR_REF(msg).msg.bc.port = port;
335  err = netconn_apimsg(lwip_netconn_do_bind, &API_MSG_VAR_REF(msg));
336  API_MSG_VAR_FREE(msg);
337
338  return err;
339}
340
341/**
342 * @ingroup netconn_common
343 * Bind a netconn to a specific interface and port.
344 * Binding one netconn twice might not always be checked correctly!
345 *
346 * @param conn the netconn to bind
347 * @param if_idx the local interface index to bind the netconn to
348 * @return ERR_OK if bound, any other err_t on failure
349 */
350err_t
351netconn_bind_if(struct netconn *conn, u8_t if_idx)
352{
353  API_MSG_VAR_DECLARE(msg);
354  err_t err;
355
356  LWIP_ERROR("netconn_bind_if: invalid conn", (conn != NULL), return ERR_ARG;);
357
358  API_MSG_VAR_ALLOC(msg);
359  API_MSG_VAR_REF(msg).conn = conn;
360  API_MSG_VAR_REF(msg).msg.bc.if_idx = if_idx;
361  err = netconn_apimsg(lwip_netconn_do_bind_if, &API_MSG_VAR_REF(msg));
362  API_MSG_VAR_FREE(msg);
363
364  return err;
365}
366
367/**
368 * @ingroup netconn_common
369 * Connect a netconn to a specific remote IP address and port.
370 *
371 * @param conn the netconn to connect
372 * @param addr the remote IP address to connect to
373 * @param port the remote port to connect to (no used for RAW)
374 * @return ERR_OK if connected, return value of tcp_/udp_/raw_connect otherwise
375 */
376err_t
377netconn_connect(struct netconn *conn, const ip_addr_t *addr, u16_t port)
378{
379  API_MSG_VAR_DECLARE(msg);
380  err_t err;
381
382  LWIP_ERROR("netconn_connect: invalid conn", (conn != NULL), return ERR_ARG;);
383
384#if LWIP_IPV4
385  /* Don't propagate NULL pointer (IP_ADDR_ANY alias) to subsequent functions */
386  if (addr == NULL) {
387    addr = IP4_ADDR_ANY;
388  }
389#endif /* LWIP_IPV4 */
390
391  API_MSG_VAR_ALLOC(msg);
392  API_MSG_VAR_REF(msg).conn = conn;
393  API_MSG_VAR_REF(msg).msg.bc.ipaddr = API_MSG_VAR_REF(addr);
394  API_MSG_VAR_REF(msg).msg.bc.port = port;
395  err = netconn_apimsg(lwip_netconn_do_connect, &API_MSG_VAR_REF(msg));
396  API_MSG_VAR_FREE(msg);
397
398  return err;
399}
400
401/**
402 * @ingroup netconn_udp
403 * Disconnect a netconn from its current peer (only valid for UDP netconns).
404 *
405 * @param conn the netconn to disconnect
406 * @return See @ref err_t
407 */
408err_t
409netconn_disconnect(struct netconn *conn)
410{
411  API_MSG_VAR_DECLARE(msg);
412  err_t err;
413
414  LWIP_ERROR("netconn_disconnect: invalid conn", (conn != NULL), return ERR_ARG;);
415
416  API_MSG_VAR_ALLOC(msg);
417  API_MSG_VAR_REF(msg).conn = conn;
418  err = netconn_apimsg(lwip_netconn_do_disconnect, &API_MSG_VAR_REF(msg));
419  API_MSG_VAR_FREE(msg);
420
421  return err;
422}
423
424/**
425 * @ingroup netconn_tcp
426 * Set a TCP netconn into listen mode
427 *
428 * @param conn the tcp netconn to set to listen mode
429 * @param backlog the listen backlog, only used if TCP_LISTEN_BACKLOG==1
430 * @return ERR_OK if the netconn was set to listen (UDP and RAW netconns
431 *         don't return any error (yet?))
432 */
433err_t
434netconn_listen_with_backlog(struct netconn *conn, u8_t backlog)
435{
436#if LWIP_TCP
437  API_MSG_VAR_DECLARE(msg);
438  err_t err;
439
440  /* This does no harm. If TCP_LISTEN_BACKLOG is off, backlog is unused. */
441  LWIP_UNUSED_ARG(backlog);
442
443  LWIP_ERROR("netconn_listen: invalid conn", (conn != NULL), return ERR_ARG;);
444
445  API_MSG_VAR_ALLOC(msg);
446  API_MSG_VAR_REF(msg).conn = conn;
447#if TCP_LISTEN_BACKLOG
448  API_MSG_VAR_REF(msg).msg.lb.backlog = backlog;
449#endif /* TCP_LISTEN_BACKLOG */
450  err = netconn_apimsg(lwip_netconn_do_listen, &API_MSG_VAR_REF(msg));
451  API_MSG_VAR_FREE(msg);
452
453  return err;
454#else /* LWIP_TCP */
455  LWIP_UNUSED_ARG(conn);
456  LWIP_UNUSED_ARG(backlog);
457  return ERR_ARG;
458#endif /* LWIP_TCP */
459}
460
461/**
462 * @ingroup netconn_tcp
463 * Accept a new connection on a TCP listening netconn.
464 *
465 * @param conn the TCP listen netconn
466 * @param new_conn pointer where the new connection is stored
467 * @return ERR_OK if a new connection has been received or an error
468 *                code otherwise
469 */
470err_t
471netconn_accept(struct netconn *conn, struct netconn **new_conn)
472{
473#if LWIP_TCP
474  err_t err;
475  void *accept_ptr;
476  struct netconn *newconn;
477#if TCP_LISTEN_BACKLOG
478  API_MSG_VAR_DECLARE(msg);
479#endif /* TCP_LISTEN_BACKLOG */
480
481  LWIP_ERROR("netconn_accept: invalid pointer",    (new_conn != NULL),                  return ERR_ARG;);
482  *new_conn = NULL;
483  LWIP_ERROR("netconn_accept: invalid conn",       (conn != NULL),                      return ERR_ARG;);
484
485  /* NOTE: Although the opengroup spec says a pending error shall be returned to
486           send/recv/getsockopt(SO_ERROR) only, we return it for listening
487           connections also, to handle embedded-system errors */
488  err = netconn_err(conn);
489  if (err != ERR_OK) {
490    /* return pending error */
491    return err;
492  }
493  if (!NETCONN_ACCEPTMBOX_WAITABLE(conn)) {
494    /* don't accept if closed: this might block the application task
495       waiting on acceptmbox forever! */
496    return ERR_CLSD;
497  }
498
499  API_MSG_VAR_ALLOC_ACCEPT(msg);
500
501  NETCONN_MBOX_WAITING_INC(conn);
502  if (netconn_is_nonblocking(conn)) {
503    if (sys_arch_mbox_tryfetch(&conn->acceptmbox, &accept_ptr) == SYS_ARCH_TIMEOUT) {
504      API_MSG_VAR_FREE_ACCEPT(msg);
505      NETCONN_MBOX_WAITING_DEC(conn);
506      return ERR_WOULDBLOCK;
507    }
508  } else {
509#if LWIP_SO_RCVTIMEO
510    if (sys_arch_mbox_fetch(&conn->acceptmbox, &accept_ptr, conn->recv_timeout) == SYS_ARCH_TIMEOUT) {
511      API_MSG_VAR_FREE_ACCEPT(msg);
512      NETCONN_MBOX_WAITING_DEC(conn);
513      return ERR_TIMEOUT;
514    }
515#else
516    sys_arch_mbox_fetch(&conn->acceptmbox, &accept_ptr, 0);
517#endif /* LWIP_SO_RCVTIMEO*/
518  }
519  NETCONN_MBOX_WAITING_DEC(conn);
520#if LWIP_NETCONN_FULLDUPLEX
521  if (conn->flags & NETCONN_FLAG_MBOXINVALID) {
522    if (lwip_netconn_is_deallocated_msg(accept_ptr)) {
523      /* the netconn has been closed from another thread */
524      API_MSG_VAR_FREE_ACCEPT(msg);
525      return ERR_CONN;
526    }
527  }
528#endif
529
530  /* Register event with callback */
531  API_EVENT(conn, NETCONN_EVT_RCVMINUS, 0);
532
533  if (lwip_netconn_is_err_msg(accept_ptr, &err)) {
534    /* a connection has been aborted: e.g. out of pcbs or out of netconns during accept */
535    API_MSG_VAR_FREE_ACCEPT(msg);
536    return err;
537  }
538  if (accept_ptr == NULL) {
539    /* connection has been aborted */
540    API_MSG_VAR_FREE_ACCEPT(msg);
541    return ERR_CLSD;
542  }
543  newconn = (struct netconn *)accept_ptr;
544#if TCP_LISTEN_BACKLOG
545  /* Let the stack know that we have accepted the connection. */
546  API_MSG_VAR_REF(msg).conn = newconn;
547  /* don't care for the return value of lwip_netconn_do_recv */
548  netconn_apimsg(lwip_netconn_do_accepted, &API_MSG_VAR_REF(msg));
549  API_MSG_VAR_FREE(msg);
550#endif /* TCP_LISTEN_BACKLOG */
551
552  *new_conn = newconn;
553  /* don't set conn->last_err: it's only ERR_OK, anyway */
554  return ERR_OK;
555#else /* LWIP_TCP */
556  LWIP_UNUSED_ARG(conn);
557  LWIP_UNUSED_ARG(new_conn);
558  return ERR_ARG;
559#endif /* LWIP_TCP */
560}
561
562/**
563 * @ingroup netconn_common
564 * Receive data: actual implementation that doesn't care whether pbuf or netbuf
565 * is received (this is internal, it's just here for describing common errors)
566 *
567 * @param conn the netconn from which to receive data
568 * @param new_buf pointer where a new pbuf/netbuf is stored when received data
569 * @param apiflags flags that control function behaviour. For now only:
570 * - NETCONN_DONTBLOCK: only read data that is available now, don't wait for more data
571 * @return ERR_OK if data has been received, an error code otherwise (timeout,
572 *                memory error or another error)
573 *         ERR_CONN if not connected
574 *         ERR_CLSD if TCP connection has been closed
575 *         ERR_WOULDBLOCK if the netconn is nonblocking but would block to wait for data
576 *         ERR_TIMEOUT if the netconn has a receive timeout and no data was received
577 */
578static err_t
579netconn_recv_data(struct netconn *conn, void **new_buf, u8_t apiflags)
580{
581  void *buf = NULL;
582  u16_t len;
583
584  LWIP_ERROR("netconn_recv: invalid pointer", (new_buf != NULL), return ERR_ARG;);
585  *new_buf = NULL;
586  LWIP_ERROR("netconn_recv: invalid conn",    (conn != NULL),    return ERR_ARG;);
587
588  if (!NETCONN_RECVMBOX_WAITABLE(conn)) {
589    err_t err = netconn_err(conn);
590    if (err != ERR_OK) {
591      /* return pending error */
592      return err;
593    }
594    return ERR_CONN;
595  }
596
597  NETCONN_MBOX_WAITING_INC(conn);
598  if (netconn_is_nonblocking(conn) || (apiflags & NETCONN_DONTBLOCK) ||
599      (conn->flags & NETCONN_FLAG_MBOXCLOSED) || (conn->pending_err != ERR_OK)) {
600    if (sys_arch_mbox_tryfetch(&conn->recvmbox, &buf) == SYS_ARCH_TIMEOUT) {
601      err_t err;
602      NETCONN_MBOX_WAITING_DEC(conn);
603      err = netconn_err(conn);
604      if (err != ERR_OK) {
605        /* return pending error */
606        return err;
607      }
608      if (conn->flags & NETCONN_FLAG_MBOXCLOSED) {
609        return ERR_CONN;
610      }
611      return ERR_WOULDBLOCK;
612    }
613  } else {
614#if LWIP_SO_RCVTIMEO
615    if (sys_arch_mbox_fetch(&conn->recvmbox, &buf, conn->recv_timeout) == SYS_ARCH_TIMEOUT) {
616      NETCONN_MBOX_WAITING_DEC(conn);
617      return ERR_TIMEOUT;
618    }
619#else
620    sys_arch_mbox_fetch(&conn->recvmbox, &buf, 0);
621#endif /* LWIP_SO_RCVTIMEO*/
622  }
623  NETCONN_MBOX_WAITING_DEC(conn);
624#if LWIP_NETCONN_FULLDUPLEX
625  if (conn->flags & NETCONN_FLAG_MBOXINVALID) {
626    if (lwip_netconn_is_deallocated_msg(buf)) {
627      /* the netconn has been closed from another thread */
628      API_MSG_VAR_FREE_ACCEPT(msg);
629      return ERR_CONN;
630    }
631  }
632#endif
633
634#if LWIP_TCP
635#if (LWIP_UDP || LWIP_RAW)
636  if (NETCONNTYPE_GROUP(conn->type) == NETCONN_TCP)
637#endif /* (LWIP_UDP || LWIP_RAW) */
638  {
639    err_t err;
640    /* Check if this is an error message or a pbuf */
641    if (lwip_netconn_is_err_msg(buf, &err)) {
642      /* new_buf has been zeroed above already */
643      if (err == ERR_CLSD) {
644        /* connection closed translates to ERR_OK with *new_buf == NULL */
645        return ERR_OK;
646      }
647      return err;
648    }
649    len = ((struct pbuf *)buf)->tot_len;
650  }
651#endif /* LWIP_TCP */
652#if LWIP_TCP && (LWIP_UDP || LWIP_RAW)
653  else
654#endif /* LWIP_TCP && (LWIP_UDP || LWIP_RAW) */
655#if (LWIP_UDP || LWIP_RAW)
656  {
657    LWIP_ASSERT("buf != NULL", buf != NULL);
658    len = netbuf_len((struct netbuf *)buf);
659  }
660#endif /* (LWIP_UDP || LWIP_RAW) */
661
662#if LWIP_SO_RCVBUF
663  SYS_ARCH_DEC(conn->recv_avail, len);
664#endif /* LWIP_SO_RCVBUF */
665  /* Register event with callback */
666  API_EVENT(conn, NETCONN_EVT_RCVMINUS, len);
667
668  LWIP_DEBUGF(API_LIB_DEBUG, ("netconn_recv_data: received %p, len=%"U16_F"\n", buf, len));
669
670  *new_buf = buf;
671  /* don't set conn->last_err: it's only ERR_OK, anyway */
672  return ERR_OK;
673}
674
675#if LWIP_TCP
676static err_t
677netconn_tcp_recvd_msg(struct netconn *conn, size_t len, struct api_msg *msg)
678{
679  LWIP_ERROR("netconn_recv_tcp_pbuf: invalid conn", (conn != NULL) &&
680             NETCONNTYPE_GROUP(netconn_type(conn)) == NETCONN_TCP, return ERR_ARG;);
681
682  msg->conn = conn;
683  msg->msg.r.len = len;
684
685  return netconn_apimsg(lwip_netconn_do_recv, msg);
686}
687
688err_t
689netconn_tcp_recvd(struct netconn *conn, size_t len)
690{
691  err_t err;
692  API_MSG_VAR_DECLARE(msg);
693  LWIP_ERROR("netconn_recv_tcp_pbuf: invalid conn", (conn != NULL) &&
694             NETCONNTYPE_GROUP(netconn_type(conn)) == NETCONN_TCP, return ERR_ARG;);
695
696  API_MSG_VAR_ALLOC(msg);
697  err = netconn_tcp_recvd_msg(conn, len, &API_VAR_REF(msg));
698  API_MSG_VAR_FREE(msg);
699  return err;
700}
701
702static err_t
703netconn_recv_data_tcp(struct netconn *conn, struct pbuf **new_buf, u8_t apiflags)
704{
705  err_t err;
706  struct pbuf *buf;
707  API_MSG_VAR_DECLARE(msg);
708#if LWIP_MPU_COMPATIBLE
709  msg = NULL;
710#endif
711
712  if (!NETCONN_RECVMBOX_WAITABLE(conn)) {
713    /* This only happens when calling this function more than once *after* receiving FIN */
714    return ERR_CONN;
715  }
716  if (netconn_is_flag_set(conn, NETCONN_FIN_RX_PENDING)) {
717    netconn_clear_flags(conn, NETCONN_FIN_RX_PENDING);
718    goto handle_fin;
719  }
720
721  if (!(apiflags & NETCONN_NOAUTORCVD)) {
722    /* need to allocate API message here so empty message pool does not result in event loss
723      * see bug #47512: MPU_COMPATIBLE may fail on empty pool */
724    API_MSG_VAR_ALLOC(msg);
725  }
726
727  err = netconn_recv_data(conn, (void **)new_buf, apiflags);
728  if (err != ERR_OK) {
729    if (!(apiflags & NETCONN_NOAUTORCVD)) {
730      API_MSG_VAR_FREE(msg);
731    }
732    return err;
733  }
734  buf = *new_buf;
735  if (!(apiflags & NETCONN_NOAUTORCVD)) {
736    /* Let the stack know that we have taken the data. */
737    u16_t len = buf ? buf->tot_len : 1;
738    /* don't care for the return value of lwip_netconn_do_recv */
739    /* @todo: this should really be fixed, e.g. by retrying in poll on error */
740    netconn_tcp_recvd_msg(conn, len,  &API_VAR_REF(msg));
741    API_MSG_VAR_FREE(msg);
742  }
743
744  /* If we are closed, we indicate that we no longer wish to use the socket */
745  if (buf == NULL) {
746    if (apiflags & NETCONN_NOFIN) {
747      /* received a FIN but the caller cannot handle it right now:
748         re-enqueue it and return "no data" */
749      netconn_set_flags(conn, NETCONN_FIN_RX_PENDING);
750      return ERR_WOULDBLOCK;
751    } else {
752handle_fin:
753      API_EVENT(conn, NETCONN_EVT_RCVMINUS, 0);
754      if (conn->pcb.ip == NULL) {
755        /* race condition: RST during recv */
756        err = netconn_err(conn);
757        if (err != ERR_OK) {
758          return err;
759        }
760        return ERR_RST;
761      }
762      /* RX side is closed, so deallocate the recvmbox */
763      netconn_close_shutdown(conn, NETCONN_SHUT_RD);
764      /* Don' store ERR_CLSD as conn->err since we are only half-closed */
765      return ERR_CLSD;
766    }
767  }
768  return err;
769}
770
771/**
772 * @ingroup netconn_tcp
773 * Receive data (in form of a pbuf) from a TCP netconn
774 *
775 * @param conn the netconn from which to receive data
776 * @param new_buf pointer where a new pbuf is stored when received data
777 * @return ERR_OK if data has been received, an error code otherwise (timeout,
778 *                memory error or another error, @see netconn_recv_data)
779 *         ERR_ARG if conn is not a TCP netconn
780 */
781err_t
782netconn_recv_tcp_pbuf(struct netconn *conn, struct pbuf **new_buf)
783{
784  LWIP_ERROR("netconn_recv_tcp_pbuf: invalid conn", (conn != NULL) &&
785             NETCONNTYPE_GROUP(netconn_type(conn)) == NETCONN_TCP, return ERR_ARG;);
786
787  return netconn_recv_data_tcp(conn, new_buf, 0);
788}
789
790/**
791 * @ingroup netconn_tcp
792 * Receive data (in form of a pbuf) from a TCP netconn
793 *
794 * @param conn the netconn from which to receive data
795 * @param new_buf pointer where a new pbuf is stored when received data
796 * @param apiflags flags that control function behaviour. For now only:
797 * - NETCONN_DONTBLOCK: only read data that is available now, don't wait for more data
798 * @return ERR_OK if data has been received, an error code otherwise (timeout,
799 *                memory error or another error, @see netconn_recv_data)
800 *         ERR_ARG if conn is not a TCP netconn
801 */
802err_t
803netconn_recv_tcp_pbuf_flags(struct netconn *conn, struct pbuf **new_buf, u8_t apiflags)
804{
805  LWIP_ERROR("netconn_recv_tcp_pbuf: invalid conn", (conn != NULL) &&
806             NETCONNTYPE_GROUP(netconn_type(conn)) == NETCONN_TCP, return ERR_ARG;);
807
808  return netconn_recv_data_tcp(conn, new_buf, apiflags);
809}
810#endif /* LWIP_TCP */
811
812/**
813 * Receive data (in form of a netbuf) from a UDP or RAW netconn
814 *
815 * @param conn the netconn from which to receive data
816 * @param new_buf pointer where a new netbuf is stored when received data
817 * @return ERR_OK if data has been received, an error code otherwise (timeout,
818 *                memory error or another error)
819 *         ERR_ARG if conn is not a UDP/RAW netconn
820 */
821err_t
822netconn_recv_udp_raw_netbuf(struct netconn *conn, struct netbuf **new_buf)
823{
824  LWIP_ERROR("netconn_recv_udp_raw_netbuf: invalid conn", (conn != NULL) &&
825             NETCONNTYPE_GROUP(netconn_type(conn)) != NETCONN_TCP, return ERR_ARG;);
826
827  return netconn_recv_data(conn, (void **)new_buf, 0);
828}
829
830/**
831 * Receive data (in form of a netbuf) from a UDP or RAW netconn
832 *
833 * @param conn the netconn from which to receive data
834 * @param new_buf pointer where a new netbuf is stored when received data
835 * @param apiflags flags that control function behaviour. For now only:
836 * - NETCONN_DONTBLOCK: only read data that is available now, don't wait for more data
837 * @return ERR_OK if data has been received, an error code otherwise (timeout,
838 *                memory error or another error)
839 *         ERR_ARG if conn is not a UDP/RAW netconn
840 */
841err_t
842netconn_recv_udp_raw_netbuf_flags(struct netconn *conn, struct netbuf **new_buf, u8_t apiflags)
843{
844  LWIP_ERROR("netconn_recv_udp_raw_netbuf: invalid conn", (conn != NULL) &&
845             NETCONNTYPE_GROUP(netconn_type(conn)) != NETCONN_TCP, return ERR_ARG;);
846
847  return netconn_recv_data(conn, (void **)new_buf, apiflags);
848}
849
850/**
851 * @ingroup netconn_common
852 * Receive data (in form of a netbuf containing a packet buffer) from a netconn
853 *
854 * @param conn the netconn from which to receive data
855 * @param new_buf pointer where a new netbuf is stored when received data
856 * @return ERR_OK if data has been received, an error code otherwise (timeout,
857 *                memory error or another error)
858 */
859err_t
860netconn_recv(struct netconn *conn, struct netbuf **new_buf)
861{
862#if LWIP_TCP
863  struct netbuf *buf = NULL;
864  err_t err;
865#endif /* LWIP_TCP */
866
867  LWIP_ERROR("netconn_recv: invalid pointer", (new_buf != NULL), return ERR_ARG;);
868  *new_buf = NULL;
869  LWIP_ERROR("netconn_recv: invalid conn",    (conn != NULL),    return ERR_ARG;);
870
871#if LWIP_TCP
872#if (LWIP_UDP || LWIP_RAW)
873  if (NETCONNTYPE_GROUP(conn->type) == NETCONN_TCP)
874#endif /* (LWIP_UDP || LWIP_RAW) */
875  {
876    struct pbuf *p = NULL;
877    /* This is not a listening netconn, since recvmbox is set */
878
879    buf = (struct netbuf *)memp_malloc(MEMP_NETBUF);
880    if (buf == NULL) {
881      return ERR_MEM;
882    }
883
884    err = netconn_recv_data_tcp(conn, &p, 0);
885    if (err != ERR_OK) {
886      memp_free(MEMP_NETBUF, buf);
887      return err;
888    }
889    LWIP_ASSERT("p != NULL", p != NULL);
890
891    buf->p = p;
892    buf->ptr = p;
893    buf->port = 0;
894    ip_addr_set_zero(&buf->addr);
895    *new_buf = buf;
896    /* don't set conn->last_err: it's only ERR_OK, anyway */
897    return ERR_OK;
898  }
899#endif /* LWIP_TCP */
900#if LWIP_TCP && (LWIP_UDP || LWIP_RAW)
901  else
902#endif /* LWIP_TCP && (LWIP_UDP || LWIP_RAW) */
903  {
904#if (LWIP_UDP || LWIP_RAW)
905    return netconn_recv_data(conn, (void **)new_buf, 0);
906#endif /* (LWIP_UDP || LWIP_RAW) */
907  }
908}
909
910/**
911 * @ingroup netconn_udp
912 * Send data (in form of a netbuf) to a specific remote IP address and port.
913 * Only to be used for UDP and RAW netconns (not TCP).
914 *
915 * @param conn the netconn over which to send data
916 * @param buf a netbuf containing the data to send
917 * @param addr the remote IP address to which to send the data
918 * @param port the remote port to which to send the data
919 * @return ERR_OK if data was sent, any other err_t on error
920 */
921err_t
922netconn_sendto(struct netconn *conn, struct netbuf *buf, const ip_addr_t *addr, u16_t port)
923{
924  if (buf != NULL) {
925    ip_addr_set(&buf->addr, addr);
926    buf->port = port;
927    return netconn_send(conn, buf);
928  }
929  return ERR_VAL;
930}
931
932/**
933 * @ingroup netconn_udp
934 * Send data over a UDP or RAW netconn (that is already connected).
935 *
936 * @param conn the UDP or RAW netconn over which to send data
937 * @param buf a netbuf containing the data to send
938 * @return ERR_OK if data was sent, any other err_t on error
939 */
940err_t
941netconn_send(struct netconn *conn, struct netbuf *buf)
942{
943  API_MSG_VAR_DECLARE(msg);
944  err_t err;
945
946  LWIP_ERROR("netconn_send: invalid conn",  (conn != NULL), return ERR_ARG;);
947
948  LWIP_DEBUGF(API_LIB_DEBUG, ("netconn_send: sending %"U16_F" bytes\n", buf->p->tot_len));
949
950  API_MSG_VAR_ALLOC(msg);
951  API_MSG_VAR_REF(msg).conn = conn;
952  API_MSG_VAR_REF(msg).msg.b = buf;
953  err = netconn_apimsg(lwip_netconn_do_send, &API_MSG_VAR_REF(msg));
954  API_MSG_VAR_FREE(msg);
955
956  return err;
957}
958
959/**
960 * @ingroup netconn_tcp
961 * Send data over a TCP netconn.
962 *
963 * @param conn the TCP netconn over which to send data
964 * @param dataptr pointer to the application buffer that contains the data to send
965 * @param size size of the application data to send
966 * @param apiflags combination of following flags :
967 * - NETCONN_COPY: data will be copied into memory belonging to the stack
968 * - NETCONN_MORE: for TCP connection, PSH flag will be set on last segment sent
969 * - NETCONN_DONTBLOCK: only write the data if all data can be written at once
970 * @param bytes_written pointer to a location that receives the number of written bytes
971 * @return ERR_OK if data was sent, any other err_t on error
972 */
973err_t
974netconn_write_partly(struct netconn *conn, const void *dataptr, size_t size,
975                     u8_t apiflags, size_t *bytes_written)
976{
977  struct netvector vector;
978  vector.ptr = dataptr;
979  vector.len = size;
980  return netconn_write_vectors_partly(conn, &vector, 1, apiflags, bytes_written);
981}
982
983/**
984 * Send vectorized data atomically over a TCP netconn.
985 *
986 * @param conn the TCP netconn over which to send data
987 * @param vectors array of vectors containing data to send
988 * @param vectorcnt number of vectors in the array
989 * @param apiflags combination of following flags :
990 * - NETCONN_COPY: data will be copied into memory belonging to the stack
991 * - NETCONN_MORE: for TCP connection, PSH flag will be set on last segment sent
992 * - NETCONN_DONTBLOCK: only write the data if all data can be written at once
993 * @param bytes_written pointer to a location that receives the number of written bytes
994 * @return ERR_OK if data was sent, any other err_t on error
995 */
996err_t
997netconn_write_vectors_partly(struct netconn *conn, struct netvector *vectors, u16_t vectorcnt,
998                             u8_t apiflags, size_t *bytes_written)
999{
1000  API_MSG_VAR_DECLARE(msg);
1001  err_t err;
1002  u8_t dontblock;
1003  size_t size;
1004  int i;
1005
1006  LWIP_ERROR("netconn_write: invalid conn",  (conn != NULL), return ERR_ARG;);
1007  LWIP_ERROR("netconn_write: invalid conn->type",  (NETCONNTYPE_GROUP(conn->type) == NETCONN_TCP), return ERR_VAL;);
1008  dontblock = netconn_is_nonblocking(conn) || (apiflags & NETCONN_DONTBLOCK);
1009#if LWIP_SO_SNDTIMEO
1010  if (conn->send_timeout != 0) {
1011    dontblock = 1;
1012  }
1013#endif /* LWIP_SO_SNDTIMEO */
1014  if (dontblock && !bytes_written) {
1015    /* This implies netconn_write() cannot be used for non-blocking send, since
1016       it has no way to return the number of bytes written. */
1017    return ERR_VAL;
1018  }
1019
1020  /* sum up the total size */
1021  size = 0;
1022  for (i = 0; i < vectorcnt; i++) {
1023    size += vectors[i].len;
1024    if (size < vectors[i].len) {
1025      /* overflow */
1026      return ERR_VAL;
1027    }
1028  }
1029  if (size == 0) {
1030    return ERR_OK;
1031  } else if (size > SSIZE_MAX) {
1032    ssize_t limited;
1033    /* this is required by the socket layer (cannot send full size_t range) */
1034    if (!bytes_written) {
1035      return ERR_VAL;
1036    }
1037    /* limit the amount of data to send */
1038    limited = SSIZE_MAX;
1039    size = (size_t)limited;
1040  }
1041
1042  API_MSG_VAR_ALLOC(msg);
1043  /* non-blocking write sends as much  */
1044  API_MSG_VAR_REF(msg).conn = conn;
1045  API_MSG_VAR_REF(msg).msg.w.vector = vectors;
1046  API_MSG_VAR_REF(msg).msg.w.vector_cnt = vectorcnt;
1047  API_MSG_VAR_REF(msg).msg.w.vector_off = 0;
1048  API_MSG_VAR_REF(msg).msg.w.apiflags = apiflags;
1049  API_MSG_VAR_REF(msg).msg.w.len = size;
1050  API_MSG_VAR_REF(msg).msg.w.offset = 0;
1051#if LWIP_SO_SNDTIMEO
1052  if (conn->send_timeout != 0) {
1053    /* get the time we started, which is later compared to
1054        sys_now() + conn->send_timeout */
1055    API_MSG_VAR_REF(msg).msg.w.time_started = sys_now();
1056  } else {
1057    API_MSG_VAR_REF(msg).msg.w.time_started = 0;
1058  }
1059#endif /* LWIP_SO_SNDTIMEO */
1060
1061  /* For locking the core: this _can_ be delayed on low memory/low send buffer,
1062     but if it is, this is done inside api_msg.c:do_write(), so we can use the
1063     non-blocking version here. */
1064  err = netconn_apimsg(lwip_netconn_do_write, &API_MSG_VAR_REF(msg));
1065  if (err == ERR_OK) {
1066    if (bytes_written != NULL) {
1067      *bytes_written = API_MSG_VAR_REF(msg).msg.w.offset;
1068    }
1069    /* for blocking, check all requested bytes were written, NOTE: send_timeout is
1070       treated as dontblock (see dontblock assignment above) */
1071    if (!dontblock) {
1072      LWIP_ASSERT("do_write failed to write all bytes", API_MSG_VAR_REF(msg).msg.w.offset == size);
1073    }
1074  }
1075  API_MSG_VAR_FREE(msg);
1076
1077  return err;
1078}
1079
1080/**
1081 * @ingroup netconn_tcp
1082 * Close or shutdown a TCP netconn (doesn't delete it).
1083 *
1084 * @param conn the TCP netconn to close or shutdown
1085 * @param how fully close or only shutdown one side?
1086 * @return ERR_OK if the netconn was closed, any other err_t on error
1087 */
1088static err_t
1089netconn_close_shutdown(struct netconn *conn, u8_t how)
1090{
1091  API_MSG_VAR_DECLARE(msg);
1092  err_t err;
1093  LWIP_UNUSED_ARG(how);
1094
1095  LWIP_ERROR("netconn_close: invalid conn",  (conn != NULL), return ERR_ARG;);
1096
1097  API_MSG_VAR_ALLOC(msg);
1098  API_MSG_VAR_REF(msg).conn = conn;
1099#if LWIP_TCP
1100  /* shutting down both ends is the same as closing */
1101  API_MSG_VAR_REF(msg).msg.sd.shut = how;
1102#if LWIP_SO_SNDTIMEO || LWIP_SO_LINGER
1103  /* get the time we started, which is later compared to
1104     sys_now() + conn->send_timeout */
1105  API_MSG_VAR_REF(msg).msg.sd.time_started = sys_now();
1106#else /* LWIP_SO_SNDTIMEO || LWIP_SO_LINGER */
1107  API_MSG_VAR_REF(msg).msg.sd.polls_left =
1108    ((LWIP_TCP_CLOSE_TIMEOUT_MS_DEFAULT + TCP_SLOW_INTERVAL - 1) / TCP_SLOW_INTERVAL) + 1;
1109#endif /* LWIP_SO_SNDTIMEO || LWIP_SO_LINGER */
1110#endif /* LWIP_TCP */
1111  err = netconn_apimsg(lwip_netconn_do_close, &API_MSG_VAR_REF(msg));
1112  API_MSG_VAR_FREE(msg);
1113
1114  return err;
1115}
1116
1117/**
1118 * @ingroup netconn_tcp
1119 * Close a TCP netconn (doesn't delete it).
1120 *
1121 * @param conn the TCP netconn to close
1122 * @return ERR_OK if the netconn was closed, any other err_t on error
1123 */
1124err_t
1125netconn_close(struct netconn *conn)
1126{
1127  /* shutting down both ends is the same as closing */
1128  return netconn_close_shutdown(conn, NETCONN_SHUT_RDWR);
1129}
1130
1131/**
1132 * @ingroup netconn_common
1133 * Get and reset pending error on a netconn
1134 *
1135 * @param conn the netconn to get the error from
1136 * @return and pending error or ERR_OK if no error was pending
1137 */
1138err_t
1139netconn_err(struct netconn *conn)
1140{
1141  err_t err;
1142  SYS_ARCH_DECL_PROTECT(lev);
1143  if (conn == NULL) {
1144    return ERR_OK;
1145  }
1146  SYS_ARCH_PROTECT(lev);
1147  err = conn->pending_err;
1148  conn->pending_err = ERR_OK;
1149  SYS_ARCH_UNPROTECT(lev);
1150  return err;
1151}
1152
1153/**
1154 * @ingroup netconn_tcp
1155 * Shut down one or both sides of a TCP netconn (doesn't delete it).
1156 *
1157 * @param conn the TCP netconn to shut down
1158 * @param shut_rx shut down the RX side (no more read possible after this)
1159 * @param shut_tx shut down the TX side (no more write possible after this)
1160 * @return ERR_OK if the netconn was closed, any other err_t on error
1161 */
1162err_t
1163netconn_shutdown(struct netconn *conn, u8_t shut_rx, u8_t shut_tx)
1164{
1165  return netconn_close_shutdown(conn, (u8_t)((shut_rx ? NETCONN_SHUT_RD : 0) | (shut_tx ? NETCONN_SHUT_WR : 0)));
1166}
1167
1168#if LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD)
1169/**
1170 * @ingroup netconn_udp
1171 * Join multicast groups for UDP netconns.
1172 *
1173 * @param conn the UDP netconn for which to change multicast addresses
1174 * @param multiaddr IP address of the multicast group to join or leave
1175 * @param netif_addr the IP address of the network interface on which to send
1176 *                  the igmp message
1177 * @param join_or_leave flag whether to send a join- or leave-message
1178 * @return ERR_OK if the action was taken, any err_t on error
1179 */
1180err_t
1181netconn_join_leave_group(struct netconn *conn,
1182                         const ip_addr_t *multiaddr,
1183                         const ip_addr_t *netif_addr,
1184                         enum netconn_igmp join_or_leave)
1185{
1186  API_MSG_VAR_DECLARE(msg);
1187  err_t err;
1188
1189  LWIP_ERROR("netconn_join_leave_group: invalid conn",  (conn != NULL), return ERR_ARG;);
1190
1191  API_MSG_VAR_ALLOC(msg);
1192
1193#if LWIP_IPV4
1194  /* Don't propagate NULL pointer (IP_ADDR_ANY alias) to subsequent functions */
1195  if (multiaddr == NULL) {
1196    multiaddr = IP4_ADDR_ANY;
1197  }
1198  if (netif_addr == NULL) {
1199    netif_addr = IP4_ADDR_ANY;
1200  }
1201#endif /* LWIP_IPV4 */
1202
1203  API_MSG_VAR_REF(msg).conn = conn;
1204  API_MSG_VAR_REF(msg).msg.jl.multiaddr = API_MSG_VAR_REF(multiaddr);
1205  API_MSG_VAR_REF(msg).msg.jl.netif_addr = API_MSG_VAR_REF(netif_addr);
1206  API_MSG_VAR_REF(msg).msg.jl.join_or_leave = join_or_leave;
1207  err = netconn_apimsg(lwip_netconn_do_join_leave_group, &API_MSG_VAR_REF(msg));
1208  API_MSG_VAR_FREE(msg);
1209
1210  return err;
1211}
1212/**
1213 * @ingroup netconn_udp
1214 * Join multicast groups for UDP netconns.
1215 *
1216 * @param conn the UDP netconn for which to change multicast addresses
1217 * @param multiaddr IP address of the multicast group to join or leave
1218 * @param if_idx the index of the netif
1219 * @param join_or_leave flag whether to send a join- or leave-message
1220 * @return ERR_OK if the action was taken, any err_t on error
1221 */
1222err_t
1223netconn_join_leave_group_netif(struct netconn *conn,
1224                               const ip_addr_t *multiaddr,
1225                               u8_t if_idx,
1226                               enum netconn_igmp join_or_leave)
1227{
1228  API_MSG_VAR_DECLARE(msg);
1229  err_t err;
1230
1231  LWIP_ERROR("netconn_join_leave_group: invalid conn",  (conn != NULL), return ERR_ARG;);
1232
1233  API_MSG_VAR_ALLOC(msg);
1234
1235#if LWIP_IPV4
1236  /* Don't propagate NULL pointer (IP_ADDR_ANY alias) to subsequent functions */
1237  if (multiaddr == NULL) {
1238    multiaddr = IP4_ADDR_ANY;
1239  }
1240  if (if_idx == NETIF_NO_INDEX) {
1241    return ERR_IF;
1242  }
1243#endif /* LWIP_IPV4 */
1244
1245  API_MSG_VAR_REF(msg).conn = conn;
1246  API_MSG_VAR_REF(msg).msg.jl.multiaddr = API_MSG_VAR_REF(multiaddr);
1247  API_MSG_VAR_REF(msg).msg.jl.if_idx = if_idx;
1248  API_MSG_VAR_REF(msg).msg.jl.join_or_leave = join_or_leave;
1249  err = netconn_apimsg(lwip_netconn_do_join_leave_group_netif, &API_MSG_VAR_REF(msg));
1250  API_MSG_VAR_FREE(msg);
1251
1252  return err;
1253}
1254#endif /* LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD) */
1255
1256#if LWIP_DNS
1257/**
1258 * @ingroup netconn_common
1259 * Execute a DNS query, only one IP address is returned
1260 *
1261 * @param name a string representation of the DNS host name to query
1262 * @param addr a preallocated ip_addr_t where to store the resolved IP address
1263 * @param dns_addrtype IP address type (IPv4 / IPv6)
1264 * @return ERR_OK: resolving succeeded
1265 *         ERR_MEM: memory error, try again later
1266 *         ERR_ARG: dns client not initialized or invalid hostname
1267 *         ERR_VAL: dns server response was invalid
1268 */
1269#if LWIP_IPV4 && LWIP_IPV6
1270err_t
1271netconn_gethostbyname_addrtype(const char *name, ip_addr_t *addr, u8_t dns_addrtype)
1272#else
1273err_t
1274netconn_gethostbyname(const char *name, ip_addr_t *addr)
1275#endif
1276{
1277  API_VAR_DECLARE(struct dns_api_msg, msg);
1278#if !LWIP_MPU_COMPATIBLE
1279  sys_sem_t sem;
1280#endif /* LWIP_MPU_COMPATIBLE */
1281  err_t err;
1282  err_t cberr;
1283
1284  LWIP_ERROR("netconn_gethostbyname: invalid name", (name != NULL), return ERR_ARG;);
1285  LWIP_ERROR("netconn_gethostbyname: invalid addr", (addr != NULL), return ERR_ARG;);
1286#if LWIP_MPU_COMPATIBLE
1287  if (strlen(name) >= DNS_MAX_NAME_LENGTH) {
1288    return ERR_ARG;
1289  }
1290#endif
1291
1292#ifdef LWIP_HOOK_NETCONN_EXTERNAL_RESOLVE
1293#if LWIP_IPV4 && LWIP_IPV6
1294  if (LWIP_HOOK_NETCONN_EXTERNAL_RESOLVE(name, addr, dns_addrtype, &err)) {
1295#else
1296  if (LWIP_HOOK_NETCONN_EXTERNAL_RESOLVE(name, addr, NETCONN_DNS_DEFAULT, &err)) {
1297#endif /* LWIP_IPV4 && LWIP_IPV6 */
1298    return err;
1299  }
1300#endif /* LWIP_HOOK_NETCONN_EXTERNAL_RESOLVE */
1301
1302  API_VAR_ALLOC(struct dns_api_msg, MEMP_DNS_API_MSG, msg, ERR_MEM);
1303#if LWIP_MPU_COMPATIBLE
1304  strncpy(API_VAR_REF(msg).name, name, DNS_MAX_NAME_LENGTH - 1);
1305  API_VAR_REF(msg).name[DNS_MAX_NAME_LENGTH - 1] = 0;
1306#else /* LWIP_MPU_COMPATIBLE */
1307  msg.err = &err;
1308  msg.sem = &sem;
1309  API_VAR_REF(msg).addr = API_VAR_REF(addr);
1310  API_VAR_REF(msg).name = name;
1311#endif /* LWIP_MPU_COMPATIBLE */
1312#if LWIP_IPV4 && LWIP_IPV6
1313  API_VAR_REF(msg).dns_addrtype = dns_addrtype;
1314#endif /* LWIP_IPV4 && LWIP_IPV6 */
1315#if LWIP_NETCONN_SEM_PER_THREAD
1316  API_VAR_REF(msg).sem = LWIP_NETCONN_THREAD_SEM_GET();
1317#else /* LWIP_NETCONN_SEM_PER_THREAD*/
1318  err = sys_sem_new(API_EXPR_REF(API_VAR_REF(msg).sem), 0);
1319  if (err != ERR_OK) {
1320    API_VAR_FREE(MEMP_DNS_API_MSG, msg);
1321    return err;
1322  }
1323#endif /* LWIP_NETCONN_SEM_PER_THREAD */
1324
1325  cberr = tcpip_send_msg_wait_sem(lwip_netconn_do_gethostbyname, &API_VAR_REF(msg), API_EXPR_REF(API_VAR_REF(msg).sem));
1326#if !LWIP_NETCONN_SEM_PER_THREAD
1327  sys_sem_free(API_EXPR_REF(API_VAR_REF(msg).sem));
1328#endif /* !LWIP_NETCONN_SEM_PER_THREAD */
1329  if (cberr != ERR_OK) {
1330    API_VAR_FREE(MEMP_DNS_API_MSG, msg);
1331    return cberr;
1332  }
1333
1334#if LWIP_MPU_COMPATIBLE
1335  *addr = msg->addr;
1336  err = msg->err;
1337#endif /* LWIP_MPU_COMPATIBLE */
1338
1339  API_VAR_FREE(MEMP_DNS_API_MSG, msg);
1340  return err;
1341}
1342#endif /* LWIP_DNS*/
1343
1344#if LWIP_NETCONN_SEM_PER_THREAD
1345void
1346netconn_thread_init(void)
1347{
1348  sys_sem_t *sem = LWIP_NETCONN_THREAD_SEM_GET();
1349  if ((sem == NULL) || !sys_sem_valid(sem)) {
1350    /* call alloc only once */
1351    LWIP_NETCONN_THREAD_SEM_ALLOC();
1352    LWIP_ASSERT("LWIP_NETCONN_THREAD_SEM_ALLOC() failed", sys_sem_valid(LWIP_NETCONN_THREAD_SEM_GET()));
1353  }
1354}
1355
1356void
1357netconn_thread_cleanup(void)
1358{
1359  sys_sem_t *sem = LWIP_NETCONN_THREAD_SEM_GET();
1360  if ((sem != NULL) && sys_sem_valid(sem)) {
1361    /* call free only once */
1362    LWIP_NETCONN_THREAD_SEM_FREE();
1363  }
1364}
1365#endif /* LWIP_NETCONN_SEM_PER_THREAD */
1366
1367#endif /* LWIP_NETCONN */
1368