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