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