libcoap 4.3.5-develop-bd47692
Loading...
Searching...
No Matches
coap_strm_lwip.c
Go to the documentation of this file.
1/*
2 * Copyright (C) 2012,2014 Olaf Bergmann <bergmann@tzi.org>
3 * 2014 chrysn <chrysn@fsfe.org>
4 * 2022-2026 Jon Shallow <supjps-libcoap@jpshallow.com>
5 *
6 * SPDX-License-Identifier: BSD-2-Clause
7 *
8 * This file is part of the CoAP library libcoap. Please see
9 * README for terms of use.
10 */
11
18
19#if defined(WITH_LWIP)
20
21#include <lwip/timeouts.h>
22#include <lwip/tcpip.h>
23
24int
26 return !COAP_DISABLE_TCP;
27}
28
29#if ! COAP_DISABLE_TCP
30
31#include <lwip/tcp.h>
32
33#if NO_SYS == 0
34extern uint32_t coap_lwip_in_call_back_ref;
35#endif /* NO_SYS == 0 */
36
37typedef struct {
38 coap_session_t *session;
39 struct tcp_pcb *tcp_pcb;
40 struct pbuf *pbuf;
41} coap_lwip_tcp_t;
42
43
44static void
45do_tcp_err(void *arg, err_t err) {
46 coap_session_t *session = (coap_session_t *)arg;
47
48 (void)err;
49
50 if (!session)
51 return;
52
53 coap_lock_lock(return);
54#if NO_SYS == 0
55 coap_lwip_in_call_back_ref++;
56#endif /* NO_SYS == 0 */
58 /*
59 * as per tcp_err() documentation, the corresponding pcb is already freed
60 * when this callback is called. So, stop a double free when
61 * coap_session_disconnected_lkd() eventually calls coap_socket_close().
62 */
63 session->sock.tcp_pcb = NULL;
65#if NO_SYS == 0
66 coap_lwip_in_call_back_ref--;
67#endif /* NO_SYS == 0 */
69#if NO_SYS == 0
70 sys_sem_signal(&session->context->coap_io_timeout_sem);
71#endif /* NO_SYS == 0 */
72}
73
79static err_t
80coap_tcp_recv(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err) {
81 coap_session_t *session = (coap_session_t *)arg;
82 coap_socket_t *sock = session ? &session->sock : NULL;
83 coap_tick_t now;
84
85 (void)tpcb;
86 if (!session)
87 return ERR_ARG;
88
89 if (p == NULL) {
90 /* remote host closed connection */
91 tcp_close(sock->tcp_pcb);
92 tcp_arg(sock->tcp_pcb, NULL);
93 tcp_recv(sock->tcp_pcb, NULL);
94 sock->tcp_pcb = NULL;
95 coap_lock_lock(return ERR_ARG);
98#if NO_SYS == 0
99 sys_sem_signal(&session->context->coap_io_timeout_sem);
100#endif /* NO_SYS == 0 */
101 return ERR_OK;
102 } else if (err != ERR_OK) {
103 /* cleanup, for unknown reason */
104 if (p != NULL) {
105 pbuf_free(p);
106 }
107 tcp_recved(sock->tcp_pcb, p->tot_len);
108#if NO_SYS == 0
109 sys_sem_signal(&session->context->coap_io_timeout_sem);
110#endif /* NO_SYS == 0 */
111 return err;
112 }
113
114 sock->p = p;
115 coap_lock_lock(return ERR_ARG);
116 coap_ticks(&now);
117#if NO_SYS == 0
118 coap_lwip_in_call_back_ref++;
119#endif /* NO_SYS == 0 */
120 coap_read_session(session->context, session, now);
121#if NO_SYS == 0
122 coap_lwip_in_call_back_ref--;
123#endif /* NO_SYS == 0 */
125#if NO_SYS == 0
126 sys_sem_signal(&session->context->coap_io_timeout_sem);
127#endif /* NO_SYS == 0 */
128 return ERR_OK;
129}
130
131#if COAP_CLIENT_SUPPORT
132
133static err_t
134do_tcp_connected(void *arg, struct tcp_pcb *tpcb, err_t err) {
135 coap_session_t *session = (coap_session_t *)arg;
136 coap_tick_t now;
137
138 if (err)
139 return err;
140 coap_lock_lock(return ERR_ARG);
141#if NO_SYS == 0
142 coap_lwip_in_call_back_ref++;
143#endif /* NO_SYS == 0 */
144 session->sock.flags |= COAP_SOCKET_CONNECTED;
145 session->addr_info.local.addr = tpcb->local_ip;
146 session->addr_info.local.port = tpcb->local_port;
147 tcp_recv(tpcb, coap_tcp_recv);
148 coap_ticks(&now);
149 coap_connect_session(session, now);
150#if NO_SYS == 0
151 coap_lwip_in_call_back_ref--;
152#endif /* NO_SYS == 0 */
154#if NO_SYS == 0
155 sys_sem_signal(&session->context->coap_io_timeout_sem);
156#endif /* NO_SYS == 0 */
157 return ERR_OK;
158}
159
160int
162 const coap_address_t *local_if,
163 const coap_address_t *server,
164 int default_port,
165 coap_address_t *local_addr,
166 coap_address_t *remote_addr) {
167 coap_address_t connect_addr;
168 err_t err;
169
170 (void)local_addr;
171 (void)remote_addr;
172
174
175 coap_lock_invert(LOCK_TCPIP_CORE(),
176 goto err_unlock);
177
178 sock->tcp_pcb = tcp_new_ip_type(IPADDR_TYPE_ANY);
179 if (sock->tcp_pcb == NULL) {
180 goto err_unlock;
181 }
182
183 tcp_arg(sock->tcp_pcb, sock->session);
184 tcp_recv(sock->tcp_pcb, coap_tcp_recv);
185 tcp_err(sock->tcp_pcb, do_tcp_err);
186 if (local_if) {
187 coap_address_t l_local_if = *local_if;
188#if LWIP_IPV6 && LWIP_IPV4
189 if (l_local_if.addr.type == IPADDR_TYPE_V6)
190 l_local_if.addr.type = IPADDR_TYPE_ANY;
191#endif /* LWIP_IPV6 && LWIP_IPV4 */
192 err = tcp_bind(sock->tcp_pcb, &l_local_if.addr, l_local_if.port);
193 if (err != ERR_OK) {
194 tcp_arg(sock->tcp_pcb, NULL);
195 tcp_recv(sock->tcp_pcb, NULL);
196 tcp_close(sock->tcp_pcb);
197 sock->tcp_pcb = NULL;
198 goto err_unlock;
199 }
200 }
201 coap_address_copy(&connect_addr, server);
202 if (connect_addr.port == 0)
203 connect_addr.port = htons(default_port);
204
205 err = tcp_connect(sock->tcp_pcb, &connect_addr.addr, connect_addr.port,
206 do_tcp_connected);
207 if (err == ERR_OK)
209 UNLOCK_TCPIP_CORE();
210 return err ? 0 : 1;
211
212err_unlock:
213 UNLOCK_TCPIP_CORE();
214 return 0;
215}
216
217int
219 coap_address_t *local_addr,
220 coap_address_t *remote_addr) {
221 (void)sock;
222 (void)local_addr;
223 (void)remote_addr;
224
226 return 1;
227}
228#endif /* COAP_CLIENT_SUPPORT */
229
230#if COAP_SERVER_SUPPORT
231
232static err_t
233do_tcp_accept(void *arg, struct tcp_pcb *newpcb, err_t err) {
234 coap_endpoint_t *endpoint = arg;
235 coap_session_t *session;
236 coap_tick_t now;
237 err_t ret_err = ERR_OK;
238
239 if ((err != ERR_OK) || (newpcb == NULL)) {
240 return ERR_VAL;
241 }
242 coap_ticks(&now);
243
244 coap_lock_lock(return ERR_MEM);
245#if NO_SYS == 0
246 coap_lwip_in_call_back_ref++;
247#endif /* NO_SYS == 0 */
248 session = coap_new_server_session(endpoint->context, endpoint, newpcb);
249
250 if (session) {
251 session->sock.tcp_pcb = newpcb;
252 session->last_rx_tx = now;
253 tcp_arg(newpcb, session);
254 tcp_setprio(newpcb, TCP_PRIO_MIN);
255 tcp_recv(newpcb, coap_tcp_recv);
256 tcp_err(newpcb, do_tcp_err);
257 } else {
258 ret_err = ERR_MEM;
259 }
260#if NO_SYS == 0
261 coap_lwip_in_call_back_ref--;
262#endif /* NO_SYS == 0 */
264#if NO_SYS == 0
265 sys_sem_signal(&session->context->coap_io_timeout_sem);
266#endif /* NO_SYS == 0 */
267 return ret_err;
268}
269
270int
272 const coap_address_t *listen_addr,
273 coap_address_t *bound_addr) {
274 int err;
275 coap_address_t l_listen = *listen_addr;
276 struct tcp_pcb *tcp_pcb;
277
278 sock->tcp_pcb = tcp_new_ip_type(IPADDR_TYPE_ANY);
279 if (sock->tcp_pcb == NULL)
280 return 0;
281
282#if LWIP_IPV6 && LWIP_IPV4
283 if (l_listen.addr.type == IPADDR_TYPE_V6)
284 l_listen.addr.type = IPADDR_TYPE_ANY;
285#endif /* LWIP_IPV6 && LWIP_IPV4 */
286 tcp_arg(sock->tcp_pcb, sock->endpoint);
287 err = tcp_bind(sock->tcp_pcb, &l_listen.addr, l_listen.port);
288 if (err != ERR_OK) {
289 tcp_arg(sock->tcp_pcb, NULL);
290 tcp_recv(sock->tcp_pcb, NULL);
291 tcp_close(sock->tcp_pcb);
292 sock->tcp_pcb = NULL;
293 return 0;
294 } else {
295 tcp_pcb = tcp_listen(sock->tcp_pcb);
296 if (tcp_pcb) {
297 sock->tcp_pcb = tcp_pcb;
298 tcp_accept(sock->tcp_pcb, do_tcp_accept);
299 } else {
300 tcp_arg(sock->tcp_pcb, NULL);
301 tcp_recv(sock->tcp_pcb, NULL);
302 tcp_close(sock->tcp_pcb);
303 sock->tcp_pcb = NULL;
304 return 0;
305 }
306 }
307 *bound_addr = l_listen;
308 return err ? 0 : 1;
309}
310
311int
313 coap_socket_t *new_client,
314 coap_address_t *local_addr,
315 coap_address_t *remote_addr,
316 void *extra) {
317 struct tcp_pcb *tcp_pcb = (struct tcp_pcb *)extra;
318
319 (void)server;
320
321 new_client->tcp_pcb = tcp_pcb;
322 local_addr->addr = tcp_pcb->local_ip;
323 local_addr->port = tcp_pcb->local_port;
324 remote_addr->addr = tcp_pcb->remote_ip;
325 remote_addr->port = tcp_pcb->remote_port;
326 return 1;
327}
328#endif /* COAP_SERVER_SUPPORT */
329
330#if NO_SYS == 0
331static void
332coap_lwip_tcp_write(void *ctx) {
333 coap_lwip_tcp_t *cb_ctx = (coap_lwip_tcp_t *)ctx;
334 int err;
335
336 if (cb_ctx && cb_ctx->pbuf) {
337 err = tcp_write(cb_ctx->tcp_pcb, cb_ctx->pbuf->payload, cb_ctx->pbuf->len, 1);
338 if (err < 0) {
339 coap_log_warn("** %s: tcp_write: error %d\n",
340 coap_session_str(cb_ctx->session), err);
341 }
342 pbuf_free(cb_ctx->pbuf);
344 sys_sem_signal(&cb_ctx->session->context->coap_io_timeout_sem);
345 }
346}
347#endif /* NO_SYS == 0 */
348
349/*
350 * strm
351 * return +ve Number of bytes written.
352 * -1 Error error in errno).
353 */
354ssize_t
355coap_socket_write(coap_socket_t *sock, const uint8_t *data, size_t data_len) {
356 struct pbuf *pbuf;
357 int err;
358
359 pbuf = pbuf_alloc(PBUF_TRANSPORT, data_len, PBUF_RAM);
360 if (pbuf == NULL)
361 return -1;
362 memcpy(pbuf->payload, data, data_len);
363
364#if NO_SYS == 0
365 if (coap_lwip_in_call_back_ref == 0) {
366 coap_lwip_tcp_t *cb_ctx = coap_malloc_type(COAP_STRING, sizeof(coap_lwip_tcp_t));
367
368 if (!cb_ctx) {
369 coap_log_warn("** %s: coap_socket_write: error\n",
371 pbuf_free(pbuf);
372 errno = ENOMEM;
373 return -1;
374 }
375 cb_ctx->session = sock->session;
376 cb_ctx->pbuf = pbuf;
377 cb_ctx->tcp_pcb = sock->tcp_pcb;
378 err = tcpip_try_callback(coap_lwip_tcp_write, cb_ctx);
379
380 if (err < 0) {
381 coap_log_warn("** %s: tcpip_try_callback: error %d\n",
382 coap_session_str(sock->session), err);
383 errno = EAGAIN;
384 return -1;
385 }
386 } else {
387#endif /* NO_SYS == 0 */
388 err = tcp_write(sock->tcp_pcb, pbuf->payload, pbuf->len, 1);
389
390 pbuf_free(pbuf);
391 if (err < 0) {
392 coap_log_warn("** %s: tcp_write: error %d\n",
393 coap_session_str(sock->session), err);
394 return -1;
395 }
396#if NO_SYS == 0
397 }
398#endif /* NO_SYS == 0 */
399 return data_len;
400}
401
402/*
403 * strm
404 * return >=0 Number of bytes read.
405 * -1 Error error in errno).
406 */
407ssize_t
408coap_socket_read(coap_socket_t *sock, uint8_t *data, size_t data_len) {
409 if (sock->p) {
410 if (data_len < sock->p->len) {
411 uint8_t *ptr = (uint8_t *)sock->p->payload;
412
413 /* Handle partial read of data request */
414 memcpy(data, sock->p->payload, data_len);
415 sock->p->payload = &ptr[data_len];
416 sock->p->len -= data_len;
417 tcp_recved(sock->tcp_pcb, data_len);
418 return data_len;
419 } else {
420 data_len = sock->p->len;
421 memcpy(data, sock->p->payload, sock->p->len);
422 pbuf_free(sock->p);
423 sock->p = NULL;
424 tcp_recved(sock->tcp_pcb, data_len);
425 return data_len;
426 }
427 }
428 return 0;
429}
430
431#if NO_SYS == 0
432static void
433coap_lwip_tcp_close(void *ctx) {
434 struct tcp_pcb *tcp_pcb = (struct tcp_pcb *)ctx;
435
436 if (tcp_pcb) {
437 tcp_err(tcp_pcb, NULL);
438 tcp_arg(tcp_pcb, NULL);
439 tcp_recv(tcp_pcb, NULL);
440 tcp_close(tcp_pcb);
441 }
442}
443#if COAP_SERVER_SUPPORT
444static void
445coap_lwip_endpoint_tcp_close(void *ctx) {
446 struct tcp_pcb *tcp_pcb = (struct tcp_pcb *)ctx;
447
448 if (tcp_pcb) {
449 tcp_accept(tcp_pcb, NULL);
450 tcp_err(tcp_pcb, NULL);
451 tcp_arg(tcp_pcb, NULL);
452 tcp_recv(tcp_pcb, NULL);
453 tcp_close(tcp_pcb);
454 }
455}
456#endif /* COAP_SERVER_SUPPORT */
457#endif /* NO_SYS == 0 */
458
459void
461 struct tcp_pcb *tcp_pcb = sock->tcp_pcb;
462
463 sock->tcp_pcb = NULL;
464 if (tcp_pcb) {
465#if NO_SYS == 0
466 err_t err;
467
468 if (coap_lwip_in_call_back_ref == 0) {
469#if COAP_SERVER_SUPPORT
470 if (sock->endpoint)
471 err = tcpip_try_callback(coap_lwip_endpoint_tcp_close, tcp_pcb);
472 else
473#endif /* COAP_SERVER_SUPPORT */
474 err = tcpip_try_callback(coap_lwip_tcp_close, tcp_pcb);
475 if (err < 0) {
476 coap_log_warn("** %s: tcpip_try_callback: error %d\n",
477 sock->session ? coap_session_str(sock->session) : "", err);
478 errno = EAGAIN;
479 return;
480 }
481 } else {
482#endif /* NO_SYS == 0 */
483#if COAP_SERVER_SUPPORT
484 if (sock->endpoint)
485 tcp_accept(tcp_pcb, NULL);
486#endif /* COAP_SERVER_SUPPORT */
487 tcp_recv(tcp_pcb, NULL);
488 tcp_arg(tcp_pcb, NULL);
489 tcp_err(tcp_pcb, NULL);
490 tcp_close(tcp_pcb);
491#if NO_SYS == 0
492 }
493#endif /* NO_SYS == 0 */
494 }
495 return;
496}
497#endif /* !COAP_DISABLE_TCP */
498
499#else /* ! WITH_LWIP */
500
501#ifdef __clang__
502/* Make compilers happy that do not like empty modules. As this function is
503 * never used, we ignore -Wunused-function at the end of compiling this file
504 */
505#pragma GCC diagnostic ignored "-Wunused-function"
506#endif
507static inline void
508dummy(void) {
509}
510
511#endif /* ! WITH_LWIP */
void coap_address_copy(coap_address_t *dst, const coap_address_t *src)
struct coap_endpoint_t coap_endpoint_t
@ COAP_NACK_NOT_DELIVERABLE
Definition coap_io.h:66
#define COAP_SOCKET_CAN_CONNECT
non blocking client socket can now connect without blocking
#define COAP_SOCKET_WANT_CONNECT
non blocking client socket is waiting for connect
#define COAP_SOCKET_CONNECTED
the socket is connected
Library specific build wrapper for coap_internal.h.
@ COAP_STRING
Definition coap_mem.h:33
void * coap_malloc_type(coap_memory_tag_t type, size_t size)
Allocates a chunk of size bytes and returns a pointer to the newly allocated memory.
void coap_free_type(coap_memory_tag_t type, void *p)
Releases the memory that was allocated by coap_malloc_type().
#define NULL
Definition coap_option.h:30
static void dummy(void)
uint64_t coap_tick_t
This data type represents internal timer ticks with COAP_TICKS_PER_SECOND resolution.
Definition coap_time.h:149
int coap_handle_event_lkd(coap_context_t *context, coap_event_t event, coap_session_t *session)
Invokes the event handler of context for the given event and data.
Definition coap_net.c:5123
void coap_ticks(coap_tick_t *t)
Returns the current value of an internal tick counter.
Definition coap_time.c:90
@ COAP_EVENT_TCP_FAILED
Triggered when TCP layer fails for some reason.
Definition coap_event.h:57
#define coap_lock_invert(alt_lock, failed)
Dummy for no thread-safe code.
#define coap_lock_unlock()
Dummy for no thread-safe code.
#define coap_lock_lock(failed)
Dummy for no thread-safe code.
const char * coap_session_str(const coap_session_t *session)
Get session description.
#define coap_log_warn(...)
Definition coap_debug.h:108
void coap_connect_session(coap_session_t *session, coap_tick_t now)
void coap_read_session(coap_context_t *ctx, coap_session_t *session, coap_tick_t now)
Definition coap_net.c:2586
void coap_session_disconnected_lkd(coap_session_t *session, coap_nack_reason_t reason)
Notify session that it has failed.
int coap_socket_bind_tcp(coap_socket_t *sock, const coap_address_t *listen_addr, coap_address_t *bound_addr)
Create a new TCP socket and then listen for new incoming TCP sessions.
ssize_t coap_socket_read(coap_socket_t *sock, uint8_t *data, size_t data_len)
Function interface for data stream receiving off a socket.
ssize_t coap_socket_write(coap_socket_t *sock, const uint8_t *data, size_t data_len)
Function interface for data stream sending off a socket.
int coap_socket_connect_tcp1(coap_socket_t *sock, const coap_address_t *local_if, const coap_address_t *server, int default_port, coap_address_t *local_addr, coap_address_t *remote_addr)
Create a new TCP socket and initiate the connection.
int coap_socket_accept_tcp(coap_socket_t *server, coap_socket_t *new_client, coap_address_t *local_addr, coap_address_t *remote_addr, void *extra)
Accept a new incoming TCP session.
void coap_socket_strm_close(coap_socket_t *sock)
Function interface to close off a stream socket.
int coap_socket_connect_tcp2(coap_socket_t *sock, coap_address_t *local_addr, coap_address_t *remote_addr)
Complete the TCP Connection.
int coap_tcp_is_supported(void)
Check whether TCP is available.
coap_address_t local
local address and port
Definition coap_io.h:59
Multi-purpose address abstraction.
union coap_address_t::@0 addr
Abstraction of virtual session that can be attached to coap_context_t (client) or coap_endpoint_t (se...
coap_socket_t sock
socket object for the session, if any
coap_addr_tuple_t addr_info
remote/local address info
coap_context_t * context
session's context
coap_session_t * session
Used to determine session owner.
coap_socket_flags_t flags
1 or more of COAP_SOCKET* flag values