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