1 /**
2 * @file
3 * Sequential API Internal module
4 *
5 */
6
7 /*
8 * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
9 * All rights reserved.
10 *
11 * Redistribution and use in source and binary forms, with or without modification,
12 * are permitted provided that the following conditions are met:
13 *
14 * 1. Redistributions of source code must retain the above copyright notice,
15 * this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright notice,
17 * this list of conditions and the following disclaimer in the documentation
18 * and/or other materials provided with the distribution.
19 * 3. The name of the author may not be used to endorse or promote products
20 * derived from this software without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
23 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
24 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
25 * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
26 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
27 * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
30 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
31 * OF SUCH DAMAGE.
32 *
33 * This file is part of the lwIP TCP/IP stack.
34 *
35 * Author: Adam Dunkels <adam@sics.se>
36 *
37 */
38
39 #include "lwip/opt.h"
40
41 #if LWIP_NETCONN /* don't build if not configured for use in lwipopts.h */
42
43 #include "lwip/priv/api_msg.h"
44
45 #include "lwip/ip.h"
46 #include "lwip/ip_addr.h"
47 #include "lwip/udp.h"
48 #include "lwip/tcp.h"
49 #include "lwip/raw.h"
50
51 #include "lwip/memp.h"
52 #include "lwip/igmp.h"
53 #include "lwip/dns.h"
54 #include "lwip/mld6.h"
55 #include "lwip/priv/tcpip_priv.h"
56
57 #include <string.h>
58
59 /* netconns are polled once per second (e.g. continue write on memory error) */
60 #define NETCONN_TCP_POLL_INTERVAL 2
61
62 #define SET_NONBLOCKING_CONNECT(conn, val) do { if (val) { \
63 netconn_set_flags(conn, NETCONN_FLAG_IN_NONBLOCKING_CONNECT); \
64 } else { \
65 netconn_clear_flags(conn, NETCONN_FLAG_IN_NONBLOCKING_CONNECT); }} while(0)
66 #define IN_NONBLOCKING_CONNECT(conn) netconn_is_flag_set(conn, NETCONN_FLAG_IN_NONBLOCKING_CONNECT)
67
68 #if LWIP_NETCONN_FULLDUPLEX
69 #define NETCONN_MBOX_VALID(conn, mbox) (sys_mbox_valid(mbox) && ((conn->flags & NETCONN_FLAG_MBOXINVALID) == 0))
70 #else
71 #define NETCONN_MBOX_VALID(conn, mbox) sys_mbox_valid(mbox)
72 #endif
73
74 /* forward declarations */
75 #if LWIP_TCP
76 #if LWIP_TCPIP_CORE_LOCKING
77 #define WRITE_DELAYED , 1
78 #define WRITE_DELAYED_PARAM , u8_t delayed
79 #else /* LWIP_TCPIP_CORE_LOCKING */
80 #define WRITE_DELAYED
81 #define WRITE_DELAYED_PARAM
82 #endif /* LWIP_TCPIP_CORE_LOCKING */
83 static err_t lwip_netconn_do_writemore(struct netconn *conn WRITE_DELAYED_PARAM);
84 static err_t lwip_netconn_do_close_internal(struct netconn *conn WRITE_DELAYED_PARAM);
85 #endif
86
87 static void netconn_drain(struct netconn *conn);
88
89 #if LWIP_TCPIP_CORE_LOCKING
90 #define TCPIP_APIMSG_ACK(m)
91 #else /* LWIP_TCPIP_CORE_LOCKING */
92 #define TCPIP_APIMSG_ACK(m) do { sys_sem_signal(LWIP_API_MSG_SEM(m)); } while(0)
93 #endif /* LWIP_TCPIP_CORE_LOCKING */
94
95 #if LWIP_NETCONN_FULLDUPLEX
96 const u8_t netconn_deleted = 0;
97
98 int
lwip_netconn_is_deallocated_msg(void * msg)99 lwip_netconn_is_deallocated_msg(void *msg)
100 {
101 if (msg == &netconn_deleted) {
102 return 1;
103 }
104 return 0;
105 }
106 #endif /* LWIP_NETCONN_FULLDUPLEX */
107
108 #if LWIP_TCP
109 const u8_t netconn_aborted = 0;
110 const u8_t netconn_reset = 0;
111 const u8_t netconn_closed = 0;
112
113 /** Translate an error to a unique void* passed via an mbox */
114 static void *
lwip_netconn_err_to_msg(err_t err)115 lwip_netconn_err_to_msg(err_t err)
116 {
117 switch (err) {
118 case ERR_ABRT:
119 return LWIP_CONST_CAST(void *, &netconn_aborted);
120 case ERR_RST:
121 return LWIP_CONST_CAST(void *, &netconn_reset);
122 case ERR_CLSD:
123 return LWIP_CONST_CAST(void *, &netconn_closed);
124 default:
125 LWIP_ASSERT("unhandled error", err == ERR_OK);
126 return NULL;
127 }
128 }
129
130 int
lwip_netconn_is_err_msg(void * msg,err_t * err)131 lwip_netconn_is_err_msg(void *msg, err_t *err)
132 {
133 LWIP_ASSERT("err != NULL", err != NULL);
134
135 if (msg == &netconn_aborted) {
136 *err = ERR_ABRT;
137 return 1;
138 } else if (msg == &netconn_reset) {
139 *err = ERR_RST;
140 return 1;
141 } else if (msg == &netconn_closed) {
142 *err = ERR_CLSD;
143 return 1;
144 }
145 return 0;
146 }
147 #endif /* LWIP_TCP */
148
149
150 #if LWIP_RAW
151 /**
152 * Receive callback function for RAW netconns.
153 * Doesn't 'eat' the packet, only copies it and sends it to
154 * conn->recvmbox
155 *
156 * @see raw.h (struct raw_pcb.recv) for parameters and return value
157 */
158 static u8_t
recv_raw(void * arg,struct raw_pcb * pcb,struct pbuf * p,const ip_addr_t * addr)159 recv_raw(void *arg, struct raw_pcb *pcb, struct pbuf *p,
160 const ip_addr_t *addr)
161 {
162 struct pbuf *q;
163 struct netbuf *buf;
164 struct netconn *conn;
165
166 LWIP_UNUSED_ARG(addr);
167 conn = (struct netconn *)arg;
168
169 if ((conn != NULL) && NETCONN_MBOX_VALID(conn, &conn->recvmbox)) {
170 #if LWIP_SO_RCVBUF
171 int recv_avail;
172 SYS_ARCH_GET(conn->recv_avail, recv_avail);
173 if ((recv_avail + (int)(p->tot_len)) > conn->recv_bufsize) {
174 return 0;
175 }
176 #endif /* LWIP_SO_RCVBUF */
177 /* copy the whole packet into new pbufs */
178 q = pbuf_clone(PBUF_RAW, PBUF_RAM, p);
179 if (q != NULL) {
180 u16_t len;
181 buf = (struct netbuf *)memp_malloc(MEMP_NETBUF);
182 if (buf == NULL) {
183 pbuf_free(q);
184 return 0;
185 }
186
187 buf->p = q;
188 buf->ptr = q;
189 ip_addr_copy(buf->addr, *ip_current_src_addr());
190 buf->port = pcb->protocol;
191
192 len = q->tot_len;
193 if (sys_mbox_trypost(&conn->recvmbox, buf) != ERR_OK) {
194 netbuf_delete(buf);
195 return 0;
196 } else {
197 #if LWIP_SO_RCVBUF
198 SYS_ARCH_INC(conn->recv_avail, len);
199 #endif /* LWIP_SO_RCVBUF */
200 /* Register event with callback */
201 API_EVENT(conn, NETCONN_EVT_RCVPLUS, len);
202 }
203 }
204 }
205
206 return 0; /* do not eat the packet */
207 }
208 #endif /* LWIP_RAW*/
209
210 #if LWIP_UDP
211 /**
212 * Receive callback function for UDP netconns.
213 * Posts the packet to conn->recvmbox or deletes it on memory error.
214 *
215 * @see udp.h (struct udp_pcb.recv) for parameters
216 */
217 static void
recv_udp(void * arg,struct udp_pcb * pcb,struct pbuf * p,const ip_addr_t * addr,u16_t port)218 recv_udp(void *arg, struct udp_pcb *pcb, struct pbuf *p,
219 const ip_addr_t *addr, u16_t port)
220 {
221 struct netbuf *buf;
222 struct netconn *conn;
223 u16_t len;
224 #if LWIP_SO_RCVBUF
225 int recv_avail;
226 #endif /* LWIP_SO_RCVBUF */
227
228 LWIP_UNUSED_ARG(pcb); /* only used for asserts... */
229 LWIP_ASSERT("recv_udp must have a pcb argument", pcb != NULL);
230 LWIP_ASSERT("recv_udp must have an argument", arg != NULL);
231 conn = (struct netconn *)arg;
232
233 if (conn == NULL) {
234 pbuf_free(p);
235 return;
236 }
237
238 LWIP_ASSERT("recv_udp: recv for wrong pcb!", conn->pcb.udp == pcb);
239
240 #if LWIP_SO_RCVBUF
241 SYS_ARCH_GET(conn->recv_avail, recv_avail);
242 if (!NETCONN_MBOX_VALID(conn, &conn->recvmbox) ||
243 ((recv_avail + (int)(p->tot_len)) > conn->recv_bufsize)) {
244 #else /* LWIP_SO_RCVBUF */
245 if (!NETCONN_MBOX_VALID(conn, &conn->recvmbox)) {
246 #endif /* LWIP_SO_RCVBUF */
247 pbuf_free(p);
248 return;
249 }
250
251 buf = (struct netbuf *)memp_malloc(MEMP_NETBUF);
252 if (buf == NULL) {
253 pbuf_free(p);
254 return;
255 } else {
256 buf->p = p;
257 buf->ptr = p;
258 ip_addr_set(&buf->addr, addr);
259 buf->port = port;
260 #if LWIP_NETBUF_RECVINFO
261 if (conn->flags & NETCONN_FLAG_PKTINFO) {
262 /* get the UDP header - always in the first pbuf, ensured by udp_input */
263 const struct udp_hdr *udphdr = (const struct udp_hdr *)ip_next_header_ptr();
264 buf->flags = NETBUF_FLAG_DESTADDR;
265 ip_addr_set(&buf->toaddr, ip_current_dest_addr());
266 buf->toport_chksum = udphdr->dest;
267 }
268 #endif /* LWIP_NETBUF_RECVINFO */
269 }
270
271 len = p->tot_len;
272 if (sys_mbox_trypost(&conn->recvmbox, buf) != ERR_OK) {
273 netbuf_delete(buf);
274 return;
275 } else {
276 #if LWIP_SO_RCVBUF
277 SYS_ARCH_INC(conn->recv_avail, len);
278 #endif /* LWIP_SO_RCVBUF */
279 /* Register event with callback */
280 API_EVENT(conn, NETCONN_EVT_RCVPLUS, len);
281 }
282 }
283 #endif /* LWIP_UDP */
284
285 #if LWIP_TCP
286 /**
287 * Receive callback function for TCP netconns.
288 * Posts the packet to conn->recvmbox, but doesn't delete it on errors.
289 *
290 * @see tcp.h (struct tcp_pcb.recv) for parameters and return value
291 */
292 static err_t
293 recv_tcp(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err)
294 {
295 struct netconn *conn;
296 u16_t len;
297 void *msg;
298
299 LWIP_UNUSED_ARG(pcb);
300 LWIP_ASSERT("recv_tcp must have a pcb argument", pcb != NULL);
301 LWIP_ASSERT("recv_tcp must have an argument", arg != NULL);
302 LWIP_ASSERT("err != ERR_OK unhandled", err == ERR_OK);
303 LWIP_UNUSED_ARG(err); /* for LWIP_NOASSERT */
304 conn = (struct netconn *)arg;
305
306 if (conn == NULL) {
307 return ERR_VAL;
308 }
309 LWIP_ASSERT("recv_tcp: recv for wrong pcb!", conn->pcb.tcp == pcb);
310
311 if (!NETCONN_MBOX_VALID(conn, &conn->recvmbox)) {
312 /* recvmbox already deleted */
313 if (p != NULL) {
314 tcp_recved(pcb, p->tot_len);
315 pbuf_free(p);
316 }
317 return ERR_OK;
318 }
319 /* Unlike for UDP or RAW pcbs, don't check for available space
320 using recv_avail since that could break the connection
321 (data is already ACKed) */
322
323 if (p != NULL) {
324 msg = p;
325 len = p->tot_len;
326 } else {
327 msg = LWIP_CONST_CAST(void *, &netconn_closed);
328 len = 0;
329 }
330
331 if (sys_mbox_trypost(&conn->recvmbox, msg) != ERR_OK) {
332 /* don't deallocate p: it is presented to us later again from tcp_fasttmr! */
333 return ERR_MEM;
334 } else {
335 #if LWIP_SO_RCVBUF
336 SYS_ARCH_INC(conn->recv_avail, len);
337 #endif /* LWIP_SO_RCVBUF */
338 /* Register event with callback */
339 API_EVENT(conn, NETCONN_EVT_RCVPLUS, len);
340 }
341
342 return ERR_OK;
343 }
344
345 /**
346 * Poll callback function for TCP netconns.
347 * Wakes up an application thread that waits for a connection to close
348 * or data to be sent. The application thread then takes the
349 * appropriate action to go on.
350 *
351 * Signals the conn->sem.
352 * netconn_close waits for conn->sem if closing failed.
353 *
354 * @see tcp.h (struct tcp_pcb.poll) for parameters and return value
355 */
356 static err_t
357 poll_tcp(void *arg, struct tcp_pcb *pcb)
358 {
359 struct netconn *conn = (struct netconn *)arg;
360
361 LWIP_UNUSED_ARG(pcb);
362 LWIP_ASSERT("conn != NULL", (conn != NULL));
363
364 if (conn->state == NETCONN_WRITE) {
365 lwip_netconn_do_writemore(conn WRITE_DELAYED);
366 } else if (conn->state == NETCONN_CLOSE) {
367 #if !LWIP_SO_SNDTIMEO && !LWIP_SO_LINGER
368 if (conn->current_msg && conn->current_msg->msg.sd.polls_left) {
369 conn->current_msg->msg.sd.polls_left--;
370 }
371 #endif /* !LWIP_SO_SNDTIMEO && !LWIP_SO_LINGER */
372 lwip_netconn_do_close_internal(conn WRITE_DELAYED);
373 }
374 /* @todo: implement connect timeout here? */
375
376 /* Did a nonblocking write fail before? Then check available write-space. */
377 if (conn->flags & NETCONN_FLAG_CHECK_WRITESPACE) {
378 /* If the queued byte- or pbuf-count drops below the configured low-water limit,
379 let select mark this pcb as writable again. */
380 if ((conn->pcb.tcp != NULL) && (tcp_sndbuf(conn->pcb.tcp) > TCP_SNDLOWAT) &&
381 (tcp_sndqueuelen(conn->pcb.tcp) < TCP_SNDQUEUELOWAT)) {
382 netconn_clear_flags(conn, NETCONN_FLAG_CHECK_WRITESPACE);
383 API_EVENT(conn, NETCONN_EVT_SENDPLUS, 0);
384 }
385 }
386
387 return ERR_OK;
388 }
389
390 /**
391 * Sent callback function for TCP netconns.
392 * Signals the conn->sem and calls API_EVENT.
393 * netconn_write waits for conn->sem if send buffer is low.
394 *
395 * @see tcp.h (struct tcp_pcb.sent) for parameters and return value
396 */
397 static err_t
398 sent_tcp(void *arg, struct tcp_pcb *pcb, u16_t len)
399 {
400 struct netconn *conn = (struct netconn *)arg;
401
402 LWIP_UNUSED_ARG(pcb);
403 LWIP_ASSERT("conn != NULL", (conn != NULL));
404
405 if (conn) {
406 if (conn->state == NETCONN_WRITE) {
407 lwip_netconn_do_writemore(conn WRITE_DELAYED);
408 } else if (conn->state == NETCONN_CLOSE) {
409 lwip_netconn_do_close_internal(conn WRITE_DELAYED);
410 }
411
412 /* If the queued byte- or pbuf-count drops below the configured low-water limit,
413 let select mark this pcb as writable again. */
414 if ((conn->pcb.tcp != NULL) && (tcp_sndbuf(conn->pcb.tcp) > TCP_SNDLOWAT) &&
415 (tcp_sndqueuelen(conn->pcb.tcp) < TCP_SNDQUEUELOWAT)) {
416 netconn_clear_flags(conn, NETCONN_FLAG_CHECK_WRITESPACE);
417 API_EVENT(conn, NETCONN_EVT_SENDPLUS, len);
418 }
419 }
420
421 return ERR_OK;
422 }
423
424 /**
425 * Error callback function for TCP netconns.
426 * Signals conn->sem, posts to all conn mboxes and calls API_EVENT.
427 * The application thread has then to decide what to do.
428 *
429 * @see tcp.h (struct tcp_pcb.err) for parameters
430 */
431 static void
432 err_tcp(void *arg, err_t err)
433 {
434 struct netconn *conn;
435 enum netconn_state old_state;
436 void *mbox_msg;
437 SYS_ARCH_DECL_PROTECT(lev);
438
439 conn = (struct netconn *)arg;
440 LWIP_ASSERT("conn != NULL", (conn != NULL));
441
442 SYS_ARCH_PROTECT(lev);
443
444 /* when err is called, the pcb is deallocated, so delete the reference */
445 conn->pcb.tcp = NULL;
446 /* store pending error */
447 conn->pending_err = err;
448 /* prevent application threads from blocking on 'recvmbox'/'acceptmbox' */
449 conn->flags |= NETCONN_FLAG_MBOXCLOSED;
450
451 /* reset conn->state now before waking up other threads */
452 old_state = conn->state;
453 conn->state = NETCONN_NONE;
454
455 SYS_ARCH_UNPROTECT(lev);
456
457 /* Notify the user layer about a connection error. Used to signal select. */
458 API_EVENT(conn, NETCONN_EVT_ERROR, 0);
459 /* Try to release selects pending on 'read' or 'write', too.
460 They will get an error if they actually try to read or write. */
461 API_EVENT(conn, NETCONN_EVT_RCVPLUS, 0);
462 API_EVENT(conn, NETCONN_EVT_SENDPLUS, 0);
463
464 mbox_msg = lwip_netconn_err_to_msg(err);
465 /* pass error message to recvmbox to wake up pending recv */
466 if (NETCONN_MBOX_VALID(conn, &conn->recvmbox)) {
467 /* use trypost to prevent deadlock */
468 sys_mbox_trypost(&conn->recvmbox, mbox_msg);
469 }
470 /* pass error message to acceptmbox to wake up pending accept */
471 if (NETCONN_MBOX_VALID(conn, &conn->acceptmbox)) {
472 /* use trypost to preven deadlock */
473 sys_mbox_trypost(&conn->acceptmbox, mbox_msg);
474 }
475
476 if ((old_state == NETCONN_WRITE) || (old_state == NETCONN_CLOSE) ||
477 (old_state == NETCONN_CONNECT)) {
478 /* calling lwip_netconn_do_writemore/lwip_netconn_do_close_internal is not necessary
479 since the pcb has already been deleted! */
480 int was_nonblocking_connect = IN_NONBLOCKING_CONNECT(conn);
481 SET_NONBLOCKING_CONNECT(conn, 0);
482
483 if (!was_nonblocking_connect) {
484 sys_sem_t *op_completed_sem;
485 /* set error return code */
486 LWIP_ASSERT("conn->current_msg != NULL", conn->current_msg != NULL);
487 if (old_state == NETCONN_CLOSE) {
488 /* let close succeed: the connection is closed after all... */
489 conn->current_msg->err = ERR_OK;
490 } else {
491 /* Write and connect fail */
492 conn->current_msg->err = err;
493 }
494 op_completed_sem = LWIP_API_MSG_SEM(conn->current_msg);
495 LWIP_ASSERT("inavlid op_completed_sem", sys_sem_valid(op_completed_sem));
496 conn->current_msg = NULL;
497 /* wake up the waiting task */
498 sys_sem_signal(op_completed_sem);
499 } else {
500 /* @todo: test what happens for error on nonblocking connect */
501 }
502 } else {
503 LWIP_ASSERT("conn->current_msg == NULL", conn->current_msg == NULL);
504 }
505 }
506
507 /**
508 * Setup a tcp_pcb with the correct callback function pointers
509 * and their arguments.
510 *
511 * @param conn the TCP netconn to setup
512 */
513 static void
514 setup_tcp(struct netconn *conn)
515 {
516 struct tcp_pcb *pcb;
517
518 pcb = conn->pcb.tcp;
519 tcp_arg(pcb, conn);
520 tcp_recv(pcb, recv_tcp);
521 tcp_sent(pcb, sent_tcp);
522 tcp_poll(pcb, poll_tcp, NETCONN_TCP_POLL_INTERVAL);
523 tcp_err(pcb, err_tcp);
524 }
525
526 /**
527 * Accept callback function for TCP netconns.
528 * Allocates a new netconn and posts that to conn->acceptmbox.
529 *
530 * @see tcp.h (struct tcp_pcb_listen.accept) for parameters and return value
531 */
532 static err_t
533 accept_function(void *arg, struct tcp_pcb *newpcb, err_t err)
534 {
535 struct netconn *newconn;
536 struct netconn *conn = (struct netconn *)arg;
537
538 if (conn == NULL) {
539 return ERR_VAL;
540 }
541 if (!NETCONN_MBOX_VALID(conn, &conn->acceptmbox)) {
542 LWIP_DEBUGF(API_MSG_DEBUG, ("accept_function: acceptmbox already deleted\n"));
543 return ERR_VAL;
544 }
545
546 if (newpcb == NULL) {
547 /* out-of-pcbs during connect: pass on this error to the application */
548 if (sys_mbox_trypost(&conn->acceptmbox, lwip_netconn_err_to_msg(ERR_ABRT)) == ERR_OK) {
549 /* Register event with callback */
550 API_EVENT(conn, NETCONN_EVT_RCVPLUS, 0);
551 }
552 return ERR_VAL;
553 }
554 LWIP_ASSERT("expect newpcb == NULL or err == ERR_OK", err == ERR_OK);
555 LWIP_UNUSED_ARG(err); /* for LWIP_NOASSERT */
556
557 LWIP_DEBUGF(API_MSG_DEBUG, ("accept_function: newpcb->state: %s\n", tcp_debug_state_str(newpcb->state)));
558
559 /* We have to set the callback here even though
560 * the new socket is unknown. newconn->socket is marked as -1. */
561 newconn = netconn_alloc(conn->type, conn->callback);
562 if (newconn == NULL) {
563 /* outof netconns: pass on this error to the application */
564 if (sys_mbox_trypost(&conn->acceptmbox, lwip_netconn_err_to_msg(ERR_ABRT)) == ERR_OK) {
565 /* Register event with callback */
566 API_EVENT(conn, NETCONN_EVT_RCVPLUS, 0);
567 }
568 return ERR_MEM;
569 }
570 newconn->pcb.tcp = newpcb;
571 setup_tcp(newconn);
572
573 /* handle backlog counter */
574 tcp_backlog_delayed(newpcb);
575
576 if (sys_mbox_trypost(&conn->acceptmbox, newconn) != ERR_OK) {
577 /* When returning != ERR_OK, the pcb is aborted in tcp_process(),
578 so do nothing here! */
579 /* remove all references to this netconn from the pcb */
580 struct tcp_pcb *pcb = newconn->pcb.tcp;
581 tcp_arg(pcb, NULL);
582 tcp_recv(pcb, NULL);
583 tcp_sent(pcb, NULL);
584 tcp_poll(pcb, NULL, 0);
585 tcp_err(pcb, NULL);
586 /* remove reference from to the pcb from this netconn */
587 newconn->pcb.tcp = NULL;
588 /* no need to drain since we know the recvmbox is empty. */
589 sys_mbox_free(&newconn->recvmbox);
590 sys_mbox_set_invalid(&newconn->recvmbox);
591 netconn_free(newconn);
592 return ERR_MEM;
593 } else {
594 /* Register event with callback */
595 API_EVENT(conn, NETCONN_EVT_RCVPLUS, 0);
596 }
597
598 return ERR_OK;
599 }
600 #endif /* LWIP_TCP */
601
602 /**
603 * Create a new pcb of a specific type.
604 * Called from lwip_netconn_do_newconn().
605 *
606 * @param msg the api_msg describing the connection type
607 */
608 static void
609 pcb_new(struct api_msg *msg)
610 {
611 enum lwip_ip_addr_type iptype = IPADDR_TYPE_V4;
612
613 LWIP_ASSERT("pcb_new: pcb already allocated", msg->conn->pcb.tcp == NULL);
614
615 #if LWIP_IPV6 && LWIP_IPV4
616 /* IPv6: Dual-stack by default, unless netconn_set_ipv6only() is called */
617 if (NETCONNTYPE_ISIPV6(netconn_type(msg->conn))) {
618 iptype = IPADDR_TYPE_ANY;
619 }
620 #endif
621
622 /* Allocate a PCB for this connection */
623 switch (NETCONNTYPE_GROUP(msg->conn->type)) {
624 #if LWIP_RAW
625 case NETCONN_RAW:
626 msg->conn->pcb.raw = raw_new_ip_type(iptype, msg->msg.n.proto);
627 if (msg->conn->pcb.raw != NULL) {
628 #if LWIP_IPV6
629 /* ICMPv6 packets should always have checksum calculated by the stack as per RFC 3542 chapter 3.1 */
630 if (NETCONNTYPE_ISIPV6(msg->conn->type) && msg->conn->pcb.raw->protocol == IP6_NEXTH_ICMP6) {
631 msg->conn->pcb.raw->chksum_reqd = 1;
632 msg->conn->pcb.raw->chksum_offset = 2;
633 }
634 #endif /* LWIP_IPV6 */
635 raw_recv(msg->conn->pcb.raw, recv_raw, msg->conn);
636 }
637 break;
638 #endif /* LWIP_RAW */
639 #if LWIP_UDP
640 case NETCONN_UDP:
641 msg->conn->pcb.udp = udp_new_ip_type(iptype);
642 if (msg->conn->pcb.udp != NULL) {
643 #if LWIP_UDPLITE
644 if (NETCONNTYPE_ISUDPLITE(msg->conn->type)) {
645 udp_setflags(msg->conn->pcb.udp, UDP_FLAGS_UDPLITE);
646 }
647 #endif /* LWIP_UDPLITE */
648 if (NETCONNTYPE_ISUDPNOCHKSUM(msg->conn->type)) {
649 udp_setflags(msg->conn->pcb.udp, UDP_FLAGS_NOCHKSUM);
650 }
651 udp_recv(msg->conn->pcb.udp, recv_udp, msg->conn);
652 }
653 break;
654 #endif /* LWIP_UDP */
655 #if LWIP_TCP
656 case NETCONN_TCP:
657 msg->conn->pcb.tcp = tcp_new_ip_type(iptype);
658 if (msg->conn->pcb.tcp != NULL) {
659 setup_tcp(msg->conn);
660 }
661 break;
662 #endif /* LWIP_TCP */
663 default:
664 /* Unsupported netconn type, e.g. protocol disabled */
665 msg->err = ERR_VAL;
666 return;
667 }
668 if (msg->conn->pcb.ip == NULL) {
669 msg->err = ERR_MEM;
670 }
671 }
672
673 /**
674 * Create a new pcb of a specific type inside a netconn.
675 * Called from netconn_new_with_proto_and_callback.
676 *
677 * @param m the api_msg describing the connection type
678 */
679 void
680 lwip_netconn_do_newconn(void *m)
681 {
682 struct api_msg *msg = (struct api_msg *)m;
683
684 msg->err = ERR_OK;
685 if (msg->conn->pcb.tcp == NULL) {
686 pcb_new(msg);
687 }
688 /* Else? This "new" connection already has a PCB allocated. */
689 /* Is this an error condition? Should it be deleted? */
690 /* We currently just are happy and return. */
691
692 TCPIP_APIMSG_ACK(msg);
693 }
694
695 /**
696 * Create a new netconn (of a specific type) that has a callback function.
697 * The corresponding pcb is NOT created!
698 *
699 * @param t the type of 'connection' to create (@see enum netconn_type)
700 * @param callback a function to call on status changes (RX available, TX'ed)
701 * @return a newly allocated struct netconn or
702 * NULL on memory error
703 */
704 struct netconn *
705 netconn_alloc(enum netconn_type t, netconn_callback callback)
706 {
707 struct netconn *conn;
708 int size;
709 u8_t init_flags = 0;
710
711 conn = (struct netconn *)memp_malloc(MEMP_NETCONN);
712 if (conn == NULL) {
713 return NULL;
714 }
715
716 conn->pending_err = ERR_OK;
717 conn->type = t;
718 conn->pcb.tcp = NULL;
719 #if LWIP_NETCONN_FULLDUPLEX
720 conn->mbox_threads_waiting = 0;
721 #endif
722
723 /* If all sizes are the same, every compiler should optimize this switch to nothing */
724 switch (NETCONNTYPE_GROUP(t)) {
725 #if LWIP_RAW
726 case NETCONN_RAW:
727 size = DEFAULT_RAW_RECVMBOX_SIZE;
728 break;
729 #endif /* LWIP_RAW */
730 #if LWIP_UDP
731 case NETCONN_UDP:
732 size = DEFAULT_UDP_RECVMBOX_SIZE;
733 #if LWIP_NETBUF_RECVINFO
734 init_flags |= NETCONN_FLAG_PKTINFO;
735 #endif /* LWIP_NETBUF_RECVINFO */
736 break;
737 #endif /* LWIP_UDP */
738 #if LWIP_TCP
739 case NETCONN_TCP:
740 size = DEFAULT_TCP_RECVMBOX_SIZE;
741 break;
742 #endif /* LWIP_TCP */
743 default:
744 LWIP_ASSERT("netconn_alloc: undefined netconn_type", 0);
745 goto free_and_return;
746 }
747
748 if (sys_mbox_new(&conn->recvmbox, size) != ERR_OK) {
749 goto free_and_return;
750 }
751 #if !LWIP_NETCONN_SEM_PER_THREAD
752 if (sys_sem_new(&conn->op_completed, 0) != ERR_OK) {
753 sys_mbox_free(&conn->recvmbox);
754 goto free_and_return;
755 }
756 #endif
757
758 #if LWIP_TCP
759 sys_mbox_set_invalid(&conn->acceptmbox);
760 #endif
761 conn->state = NETCONN_NONE;
762 #if LWIP_SOCKET
763 /* initialize socket to -1 since 0 is a valid socket */
764 conn->socket = -1;
765 #endif /* LWIP_SOCKET */
766 conn->callback = callback;
767 #if LWIP_TCP
768 conn->current_msg = NULL;
769 #endif /* LWIP_TCP */
770 #if LWIP_SO_SNDTIMEO
771 conn->send_timeout = 0;
772 #endif /* LWIP_SO_SNDTIMEO */
773 #if LWIP_SO_RCVTIMEO
774 conn->recv_timeout = 0;
775 #endif /* LWIP_SO_RCVTIMEO */
776 #if LWIP_SO_RCVBUF
777 conn->recv_bufsize = RECV_BUFSIZE_DEFAULT;
778 conn->recv_avail = 0;
779 #endif /* LWIP_SO_RCVBUF */
780 #if LWIP_SO_LINGER
781 conn->linger = -1;
782 #endif /* LWIP_SO_LINGER */
783 conn->flags = init_flags;
784 return conn;
785 free_and_return:
786 memp_free(MEMP_NETCONN, conn);
787 return NULL;
788 }
789
790 /**
791 * Delete a netconn and all its resources.
792 * The pcb is NOT freed (since we might not be in the right thread context do this).
793 *
794 * @param conn the netconn to free
795 */
796 void
797 netconn_free(struct netconn *conn)
798 {
799 LWIP_ASSERT("PCB must be deallocated outside this function", conn->pcb.tcp == NULL);
800
801 #if LWIP_NETCONN_FULLDUPLEX
802 /* in fullduplex, netconn is drained here */
803 netconn_drain(conn);
804 #endif /* LWIP_NETCONN_FULLDUPLEX */
805
806 LWIP_ASSERT("recvmbox must be deallocated before calling this function",
807 !sys_mbox_valid(&conn->recvmbox));
808 #if LWIP_TCP
809 LWIP_ASSERT("acceptmbox must be deallocated before calling this function",
810 !sys_mbox_valid(&conn->acceptmbox));
811 #endif /* LWIP_TCP */
812
813 #if !LWIP_NETCONN_SEM_PER_THREAD
814 sys_sem_free(&conn->op_completed);
815 sys_sem_set_invalid(&conn->op_completed);
816 #endif
817
818 memp_free(MEMP_NETCONN, conn);
819 }
820
821 /**
822 * Delete rcvmbox and acceptmbox of a netconn and free the left-over data in
823 * these mboxes
824 *
825 * @param conn the netconn to free
826 * @bytes_drained bytes drained from recvmbox
827 * @accepts_drained pending connections drained from acceptmbox
828 */
829 static void
830 netconn_drain(struct netconn *conn)
831 {
832 void *mem;
833
834 /* This runs when mbox and netconn are marked as closed,
835 so we don't need to lock against rx packets */
836 #if LWIP_NETCONN_FULLDUPLEX
837 LWIP_ASSERT("netconn marked closed", conn->flags & NETCONN_FLAG_MBOXINVALID);
838 #endif /* LWIP_NETCONN_FULLDUPLEX */
839
840 /* Delete and drain the recvmbox. */
841 if (sys_mbox_valid(&conn->recvmbox)) {
842 while (sys_mbox_tryfetch(&conn->recvmbox, &mem) != SYS_MBOX_EMPTY) {
843 #if LWIP_NETCONN_FULLDUPLEX
844 if (!lwip_netconn_is_deallocated_msg(mem))
845 #endif /* LWIP_NETCONN_FULLDUPLEX */
846 {
847 #if LWIP_TCP
848 if (NETCONNTYPE_GROUP(conn->type) == NETCONN_TCP) {
849 err_t err;
850 if (!lwip_netconn_is_err_msg(mem, &err)) {
851 pbuf_free((struct pbuf *)mem);
852 }
853 } else
854 #endif /* LWIP_TCP */
855 {
856 netbuf_delete((struct netbuf *)mem);
857 }
858 }
859 }
860 sys_mbox_free(&conn->recvmbox);
861 sys_mbox_set_invalid(&conn->recvmbox);
862 }
863
864 /* Delete and drain the acceptmbox. */
865 #if LWIP_TCP
866 if (sys_mbox_valid(&conn->acceptmbox)) {
867 while (sys_mbox_tryfetch(&conn->acceptmbox, &mem) != SYS_MBOX_EMPTY) {
868 #if LWIP_NETCONN_FULLDUPLEX
869 if (!lwip_netconn_is_deallocated_msg(mem))
870 #endif /* LWIP_NETCONN_FULLDUPLEX */
871 {
872 err_t err;
873 if (!lwip_netconn_is_err_msg(mem, &err)) {
874 struct netconn *newconn = (struct netconn *)mem;
875 /* Only tcp pcbs have an acceptmbox, so no need to check conn->type */
876 /* pcb might be set to NULL already by err_tcp() */
877 /* drain recvmbox */
878 netconn_drain(newconn);
879 if (newconn->pcb.tcp != NULL) {
880 tcp_abort(newconn->pcb.tcp);
881 newconn->pcb.tcp = NULL;
882 }
883 netconn_free(newconn);
884 }
885 }
886 }
887 sys_mbox_free(&conn->acceptmbox);
888 sys_mbox_set_invalid(&conn->acceptmbox);
889 }
890 #endif /* LWIP_TCP */
891 }
892
893 #if LWIP_NETCONN_FULLDUPLEX
894 static void
895 netconn_mark_mbox_invalid(struct netconn *conn)
896 {
897 int i, num_waiting;
898 void *msg = LWIP_CONST_CAST(void *, &netconn_deleted);
899
900 /* Prevent new calls/threads from reading from the mbox */
901 conn->flags |= NETCONN_FLAG_MBOXINVALID;
902
903 SYS_ARCH_LOCKED(num_waiting = conn->mbox_threads_waiting);
904 for (i = 0; i < num_waiting; i++) {
905 if (sys_mbox_valid_val(conn->recvmbox)) {
906 sys_mbox_trypost(&conn->recvmbox, msg);
907 } else {
908 sys_mbox_trypost(&conn->acceptmbox, msg);
909 }
910 }
911 }
912 #endif /* LWIP_NETCONN_FULLDUPLEX */
913
914 #if LWIP_TCP
915 /**
916 * Internal helper function to close a TCP netconn: since this sometimes
917 * doesn't work at the first attempt, this function is called from multiple
918 * places.
919 *
920 * @param conn the TCP netconn to close
921 */
922 static err_t
923 lwip_netconn_do_close_internal(struct netconn *conn WRITE_DELAYED_PARAM)
924 {
925 err_t err;
926 u8_t shut, shut_rx, shut_tx, shut_close;
927 u8_t close_finished = 0;
928 struct tcp_pcb *tpcb;
929 #if LWIP_SO_LINGER
930 u8_t linger_wait_required = 0;
931 #endif /* LWIP_SO_LINGER */
932
933 LWIP_ASSERT("invalid conn", (conn != NULL));
934 LWIP_ASSERT("this is for tcp netconns only", (NETCONNTYPE_GROUP(conn->type) == NETCONN_TCP));
935 LWIP_ASSERT("conn must be in state NETCONN_CLOSE", (conn->state == NETCONN_CLOSE));
936 LWIP_ASSERT("pcb already closed", (conn->pcb.tcp != NULL));
937 LWIP_ASSERT("conn->current_msg != NULL", conn->current_msg != NULL);
938
939 tpcb = conn->pcb.tcp;
940 shut = conn->current_msg->msg.sd.shut;
941 shut_rx = shut & NETCONN_SHUT_RD;
942 shut_tx = shut & NETCONN_SHUT_WR;
943 /* shutting down both ends is the same as closing
944 (also if RD or WR side was shut down before already) */
945 if (shut == NETCONN_SHUT_RDWR) {
946 shut_close = 1;
947 } else if (shut_rx &&
948 ((tpcb->state == FIN_WAIT_1) ||
949 (tpcb->state == FIN_WAIT_2) ||
950 (tpcb->state == CLOSING))) {
951 shut_close = 1;
952 } else if (shut_tx && ((tpcb->flags & TF_RXCLOSED) != 0)) {
953 shut_close = 1;
954 } else {
955 shut_close = 0;
956 }
957
958 /* Set back some callback pointers */
959 if (shut_close) {
960 tcp_arg(tpcb, NULL);
961 }
962 if (tpcb->state == LISTEN) {
963 tcp_accept(tpcb, NULL);
964 } else {
965 /* some callbacks have to be reset if tcp_close is not successful */
966 if (shut_rx) {
967 tcp_recv(tpcb, NULL);
968 tcp_accept(tpcb, NULL);
969 }
970 if (shut_tx) {
971 tcp_sent(tpcb, NULL);
972 }
973 if (shut_close) {
974 tcp_poll(tpcb, NULL, 0);
975 tcp_err(tpcb, NULL);
976 }
977 }
978 /* Try to close the connection */
979 if (shut_close) {
980 #if LWIP_SO_LINGER
981 /* check linger possibilites before calling tcp_close */
982 err = ERR_OK;
983 /* linger enabled/required at all? (i.e. is there untransmitted data left?) */
984 if ((conn->linger >= 0) && (conn->pcb.tcp->unsent || conn->pcb.tcp->unacked)) {
985 if ((conn->linger == 0)) {
986 /* data left but linger prevents waiting */
987 tcp_abort(tpcb);
988 tpcb = NULL;
989 } else if (conn->linger > 0) {
990 /* data left and linger says we should wait */
991 if (netconn_is_nonblocking(conn)) {
992 /* data left on a nonblocking netconn -> cannot linger */
993 err = ERR_WOULDBLOCK;
994 } else if ((s32_t)(sys_now() - conn->current_msg->msg.sd.time_started) >=
995 (conn->linger * 1000)) {
996 /* data left but linger timeout has expired (this happens on further
997 calls to this function through poll_tcp */
998 tcp_abort(tpcb);
999 tpcb = NULL;
1000 } else {
1001 /* data left -> need to wait for ACK after successful close */
1002 linger_wait_required = 1;
1003 }
1004 }
1005 }
1006 if ((err == ERR_OK) && (tpcb != NULL))
1007 #endif /* LWIP_SO_LINGER */
1008 {
1009 err = tcp_close(tpcb);
1010 }
1011 } else {
1012 err = tcp_shutdown(tpcb, shut_rx, shut_tx);
1013 }
1014 if (err == ERR_OK) {
1015 close_finished = 1;
1016 #if LWIP_SO_LINGER
1017 if (linger_wait_required) {
1018 /* wait for ACK of all unsent/unacked data by just getting called again */
1019 close_finished = 0;
1020 err = ERR_INPROGRESS;
1021 }
1022 #endif /* LWIP_SO_LINGER */
1023 } else {
1024 if (err == ERR_MEM) {
1025 /* Closing failed because of memory shortage, try again later. Even for
1026 nonblocking netconns, we have to wait since no standard socket application
1027 is prepared for close failing because of resource shortage.
1028 Check the timeout: this is kind of an lwip addition to the standard sockets:
1029 we wait for some time when failing to allocate a segment for the FIN */
1030 #if LWIP_SO_SNDTIMEO || LWIP_SO_LINGER
1031 s32_t close_timeout = LWIP_TCP_CLOSE_TIMEOUT_MS_DEFAULT;
1032 #if LWIP_SO_SNDTIMEO
1033 if (conn->send_timeout > 0) {
1034 close_timeout = conn->send_timeout;
1035 }
1036 #endif /* LWIP_SO_SNDTIMEO */
1037 #if LWIP_SO_LINGER
1038 if (conn->linger >= 0) {
1039 /* use linger timeout (seconds) */
1040 close_timeout = conn->linger * 1000U;
1041 }
1042 #endif
1043 if ((s32_t)(sys_now() - conn->current_msg->msg.sd.time_started) >= close_timeout) {
1044 #else /* LWIP_SO_SNDTIMEO || LWIP_SO_LINGER */
1045 if (conn->current_msg->msg.sd.polls_left == 0) {
1046 #endif /* LWIP_SO_SNDTIMEO || LWIP_SO_LINGER */
1047 close_finished = 1;
1048 if (shut_close) {
1049 /* in this case, we want to RST the connection */
1050 tcp_abort(tpcb);
1051 err = ERR_OK;
1052 }
1053 }
1054 } else {
1055 /* Closing failed for a non-memory error: give up */
1056 close_finished = 1;
1057 }
1058 }
1059 if (close_finished) {
1060 /* Closing done (succeeded, non-memory error, nonblocking error or timeout) */
1061 sys_sem_t *op_completed_sem = LWIP_API_MSG_SEM(conn->current_msg);
1062 conn->current_msg->err = err;
1063 conn->current_msg = NULL;
1064 conn->state = NETCONN_NONE;
1065 if (err == ERR_OK) {
1066 if (shut_close) {
1067 /* Set back some callback pointers as conn is going away */
1068 conn->pcb.tcp = NULL;
1069 /* Trigger select() in socket layer. Make sure everybody notices activity
1070 on the connection, error first! */
1071 API_EVENT(conn, NETCONN_EVT_ERROR, 0);
1072 }
1073 if (shut_rx) {
1074 API_EVENT(conn, NETCONN_EVT_RCVPLUS, 0);
1075 }
1076 if (shut_tx) {
1077 API_EVENT(conn, NETCONN_EVT_SENDPLUS, 0);
1078 }
1079 }
1080 #if LWIP_TCPIP_CORE_LOCKING
1081 if (delayed)
1082 #endif
1083 {
1084 /* wake up the application task */
1085 sys_sem_signal(op_completed_sem);
1086 }
1087 return ERR_OK;
1088 }
1089 if (!close_finished) {
1090 /* Closing failed and we want to wait: restore some of the callbacks */
1091 /* Closing of listen pcb will never fail! */
1092 LWIP_ASSERT("Closing a listen pcb may not fail!", (tpcb->state != LISTEN));
1093 if (shut_tx) {
1094 tcp_sent(tpcb, sent_tcp);
1095 }
1096 /* when waiting for close, set up poll interval to 500ms */
1097 tcp_poll(tpcb, poll_tcp, 1);
1098 tcp_err(tpcb, err_tcp);
1099 tcp_arg(tpcb, conn);
1100 /* don't restore recv callback: we don't want to receive any more data */
1101 }
1102 /* If closing didn't succeed, we get called again either
1103 from poll_tcp or from sent_tcp */
1104 LWIP_ASSERT("err != ERR_OK", err != ERR_OK);
1105 return err;
1106 }
1107 #endif /* LWIP_TCP */
1108
1109 /**
1110 * Delete the pcb inside a netconn.
1111 * Called from netconn_delete.
1112 *
1113 * @param m the api_msg pointing to the connection
1114 */
1115 void
1116 lwip_netconn_do_delconn(void *m)
1117 {
1118 struct api_msg *msg = (struct api_msg *)m;
1119
1120 enum netconn_state state = msg->conn->state;
1121 LWIP_ASSERT("netconn state error", /* this only happens for TCP netconns */
1122 (state == NETCONN_NONE) || (NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_TCP));
1123 #if LWIP_NETCONN_FULLDUPLEX
1124 /* In full duplex mode, blocking write/connect is aborted with ERR_CLSD */
1125 if (state != NETCONN_NONE) {
1126 if ((state == NETCONN_WRITE) ||
1127 ((state == NETCONN_CONNECT) && !IN_NONBLOCKING_CONNECT(msg->conn))) {
1128 /* close requested, abort running write/connect */
1129 sys_sem_t *op_completed_sem;
1130 LWIP_ASSERT("msg->conn->current_msg != NULL", msg->conn->current_msg != NULL);
1131 op_completed_sem = LWIP_API_MSG_SEM(msg->conn->current_msg);
1132 msg->conn->current_msg->err = ERR_CLSD;
1133 msg->conn->current_msg = NULL;
1134 msg->conn->state = NETCONN_NONE;
1135 sys_sem_signal(op_completed_sem);
1136 }
1137 }
1138 #else /* LWIP_NETCONN_FULLDUPLEX */
1139 if (((state != NETCONN_NONE) &&
1140 (state != NETCONN_LISTEN) &&
1141 (state != NETCONN_CONNECT)) ||
1142 ((state == NETCONN_CONNECT) && !IN_NONBLOCKING_CONNECT(msg->conn))) {
1143 /* This means either a blocking write or blocking connect is running
1144 (nonblocking write returns and sets state to NONE) */
1145 msg->err = ERR_INPROGRESS;
1146 } else
1147 #endif /* LWIP_NETCONN_FULLDUPLEX */
1148 {
1149 LWIP_ASSERT("blocking connect in progress",
1150 (state != NETCONN_CONNECT) || IN_NONBLOCKING_CONNECT(msg->conn));
1151 msg->err = ERR_OK;
1152 #if LWIP_NETCONN_FULLDUPLEX
1153 /* Mark mboxes invalid */
1154 netconn_mark_mbox_invalid(msg->conn);
1155 #else /* LWIP_NETCONN_FULLDUPLEX */
1156 netconn_drain(msg->conn);
1157 #endif /* LWIP_NETCONN_FULLDUPLEX */
1158
1159 if (msg->conn->pcb.tcp != NULL) {
1160
1161 switch (NETCONNTYPE_GROUP(msg->conn->type)) {
1162 #if LWIP_RAW
1163 case NETCONN_RAW:
1164 raw_remove(msg->conn->pcb.raw);
1165 break;
1166 #endif /* LWIP_RAW */
1167 #if LWIP_UDP
1168 case NETCONN_UDP:
1169 msg->conn->pcb.udp->recv_arg = NULL;
1170 udp_remove(msg->conn->pcb.udp);
1171 break;
1172 #endif /* LWIP_UDP */
1173 #if LWIP_TCP
1174 case NETCONN_TCP:
1175 LWIP_ASSERT("already writing or closing", msg->conn->current_msg == NULL);
1176 msg->conn->state = NETCONN_CLOSE;
1177 msg->msg.sd.shut = NETCONN_SHUT_RDWR;
1178 msg->conn->current_msg = msg;
1179 #if LWIP_TCPIP_CORE_LOCKING
1180 if (lwip_netconn_do_close_internal(msg->conn, 0) != ERR_OK) {
1181 LWIP_ASSERT("state!", msg->conn->state == NETCONN_CLOSE);
1182 UNLOCK_TCPIP_CORE();
1183 sys_arch_sem_wait(LWIP_API_MSG_SEM(msg), 0);
1184 LOCK_TCPIP_CORE();
1185 LWIP_ASSERT("state!", msg->conn->state == NETCONN_NONE);
1186 }
1187 #else /* LWIP_TCPIP_CORE_LOCKING */
1188 lwip_netconn_do_close_internal(msg->conn);
1189 #endif /* LWIP_TCPIP_CORE_LOCKING */
1190 /* API_EVENT is called inside lwip_netconn_do_close_internal, before releasing
1191 the application thread, so we can return at this point! */
1192 return;
1193 #endif /* LWIP_TCP */
1194 default:
1195 break;
1196 }
1197 msg->conn->pcb.tcp = NULL;
1198 }
1199 /* tcp netconns don't come here! */
1200
1201 /* @todo: this lets select make the socket readable and writable,
1202 which is wrong! errfd instead? */
1203 API_EVENT(msg->conn, NETCONN_EVT_RCVPLUS, 0);
1204 API_EVENT(msg->conn, NETCONN_EVT_SENDPLUS, 0);
1205 }
1206 if (sys_sem_valid(LWIP_API_MSG_SEM(msg))) {
1207 TCPIP_APIMSG_ACK(msg);
1208 }
1209 }
1210
1211 /**
1212 * Bind a pcb contained in a netconn
1213 * Called from netconn_bind.
1214 *
1215 * @param m the api_msg pointing to the connection and containing
1216 * the IP address and port to bind to
1217 */
1218 void
1219 lwip_netconn_do_bind(void *m)
1220 {
1221 struct api_msg *msg = (struct api_msg *)m;
1222 err_t err;
1223
1224 if (msg->conn->pcb.tcp != NULL) {
1225 switch (NETCONNTYPE_GROUP(msg->conn->type)) {
1226 #if LWIP_RAW
1227 case NETCONN_RAW:
1228 err = raw_bind(msg->conn->pcb.raw, API_EXPR_REF(msg->msg.bc.ipaddr));
1229 break;
1230 #endif /* LWIP_RAW */
1231 #if LWIP_UDP
1232 case NETCONN_UDP:
1233 err = udp_bind(msg->conn->pcb.udp, API_EXPR_REF(msg->msg.bc.ipaddr), msg->msg.bc.port);
1234 break;
1235 #endif /* LWIP_UDP */
1236 #if LWIP_TCP
1237 case NETCONN_TCP:
1238 err = tcp_bind(msg->conn->pcb.tcp, API_EXPR_REF(msg->msg.bc.ipaddr), msg->msg.bc.port);
1239 break;
1240 #endif /* LWIP_TCP */
1241 default:
1242 err = ERR_VAL;
1243 break;
1244 }
1245 } else {
1246 err = ERR_VAL;
1247 }
1248 msg->err = err;
1249 TCPIP_APIMSG_ACK(msg);
1250 }
1251 /**
1252 * Bind a pcb contained in a netconn to an interface
1253 * Called from netconn_bind_if.
1254 *
1255 * @param m the api_msg pointing to the connection and containing
1256 * the IP address and port to bind to
1257 */
1258 void
1259 lwip_netconn_do_bind_if(void *m)
1260 {
1261 struct netif *netif;
1262 struct api_msg *msg = (struct api_msg *)m;
1263 err_t err;
1264
1265 netif = netif_get_by_index(msg->msg.bc.if_idx);
1266
1267 if ((netif != NULL) && (msg->conn->pcb.tcp != NULL)) {
1268 err = ERR_OK;
1269 switch (NETCONNTYPE_GROUP(msg->conn->type)) {
1270 #if LWIP_RAW
1271 case NETCONN_RAW:
1272 raw_bind_netif(msg->conn->pcb.raw, netif);
1273 break;
1274 #endif /* LWIP_RAW */
1275 #if LWIP_UDP
1276 case NETCONN_UDP:
1277 udp_bind_netif(msg->conn->pcb.udp, netif);
1278 break;
1279 #endif /* LWIP_UDP */
1280 #if LWIP_TCP
1281 case NETCONN_TCP:
1282 tcp_bind_netif(msg->conn->pcb.tcp, netif);
1283 break;
1284 #endif /* LWIP_TCP */
1285 default:
1286 err = ERR_VAL;
1287 break;
1288 }
1289 } else {
1290 err = ERR_VAL;
1291 }
1292 msg->err = err;
1293 TCPIP_APIMSG_ACK(msg);
1294 }
1295
1296 #if LWIP_TCP
1297 /**
1298 * TCP callback function if a connection (opened by tcp_connect/lwip_netconn_do_connect) has
1299 * been established (or reset by the remote host).
1300 *
1301 * @see tcp.h (struct tcp_pcb.connected) for parameters and return values
1302 */
1303 static err_t
1304 lwip_netconn_do_connected(void *arg, struct tcp_pcb *pcb, err_t err)
1305 {
1306 struct netconn *conn;
1307 int was_blocking;
1308 sys_sem_t *op_completed_sem = NULL;
1309
1310 LWIP_UNUSED_ARG(pcb);
1311
1312 conn = (struct netconn *)arg;
1313
1314 if (conn == NULL) {
1315 return ERR_VAL;
1316 }
1317
1318 LWIP_ASSERT("conn->state == NETCONN_CONNECT", conn->state == NETCONN_CONNECT);
1319 LWIP_ASSERT("(conn->current_msg != NULL) || conn->in_non_blocking_connect",
1320 (conn->current_msg != NULL) || IN_NONBLOCKING_CONNECT(conn));
1321
1322 if (conn->current_msg != NULL) {
1323 conn->current_msg->err = err;
1324 op_completed_sem = LWIP_API_MSG_SEM(conn->current_msg);
1325 }
1326 if ((NETCONNTYPE_GROUP(conn->type) == NETCONN_TCP) && (err == ERR_OK)) {
1327 setup_tcp(conn);
1328 }
1329 was_blocking = !IN_NONBLOCKING_CONNECT(conn);
1330 SET_NONBLOCKING_CONNECT(conn, 0);
1331 LWIP_ASSERT("blocking connect state error",
1332 (was_blocking && op_completed_sem != NULL) ||
1333 (!was_blocking && op_completed_sem == NULL));
1334 conn->current_msg = NULL;
1335 conn->state = NETCONN_NONE;
1336 API_EVENT(conn, NETCONN_EVT_SENDPLUS, 0);
1337
1338 if (was_blocking) {
1339 sys_sem_signal(op_completed_sem);
1340 }
1341 return ERR_OK;
1342 }
1343 #endif /* LWIP_TCP */
1344
1345 /**
1346 * Connect a pcb contained inside a netconn
1347 * Called from netconn_connect.
1348 *
1349 * @param m the api_msg pointing to the connection and containing
1350 * the IP address and port to connect to
1351 */
1352 void
1353 lwip_netconn_do_connect(void *m)
1354 {
1355 struct api_msg *msg = (struct api_msg *)m;
1356 err_t err;
1357
1358 if (msg->conn->pcb.tcp == NULL) {
1359 /* This may happen when calling netconn_connect() a second time */
1360 err = ERR_CLSD;
1361 } else {
1362 switch (NETCONNTYPE_GROUP(msg->conn->type)) {
1363 #if LWIP_RAW
1364 case NETCONN_RAW:
1365 err = raw_connect(msg->conn->pcb.raw, API_EXPR_REF(msg->msg.bc.ipaddr));
1366 break;
1367 #endif /* LWIP_RAW */
1368 #if LWIP_UDP
1369 case NETCONN_UDP:
1370 err = udp_connect(msg->conn->pcb.udp, API_EXPR_REF(msg->msg.bc.ipaddr), msg->msg.bc.port);
1371 break;
1372 #endif /* LWIP_UDP */
1373 #if LWIP_TCP
1374 case NETCONN_TCP:
1375 /* Prevent connect while doing any other action. */
1376 if (msg->conn->state == NETCONN_CONNECT) {
1377 err = ERR_ALREADY;
1378 } else if (msg->conn->state != NETCONN_NONE) {
1379 err = ERR_ISCONN;
1380 } else {
1381 setup_tcp(msg->conn);
1382 err = tcp_connect(msg->conn->pcb.tcp, API_EXPR_REF(msg->msg.bc.ipaddr),
1383 msg->msg.bc.port, lwip_netconn_do_connected);
1384 if (err == ERR_OK) {
1385 u8_t non_blocking = netconn_is_nonblocking(msg->conn);
1386 msg->conn->state = NETCONN_CONNECT;
1387 SET_NONBLOCKING_CONNECT(msg->conn, non_blocking);
1388 if (non_blocking) {
1389 err = ERR_INPROGRESS;
1390 } else {
1391 msg->conn->current_msg = msg;
1392 /* sys_sem_signal() is called from lwip_netconn_do_connected (or err_tcp()),
1393 when the connection is established! */
1394 #if LWIP_TCPIP_CORE_LOCKING
1395 LWIP_ASSERT("state!", msg->conn->state == NETCONN_CONNECT);
1396 UNLOCK_TCPIP_CORE();
1397 sys_arch_sem_wait(LWIP_API_MSG_SEM(msg), 0);
1398 LOCK_TCPIP_CORE();
1399 LWIP_ASSERT("state!", msg->conn->state != NETCONN_CONNECT);
1400 #endif /* LWIP_TCPIP_CORE_LOCKING */
1401 return;
1402 }
1403 }
1404 }
1405 break;
1406 #endif /* LWIP_TCP */
1407 default:
1408 LWIP_ERROR("Invalid netconn type", 0, do {
1409 err = ERR_VAL;
1410 } while (0));
1411 break;
1412 }
1413 }
1414 msg->err = err;
1415 /* For all other protocols, netconn_connect() calls netconn_apimsg(),
1416 so use TCPIP_APIMSG_ACK() here. */
1417 TCPIP_APIMSG_ACK(msg);
1418 }
1419
1420 /**
1421 * Disconnect a pcb contained inside a netconn
1422 * Only used for UDP netconns.
1423 * Called from netconn_disconnect.
1424 *
1425 * @param m the api_msg pointing to the connection to disconnect
1426 */
1427 void
1428 lwip_netconn_do_disconnect(void *m)
1429 {
1430 struct api_msg *msg = (struct api_msg *)m;
1431
1432 #if LWIP_UDP
1433 if (NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_UDP) {
1434 udp_disconnect(msg->conn->pcb.udp);
1435 msg->err = ERR_OK;
1436 } else
1437 #endif /* LWIP_UDP */
1438 {
1439 msg->err = ERR_VAL;
1440 }
1441 TCPIP_APIMSG_ACK(msg);
1442 }
1443
1444 #if LWIP_TCP
1445 /**
1446 * Set a TCP pcb contained in a netconn into listen mode
1447 * Called from netconn_listen.
1448 *
1449 * @param m the api_msg pointing to the connection
1450 */
1451 void
1452 lwip_netconn_do_listen(void *m)
1453 {
1454 struct api_msg *msg = (struct api_msg *)m;
1455 err_t err;
1456
1457 if (msg->conn->pcb.tcp != NULL) {
1458 if (NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_TCP) {
1459 if (msg->conn->state == NETCONN_NONE) {
1460 struct tcp_pcb *lpcb;
1461 if (msg->conn->pcb.tcp->state != CLOSED) {
1462 /* connection is not closed, cannot listen */
1463 err = ERR_VAL;
1464 } else {
1465 u8_t backlog;
1466 #if TCP_LISTEN_BACKLOG
1467 backlog = msg->msg.lb.backlog;
1468 #else /* TCP_LISTEN_BACKLOG */
1469 backlog = TCP_DEFAULT_LISTEN_BACKLOG;
1470 #endif /* TCP_LISTEN_BACKLOG */
1471 #if LWIP_IPV4 && LWIP_IPV6
1472 /* "Socket API like" dual-stack support: If IP to listen to is IP6_ADDR_ANY,
1473 * and NETCONN_FLAG_IPV6_V6ONLY is NOT set, use IP_ANY_TYPE to listen
1474 */
1475 if (ip_addr_cmp(&msg->conn->pcb.ip->local_ip, IP6_ADDR_ANY) &&
1476 (netconn_get_ipv6only(msg->conn) == 0)) {
1477 /* change PCB type to IPADDR_TYPE_ANY */
1478 IP_SET_TYPE_VAL(msg->conn->pcb.tcp->local_ip, IPADDR_TYPE_ANY);
1479 IP_SET_TYPE_VAL(msg->conn->pcb.tcp->remote_ip, IPADDR_TYPE_ANY);
1480 }
1481 #endif /* LWIP_IPV4 && LWIP_IPV6 */
1482
1483 lpcb = tcp_listen_with_backlog_and_err(msg->conn->pcb.tcp, backlog, &err);
1484
1485 if (lpcb == NULL) {
1486 /* in this case, the old pcb is still allocated */
1487 } else {
1488 /* delete the recvmbox and allocate the acceptmbox */
1489 if (sys_mbox_valid(&msg->conn->recvmbox)) {
1490 /** @todo: should we drain the recvmbox here? */
1491 sys_mbox_free(&msg->conn->recvmbox);
1492 sys_mbox_set_invalid(&msg->conn->recvmbox);
1493 }
1494 err = ERR_OK;
1495 if (!sys_mbox_valid(&msg->conn->acceptmbox)) {
1496 err = sys_mbox_new(&msg->conn->acceptmbox, DEFAULT_ACCEPTMBOX_SIZE);
1497 }
1498 if (err == ERR_OK) {
1499 msg->conn->state = NETCONN_LISTEN;
1500 msg->conn->pcb.tcp = lpcb;
1501 tcp_arg(msg->conn->pcb.tcp, msg->conn);
1502 tcp_accept(msg->conn->pcb.tcp, accept_function);
1503 } else {
1504 /* since the old pcb is already deallocated, free lpcb now */
1505 tcp_close(lpcb);
1506 msg->conn->pcb.tcp = NULL;
1507 }
1508 }
1509 }
1510 } else if (msg->conn->state == NETCONN_LISTEN) {
1511 /* already listening, allow updating of the backlog */
1512 err = ERR_OK;
1513 tcp_backlog_set(msg->conn->pcb.tcp, msg->msg.lb.backlog);
1514 } else {
1515 err = ERR_CONN;
1516 }
1517 } else {
1518 err = ERR_ARG;
1519 }
1520 } else {
1521 err = ERR_CONN;
1522 }
1523 msg->err = err;
1524 TCPIP_APIMSG_ACK(msg);
1525 }
1526 #endif /* LWIP_TCP */
1527
1528 /**
1529 * Send some data on a RAW or UDP pcb contained in a netconn
1530 * Called from netconn_send
1531 *
1532 * @param m the api_msg pointing to the connection
1533 */
1534 void
1535 lwip_netconn_do_send(void *m)
1536 {
1537 struct api_msg *msg = (struct api_msg *)m;
1538
1539 err_t err = netconn_err(msg->conn);
1540 if (err == ERR_OK) {
1541 if (msg->conn->pcb.tcp != NULL) {
1542 switch (NETCONNTYPE_GROUP(msg->conn->type)) {
1543 #if LWIP_RAW
1544 case NETCONN_RAW:
1545 if (ip_addr_isany(&msg->msg.b->addr) || IP_IS_ANY_TYPE_VAL(msg->msg.b->addr)) {
1546 err = raw_send(msg->conn->pcb.raw, msg->msg.b->p);
1547 } else {
1548 err = raw_sendto(msg->conn->pcb.raw, msg->msg.b->p, &msg->msg.b->addr);
1549 }
1550 break;
1551 #endif
1552 #if LWIP_UDP
1553 case NETCONN_UDP:
1554 #if LWIP_CHECKSUM_ON_COPY
1555 if (ip_addr_isany(&msg->msg.b->addr) || IP_IS_ANY_TYPE_VAL(msg->msg.b->addr)) {
1556 err = udp_send_chksum(msg->conn->pcb.udp, msg->msg.b->p,
1557 msg->msg.b->flags & NETBUF_FLAG_CHKSUM, msg->msg.b->toport_chksum);
1558 } else {
1559 err = udp_sendto_chksum(msg->conn->pcb.udp, msg->msg.b->p,
1560 &msg->msg.b->addr, msg->msg.b->port,
1561 msg->msg.b->flags & NETBUF_FLAG_CHKSUM, msg->msg.b->toport_chksum);
1562 }
1563 #else /* LWIP_CHECKSUM_ON_COPY */
1564 if (ip_addr_isany_val(msg->msg.b->addr) || IP_IS_ANY_TYPE_VAL(msg->msg.b->addr)) {
1565 err = udp_send(msg->conn->pcb.udp, msg->msg.b->p);
1566 } else {
1567 err = udp_sendto(msg->conn->pcb.udp, msg->msg.b->p, &msg->msg.b->addr, msg->msg.b->port);
1568 }
1569 #endif /* LWIP_CHECKSUM_ON_COPY */
1570 break;
1571 #endif /* LWIP_UDP */
1572 default:
1573 err = ERR_CONN;
1574 break;
1575 }
1576 } else {
1577 err = ERR_CONN;
1578 }
1579 }
1580 msg->err = err;
1581 TCPIP_APIMSG_ACK(msg);
1582 }
1583
1584 #if LWIP_TCP
1585 /**
1586 * Indicate data has been received from a TCP pcb contained in a netconn
1587 * Called from netconn_recv
1588 *
1589 * @param m the api_msg pointing to the connection
1590 */
1591 void
1592 lwip_netconn_do_recv(void *m)
1593 {
1594 struct api_msg *msg = (struct api_msg *)m;
1595
1596 msg->err = ERR_OK;
1597 if (msg->conn->pcb.tcp != NULL) {
1598 if (NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_TCP) {
1599 size_t remaining = msg->msg.r.len;
1600 do {
1601 u16_t recved = (u16_t)((remaining > 0xffff) ? 0xffff : remaining);
1602 tcp_recved(msg->conn->pcb.tcp, recved);
1603 remaining -= recved;
1604 } while (remaining != 0);
1605 }
1606 }
1607 TCPIP_APIMSG_ACK(msg);
1608 }
1609
1610 #if TCP_LISTEN_BACKLOG
1611 /** Indicate that a TCP pcb has been accepted
1612 * Called from netconn_accept
1613 *
1614 * @param m the api_msg pointing to the connection
1615 */
1616 void
1617 lwip_netconn_do_accepted(void *m)
1618 {
1619 struct api_msg *msg = (struct api_msg *)m;
1620
1621 msg->err = ERR_OK;
1622 if (msg->conn->pcb.tcp != NULL) {
1623 if (NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_TCP) {
1624 tcp_backlog_accepted(msg->conn->pcb.tcp);
1625 }
1626 }
1627 TCPIP_APIMSG_ACK(msg);
1628 }
1629 #endif /* TCP_LISTEN_BACKLOG */
1630
1631 /**
1632 * See if more data needs to be written from a previous call to netconn_write.
1633 * Called initially from lwip_netconn_do_write. If the first call can't send all data
1634 * (because of low memory or empty send-buffer), this function is called again
1635 * from sent_tcp() or poll_tcp() to send more data. If all data is sent, the
1636 * blocking application thread (waiting in netconn_write) is released.
1637 *
1638 * @param conn netconn (that is currently in state NETCONN_WRITE) to process
1639 * @return ERR_OK
1640 * ERR_MEM if LWIP_TCPIP_CORE_LOCKING=1 and sending hasn't yet finished
1641 */
1642 static err_t
1643 lwip_netconn_do_writemore(struct netconn *conn WRITE_DELAYED_PARAM)
1644 {
1645 err_t err;
1646 const void *dataptr;
1647 u16_t len, available;
1648 u8_t write_finished = 0;
1649 size_t diff;
1650 u8_t dontblock;
1651 u8_t apiflags;
1652 u8_t write_more;
1653
1654 LWIP_ASSERT("conn != NULL", conn != NULL);
1655 LWIP_ASSERT("conn->state == NETCONN_WRITE", (conn->state == NETCONN_WRITE));
1656 LWIP_ASSERT("conn->current_msg != NULL", conn->current_msg != NULL);
1657 LWIP_ASSERT("conn->pcb.tcp != NULL", conn->pcb.tcp != NULL);
1658 LWIP_ASSERT("conn->current_msg->msg.w.offset < conn->current_msg->msg.w.len",
1659 conn->current_msg->msg.w.offset < conn->current_msg->msg.w.len);
1660 LWIP_ASSERT("conn->current_msg->msg.w.vector_cnt > 0", conn->current_msg->msg.w.vector_cnt > 0);
1661
1662 apiflags = conn->current_msg->msg.w.apiflags;
1663 dontblock = netconn_is_nonblocking(conn) || (apiflags & NETCONN_DONTBLOCK);
1664
1665 #if LWIP_SO_SNDTIMEO
1666 if ((conn->send_timeout != 0) &&
1667 ((s32_t)(sys_now() - conn->current_msg->msg.w.time_started) >= conn->send_timeout)) {
1668 write_finished = 1;
1669 if (conn->current_msg->msg.w.offset == 0) {
1670 /* nothing has been written */
1671 err = ERR_WOULDBLOCK;
1672 } else {
1673 /* partial write */
1674 err = ERR_OK;
1675 }
1676 } else
1677 #endif /* LWIP_SO_SNDTIMEO */
1678 {
1679 do {
1680 dataptr = (const u8_t *)conn->current_msg->msg.w.vector->ptr + conn->current_msg->msg.w.vector_off;
1681 diff = conn->current_msg->msg.w.vector->len - conn->current_msg->msg.w.vector_off;
1682 if (diff > 0xffffUL) { /* max_u16_t */
1683 len = 0xffff;
1684 apiflags |= TCP_WRITE_FLAG_MORE;
1685 } else {
1686 len = (u16_t)diff;
1687 }
1688 available = tcp_sndbuf(conn->pcb.tcp);
1689 if (available < len) {
1690 /* don't try to write more than sendbuf */
1691 len = available;
1692 if (dontblock) {
1693 if (!len) {
1694 /* set error according to partial write or not */
1695 err = (conn->current_msg->msg.w.offset == 0) ? ERR_WOULDBLOCK : ERR_OK;
1696 goto err_mem;
1697 }
1698 } else {
1699 apiflags |= TCP_WRITE_FLAG_MORE;
1700 }
1701 }
1702 LWIP_ASSERT("lwip_netconn_do_writemore: invalid length!",
1703 ((conn->current_msg->msg.w.vector_off + len) <= conn->current_msg->msg.w.vector->len));
1704 /* we should loop around for more sending in the following cases:
1705 1) We couldn't finish the current vector because of 16-bit size limitations.
1706 tcp_write() and tcp_sndbuf() both are limited to 16-bit sizes
1707 2) We are sending the remainder of the current vector and have more */
1708 if ((len == 0xffff && diff > 0xffffUL) ||
1709 (len == (u16_t)diff && conn->current_msg->msg.w.vector_cnt > 1)) {
1710 write_more = 1;
1711 apiflags |= TCP_WRITE_FLAG_MORE;
1712 } else {
1713 write_more = 0;
1714 }
1715 err = tcp_write(conn->pcb.tcp, dataptr, len, apiflags);
1716 if (err == ERR_OK) {
1717 conn->current_msg->msg.w.offset += len;
1718 conn->current_msg->msg.w.vector_off += len;
1719 /* check if current vector is finished */
1720 if (conn->current_msg->msg.w.vector_off == conn->current_msg->msg.w.vector->len) {
1721 conn->current_msg->msg.w.vector_cnt--;
1722 /* if we have additional vectors, move on to them */
1723 if (conn->current_msg->msg.w.vector_cnt > 0) {
1724 conn->current_msg->msg.w.vector++;
1725 conn->current_msg->msg.w.vector_off = 0;
1726 }
1727 }
1728 }
1729 } while (write_more && err == ERR_OK);
1730 /* if OK or memory error, check available space */
1731 if ((err == ERR_OK) || (err == ERR_MEM)) {
1732 err_mem:
1733 if (dontblock && (conn->current_msg->msg.w.offset < conn->current_msg->msg.w.len)) {
1734 /* non-blocking write did not write everything: mark the pcb non-writable
1735 and let poll_tcp check writable space to mark the pcb writable again */
1736 API_EVENT(conn, NETCONN_EVT_SENDMINUS, 0);
1737 conn->flags |= NETCONN_FLAG_CHECK_WRITESPACE;
1738 } else if ((tcp_sndbuf(conn->pcb.tcp) <= TCP_SNDLOWAT) ||
1739 (tcp_sndqueuelen(conn->pcb.tcp) >= TCP_SNDQUEUELOWAT)) {
1740 /* The queued byte- or pbuf-count exceeds the configured low-water limit,
1741 let select mark this pcb as non-writable. */
1742 API_EVENT(conn, NETCONN_EVT_SENDMINUS, 0);
1743 }
1744 }
1745
1746 if (err == ERR_OK) {
1747 err_t out_err;
1748 if ((conn->current_msg->msg.w.offset == conn->current_msg->msg.w.len) || dontblock) {
1749 /* return sent length (caller reads length from msg.w.offset) */
1750 write_finished = 1;
1751 }
1752 out_err = tcp_output(conn->pcb.tcp);
1753 if (out_err == ERR_RTE) {
1754 /* If tcp_output fails because no route is found,
1755 don't try writing any more but return the error
1756 to the application thread. */
1757 err = out_err;
1758 write_finished = 1;
1759 }
1760 } else if (err == ERR_MEM) {
1761 /* If ERR_MEM, we wait for sent_tcp or poll_tcp to be called.
1762 For blocking sockets, we do NOT return to the application
1763 thread, since ERR_MEM is only a temporary error! Non-blocking
1764 will remain non-writable until sent_tcp/poll_tcp is called */
1765
1766 /* tcp_write returned ERR_MEM, try tcp_output anyway */
1767 err_t out_err = tcp_output(conn->pcb.tcp);
1768 if (out_err == ERR_RTE) {
1769 /* If tcp_output fails because no route is found,
1770 don't try writing any more but return the error
1771 to the application thread. */
1772 err = out_err;
1773 write_finished = 1;
1774 } else if (dontblock) {
1775 /* non-blocking write is done on ERR_MEM, set error according
1776 to partial write or not */
1777 err = (conn->current_msg->msg.w.offset == 0) ? ERR_WOULDBLOCK : ERR_OK;
1778 write_finished = 1;
1779 }
1780 } else {
1781 /* On errors != ERR_MEM, we don't try writing any more but return
1782 the error to the application thread. */
1783 write_finished = 1;
1784 }
1785 }
1786 if (write_finished) {
1787 /* everything was written: set back connection state
1788 and back to application task */
1789 sys_sem_t *op_completed_sem = LWIP_API_MSG_SEM(conn->current_msg);
1790 conn->current_msg->err = err;
1791 conn->current_msg = NULL;
1792 conn->state = NETCONN_NONE;
1793 #if LWIP_TCPIP_CORE_LOCKING
1794 if (delayed)
1795 #endif
1796 {
1797 sys_sem_signal(op_completed_sem);
1798 }
1799 }
1800 #if LWIP_TCPIP_CORE_LOCKING
1801 else {
1802 return ERR_MEM;
1803 }
1804 #endif
1805 return ERR_OK;
1806 }
1807 #endif /* LWIP_TCP */
1808
1809 /**
1810 * Send some data on a TCP pcb contained in a netconn
1811 * Called from netconn_write
1812 *
1813 * @param m the api_msg pointing to the connection
1814 */
1815 void
1816 lwip_netconn_do_write(void *m)
1817 {
1818 struct api_msg *msg = (struct api_msg *)m;
1819
1820 err_t err = netconn_err(msg->conn);
1821 if (err == ERR_OK) {
1822 if (NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_TCP) {
1823 #if LWIP_TCP
1824 if (msg->conn->state != NETCONN_NONE) {
1825 /* netconn is connecting, closing or in blocking write */
1826 err = ERR_INPROGRESS;
1827 } else if (msg->conn->pcb.tcp != NULL) {
1828 msg->conn->state = NETCONN_WRITE;
1829 /* set all the variables used by lwip_netconn_do_writemore */
1830 LWIP_ASSERT("already writing or closing", msg->conn->current_msg == NULL);
1831 LWIP_ASSERT("msg->msg.w.len != 0", msg->msg.w.len != 0);
1832 msg->conn->current_msg = msg;
1833 #if LWIP_TCPIP_CORE_LOCKING
1834 if (lwip_netconn_do_writemore(msg->conn, 0) != ERR_OK) {
1835 LWIP_ASSERT("state!", msg->conn->state == NETCONN_WRITE);
1836 UNLOCK_TCPIP_CORE();
1837 sys_arch_sem_wait(LWIP_API_MSG_SEM(msg), 0);
1838 LOCK_TCPIP_CORE();
1839 LWIP_ASSERT("state!", msg->conn->state != NETCONN_WRITE);
1840 }
1841 #else /* LWIP_TCPIP_CORE_LOCKING */
1842 lwip_netconn_do_writemore(msg->conn);
1843 #endif /* LWIP_TCPIP_CORE_LOCKING */
1844 /* for both cases: if lwip_netconn_do_writemore was called, don't ACK the APIMSG
1845 since lwip_netconn_do_writemore ACKs it! */
1846 return;
1847 } else {
1848 err = ERR_CONN;
1849 }
1850 #else /* LWIP_TCP */
1851 err = ERR_VAL;
1852 #endif /* LWIP_TCP */
1853 #if (LWIP_UDP || LWIP_RAW)
1854 } else {
1855 err = ERR_VAL;
1856 #endif /* (LWIP_UDP || LWIP_RAW) */
1857 }
1858 }
1859 msg->err = err;
1860 TCPIP_APIMSG_ACK(msg);
1861 }
1862
1863 /**
1864 * Return a connection's local or remote address
1865 * Called from netconn_getaddr
1866 *
1867 * @param m the api_msg pointing to the connection
1868 */
1869 void
1870 lwip_netconn_do_getaddr(void *m)
1871 {
1872 struct api_msg *msg = (struct api_msg *)m;
1873
1874 if (msg->conn->pcb.ip != NULL) {
1875 if (msg->msg.ad.local) {
1876 ip_addr_copy(API_EXPR_DEREF(msg->msg.ad.ipaddr),
1877 msg->conn->pcb.ip->local_ip);
1878 } else {
1879 ip_addr_copy(API_EXPR_DEREF(msg->msg.ad.ipaddr),
1880 msg->conn->pcb.ip->remote_ip);
1881 }
1882
1883 msg->err = ERR_OK;
1884 switch (NETCONNTYPE_GROUP(msg->conn->type)) {
1885 #if LWIP_RAW
1886 case NETCONN_RAW:
1887 if (msg->msg.ad.local) {
1888 API_EXPR_DEREF(msg->msg.ad.port) = msg->conn->pcb.raw->protocol;
1889 } else {
1890 /* return an error as connecting is only a helper for upper layers */
1891 msg->err = ERR_CONN;
1892 }
1893 break;
1894 #endif /* LWIP_RAW */
1895 #if LWIP_UDP
1896 case NETCONN_UDP:
1897 if (msg->msg.ad.local) {
1898 API_EXPR_DEREF(msg->msg.ad.port) = msg->conn->pcb.udp->local_port;
1899 } else {
1900 if ((msg->conn->pcb.udp->flags & UDP_FLAGS_CONNECTED) == 0) {
1901 msg->err = ERR_CONN;
1902 } else {
1903 API_EXPR_DEREF(msg->msg.ad.port) = msg->conn->pcb.udp->remote_port;
1904 }
1905 }
1906 break;
1907 #endif /* LWIP_UDP */
1908 #if LWIP_TCP
1909 case NETCONN_TCP:
1910 if ((msg->msg.ad.local == 0) &&
1911 ((msg->conn->pcb.tcp->state == CLOSED) || (msg->conn->pcb.tcp->state == LISTEN))) {
1912 /* pcb is not connected and remote name is requested */
1913 msg->err = ERR_CONN;
1914 } else {
1915 API_EXPR_DEREF(msg->msg.ad.port) = (msg->msg.ad.local ? msg->conn->pcb.tcp->local_port : msg->conn->pcb.tcp->remote_port);
1916 }
1917 break;
1918 #endif /* LWIP_TCP */
1919 default:
1920 LWIP_ASSERT("invalid netconn_type", 0);
1921 break;
1922 }
1923 } else {
1924 msg->err = ERR_CONN;
1925 }
1926 TCPIP_APIMSG_ACK(msg);
1927 }
1928
1929 /**
1930 * Close or half-shutdown a TCP pcb contained in a netconn
1931 * Called from netconn_close
1932 * In contrast to closing sockets, the netconn is not deallocated.
1933 *
1934 * @param m the api_msg pointing to the connection
1935 */
1936 void
1937 lwip_netconn_do_close(void *m)
1938 {
1939 struct api_msg *msg = (struct api_msg *)m;
1940
1941 #if LWIP_TCP
1942 enum netconn_state state = msg->conn->state;
1943 /* First check if this is a TCP netconn and if it is in a correct state
1944 (LISTEN doesn't support half shutdown) */
1945 if ((msg->conn->pcb.tcp != NULL) &&
1946 (NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_TCP) &&
1947 ((msg->msg.sd.shut == NETCONN_SHUT_RDWR) || (state != NETCONN_LISTEN))) {
1948 /* Check if we are in a connected state */
1949 if (state == NETCONN_CONNECT) {
1950 /* TCP connect in progress: cannot shutdown */
1951 msg->err = ERR_CONN;
1952 } else if (state == NETCONN_WRITE) {
1953 #if LWIP_NETCONN_FULLDUPLEX
1954 if (msg->msg.sd.shut & NETCONN_SHUT_WR) {
1955 /* close requested, abort running write */
1956 sys_sem_t *write_completed_sem;
1957 LWIP_ASSERT("msg->conn->current_msg != NULL", msg->conn->current_msg != NULL);
1958 write_completed_sem = LWIP_API_MSG_SEM(msg->conn->current_msg);
1959 msg->conn->current_msg->err = ERR_CLSD;
1960 msg->conn->current_msg = NULL;
1961 msg->conn->state = NETCONN_NONE;
1962 state = NETCONN_NONE;
1963 sys_sem_signal(write_completed_sem);
1964 } else {
1965 LWIP_ASSERT("msg->msg.sd.shut == NETCONN_SHUT_RD", msg->msg.sd.shut == NETCONN_SHUT_RD);
1966 /* In this case, let the write continue and do not interfere with
1967 conn->current_msg or conn->state! */
1968 msg->err = tcp_shutdown(msg->conn->pcb.tcp, 1, 0);
1969 }
1970 }
1971 if (state == NETCONN_NONE) {
1972 #else /* LWIP_NETCONN_FULLDUPLEX */
1973 msg->err = ERR_INPROGRESS;
1974 } else {
1975 #endif /* LWIP_NETCONN_FULLDUPLEX */
1976 if (msg->msg.sd.shut & NETCONN_SHUT_RD) {
1977 #if LWIP_NETCONN_FULLDUPLEX
1978 /* Mark mboxes invalid */
1979 netconn_mark_mbox_invalid(msg->conn);
1980 #else /* LWIP_NETCONN_FULLDUPLEX */
1981 netconn_drain(msg->conn);
1982 #endif /* LWIP_NETCONN_FULLDUPLEX */
1983 }
1984 LWIP_ASSERT("already writing or closing", msg->conn->current_msg == NULL);
1985 msg->conn->state = NETCONN_CLOSE;
1986 msg->conn->current_msg = msg;
1987 #if LWIP_TCPIP_CORE_LOCKING
1988 if (lwip_netconn_do_close_internal(msg->conn, 0) != ERR_OK) {
1989 LWIP_ASSERT("state!", msg->conn->state == NETCONN_CLOSE);
1990 UNLOCK_TCPIP_CORE();
1991 sys_arch_sem_wait(LWIP_API_MSG_SEM(msg), 0);
1992 LOCK_TCPIP_CORE();
1993 LWIP_ASSERT("state!", msg->conn->state == NETCONN_NONE);
1994 }
1995 #else /* LWIP_TCPIP_CORE_LOCKING */
1996 lwip_netconn_do_close_internal(msg->conn);
1997 #endif /* LWIP_TCPIP_CORE_LOCKING */
1998 /* for tcp netconns, lwip_netconn_do_close_internal ACKs the message */
1999 return;
2000 }
2001 } else
2002 #endif /* LWIP_TCP */
2003 {
2004 msg->err = ERR_CONN;
2005 }
2006 TCPIP_APIMSG_ACK(msg);
2007 }
2008
2009 #if LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD)
2010 /**
2011 * Join multicast groups for UDP netconns.
2012 * Called from netconn_join_leave_group
2013 *
2014 * @param m the api_msg pointing to the connection
2015 */
2016 void
2017 lwip_netconn_do_join_leave_group(void *m)
2018 {
2019 struct api_msg *msg = (struct api_msg *)m;
2020
2021 msg->err = ERR_CONN;
2022 if (msg->conn->pcb.tcp != NULL) {
2023 if (NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_UDP) {
2024 #if LWIP_UDP
2025 #if LWIP_IPV6 && LWIP_IPV6_MLD
2026 if (NETCONNTYPE_ISIPV6(msg->conn->type)) {
2027 if (msg->msg.jl.join_or_leave == NETCONN_JOIN) {
2028 msg->err = mld6_joingroup(ip_2_ip6(API_EXPR_REF(msg->msg.jl.netif_addr)),
2029 ip_2_ip6(API_EXPR_REF(msg->msg.jl.multiaddr)));
2030 } else {
2031 msg->err = mld6_leavegroup(ip_2_ip6(API_EXPR_REF(msg->msg.jl.netif_addr)),
2032 ip_2_ip6(API_EXPR_REF(msg->msg.jl.multiaddr)));
2033 }
2034 } else
2035 #endif /* LWIP_IPV6 && LWIP_IPV6_MLD */
2036 {
2037 #if LWIP_IGMP
2038 if (msg->msg.jl.join_or_leave == NETCONN_JOIN) {
2039 msg->err = igmp_joingroup(ip_2_ip4(API_EXPR_REF(msg->msg.jl.netif_addr)),
2040 ip_2_ip4(API_EXPR_REF(msg->msg.jl.multiaddr)));
2041 } else {
2042 msg->err = igmp_leavegroup(ip_2_ip4(API_EXPR_REF(msg->msg.jl.netif_addr)),
2043 ip_2_ip4(API_EXPR_REF(msg->msg.jl.multiaddr)));
2044 }
2045 #endif /* LWIP_IGMP */
2046 }
2047 #endif /* LWIP_UDP */
2048 #if (LWIP_TCP || LWIP_RAW)
2049 } else {
2050 msg->err = ERR_VAL;
2051 #endif /* (LWIP_TCP || LWIP_RAW) */
2052 }
2053 }
2054 TCPIP_APIMSG_ACK(msg);
2055 }
2056 /**
2057 * Join multicast groups for UDP netconns.
2058 * Called from netconn_join_leave_group_netif
2059 *
2060 * @param m the api_msg pointing to the connection
2061 */
2062 void
2063 lwip_netconn_do_join_leave_group_netif(void *m)
2064 {
2065 struct api_msg *msg = (struct api_msg *)m;
2066 struct netif *netif;
2067
2068 netif = netif_get_by_index(msg->msg.jl.if_idx);
2069 if (netif == NULL) {
2070 msg->err = ERR_IF;
2071 goto done;
2072 }
2073
2074 msg->err = ERR_CONN;
2075 if (msg->conn->pcb.tcp != NULL) {
2076 if (NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_UDP) {
2077 #if LWIP_UDP
2078 #if LWIP_IPV6 && LWIP_IPV6_MLD
2079 if (NETCONNTYPE_ISIPV6(msg->conn->type)) {
2080 if (msg->msg.jl.join_or_leave == NETCONN_JOIN) {
2081 msg->err = mld6_joingroup_netif(netif,
2082 ip_2_ip6(API_EXPR_REF(msg->msg.jl.multiaddr)));
2083 } else {
2084 msg->err = mld6_leavegroup_netif(netif,
2085 ip_2_ip6(API_EXPR_REF(msg->msg.jl.multiaddr)));
2086 }
2087 } else
2088 #endif /* LWIP_IPV6 && LWIP_IPV6_MLD */
2089 {
2090 #if LWIP_IGMP
2091 if (msg->msg.jl.join_or_leave == NETCONN_JOIN) {
2092 msg->err = igmp_joingroup_netif(netif,
2093 ip_2_ip4(API_EXPR_REF(msg->msg.jl.multiaddr)));
2094 } else {
2095 msg->err = igmp_leavegroup_netif(netif,
2096 ip_2_ip4(API_EXPR_REF(msg->msg.jl.multiaddr)));
2097 }
2098 #endif /* LWIP_IGMP */
2099 }
2100 #endif /* LWIP_UDP */
2101 #if (LWIP_TCP || LWIP_RAW)
2102 } else {
2103 msg->err = ERR_VAL;
2104 #endif /* (LWIP_TCP || LWIP_RAW) */
2105 }
2106 }
2107
2108 done:
2109 TCPIP_APIMSG_ACK(msg);
2110 }
2111 #endif /* LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD) */
2112
2113 #if LWIP_DNS
2114 /**
2115 * Callback function that is called when DNS name is resolved
2116 * (or on timeout). A waiting application thread is waked up by
2117 * signaling the semaphore.
2118 */
2119 static void
2120 lwip_netconn_do_dns_found(const char *name, const ip_addr_t *ipaddr, void *arg)
2121 {
2122 struct dns_api_msg *msg = (struct dns_api_msg *)arg;
2123
2124 /* we trust the internal implementation to be correct :-) */
2125 LWIP_UNUSED_ARG(name);
2126
2127 if (ipaddr == NULL) {
2128 /* timeout or memory error */
2129 API_EXPR_DEREF(msg->err) = ERR_VAL;
2130 } else {
2131 /* address was resolved */
2132 API_EXPR_DEREF(msg->err) = ERR_OK;
2133 API_EXPR_DEREF(msg->addr) = *ipaddr;
2134 }
2135 /* wake up the application task waiting in netconn_gethostbyname */
2136 sys_sem_signal(API_EXPR_REF_SEM(msg->sem));
2137 }
2138
2139 /**
2140 * Execute a DNS query
2141 * Called from netconn_gethostbyname
2142 *
2143 * @param arg the dns_api_msg pointing to the query
2144 */
2145 void
2146 lwip_netconn_do_gethostbyname(void *arg)
2147 {
2148 struct dns_api_msg *msg = (struct dns_api_msg *)arg;
2149 u8_t addrtype =
2150 #if LWIP_IPV4 && LWIP_IPV6
2151 msg->dns_addrtype;
2152 #else
2153 LWIP_DNS_ADDRTYPE_DEFAULT;
2154 #endif
2155
2156 API_EXPR_DEREF(msg->err) = dns_gethostbyname_addrtype(msg->name,
2157 API_EXPR_REF(msg->addr), lwip_netconn_do_dns_found, msg, addrtype);
2158 #if LWIP_TCPIP_CORE_LOCKING
2159 /* For core locking, only block if we need to wait for answer/timeout */
2160 if (API_EXPR_DEREF(msg->err) == ERR_INPROGRESS) {
2161 UNLOCK_TCPIP_CORE();
2162 sys_sem_wait(API_EXPR_REF_SEM(msg->sem));
2163 LOCK_TCPIP_CORE();
2164 LWIP_ASSERT("do_gethostbyname still in progress!!", API_EXPR_DEREF(msg->err) != ERR_INPROGRESS);
2165 }
2166 #else /* LWIP_TCPIP_CORE_LOCKING */
2167 if (API_EXPR_DEREF(msg->err) != ERR_INPROGRESS) {
2168 /* on error or immediate success, wake up the application
2169 * task waiting in netconn_gethostbyname */
2170 sys_sem_signal(API_EXPR_REF_SEM(msg->sem));
2171 }
2172 #endif /* LWIP_TCPIP_CORE_LOCKING */
2173 }
2174 #endif /* LWIP_DNS */
2175
2176 #endif /* LWIP_NETCONN */
2177