libcoap 4.3.5-develop-aef27f2
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 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 coap_lock_lock(return);
52 /*
53 * as per tcp_err() documentation, the corresponding pcb is already freed
54 * when this callback is called. So, stop a double free when
55 * coap_session_disconnected_lkd() eventually calls coap_socket_close().
56 */
57 session->sock.tcp_pcb = NULL;
60}
61
67static err_t
68coap_tcp_recv(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err) {
69 coap_session_t *session = (coap_session_t *)arg;
70 coap_socket_t *sock = &session->sock;
71 coap_tick_t now;
72
73 (void)tpcb;
74 if (p == NULL) {
75 /* remote host closed connection */
76 tcp_arg(sock->tcp_pcb, NULL);
77 tcp_recv(sock->tcp_pcb, NULL);
78 tcp_close(sock->tcp_pcb);
79 sock->tcp_pcb = NULL;
80 coap_lock_lock(return ERR_ARG);
83 return ERR_OK;
84 } else if (err != ERR_OK) {
85 /* cleanup, for unknown reason */
86 if (p != NULL) {
87 pbuf_free(p);
88 }
89 return err;
90 }
91
92 sock->p = p;
93 coap_lock_lock(return ERR_ARG);
94 coap_ticks(&now);
95#if NO_SYS == 0
96 coap_lwip_in_call_back_ref++;
97#endif /* NO_SYS == 0 */
98 coap_read_session(session->context, session, now);
99#if NO_SYS == 0
100 coap_lwip_in_call_back_ref--;
101#endif /* NO_SYS == 0 */
103 return ERR_OK;
104}
105
106#if COAP_CLIENT_SUPPORT
107
108static err_t
109do_tcp_connected(void *arg, struct tcp_pcb *tpcb, err_t err) {
110 coap_session_t *session = (coap_session_t *)arg;
111 coap_tick_t now;
112
113 if (err)
114 return err;
115 coap_lock_lock(return ERR_ARG);
116 session->sock.flags |= COAP_SOCKET_CONNECTED;
117 session->addr_info.local.addr = tpcb->local_ip;
118 session->addr_info.local.port = tpcb->local_port;
119 tcp_recv(tpcb, coap_tcp_recv);
120 coap_ticks(&now);
121 coap_connect_session(session, now);
123 return ERR_OK;
124}
125
126int
128 const coap_address_t *local_if,
129 const coap_address_t *server,
130 int default_port,
131 coap_address_t *local_addr,
132 coap_address_t *remote_addr) {
133 coap_address_t connect_addr;
134 err_t err;
135
136 (void)local_addr;
137 (void)remote_addr;
138
140
141 sock->tcp_pcb = tcp_new_ip_type(IPADDR_TYPE_ANY);
142 if (sock->tcp_pcb == NULL)
143 return 0;
144
145 tcp_arg(sock->tcp_pcb, sock->session);
146 tcp_recv(sock->tcp_pcb, coap_tcp_recv);
147 tcp_err(sock->tcp_pcb, do_tcp_err);
148 if (local_if) {
149 coap_address_t l_local_if = *local_if;
150#if LWIP_IPV6 && LWIP_IPV4
151 if (l_local_if.addr.type == IPADDR_TYPE_V6)
152 l_local_if.addr.type = IPADDR_TYPE_ANY;
153#endif /* LWIP_IPV6 && LWIP_IPV4 */
154 err = tcp_bind(sock->tcp_pcb, &l_local_if.addr, l_local_if.port);
155 if (err != ERR_OK) {
156 tcp_arg(sock->tcp_pcb, NULL);
157 tcp_recv(sock->tcp_pcb, NULL);
158 tcp_close(sock->tcp_pcb);
159 sock->tcp_pcb = NULL;
160 return 0;
161 }
162 }
163 coap_address_copy(&connect_addr, server);
164 if (connect_addr.port == 0)
165 connect_addr.port = htons(default_port);
166
167 err = tcp_connect(sock->tcp_pcb, &connect_addr.addr, connect_addr.port,
168 do_tcp_connected);
169 if (err == ERR_OK)
171 return err ? 0 : 1;
172}
173
174int
176 coap_address_t *local_addr,
177 coap_address_t *remote_addr) {
178 (void)sock;
179 (void)local_addr;
180 (void)remote_addr;
181
183 return 1;
184}
185#endif /* COAP_CLIENT_SUPPORT */
186
187#if COAP_SERVER_SUPPORT
188
189static err_t
190do_tcp_accept(void *arg, struct tcp_pcb *newpcb, err_t err) {
191 coap_endpoint_t *endpoint = arg;
192 coap_session_t *session;
193 coap_tick_t now;
194 err_t ret_err = ERR_OK;
195
196 if ((err != ERR_OK) || (newpcb == NULL)) {
197 return ERR_VAL;
198 }
199 coap_ticks(&now);
200
201 coap_lock_lock(return ERR_MEM);
202 session = coap_new_server_session(endpoint->context, endpoint, newpcb);
203
204 if (session) {
205 session->sock.tcp_pcb = newpcb;
206 session->last_rx_tx = now;
207 tcp_arg(newpcb, session);
208 tcp_setprio(newpcb, TCP_PRIO_MIN);
209 tcp_recv(newpcb, coap_tcp_recv);
210 tcp_err(newpcb, do_tcp_err);
211 } else {
212 ret_err = ERR_MEM;
213 }
215 return ret_err;
216}
217
218int
220 const coap_address_t *listen_addr,
221 coap_address_t *bound_addr) {
222 int err;
223 coap_address_t l_listen = *listen_addr;
224 struct tcp_pcb *tcp_pcb;
225
226 sock->tcp_pcb = tcp_new_ip_type(IPADDR_TYPE_ANY);
227 if (sock->tcp_pcb == NULL)
228 return 0;
229
230#if LWIP_IPV6 && LWIP_IPV4
231 if (l_listen.addr.type == IPADDR_TYPE_V6)
232 l_listen.addr.type = IPADDR_TYPE_ANY;
233#endif /* LWIP_IPV6 && LWIP_IPV4 */
234 tcp_arg(sock->tcp_pcb, sock->endpoint);
235 err = tcp_bind(sock->tcp_pcb, &l_listen.addr, l_listen.port);
236 if (err != ERR_OK) {
237 tcp_arg(sock->tcp_pcb, NULL);
238 tcp_recv(sock->tcp_pcb, NULL);
239 tcp_close(sock->tcp_pcb);
240 sock->tcp_pcb = NULL;
241 return 0;
242 } else {
243 tcp_pcb = tcp_listen(sock->tcp_pcb);
244 if (tcp_pcb) {
245 sock->tcp_pcb = tcp_pcb;
246 tcp_accept(sock->tcp_pcb, do_tcp_accept);
247 } else {
248 tcp_arg(sock->tcp_pcb, NULL);
249 tcp_recv(sock->tcp_pcb, NULL);
250 tcp_close(sock->tcp_pcb);
251 sock->tcp_pcb = NULL;
252 return 0;
253 }
254 }
255 *bound_addr = l_listen;
256 return err ? 0 : 1;
257}
258
259int
261 coap_socket_t *new_client,
262 coap_address_t *local_addr,
263 coap_address_t *remote_addr,
264 void *extra) {
265 struct tcp_pcb *tcp_pcb = (struct tcp_pcb *)extra;
266
267 (void)server;
268
269 new_client->tcp_pcb = tcp_pcb;
270 local_addr->addr = tcp_pcb->local_ip;
271 local_addr->port = tcp_pcb->local_port;
272 remote_addr->addr = tcp_pcb->remote_ip;
273 remote_addr->port = tcp_pcb->remote_port;
274 return 1;
275}
276#endif /* COAP_SERVER_SUPPORT */
277
278#if NO_SYS == 0
279static void
280coap_lwip_tcp_write(void *ctx) {
281 coap_lwip_tcp_t *cb_ctx = (coap_lwip_tcp_t *)ctx;
282 int err;
283
284 if (cb_ctx && cb_ctx->pbuf) {
285 err = tcp_write(cb_ctx->tcp_pcb, cb_ctx->pbuf->payload, cb_ctx->pbuf->len, 1);
286 if (err < 0) {
287 coap_log_warn("** %s: tcp_write: error %d\n",
288 coap_session_str(cb_ctx->session), err);
289 }
290 pbuf_free(cb_ctx->pbuf);
292 }
293}
294#endif /* NO_SYS == 0 */
295
296/*
297 * strm
298 * return +ve Number of bytes written.
299 * -1 Error error in errno).
300 */
301ssize_t
302coap_socket_write(coap_socket_t *sock, const uint8_t *data, size_t data_len) {
303 struct pbuf *pbuf;
304 int err;
305
306 pbuf = pbuf_alloc(PBUF_TRANSPORT, data_len, PBUF_RAM);
307 if (pbuf == NULL)
308 return -1;
309 memcpy(pbuf->payload, data, data_len);
310
311#if NO_SYS == 0
312 if (coap_lwip_in_call_back_ref == 0) {
313 coap_lwip_tcp_t *cb_ctx = coap_malloc_type(COAP_STRING, sizeof(coap_lwip_tcp_t));
314
315 if (!cb_ctx) {
316 coap_log_warn("** %s: coap_socket_write: error\n",
318 pbuf_free(pbuf);
319 errno = ENOMEM;
320 return -1;
321 }
322 cb_ctx->session = sock->session;
323 cb_ctx->pbuf = pbuf;
324 cb_ctx->tcp_pcb = sock->tcp_pcb;
325 err = tcpip_try_callback(coap_lwip_tcp_write, cb_ctx);
326
327 if (err < 0) {
328 coap_log_warn("** %s: tcpip_try_callback: error %d\n",
329 coap_session_str(sock->session), err);
330 errno = EAGAIN;
331 return -1;
332 }
333 } else {
334#endif /* NO_SYS == 0 */
335 err = tcp_write(sock->tcp_pcb, pbuf->payload, pbuf->len, 1);
336
337 pbuf_free(pbuf);
338 if (err < 0) {
339 coap_log_warn("** %s: tcp_write: error %d\n",
340 coap_session_str(sock->session), err);
341 return -1;
342 }
343#if NO_SYS == 0
344 }
345#endif /* NO_SYS == 0 */
346 return data_len;
347}
348
349/*
350 * strm
351 * return >=0 Number of bytes read.
352 * -1 Error error in errno).
353 */
354ssize_t
355coap_socket_read(coap_socket_t *sock, uint8_t *data, size_t data_len) {
356 if (sock->p) {
357 if (data_len < sock->p->len) {
358 uint8_t *ptr = (uint8_t *)sock->p->payload;
359
360 /* Handle partial read of data request */
361 memcpy(data, sock->p->payload, data_len);
362 sock->p->payload = &ptr[data_len];
363 sock->p->len -= data_len;
364 return data_len;
365 } else {
366 data_len = sock->p->len;
367 memcpy(data, sock->p->payload, sock->p->len);
368 pbuf_free(sock->p);
369 sock->p = NULL;
370 return data_len;
371 }
372 }
373 return 0;
374}
375
376#if NO_SYS == 0
377static void
378coap_lwip_tcp_close(void *ctx) {
379 struct tcp_pcb *tcp_pcb = (struct tcp_pcb *)ctx;
380
381 if (tcp_pcb) {
382 tcp_recv(tcp_pcb, NULL);
383 tcp_close(tcp_pcb);
384 }
385}
386#if COAP_SERVER_SUPPORT
387static void
388coap_lwip_endpoint_tcp_close(void *ctx) {
389 struct tcp_pcb *tcp_pcb = (struct tcp_pcb *)ctx;
390
391 if (tcp_pcb) {
392 tcp_close(tcp_pcb);
393 }
394}
395#endif /* COAP_SERVER_SUPPORT */
396#endif /* NO_SYS == 0 */
397
398void
400 struct tcp_pcb *tcp_pcb = sock->tcp_pcb;
401 err_t err;
402
403 sock->tcp_pcb = NULL;
404 if (tcp_pcb) {
405 tcp_arg(tcp_pcb, NULL);
406#if NO_SYS == 0
407 if (coap_lwip_in_call_back_ref == 0) {
408#if COAP_SERVER_SUPPORT
409 if (sock->endpoint)
410 err = tcpip_try_callback(coap_lwip_endpoint_tcp_close, tcp_pcb);
411 else
412#endif /* COAP_SERVER_SUPPORT */
413 err = tcpip_try_callback(coap_lwip_tcp_close, tcp_pcb);
414 if (err < 0) {
415 coap_log_warn("** %s: tcpip_try_callback: error %d\n",
416 sock->session ? coap_session_str(sock->session) : "", err);
417 errno = EAGAIN;
418 return;
419 }
420 } else {
421#endif /* NO_SYS == 0 */
422#if COAP_SERVER_SUPPORT
423 if (!sock->endpoint)
424#endif /* COAP_SERVER_SUPPORT */
425 tcp_recv(tcp_pcb, NULL);
426 tcp_close(tcp_pcb);
427#if NO_SYS == 0
428 }
429#endif /* NO_SYS == 0 */
430 }
431 return;
432}
433#endif /* !COAP_DISABLE_TCP */
434
435#else /* ! WITH_LWIP */
436
437#ifdef __clang__
438/* Make compilers happy that do not like empty modules. As this function is
439 * never used, we ignore -Wunused-function at the end of compiling this file
440 */
441#pragma GCC diagnostic ignored "-Wunused-function"
442#endif
443static inline void
444dummy(void) {
445}
446
447#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:4915
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_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:2439
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