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