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 
106 static 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  */
117 static err_t
netconn_apimsg(tcpip_callback_fn fn,struct api_msg * apimsg)118 netconn_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  */
148 struct netconn *
netconn_new_with_proto_and_callback(enum netconn_type t,u8_t proto,netconn_callback callback)149 netconn_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  */
191 err_t
netconn_prepare_delete(struct netconn * conn)192 netconn_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_TCP
205 #if LWIP_SO_SNDTIMEO || LWIP_SO_LINGER
206   /* get the time we started, which is later compared to
207      sys_now() + conn->send_timeout */
208   API_MSG_VAR_REF(msg).msg.sd.time_started = sys_now();
209 #else /* LWIP_SO_SNDTIMEO || LWIP_SO_LINGER */
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_SO_SNDTIMEO || LWIP_SO_LINGER */
213 #endif /* LWIP_TCP */
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  */
232 err_t
netconn_delete(struct netconn * conn)233 netconn_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  */
268 err_t
netconn_getaddr(struct netconn * conn,ip_addr_t * addr,u16_t * port,u8_t local)269 netconn_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  */
306 err_t
netconn_bind(struct netconn * conn,const ip_addr_t * addr,u16_t port)307 netconn_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_eq(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  */
350 err_t
netconn_bind_if(struct netconn * conn,u8_t if_idx)351 netconn_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  */
376 err_t
netconn_connect(struct netconn * conn,const ip_addr_t * addr,u16_t port)377 netconn_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  */
408 err_t
netconn_disconnect(struct netconn * conn)409 netconn_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  */
433 err_t
netconn_listen_with_backlog(struct netconn * conn,u8_t backlog)434 netconn_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  */
470 err_t
netconn_accept(struct netconn * conn,struct netconn ** new_conn)471 netconn_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_MBOX_EMPTY) {
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  */
578 static err_t
netconn_recv_data(struct netconn * conn,void ** new_buf,u8_t apiflags)579 netconn_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_MBOX_EMPTY) {
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
676 static err_t
netconn_tcp_recvd_msg(struct netconn * conn,size_t len,struct api_msg * msg)677 netconn_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 
688 err_t
netconn_tcp_recvd(struct netconn * conn,size_t len)689 netconn_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 
702 static err_t
netconn_recv_data_tcp(struct netconn * conn,struct pbuf ** new_buf,u8_t apiflags)703 netconn_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 {
752 handle_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  */
781 err_t
netconn_recv_tcp_pbuf(struct netconn * conn,struct pbuf ** new_buf)782 netconn_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  */
802 err_t
netconn_recv_tcp_pbuf_flags(struct netconn * conn,struct pbuf ** new_buf,u8_t apiflags)803 netconn_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  */
821 err_t
netconn_recv_udp_raw_netbuf(struct netconn * conn,struct netbuf ** new_buf)822 netconn_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  */
841 err_t
netconn_recv_udp_raw_netbuf_flags(struct netconn * conn,struct netbuf ** new_buf,u8_t apiflags)842 netconn_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  */
859 err_t
netconn_recv(struct netconn * conn,struct netbuf ** new_buf)860 netconn_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  */
921 err_t
netconn_sendto(struct netconn * conn,struct netbuf * buf,const ip_addr_t * addr,u16_t port)922 netconn_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  */
940 err_t
netconn_send(struct netconn * conn,struct netbuf * buf)941 netconn_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  */
973 err_t
netconn_write_partly(struct netconn * conn,const void * dataptr,size_t size,u8_t apiflags,size_t * bytes_written)974 netconn_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  */
996 err_t
netconn_write_vectors_partly(struct netconn * conn,struct netvector * vectors,u16_t vectorcnt,u8_t apiflags,size_t * bytes_written)997 netconn_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  */
1088 static err_t
netconn_close_shutdown(struct netconn * conn,u8_t how)1089 netconn_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  */
1124 err_t
netconn_close(struct netconn * conn)1125 netconn_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  */
1138 err_t
netconn_err(struct netconn * conn)1139 netconn_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  */
1162 err_t
netconn_shutdown(struct netconn * conn,u8_t shut_rx,u8_t shut_tx)1163 netconn_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  */
1180 err_t
netconn_join_leave_group(struct netconn * conn,const ip_addr_t * multiaddr,const ip_addr_t * netif_addr,enum netconn_igmp join_or_leave)1181 netconn_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  */
1222 err_t
netconn_join_leave_group_netif(struct netconn * conn,const ip_addr_t * multiaddr,u8_t if_idx,enum netconn_igmp join_or_leave)1223 netconn_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  * @return ERR_OK: resolving succeeded
1264  *         ERR_MEM: memory error, try again later
1265  *         ERR_ARG: dns client not initialized or invalid hostname
1266  *         ERR_VAL: dns server response was invalid
1267  */
1268 #if LWIP_IPV4 && LWIP_IPV6
1269 /** @param dns_addrtype IP address type (IPv4 / IPv6) */
1270 err_t
netconn_gethostbyname_addrtype(const char * name,ip_addr_t * addr,u8_t dns_addrtype)1271 netconn_gethostbyname_addrtype(const char *name, ip_addr_t *addr, u8_t dns_addrtype)
1272 #else
1273 err_t
1274 netconn_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
1345 void
1346 netconn_thread_init(void)
1347 {
1348   sys_sem_t *sem = LWIP_NETCONN_THREAD_SEM_GET();
1349   if (!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 
1356 void
1357 netconn_thread_cleanup(void)
1358 {
1359   sys_sem_t *sem = LWIP_NETCONN_THREAD_SEM_GET();
1360   if (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