libcoap 4.3.5-develop-5a5330d
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-2025 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 sys_sem_t coap_io_timeout_sem;
35extern uint32_t coap_lwip_in_call_back_ref;
36#endif /* NO_SYS == 0 */
37
38typedef struct {
39 coap_session_t *session;
40 struct tcp_pcb *tcp_pcb;
41 struct pbuf *pbuf;
42} coap_lwip_tcp_t;
43
44
45static void
46do_tcp_err(void *arg, err_t err) {
47 coap_session_t *session = (coap_session_t *)arg;
48
49 (void)err;
50
51 if (!session)
52 return;
53
54 coap_lock_lock(return);
55#if NO_SYS == 0
56 coap_lwip_in_call_back_ref++;
57#endif /* NO_SYS == 0 */
59 /*
60 * as per tcp_err() documentation, the corresponding pcb is already freed
61 * when this callback is called. So, stop a double free when
62 * coap_session_disconnected_lkd() eventually calls coap_socket_close().
63 */
64 session->sock.tcp_pcb = NULL;
66#if NO_SYS == 0
67 coap_lwip_in_call_back_ref--;
68#endif /* NO_SYS == 0 */
70#if NO_SYS == 0
71 sys_sem_signal(&coap_io_timeout_sem);
72#endif /* NO_SYS == 0 */
73}
74
80static err_t
81coap_tcp_recv(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err) {
82 coap_session_t *session = (coap_session_t *)arg;
83 coap_socket_t *sock = session ? &session->sock : NULL;
84 coap_tick_t now;
85
86 (void)tpcb;
87 if (!session)
88 return ERR_ARG;
89
90 if (p == NULL) {
91 /* remote host closed connection */
92 tcp_close(sock->tcp_pcb);
93 tcp_arg(sock->tcp_pcb, NULL);
94 tcp_recv(sock->tcp_pcb, NULL);
95 sock->tcp_pcb = NULL;
96 coap_lock_lock(return ERR_ARG);
99#if NO_SYS == 0
100 sys_sem_signal(&coap_io_timeout_sem);
101#endif /* NO_SYS == 0 */
102 return ERR_OK;
103 } else if (err != ERR_OK) {
104 /* cleanup, for unknown reason */
105 if (p != NULL) {
106 pbuf_free(p);
107 }
108 tcp_recved(sock->tcp_pcb, p->tot_len);
109#if NO_SYS == 0
110 sys_sem_signal(&coap_io_timeout_sem);
111#endif /* NO_SYS == 0 */
112 return err;
113 }
114
115 sock->p = p;
116 coap_lock_lock(return ERR_ARG);
117 coap_ticks(&now);
118#if NO_SYS == 0
119 coap_lwip_in_call_back_ref++;
120#endif /* NO_SYS == 0 */
121 coap_read_session(session->context, session, now);
122#if NO_SYS == 0
123 coap_lwip_in_call_back_ref--;
124#endif /* NO_SYS == 0 */
126#if NO_SYS == 0
127 sys_sem_signal(&coap_io_timeout_sem);
128#endif /* NO_SYS == 0 */
129 return ERR_OK;
130}
131
132#if COAP_CLIENT_SUPPORT
133
134static err_t
135do_tcp_connected(void *arg, struct tcp_pcb *tpcb, err_t err) {
136 coap_session_t *session = (coap_session_t *)arg;
137 coap_tick_t now;
138
139 if (err)
140 return err;
141 coap_lock_lock(return ERR_ARG);
142#if NO_SYS == 0
143 coap_lwip_in_call_back_ref++;
144#endif /* NO_SYS == 0 */
145 session->sock.flags |= COAP_SOCKET_CONNECTED;
146 session->addr_info.local.addr = tpcb->local_ip;
147 session->addr_info.local.port = tpcb->local_port;
148 tcp_recv(tpcb, coap_tcp_recv);
149 coap_ticks(&now);
150 coap_connect_session(session, now);
151#if NO_SYS == 0
152 coap_lwip_in_call_back_ref--;
153#endif /* NO_SYS == 0 */
155#if NO_SYS == 0
156 sys_sem_signal(&coap_io_timeout_sem);
157#endif /* NO_SYS == 0 */
158 return ERR_OK;
159}
160
161int
163 const coap_address_t *local_if,
164 const coap_address_t *server,
165 int default_port,
166 coap_address_t *local_addr,
167 coap_address_t *remote_addr) {
168 coap_address_t connect_addr;
169 err_t err;
170
171 (void)local_addr;
172 (void)remote_addr;
173
175
176 coap_lock_invert(LOCK_TCPIP_CORE(),
177 goto err_unlock);
178
179 sock->tcp_pcb = tcp_new_ip_type(IPADDR_TYPE_ANY);
180 if (sock->tcp_pcb == NULL) {
181 goto err_unlock;
182 }
183
184 tcp_arg(sock->tcp_pcb, sock->session);
185 tcp_recv(sock->tcp_pcb, coap_tcp_recv);
186 tcp_err(sock->tcp_pcb, do_tcp_err);
187 if (local_if) {
188 coap_address_t l_local_if = *local_if;
189#if LWIP_IPV6 && LWIP_IPV4
190 if (l_local_if.addr.type == IPADDR_TYPE_V6)
191 l_local_if.addr.type = IPADDR_TYPE_ANY;
192#endif /* LWIP_IPV6 && LWIP_IPV4 */
193 err = tcp_bind(sock->tcp_pcb, &l_local_if.addr, l_local_if.port);
194 if (err != ERR_OK) {
195 tcp_arg(sock->tcp_pcb, NULL);
196 tcp_recv(sock->tcp_pcb, NULL);
197 tcp_close(sock->tcp_pcb);
198 sock->tcp_pcb = NULL;
199 goto err_unlock;
200 }
201 }
202 coap_address_copy(&connect_addr, server);
203 if (connect_addr.port == 0)
204 connect_addr.port = htons(default_port);
205
206 err = tcp_connect(sock->tcp_pcb, &connect_addr.addr, connect_addr.port,
207 do_tcp_connected);
208 if (err == ERR_OK)
210 UNLOCK_TCPIP_CORE();
211 return err ? 0 : 1;
212
213err_unlock:
214 UNLOCK_TCPIP_CORE();
215 return 0;
216}
217
218int
220 coap_address_t *local_addr,
221 coap_address_t *remote_addr) {
222 (void)sock;
223 (void)local_addr;
224 (void)remote_addr;
225
227 return 1;
228}
229#endif /* COAP_CLIENT_SUPPORT */
230
231#if COAP_SERVER_SUPPORT
232
233static err_t
234do_tcp_accept(void *arg, struct tcp_pcb *newpcb, err_t err) {
235 coap_endpoint_t *endpoint = arg;
236 coap_session_t *session;
237 coap_tick_t now;
238 err_t ret_err = ERR_OK;
239
240 if ((err != ERR_OK) || (newpcb == NULL)) {
241 return ERR_VAL;
242 }
243 coap_ticks(&now);
244
245 coap_lock_lock(return ERR_MEM);
246#if NO_SYS == 0
247 coap_lwip_in_call_back_ref++;
248#endif /* NO_SYS == 0 */
249 session = coap_new_server_session(endpoint->context, endpoint, newpcb);
250
251 if (session) {
252 session->sock.tcp_pcb = newpcb;
253 session->last_rx_tx = now;
254 tcp_arg(newpcb, session);
255 tcp_setprio(newpcb, TCP_PRIO_MIN);
256 tcp_recv(newpcb, coap_tcp_recv);
257 tcp_err(newpcb, do_tcp_err);
258 } else {
259 ret_err = ERR_MEM;
260 }
261#if NO_SYS == 0
262 coap_lwip_in_call_back_ref--;
263#endif /* NO_SYS == 0 */
265#if NO_SYS == 0
266 sys_sem_signal(&coap_io_timeout_sem);
267#endif /* NO_SYS == 0 */
268 return ret_err;
269}
270
271int
273 const coap_address_t *listen_addr,
274 coap_address_t *bound_addr) {
275 int err;
276 coap_address_t l_listen = *listen_addr;
277 struct tcp_pcb *tcp_pcb;
278
279 sock->tcp_pcb = tcp_new_ip_type(IPADDR_TYPE_ANY);
280 if (sock->tcp_pcb == NULL)
281 return 0;
282
283#if LWIP_IPV6 && LWIP_IPV4
284 if (l_listen.addr.type == IPADDR_TYPE_V6)
285 l_listen.addr.type = IPADDR_TYPE_ANY;
286#endif /* LWIP_IPV6 && LWIP_IPV4 */
287 tcp_arg(sock->tcp_pcb, sock->endpoint);
288 err = tcp_bind(sock->tcp_pcb, &l_listen.addr, l_listen.port);
289 if (err != ERR_OK) {
290 tcp_arg(sock->tcp_pcb, NULL);
291 tcp_recv(sock->tcp_pcb, NULL);
292 tcp_close(sock->tcp_pcb);
293 sock->tcp_pcb = NULL;
294 return 0;
295 } else {
296 tcp_pcb = tcp_listen(sock->tcp_pcb);
297 if (tcp_pcb) {
298 sock->tcp_pcb = tcp_pcb;
299 tcp_accept(sock->tcp_pcb, do_tcp_accept);
300 } else {
301 tcp_arg(sock->tcp_pcb, NULL);
302 tcp_recv(sock->tcp_pcb, NULL);
303 tcp_close(sock->tcp_pcb);
304 sock->tcp_pcb = NULL;
305 return 0;
306 }
307 }
308 *bound_addr = l_listen;
309 return err ? 0 : 1;
310}
311
312int
314 coap_socket_t *new_client,
315 coap_address_t *local_addr,
316 coap_address_t *remote_addr,
317 void *extra) {
318 struct tcp_pcb *tcp_pcb = (struct tcp_pcb *)extra;
319
320 (void)server;
321
322 new_client->tcp_pcb = tcp_pcb;
323 local_addr->addr = tcp_pcb->local_ip;
324 local_addr->port = tcp_pcb->local_port;
325 remote_addr->addr = tcp_pcb->remote_ip;
326 remote_addr->port = tcp_pcb->remote_port;
327 return 1;
328}
329#endif /* COAP_SERVER_SUPPORT */
330
331#if NO_SYS == 0
332static void
333coap_lwip_tcp_write(void *ctx) {
334 coap_lwip_tcp_t *cb_ctx = (coap_lwip_tcp_t *)ctx;
335 int err;
336
337 if (cb_ctx && cb_ctx->pbuf) {
338 err = tcp_write(cb_ctx->tcp_pcb, cb_ctx->pbuf->payload, cb_ctx->pbuf->len, 1);
339 if (err < 0) {
340 coap_log_warn("** %s: tcp_write: error %d\n",
341 coap_session_str(cb_ctx->session), err);
342 }
343 pbuf_free(cb_ctx->pbuf);
345 sys_sem_signal(&coap_io_timeout_sem);
346 }
347}
348#endif /* NO_SYS == 0 */
349
350/*
351 * strm
352 * return +ve Number of bytes written.
353 * -1 Error error in errno).
354 */
355ssize_t
356coap_socket_write(coap_socket_t *sock, const uint8_t *data, size_t data_len) {
357 struct pbuf *pbuf;
358 int err;
359
360 pbuf = pbuf_alloc(PBUF_TRANSPORT, data_len, PBUF_RAM);
361 if (pbuf == NULL)
362 return -1;
363 memcpy(pbuf->payload, data, data_len);
364
365#if NO_SYS == 0
366 if (coap_lwip_in_call_back_ref == 0) {
367 coap_lwip_tcp_t *cb_ctx = coap_malloc_type(COAP_STRING, sizeof(coap_lwip_tcp_t));
368
369 if (!cb_ctx) {
370 coap_log_warn("** %s: coap_socket_write: error\n",
372 pbuf_free(pbuf);
373 errno = ENOMEM;
374 return -1;
375 }
376 cb_ctx->session = sock->session;
377 cb_ctx->pbuf = pbuf;
378 cb_ctx->tcp_pcb = sock->tcp_pcb;
379 err = tcpip_try_callback(coap_lwip_tcp_write, cb_ctx);
380
381 if (err < 0) {
382 coap_log_warn("** %s: tcpip_try_callback: error %d\n",
383 coap_session_str(sock->session), err);
384 errno = EAGAIN;
385 return -1;
386 }
387 } else {
388#endif /* NO_SYS == 0 */
389 err = tcp_write(sock->tcp_pcb, pbuf->payload, pbuf->len, 1);
390
391 pbuf_free(pbuf);
392 if (err < 0) {
393 coap_log_warn("** %s: tcp_write: error %d\n",
394 coap_session_str(sock->session), err);
395 return -1;
396 }
397#if NO_SYS == 0
398 }
399#endif /* NO_SYS == 0 */
400 return data_len;
401}
402
403/*
404 * strm
405 * return >=0 Number of bytes read.
406 * -1 Error error in errno).
407 */
408ssize_t
409coap_socket_read(coap_socket_t *sock, uint8_t *data, size_t data_len) {
410 if (sock->p) {
411 if (data_len < sock->p->len) {
412 uint8_t *ptr = (uint8_t *)sock->p->payload;
413
414 /* Handle partial read of data request */
415 memcpy(data, sock->p->payload, data_len);
416 sock->p->payload = &ptr[data_len];
417 sock->p->len -= data_len;
418 tcp_recved(sock->tcp_pcb, data_len);
419 return data_len;
420 } else {
421 data_len = sock->p->len;
422 memcpy(data, sock->p->payload, sock->p->len);
423 pbuf_free(sock->p);
424 sock->p = NULL;
425 tcp_recved(sock->tcp_pcb, data_len);
426 return data_len;
427 }
428 }
429 return 0;
430}
431
432#if NO_SYS == 0
433static void
434coap_lwip_tcp_close(void *ctx) {
435 struct tcp_pcb *tcp_pcb = (struct tcp_pcb *)ctx;
436
437 if (tcp_pcb) {
438 tcp_err(tcp_pcb, NULL);
439 tcp_arg(tcp_pcb, NULL);
440 tcp_recv(tcp_pcb, NULL);
441 tcp_close(tcp_pcb);
442 }
443}
444#if COAP_SERVER_SUPPORT
445static void
446coap_lwip_endpoint_tcp_close(void *ctx) {
447 struct tcp_pcb *tcp_pcb = (struct tcp_pcb *)ctx;
448
449 if (tcp_pcb) {
450 tcp_accept(tcp_pcb, NULL);
451 tcp_err(tcp_pcb, NULL);
452 tcp_arg(tcp_pcb, NULL);
453 tcp_recv(tcp_pcb, NULL);
454 tcp_close(tcp_pcb);
455 }
456}
457#endif /* COAP_SERVER_SUPPORT */
458#endif /* NO_SYS == 0 */
459
460void
462 struct tcp_pcb *tcp_pcb = sock->tcp_pcb;
463
464 sock->tcp_pcb = NULL;
465 if (tcp_pcb) {
466#if NO_SYS == 0
467 err_t err;
468
469 if (coap_lwip_in_call_back_ref == 0) {
470#if COAP_SERVER_SUPPORT
471 if (sock->endpoint)
472 err = tcpip_try_callback(coap_lwip_endpoint_tcp_close, tcp_pcb);
473 else
474#endif /* COAP_SERVER_SUPPORT */
475 err = tcpip_try_callback(coap_lwip_tcp_close, tcp_pcb);
476 if (err < 0) {
477 coap_log_warn("** %s: tcpip_try_callback: error %d\n",
478 sock->session ? coap_session_str(sock->session) : "", err);
479 errno = EAGAIN;
480 return;
481 }
482 } else {
483#endif /* NO_SYS == 0 */
484#if COAP_SERVER_SUPPORT
485 if (sock->endpoint)
486 tcp_accept(tcp_pcb, NULL);
487#endif /* COAP_SERVER_SUPPORT */
488 tcp_recv(tcp_pcb, NULL);
489 tcp_arg(tcp_pcb, NULL);
490 tcp_err(tcp_pcb, NULL);
491 tcp_close(tcp_pcb);
492#if NO_SYS == 0
493 }
494#endif /* NO_SYS == 0 */
495 }
496 return;
497}
498#endif /* !COAP_DISABLE_TCP */
499
500#else /* ! WITH_LWIP */
501
502#ifdef __clang__
503/* Make compilers happy that do not like empty modules. As this function is
504 * never used, we ignore -Wunused-function at the end of compiling this file
505 */
506#pragma GCC diagnostic ignored "-Wunused-function"
507#endif
508static inline void
509dummy(void) {
510}
511
512#endif /* ! WITH_LWIP */
void coap_address_copy(coap_address_t *dst, const coap_address_t *src)
@ COAP_NACK_NOT_DELIVERABLE
Definition coap_io.h:68
#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:34
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().
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:151
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:4963
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:59
#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:2468
void coap_session_disconnected_lkd(coap_session_t *session, coap_nack_reason_t reason)
Notify session that it has failed.
coap_session_t * coap_new_server_session(coap_context_t *ctx, coap_endpoint_t *ep, void *extra)
Creates a new server session for the specified endpoint.
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:61
Multi-purpose address abstraction.
union coap_address_t::@0 addr
Abstraction of virtual endpoint that can be attached to coap_context_t.
coap_context_t * context
endpoint's context
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_endpoint_t * endpoint
Used by the epoll logic for a listening endpoint.
coap_socket_flags_t flags
1 or more of COAP_SOCKET* flag values