xref: /nrf52832-nimble/rt-thread/components/net/lwip-2.0.2/src/api/api_lib.c (revision 167494296f0543431a51b6b1b83e957045294e05)
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 <[email protected]>
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 #include <string.h>
73 
74 #define API_MSG_VAR_REF(name)               API_VAR_REF(name)
75 #define API_MSG_VAR_DECLARE(name)           API_VAR_DECLARE(struct api_msg, name)
76 #define API_MSG_VAR_ALLOC(name)             API_VAR_ALLOC(struct api_msg, MEMP_API_MSG, name, ERR_MEM)
77 #define API_MSG_VAR_ALLOC_RETURN_NULL(name) API_VAR_ALLOC(struct api_msg, MEMP_API_MSG, name, NULL)
78 #define API_MSG_VAR_FREE(name)              API_VAR_FREE(MEMP_API_MSG, name)
79 
80 static err_t netconn_close_shutdown(struct netconn *conn, u8_t how);
81 
82 /**
83  * Call the lower part of a netconn_* function
84  * This function is then running in the thread context
85  * of tcpip_thread and has exclusive access to lwIP core code.
86  *
87  * @param fn function to call
88  * @param apimsg a struct containing the function to call and its parameters
89  * @return ERR_OK if the function was called, another err_t if not
90  */
91 static err_t
92 netconn_apimsg(tcpip_callback_fn fn, struct api_msg *apimsg)
93 {
94   err_t err;
95 
96 #ifdef LWIP_DEBUG
97   /* catch functions that don't set err */
98   apimsg->err = ERR_VAL;
99 #endif /* LWIP_DEBUG */
100 
101 #if LWIP_NETCONN_SEM_PER_THREAD
102   apimsg->op_completed_sem = LWIP_NETCONN_THREAD_SEM_GET();
103 #endif /* LWIP_NETCONN_SEM_PER_THREAD */
104 
105   err = tcpip_send_msg_wait_sem(fn, apimsg, LWIP_API_MSG_SEM(apimsg));
106   if (err == ERR_OK) {
107     return apimsg->err;
108   }
109   return err;
110 }
111 
112 /**
113  * Create a new netconn (of a specific type) that has a callback function.
114  * The corresponding pcb is also created.
115  *
116  * @param t the type of 'connection' to create (@see enum netconn_type)
117  * @param proto the IP protocol for RAW IP pcbs
118  * @param callback a function to call on status changes (RX available, TX'ed)
119  * @return a newly allocated struct netconn or
120  *         NULL on memory error
121  */
122 struct netconn*
123 netconn_new_with_proto_and_callback(enum netconn_type t, u8_t proto, netconn_callback callback)
124 {
125   struct netconn *conn;
126   API_MSG_VAR_DECLARE(msg);
127   API_MSG_VAR_ALLOC_RETURN_NULL(msg);
128 
129   conn = netconn_alloc(t, callback);
130   if (conn != NULL) {
131     err_t err;
132 
133     API_MSG_VAR_REF(msg).msg.n.proto = proto;
134     API_MSG_VAR_REF(msg).conn = conn;
135     err = netconn_apimsg(lwip_netconn_do_newconn, &API_MSG_VAR_REF(msg));
136     if (err != ERR_OK) {
137       LWIP_ASSERT("freeing conn without freeing pcb", conn->pcb.tcp == NULL);
138       LWIP_ASSERT("conn has no recvmbox", sys_mbox_valid(&conn->recvmbox));
139 #if LWIP_TCP
140       LWIP_ASSERT("conn->acceptmbox shouldn't exist", !sys_mbox_valid(&conn->acceptmbox));
141 #endif /* LWIP_TCP */
142 #if !LWIP_NETCONN_SEM_PER_THREAD
143       LWIP_ASSERT("conn has no op_completed", sys_sem_valid(&conn->op_completed));
144       sys_sem_free(&conn->op_completed);
145 #endif /* !LWIP_NETCONN_SEM_PER_THREAD */
146       sys_mbox_free(&conn->recvmbox);
147       memp_free(MEMP_NETCONN, conn);
148       API_MSG_VAR_FREE(msg);
149       return NULL;
150     }
151   }
152   API_MSG_VAR_FREE(msg);
153   return conn;
154 }
155 
156 /**
157  * @ingroup netconn_common
158  * Close a netconn 'connection' and free its resources.
159  * UDP and RAW connection are completely closed, TCP pcbs might still be in a waitstate
160  * after this returns.
161  *
162  * @param conn the netconn to delete
163  * @return ERR_OK if the connection was deleted
164  */
165 err_t
166 netconn_delete(struct netconn *conn)
167 {
168   err_t err;
169   API_MSG_VAR_DECLARE(msg);
170 
171   /* No ASSERT here because possible to get a (conn == NULL) if we got an accept error */
172   if (conn == NULL) {
173     return ERR_OK;
174   }
175 
176   API_MSG_VAR_ALLOC(msg);
177   API_MSG_VAR_REF(msg).conn = conn;
178 #if LWIP_SO_SNDTIMEO || LWIP_SO_LINGER
179   /* get the time we started, which is later compared to
180      sys_now() + conn->send_timeout */
181   API_MSG_VAR_REF(msg).msg.sd.time_started = sys_now();
182 #else /* LWIP_SO_SNDTIMEO || LWIP_SO_LINGER */
183 #if LWIP_TCP
184   API_MSG_VAR_REF(msg).msg.sd.polls_left =
185     ((LWIP_TCP_CLOSE_TIMEOUT_MS_DEFAULT + TCP_SLOW_INTERVAL - 1) / TCP_SLOW_INTERVAL) + 1;
186 #endif /* LWIP_TCP */
187 #endif /* LWIP_SO_SNDTIMEO || LWIP_SO_LINGER */
188   err = netconn_apimsg(lwip_netconn_do_delconn, &API_MSG_VAR_REF(msg));
189   API_MSG_VAR_FREE(msg);
190 
191   if (err != ERR_OK) {
192     return err;
193   }
194 
195   netconn_free(conn);
196 
197   return ERR_OK;
198 }
199 
200 /**
201  * Get the local or remote IP address and port of a netconn.
202  * For RAW netconns, this returns the protocol instead of a port!
203  *
204  * @param conn the netconn to query
205  * @param addr a pointer to which to save the IP address
206  * @param port a pointer to which to save the port (or protocol for RAW)
207  * @param local 1 to get the local IP address, 0 to get the remote one
208  * @return ERR_CONN for invalid connections
209  *         ERR_OK if the information was retrieved
210  */
211 err_t
212 netconn_getaddr(struct netconn *conn, ip_addr_t *addr, u16_t *port, u8_t local)
213 {
214   API_MSG_VAR_DECLARE(msg);
215   err_t err;
216 
217   LWIP_ERROR("netconn_getaddr: invalid conn", (conn != NULL), return ERR_ARG;);
218   LWIP_ERROR("netconn_getaddr: invalid addr", (addr != NULL), return ERR_ARG;);
219   LWIP_ERROR("netconn_getaddr: invalid port", (port != NULL), return ERR_ARG;);
220 
221   API_MSG_VAR_ALLOC(msg);
222   API_MSG_VAR_REF(msg).conn = conn;
223   API_MSG_VAR_REF(msg).msg.ad.local = local;
224 #if LWIP_MPU_COMPATIBLE
225   err = netconn_apimsg(lwip_netconn_do_getaddr, &API_MSG_VAR_REF(msg));
226   *addr = msg->msg.ad.ipaddr;
227   *port = msg->msg.ad.port;
228 #else /* LWIP_MPU_COMPATIBLE */
229   msg.msg.ad.ipaddr = addr;
230   msg.msg.ad.port = port;
231   err = netconn_apimsg(lwip_netconn_do_getaddr, &msg);
232 #endif /* LWIP_MPU_COMPATIBLE */
233   API_MSG_VAR_FREE(msg);
234 
235   return err;
236 }
237 
238 /**
239  * @ingroup netconn_common
240  * Bind a netconn to a specific local IP address and port.
241  * Binding one netconn twice might not always be checked correctly!
242  *
243  * @param conn the netconn to bind
244  * @param addr the local IP address to bind the netconn to
245  *             (use IP4_ADDR_ANY/IP6_ADDR_ANY to bind to all addresses)
246  * @param port the local port to bind the netconn to (not used for RAW)
247  * @return ERR_OK if bound, any other err_t on failure
248  */
249 err_t
250 netconn_bind(struct netconn *conn, const ip_addr_t *addr, u16_t port)
251 {
252   API_MSG_VAR_DECLARE(msg);
253   err_t err;
254 
255   LWIP_ERROR("netconn_bind: invalid conn", (conn != NULL), return ERR_ARG;);
256 
257 #if LWIP_IPV4
258   /* Don't propagate NULL pointer (IP_ADDR_ANY alias) to subsequent functions */
259   if (addr == NULL) {
260     addr = IP4_ADDR_ANY;
261   }
262 #endif /* LWIP_IPV4 */
263 
264 #if LWIP_IPV4 && LWIP_IPV6
265   /* "Socket API like" dual-stack support: If IP to bind to is IP6_ADDR_ANY,
266    * and NETCONN_FLAG_IPV6_V6ONLY is 0, use IP_ANY_TYPE to bind
267    */
268   if ((netconn_get_ipv6only(conn) == 0) &&
269      ip_addr_cmp(addr, IP6_ADDR_ANY)) {
270     addr = IP_ANY_TYPE;
271   }
272 #endif /* LWIP_IPV4 && LWIP_IPV6 */
273 
274   API_MSG_VAR_ALLOC(msg);
275   API_MSG_VAR_REF(msg).conn = conn;
276   API_MSG_VAR_REF(msg).msg.bc.ipaddr = API_MSG_VAR_REF(addr);
277   API_MSG_VAR_REF(msg).msg.bc.port = port;
278   err = netconn_apimsg(lwip_netconn_do_bind, &API_MSG_VAR_REF(msg));
279   API_MSG_VAR_FREE(msg);
280 
281   return err;
282 }
283 
284 /**
285  * @ingroup netconn_common
286  * Connect a netconn to a specific remote IP address and port.
287  *
288  * @param conn the netconn to connect
289  * @param addr the remote IP address to connect to
290  * @param port the remote port to connect to (no used for RAW)
291  * @return ERR_OK if connected, return value of tcp_/udp_/raw_connect otherwise
292  */
293 err_t
294 netconn_connect(struct netconn *conn, const ip_addr_t *addr, u16_t port)
295 {
296   API_MSG_VAR_DECLARE(msg);
297   err_t err;
298 
299   LWIP_ERROR("netconn_connect: invalid conn", (conn != NULL), return ERR_ARG;);
300 
301 #if LWIP_IPV4
302   /* Don't propagate NULL pointer (IP_ADDR_ANY alias) to subsequent functions */
303   if (addr == NULL) {
304     addr = IP4_ADDR_ANY;
305   }
306 #endif /* LWIP_IPV4 */
307 
308   API_MSG_VAR_ALLOC(msg);
309   API_MSG_VAR_REF(msg).conn = conn;
310   API_MSG_VAR_REF(msg).msg.bc.ipaddr = API_MSG_VAR_REF(addr);
311   API_MSG_VAR_REF(msg).msg.bc.port = port;
312   err = netconn_apimsg(lwip_netconn_do_connect, &API_MSG_VAR_REF(msg));
313   API_MSG_VAR_FREE(msg);
314 
315   return err;
316 }
317 
318 /**
319  * @ingroup netconn_udp
320  * Disconnect a netconn from its current peer (only valid for UDP netconns).
321  *
322  * @param conn the netconn to disconnect
323  * @return See @ref err_t
324  */
325 err_t
326 netconn_disconnect(struct netconn *conn)
327 {
328   API_MSG_VAR_DECLARE(msg);
329   err_t err;
330 
331   LWIP_ERROR("netconn_disconnect: invalid conn", (conn != NULL), return ERR_ARG;);
332 
333   API_MSG_VAR_ALLOC(msg);
334   API_MSG_VAR_REF(msg).conn = conn;
335   err = netconn_apimsg(lwip_netconn_do_disconnect, &API_MSG_VAR_REF(msg));
336   API_MSG_VAR_FREE(msg);
337 
338   return err;
339 }
340 
341 /**
342  * @ingroup netconn_tcp
343  * Set a TCP netconn into listen mode
344  *
345  * @param conn the tcp netconn to set to listen mode
346  * @param backlog the listen backlog, only used if TCP_LISTEN_BACKLOG==1
347  * @return ERR_OK if the netconn was set to listen (UDP and RAW netconns
348  *         don't return any error (yet?))
349  */
350 err_t
351 netconn_listen_with_backlog(struct netconn *conn, u8_t backlog)
352 {
353 #if LWIP_TCP
354   API_MSG_VAR_DECLARE(msg);
355   err_t err;
356 
357   /* This does no harm. If TCP_LISTEN_BACKLOG is off, backlog is unused. */
358   LWIP_UNUSED_ARG(backlog);
359 
360   LWIP_ERROR("netconn_listen: invalid conn", (conn != NULL), return ERR_ARG;);
361 
362   API_MSG_VAR_ALLOC(msg);
363   API_MSG_VAR_REF(msg).conn = conn;
364 #if TCP_LISTEN_BACKLOG
365   API_MSG_VAR_REF(msg).msg.lb.backlog = backlog;
366 #endif /* TCP_LISTEN_BACKLOG */
367   err = netconn_apimsg(lwip_netconn_do_listen, &API_MSG_VAR_REF(msg));
368   API_MSG_VAR_FREE(msg);
369 
370   return err;
371 #else /* LWIP_TCP */
372   LWIP_UNUSED_ARG(conn);
373   LWIP_UNUSED_ARG(backlog);
374   return ERR_ARG;
375 #endif /* LWIP_TCP */
376 }
377 
378 /**
379  * @ingroup netconn_tcp
380  * Accept a new connection on a TCP listening netconn.
381  *
382  * @param conn the TCP listen netconn
383  * @param new_conn pointer where the new connection is stored
384  * @return ERR_OK if a new connection has been received or an error
385  *                code otherwise
386  */
387 err_t
388 netconn_accept(struct netconn *conn, struct netconn **new_conn)
389 {
390 #if LWIP_TCP
391   void *accept_ptr;
392   struct netconn *newconn;
393 #if TCP_LISTEN_BACKLOG
394   API_MSG_VAR_DECLARE(msg);
395 #endif /* TCP_LISTEN_BACKLOG */
396 
397   LWIP_ERROR("netconn_accept: invalid pointer",    (new_conn != NULL),                  return ERR_ARG;);
398   *new_conn = NULL;
399   LWIP_ERROR("netconn_accept: invalid conn",       (conn != NULL),                      return ERR_ARG;);
400 
401   if (ERR_IS_FATAL(conn->last_err)) {
402     /* don't recv on fatal errors: this might block the application task
403        waiting on acceptmbox forever! */
404     return conn->last_err;
405   }
406   if (!sys_mbox_valid(&conn->acceptmbox)) {
407     return ERR_CLSD;
408   }
409 
410 #if TCP_LISTEN_BACKLOG
411   API_MSG_VAR_ALLOC(msg);
412 #endif /* TCP_LISTEN_BACKLOG */
413 
414 #if LWIP_SO_RCVTIMEO
415   if (sys_arch_mbox_fetch(&conn->acceptmbox, &accept_ptr, conn->recv_timeout) == SYS_ARCH_TIMEOUT) {
416 #if TCP_LISTEN_BACKLOG
417     API_MSG_VAR_FREE(msg);
418 #endif /* TCP_LISTEN_BACKLOG */
419     return ERR_TIMEOUT;
420   }
421 #else
422   sys_arch_mbox_fetch(&conn->acceptmbox, &accept_ptr, 0);
423 #endif /* LWIP_SO_RCVTIMEO*/
424   newconn = (struct netconn *)accept_ptr;
425   /* Register event with callback */
426   API_EVENT(conn, NETCONN_EVT_RCVMINUS, 0);
427 
428   if (accept_ptr == &netconn_aborted) {
429     /* a connection has been aborted: out of pcbs or out of netconns during accept */
430     /* @todo: set netconn error, but this would be fatal and thus block further accepts */
431 #if TCP_LISTEN_BACKLOG
432     API_MSG_VAR_FREE(msg);
433 #endif /* TCP_LISTEN_BACKLOG */
434     return ERR_ABRT;
435   }
436   if (newconn == NULL) {
437     /* connection has been aborted */
438     /* in this special case, we set the netconn error from application thread, as
439        on a ready-to-accept listening netconn, there should not be anything running
440        in tcpip_thread */
441     NETCONN_SET_SAFE_ERR(conn, ERR_CLSD);
442 #if TCP_LISTEN_BACKLOG
443     API_MSG_VAR_FREE(msg);
444 #endif /* TCP_LISTEN_BACKLOG */
445     return ERR_CLSD;
446   }
447 #if TCP_LISTEN_BACKLOG
448   /* Let the stack know that we have accepted the connection. */
449   API_MSG_VAR_REF(msg).conn = newconn;
450   /* don't care for the return value of lwip_netconn_do_recv */
451   netconn_apimsg(lwip_netconn_do_accepted, &API_MSG_VAR_REF(msg));
452   API_MSG_VAR_FREE(msg);
453 #endif /* TCP_LISTEN_BACKLOG */
454 
455   *new_conn = newconn;
456   /* don't set conn->last_err: it's only ERR_OK, anyway */
457   return ERR_OK;
458 #else /* LWIP_TCP */
459   LWIP_UNUSED_ARG(conn);
460   LWIP_UNUSED_ARG(new_conn);
461   return ERR_ARG;
462 #endif /* LWIP_TCP */
463 }
464 
465 /**
466  * @ingroup netconn_common
467  * Receive data: actual implementation that doesn't care whether pbuf or netbuf
468  * is received
469  *
470  * @param conn the netconn from which to receive data
471  * @param new_buf pointer where a new pbuf/netbuf is stored when received data
472  * @return ERR_OK if data has been received, an error code otherwise (timeout,
473  *                memory error or another error)
474  */
475 static err_t
476 netconn_recv_data(struct netconn *conn, void **new_buf)
477 {
478   void *buf = NULL;
479   u16_t len;
480 #if LWIP_TCP
481   API_MSG_VAR_DECLARE(msg);
482 #if LWIP_MPU_COMPATIBLE
483   msg = NULL;
484 #endif
485 #endif /* LWIP_TCP */
486 
487   LWIP_ERROR("netconn_recv: invalid pointer", (new_buf != NULL), return ERR_ARG;);
488   *new_buf = NULL;
489   LWIP_ERROR("netconn_recv: invalid conn",    (conn != NULL),    return ERR_ARG;);
490 #if LWIP_TCP
491 #if (LWIP_UDP || LWIP_RAW)
492   if (NETCONNTYPE_GROUP(conn->type) == NETCONN_TCP)
493 #endif /* (LWIP_UDP || LWIP_RAW) */
494   {
495     if (!sys_mbox_valid(&conn->recvmbox)) {
496       /* This happens when calling this function after receiving FIN */
497       return sys_mbox_valid(&conn->acceptmbox) ? ERR_CONN : ERR_CLSD;
498     }
499   }
500 #endif /* LWIP_TCP */
501   LWIP_ERROR("netconn_recv: invalid recvmbox", sys_mbox_valid(&conn->recvmbox), return ERR_CONN;);
502 
503   if (ERR_IS_FATAL(conn->last_err)) {
504     /* don't recv on fatal errors: this might block the application task
505        waiting on recvmbox forever! */
506     /* @todo: this does not allow us to fetch data that has been put into recvmbox
507        before the fatal error occurred - is that a problem? */
508     return conn->last_err;
509   }
510 #if LWIP_TCP
511 #if (LWIP_UDP || LWIP_RAW)
512   if (NETCONNTYPE_GROUP(conn->type) == NETCONN_TCP)
513 #endif /* (LWIP_UDP || LWIP_RAW) */
514   {
515     API_MSG_VAR_ALLOC(msg);
516   }
517 #endif /* LWIP_TCP */
518 
519 #if LWIP_SO_RCVTIMEO
520   if (sys_arch_mbox_fetch(&conn->recvmbox, &buf, conn->recv_timeout) == SYS_ARCH_TIMEOUT) {
521 #if LWIP_TCP
522 #if (LWIP_UDP || LWIP_RAW)
523     if (NETCONNTYPE_GROUP(conn->type) == NETCONN_TCP)
524 #endif /* (LWIP_UDP || LWIP_RAW) */
525     {
526       API_MSG_VAR_FREE(msg);
527     }
528 #endif /* LWIP_TCP */
529     return ERR_TIMEOUT;
530   }
531 #else
532   sys_arch_mbox_fetch(&conn->recvmbox, &buf, 0);
533 #endif /* LWIP_SO_RCVTIMEO*/
534 
535 #if LWIP_TCP
536 #if (LWIP_UDP || LWIP_RAW)
537   if (NETCONNTYPE_GROUP(conn->type) == NETCONN_TCP)
538 #endif /* (LWIP_UDP || LWIP_RAW) */
539   {
540     /* Let the stack know that we have taken the data. */
541     /* @todo: Speedup: Don't block and wait for the answer here
542        (to prevent multiple thread-switches). */
543     API_MSG_VAR_REF(msg).conn = conn;
544     if (buf != NULL) {
545       API_MSG_VAR_REF(msg).msg.r.len = ((struct pbuf *)buf)->tot_len;
546     } else {
547       API_MSG_VAR_REF(msg).msg.r.len = 1;
548     }
549 
550     /* don't care for the return value of lwip_netconn_do_recv */
551     netconn_apimsg(lwip_netconn_do_recv, &API_MSG_VAR_REF(msg));
552     API_MSG_VAR_FREE(msg);
553 
554     /* If we are closed, we indicate that we no longer wish to use the socket */
555     if (buf == NULL) {
556       API_EVENT(conn, NETCONN_EVT_RCVMINUS, 0);
557       if (conn->pcb.ip == NULL) {
558         /* race condition: RST during recv */
559         return conn->last_err == ERR_OK ? ERR_RST : conn->last_err;
560       }
561       /* RX side is closed, so deallocate the recvmbox */
562       netconn_close_shutdown(conn, NETCONN_SHUT_RD);
563       /* Don' store ERR_CLSD as conn->err since we are only half-closed */
564       return ERR_CLSD;
565     }
566     len = ((struct pbuf *)buf)->tot_len;
567   }
568 #endif /* LWIP_TCP */
569 #if LWIP_TCP && (LWIP_UDP || LWIP_RAW)
570   else
571 #endif /* LWIP_TCP && (LWIP_UDP || LWIP_RAW) */
572 #if (LWIP_UDP || LWIP_RAW)
573   {
574     LWIP_ASSERT("buf != NULL", buf != NULL);
575     len = netbuf_len((struct netbuf*)buf);
576   }
577 #endif /* (LWIP_UDP || LWIP_RAW) */
578 
579 #if LWIP_SO_RCVBUF
580   SYS_ARCH_DEC(conn->recv_avail, len);
581 #endif /* LWIP_SO_RCVBUF */
582   /* Register event with callback */
583   API_EVENT(conn, NETCONN_EVT_RCVMINUS, len);
584 
585   LWIP_DEBUGF(API_LIB_DEBUG, ("netconn_recv_data: received %p, len=%"U16_F"\n", buf, len));
586 
587   *new_buf = buf;
588   /* don't set conn->last_err: it's only ERR_OK, anyway */
589   return ERR_OK;
590 }
591 
592 /**
593  * @ingroup netconn_tcp
594  * Receive data (in form of a pbuf) from a TCP netconn
595  *
596  * @param conn the netconn from which to receive data
597  * @param new_buf pointer where a new pbuf is stored when received data
598  * @return ERR_OK if data has been received, an error code otherwise (timeout,
599  *                memory error or another error)
600  *         ERR_ARG if conn is not a TCP netconn
601  */
602 err_t
603 netconn_recv_tcp_pbuf(struct netconn *conn, struct pbuf **new_buf)
604 {
605   LWIP_ERROR("netconn_recv: invalid conn", (conn != NULL) &&
606              NETCONNTYPE_GROUP(netconn_type(conn)) == NETCONN_TCP, return ERR_ARG;);
607 
608   return netconn_recv_data(conn, (void **)new_buf);
609 }
610 
611 /**
612  * @ingroup netconn_common
613  * Receive data (in form of a netbuf containing a packet buffer) from a netconn
614  *
615  * @param conn the netconn from which to receive data
616  * @param new_buf pointer where a new netbuf is stored when received data
617  * @return ERR_OK if data has been received, an error code otherwise (timeout,
618  *                memory error or another error)
619  */
620 err_t
621 netconn_recv(struct netconn *conn, struct netbuf **new_buf)
622 {
623 #if LWIP_TCP
624   struct netbuf *buf = NULL;
625   err_t err;
626 #endif /* LWIP_TCP */
627 
628   LWIP_ERROR("netconn_recv: invalid pointer", (new_buf != NULL), return ERR_ARG;);
629   *new_buf = NULL;
630   LWIP_ERROR("netconn_recv: invalid conn",    (conn != NULL),    return ERR_ARG;);
631 
632 #if LWIP_TCP
633 #if (LWIP_UDP || LWIP_RAW)
634   if (NETCONNTYPE_GROUP(conn->type) == NETCONN_TCP)
635 #endif /* (LWIP_UDP || LWIP_RAW) */
636   {
637     struct pbuf *p = NULL;
638     /* This is not a listening netconn, since recvmbox is set */
639 
640     buf = (struct netbuf *)memp_malloc(MEMP_NETBUF);
641     if (buf == NULL) {
642       return ERR_MEM;
643     }
644 
645     err = netconn_recv_data(conn, (void **)&p);
646     if (err != ERR_OK) {
647       memp_free(MEMP_NETBUF, buf);
648       return err;
649     }
650     LWIP_ASSERT("p != NULL", p != NULL);
651 
652     buf->p = p;
653     buf->ptr = p;
654     buf->port = 0;
655     ip_addr_set_zero(&buf->addr);
656     *new_buf = buf;
657     /* don't set conn->last_err: it's only ERR_OK, anyway */
658     return ERR_OK;
659   }
660 #endif /* LWIP_TCP */
661 #if LWIP_TCP && (LWIP_UDP || LWIP_RAW)
662   else
663 #endif /* LWIP_TCP && (LWIP_UDP || LWIP_RAW) */
664   {
665 #if (LWIP_UDP || LWIP_RAW)
666     return netconn_recv_data(conn, (void **)new_buf);
667 #endif /* (LWIP_UDP || LWIP_RAW) */
668   }
669 }
670 
671 /**
672  * @ingroup netconn_udp
673  * Send data (in form of a netbuf) to a specific remote IP address and port.
674  * Only to be used for UDP and RAW netconns (not TCP).
675  *
676  * @param conn the netconn over which to send data
677  * @param buf a netbuf containing the data to send
678  * @param addr the remote IP address to which to send the data
679  * @param port the remote port to which to send the data
680  * @return ERR_OK if data was sent, any other err_t on error
681  */
682 err_t
683 netconn_sendto(struct netconn *conn, struct netbuf *buf, const ip_addr_t *addr, u16_t port)
684 {
685   if (buf != NULL) {
686     ip_addr_set(&buf->addr, addr);
687     buf->port = port;
688     return netconn_send(conn, buf);
689   }
690   return ERR_VAL;
691 }
692 
693 /**
694  * @ingroup netconn_udp
695  * Send data over a UDP or RAW netconn (that is already connected).
696  *
697  * @param conn the UDP or RAW netconn over which to send data
698  * @param buf a netbuf containing the data to send
699  * @return ERR_OK if data was sent, any other err_t on error
700  */
701 err_t
702 netconn_send(struct netconn *conn, struct netbuf *buf)
703 {
704   API_MSG_VAR_DECLARE(msg);
705   err_t err;
706 
707   LWIP_ERROR("netconn_send: invalid conn",  (conn != NULL), return ERR_ARG;);
708 
709   LWIP_DEBUGF(API_LIB_DEBUG, ("netconn_send: sending %"U16_F" bytes\n", buf->p->tot_len));
710 
711   API_MSG_VAR_ALLOC(msg);
712   API_MSG_VAR_REF(msg).conn = conn;
713   API_MSG_VAR_REF(msg).msg.b = buf;
714   err = netconn_apimsg(lwip_netconn_do_send, &API_MSG_VAR_REF(msg));
715   API_MSG_VAR_FREE(msg);
716 
717   return err;
718 }
719 
720 /**
721  * @ingroup netconn_tcp
722  * Send data over a TCP netconn.
723  *
724  * @param conn the TCP netconn over which to send data
725  * @param dataptr pointer to the application buffer that contains the data to send
726  * @param size size of the application data to send
727  * @param apiflags combination of following flags :
728  * - NETCONN_COPY: data will be copied into memory belonging to the stack
729  * - NETCONN_MORE: for TCP connection, PSH flag will be set on last segment sent
730  * - NETCONN_DONTBLOCK: only write the data if all data can be written at once
731  * @param bytes_written pointer to a location that receives the number of written bytes
732  * @return ERR_OK if data was sent, any other err_t on error
733  */
734 err_t
735 netconn_write_partly(struct netconn *conn, const void *dataptr, size_t size,
736                      u8_t apiflags, size_t *bytes_written)
737 {
738   API_MSG_VAR_DECLARE(msg);
739   err_t err;
740   u8_t dontblock;
741 
742   LWIP_ERROR("netconn_write: invalid conn",  (conn != NULL), return ERR_ARG;);
743   LWIP_ERROR("netconn_write: invalid conn->type",  (NETCONNTYPE_GROUP(conn->type)== NETCONN_TCP), return ERR_VAL;);
744   if (size == 0) {
745     return ERR_OK;
746   }
747   dontblock = netconn_is_nonblocking(conn) || (apiflags & NETCONN_DONTBLOCK);
748 #if LWIP_SO_SNDTIMEO
749   if (conn->send_timeout != 0) {
750     dontblock = 1;
751   }
752 #endif /* LWIP_SO_SNDTIMEO */
753   if (dontblock && !bytes_written) {
754     /* This implies netconn_write() cannot be used for non-blocking send, since
755        it has no way to return the number of bytes written. */
756     return ERR_VAL;
757   }
758 
759   API_MSG_VAR_ALLOC(msg);
760   /* non-blocking write sends as much  */
761   API_MSG_VAR_REF(msg).conn = conn;
762   API_MSG_VAR_REF(msg).msg.w.dataptr = dataptr;
763   API_MSG_VAR_REF(msg).msg.w.apiflags = apiflags;
764   API_MSG_VAR_REF(msg).msg.w.len = size;
765 #if LWIP_SO_SNDTIMEO
766   if (conn->send_timeout != 0) {
767     /* get the time we started, which is later compared to
768         sys_now() + conn->send_timeout */
769     API_MSG_VAR_REF(msg).msg.w.time_started = sys_now();
770   } else {
771     API_MSG_VAR_REF(msg).msg.w.time_started = 0;
772   }
773 #endif /* LWIP_SO_SNDTIMEO */
774 
775   /* For locking the core: this _can_ be delayed on low memory/low send buffer,
776      but if it is, this is done inside api_msg.c:do_write(), so we can use the
777      non-blocking version here. */
778   err = netconn_apimsg(lwip_netconn_do_write, &API_MSG_VAR_REF(msg));
779   if ((err == ERR_OK) && (bytes_written != NULL)) {
780     if (dontblock) {
781       /* nonblocking write: maybe the data has been sent partly */
782       *bytes_written = API_MSG_VAR_REF(msg).msg.w.len;
783     } else {
784       /* blocking call succeeded: all data has been sent if it */
785       *bytes_written = size;
786     }
787   }
788   API_MSG_VAR_FREE(msg);
789 
790   return err;
791 }
792 
793 /**
794  * @ingroup netconn_tcp
795  * Close or shutdown a TCP netconn (doesn't delete it).
796  *
797  * @param conn the TCP netconn to close or shutdown
798  * @param how fully close or only shutdown one side?
799  * @return ERR_OK if the netconn was closed, any other err_t on error
800  */
801 static err_t
802 netconn_close_shutdown(struct netconn *conn, u8_t how)
803 {
804   API_MSG_VAR_DECLARE(msg);
805   err_t err;
806   LWIP_UNUSED_ARG(how);
807 
808   LWIP_ERROR("netconn_close: invalid conn",  (conn != NULL), return ERR_ARG;);
809 
810   API_MSG_VAR_ALLOC(msg);
811   API_MSG_VAR_REF(msg).conn = conn;
812 #if LWIP_TCP
813   /* shutting down both ends is the same as closing */
814   API_MSG_VAR_REF(msg).msg.sd.shut = how;
815 #if LWIP_SO_SNDTIMEO || LWIP_SO_LINGER
816   /* get the time we started, which is later compared to
817      sys_now() + conn->send_timeout */
818   API_MSG_VAR_REF(msg).msg.sd.time_started = sys_now();
819 #else /* LWIP_SO_SNDTIMEO || LWIP_SO_LINGER */
820   API_MSG_VAR_REF(msg).msg.sd.polls_left =
821     ((LWIP_TCP_CLOSE_TIMEOUT_MS_DEFAULT + TCP_SLOW_INTERVAL - 1) / TCP_SLOW_INTERVAL) + 1;
822 #endif /* LWIP_SO_SNDTIMEO || LWIP_SO_LINGER */
823 #endif /* LWIP_TCP */
824   err = netconn_apimsg(lwip_netconn_do_close, &API_MSG_VAR_REF(msg));
825   API_MSG_VAR_FREE(msg);
826 
827   return err;
828 }
829 
830 /**
831  * @ingroup netconn_tcp
832  * Close a TCP netconn (doesn't delete it).
833  *
834  * @param conn the TCP netconn to close
835  * @return ERR_OK if the netconn was closed, any other err_t on error
836  */
837 err_t
838 netconn_close(struct netconn *conn)
839 {
840   /* shutting down both ends is the same as closing */
841   return netconn_close_shutdown(conn, NETCONN_SHUT_RDWR);
842 }
843 
844 /**
845  * @ingroup netconn_tcp
846  * Shut down one or both sides of a TCP netconn (doesn't delete it).
847  *
848  * @param conn the TCP netconn to shut down
849  * @param shut_rx shut down the RX side (no more read possible after this)
850  * @param shut_tx shut down the TX side (no more write possible after this)
851  * @return ERR_OK if the netconn was closed, any other err_t on error
852  */
853 err_t
854 netconn_shutdown(struct netconn *conn, u8_t shut_rx, u8_t shut_tx)
855 {
856   return netconn_close_shutdown(conn, (shut_rx ? NETCONN_SHUT_RD : 0) | (shut_tx ? NETCONN_SHUT_WR : 0));
857 }
858 
859 #if LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD)
860 /**
861  * @ingroup netconn_udp
862  * Join multicast groups for UDP netconns.
863  *
864  * @param conn the UDP netconn for which to change multicast addresses
865  * @param multiaddr IP address of the multicast group to join or leave
866  * @param netif_addr the IP address of the network interface on which to send
867  *                  the igmp message
868  * @param join_or_leave flag whether to send a join- or leave-message
869  * @return ERR_OK if the action was taken, any err_t on error
870  */
871 err_t
872 netconn_join_leave_group(struct netconn *conn,
873                          const ip_addr_t *multiaddr,
874                          const ip_addr_t *netif_addr,
875                          enum netconn_igmp join_or_leave)
876 {
877   API_MSG_VAR_DECLARE(msg);
878   err_t err;
879 
880   LWIP_ERROR("netconn_join_leave_group: invalid conn",  (conn != NULL), return ERR_ARG;);
881 
882   API_MSG_VAR_ALLOC(msg);
883 
884 #if LWIP_IPV4
885   /* Don't propagate NULL pointer (IP_ADDR_ANY alias) to subsequent functions */
886   if (multiaddr == NULL) {
887     multiaddr = IP4_ADDR_ANY;
888   }
889   if (netif_addr == NULL) {
890     netif_addr = IP4_ADDR_ANY;
891   }
892 #endif /* LWIP_IPV4 */
893 
894   API_MSG_VAR_REF(msg).conn = conn;
895   API_MSG_VAR_REF(msg).msg.jl.multiaddr = API_MSG_VAR_REF(multiaddr);
896   API_MSG_VAR_REF(msg).msg.jl.netif_addr = API_MSG_VAR_REF(netif_addr);
897   API_MSG_VAR_REF(msg).msg.jl.join_or_leave = join_or_leave;
898   err = netconn_apimsg(lwip_netconn_do_join_leave_group, &API_MSG_VAR_REF(msg));
899   API_MSG_VAR_FREE(msg);
900 
901   return err;
902 }
903 #endif /* LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD) */
904 
905 #if LWIP_DNS
906 /**
907  * @ingroup netconn_common
908  * Execute a DNS query, only one IP address is returned
909  *
910  * @param name a string representation of the DNS host name to query
911  * @param addr a preallocated ip_addr_t where to store the resolved IP address
912  * @param dns_addrtype IP address type (IPv4 / IPv6)
913  * @return ERR_OK: resolving succeeded
914  *         ERR_MEM: memory error, try again later
915  *         ERR_ARG: dns client not initialized or invalid hostname
916  *         ERR_VAL: dns server response was invalid
917  */
918 #if LWIP_IPV4 && LWIP_IPV6
919 err_t
920 netconn_gethostbyname_addrtype(const char *name, ip_addr_t *addr, u8_t dns_addrtype)
921 #else
922 err_t
923 netconn_gethostbyname(const char *name, ip_addr_t *addr)
924 #endif
925 {
926   API_VAR_DECLARE(struct dns_api_msg, msg);
927 #if !LWIP_MPU_COMPATIBLE
928   sys_sem_t sem;
929 #endif /* LWIP_MPU_COMPATIBLE */
930   err_t err;
931   err_t cberr;
932 
933   LWIP_ERROR("netconn_gethostbyname: invalid name", (name != NULL), return ERR_ARG;);
934   LWIP_ERROR("netconn_gethostbyname: invalid addr", (addr != NULL), return ERR_ARG;);
935 #if LWIP_MPU_COMPATIBLE
936   if (strlen(name) >= DNS_MAX_NAME_LENGTH) {
937     return ERR_ARG;
938   }
939 #endif
940 
941   API_VAR_ALLOC(struct dns_api_msg, MEMP_DNS_API_MSG, msg, ERR_MEM);
942 #if LWIP_MPU_COMPATIBLE
943   strncpy(API_VAR_REF(msg).name, name, DNS_MAX_NAME_LENGTH-1);
944   API_VAR_REF(msg).name[DNS_MAX_NAME_LENGTH-1] = 0;
945 #else /* LWIP_MPU_COMPATIBLE */
946   msg.err = &err;
947   msg.sem = &sem;
948   API_VAR_REF(msg).addr = API_VAR_REF(addr);
949   API_VAR_REF(msg).name = name;
950 #endif /* LWIP_MPU_COMPATIBLE */
951 #if LWIP_IPV4 && LWIP_IPV6
952   API_VAR_REF(msg).dns_addrtype = dns_addrtype;
953 #endif /* LWIP_IPV4 && LWIP_IPV6 */
954 #if LWIP_NETCONN_SEM_PER_THREAD
955   API_VAR_REF(msg).sem = LWIP_NETCONN_THREAD_SEM_GET();
956 #else /* LWIP_NETCONN_SEM_PER_THREAD*/
957   err = sys_sem_new(API_EXPR_REF(API_VAR_REF(msg).sem), 0);
958   if (err != ERR_OK) {
959     API_VAR_FREE(MEMP_DNS_API_MSG, msg);
960     return err;
961   }
962 #endif /* LWIP_NETCONN_SEM_PER_THREAD */
963 
964   cberr = tcpip_callback(lwip_netconn_do_gethostbyname, &API_VAR_REF(msg));
965   if (cberr != ERR_OK) {
966 #if !LWIP_NETCONN_SEM_PER_THREAD
967     sys_sem_free(API_EXPR_REF(API_VAR_REF(msg).sem));
968 #endif /* !LWIP_NETCONN_SEM_PER_THREAD */
969     API_VAR_FREE(MEMP_DNS_API_MSG, msg);
970     return cberr;
971   }
972   sys_sem_wait(API_EXPR_REF_SEM(API_VAR_REF(msg).sem));
973 #if !LWIP_NETCONN_SEM_PER_THREAD
974   sys_sem_free(API_EXPR_REF(API_VAR_REF(msg).sem));
975 #endif /* !LWIP_NETCONN_SEM_PER_THREAD */
976 
977 #if LWIP_MPU_COMPATIBLE
978   *addr = msg->addr;
979   err = msg->err;
980 #endif /* LWIP_MPU_COMPATIBLE */
981 
982   API_VAR_FREE(MEMP_DNS_API_MSG, msg);
983   return err;
984 }
985 #endif /* LWIP_DNS*/
986 
987 #if LWIP_NETCONN_SEM_PER_THREAD
988 void
989 netconn_thread_init(void)
990 {
991   sys_sem_t *sem = LWIP_NETCONN_THREAD_SEM_GET();
992   if ((sem == NULL) || !sys_sem_valid(sem)) {
993     /* call alloc only once */
994     LWIP_NETCONN_THREAD_SEM_ALLOC();
995     LWIP_ASSERT("LWIP_NETCONN_THREAD_SEM_ALLOC() failed", sys_sem_valid(LWIP_NETCONN_THREAD_SEM_GET()));
996   }
997 }
998 
999 void
1000 netconn_thread_cleanup(void)
1001 {
1002   sys_sem_t *sem = LWIP_NETCONN_THREAD_SEM_GET();
1003   if ((sem != NULL) && sys_sem_valid(sem)) {
1004     /* call free only once */
1005     LWIP_NETCONN_THREAD_SEM_FREE();
1006   }
1007 }
1008 #endif /* LWIP_NETCONN_SEM_PER_THREAD */
1009 
1010 #endif /* LWIP_NETCONN */
1011