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