libcoap 4.3.5-develop-490e4e0
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
33static void
34do_tcp_err(void *arg, err_t err) {
35 coap_session_t *session = (coap_session_t *)arg;
36
37 (void)err;
38
39 coap_lock_lock(return);
41 /*
42 * as per tcp_err() documentation, the corresponding pcb is already freed
43 * when this callback is called. So, stop a double free when
44 * coap_session_disconnected_lkd() eventually coap_socket_close() is called.
45 */
46 session->sock.tcp_pcb = NULL;
49}
50
56static err_t
57coap_tcp_recv(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err) {
58 coap_session_t *session = (coap_session_t *)arg;
59 coap_socket_t *sock = &session->sock;
60 coap_tick_t now;
61
62 (void)tpcb;
63 if (p == NULL) {
64 /* remote host closed connection */
65 tcp_arg(sock->tcp_pcb, NULL);
66 tcp_recv(sock->tcp_pcb, NULL);
67 tcp_close(sock->tcp_pcb);
68 sock->tcp_pcb = NULL;
69 coap_lock_lock(return ERR_ARG);
72 return ERR_OK;
73 } else if (err != ERR_OK) {
74 /* cleanup, for unknown reason */
75 if (p != NULL) {
76 pbuf_free(p);
77 }
78 return err;
79 }
80
81 sock->p = p;
82 coap_lock_lock(return ERR_ARG);
83 coap_ticks(&now);
84 coap_read_session(session->context, session, now);
86 return ERR_OK;
87}
88
89#if COAP_CLIENT_SUPPORT
90
91static err_t
92do_tcp_connected(void *arg, struct tcp_pcb *tpcb, err_t err) {
93 coap_session_t *session = (coap_session_t *)arg;
94 coap_tick_t now;
95
96 if (err)
97 return err;
98 coap_lock_lock(return ERR_ARG);
100 session->addr_info.local.addr = tpcb->local_ip;
101 session->addr_info.local.port = tpcb->local_port;
102 tcp_recv(tpcb, coap_tcp_recv);
103 coap_ticks(&now);
104 coap_connect_session(session, now);
106 return ERR_OK;
107}
108
109int
111 const coap_address_t *local_if,
112 const coap_address_t *server,
113 int default_port,
114 coap_address_t *local_addr,
115 coap_address_t *remote_addr) {
116 coap_address_t connect_addr;
117 err_t err;
118
119 (void)local_addr;
120 (void)remote_addr;
121
123
124 sock->tcp_pcb = tcp_new_ip_type(IPADDR_TYPE_ANY);
125 if (sock->tcp_pcb == NULL)
126 return 0;
127
128 tcp_arg(sock->tcp_pcb, sock->session);
129 tcp_recv(sock->tcp_pcb, coap_tcp_recv);
130 tcp_err(sock->tcp_pcb, do_tcp_err);
131 if (local_if) {
132 coap_address_t l_local_if = *local_if;
133#if LWIP_IPV6 && LWIP_IPV4
134 if (l_local_if.addr.type == IPADDR_TYPE_V6)
135 l_local_if.addr.type = IPADDR_TYPE_ANY;
136#endif /* LWIP_IPV6 && LWIP_IPV4 */
137 err = tcp_bind(sock->tcp_pcb, &l_local_if.addr, l_local_if.port);
138 if (err != ERR_OK) {
139 tcp_arg(sock->tcp_pcb, NULL);
140 tcp_recv(sock->tcp_pcb, NULL);
141 tcp_close(sock->tcp_pcb);
142 sock->tcp_pcb = NULL;
143 return 0;
144 }
145 }
146 coap_address_copy(&connect_addr, server);
147 if (connect_addr.port == 0)
148 connect_addr.port = htons(default_port);
149
150 err = tcp_connect(sock->tcp_pcb, &connect_addr.addr, connect_addr.port,
151 do_tcp_connected);
152 if (err == ERR_OK)
154 return err ? 0 : 1;
155}
156
157int
159 coap_address_t *local_addr,
160 coap_address_t *remote_addr) {
161 (void)sock;
162 (void)local_addr;
163 (void)remote_addr;
164
166 return 1;
167}
168#endif /* COAP_CLIENT_SUPPORT */
169
170#if COAP_SERVER_SUPPORT
171
172static err_t
173do_tcp_accept(void *arg, struct tcp_pcb *newpcb, err_t err) {
174 coap_endpoint_t *endpoint = arg;
175 coap_session_t *session;
176 coap_tick_t now;
177 err_t ret_err = ERR_OK;
178
179 if ((err != ERR_OK) || (newpcb == NULL)) {
180 return ERR_VAL;
181 }
182 coap_ticks(&now);
183
184 coap_lock_lock(return ERR_MEM);
185 session = coap_new_server_session(endpoint->context, endpoint, newpcb);
186
187 if (session) {
188 session->sock.tcp_pcb = newpcb;
189 session->last_rx_tx = now;
190 tcp_arg(newpcb, session);
191 tcp_setprio(newpcb, TCP_PRIO_MIN);
192 tcp_recv(newpcb, coap_tcp_recv);
193 tcp_err(newpcb, do_tcp_err);
194 } else {
195 ret_err = ERR_MEM;
196 }
198 return ret_err;
199}
200
201int
203 const coap_address_t *listen_addr,
204 coap_address_t *bound_addr) {
205 int err;
206 coap_address_t l_listen = *listen_addr;
207 struct tcp_pcb *tcp_pcb;
208
209 sock->tcp_pcb = tcp_new_ip_type(IPADDR_TYPE_ANY);
210 if (sock->tcp_pcb == NULL)
211 return 0;
212
213#if LWIP_IPV6 && LWIP_IPV4
214 if (l_listen.addr.type == IPADDR_TYPE_V6)
215 l_listen.addr.type = IPADDR_TYPE_ANY;
216#endif /* LWIP_IPV6 && LWIP_IPV4 */
217 tcp_arg(sock->tcp_pcb, sock->endpoint);
218 err = tcp_bind(sock->tcp_pcb, &l_listen.addr, l_listen.port);
219 if (err != ERR_OK) {
220 tcp_arg(sock->tcp_pcb, NULL);
221 tcp_recv(sock->tcp_pcb, NULL);
222 tcp_close(sock->tcp_pcb);
223 sock->tcp_pcb = NULL;
224 return 0;
225 } else {
226 tcp_pcb = tcp_listen(sock->tcp_pcb);
227 if (tcp_pcb) {
228 sock->tcp_pcb = tcp_pcb;
229 tcp_accept(sock->tcp_pcb, do_tcp_accept);
230 } else {
231 tcp_arg(sock->tcp_pcb, NULL);
232 tcp_recv(sock->tcp_pcb, NULL);
233 tcp_close(sock->tcp_pcb);
234 sock->tcp_pcb = NULL;
235 return 0;
236 }
237 }
238 *bound_addr = l_listen;
239 return err ? 0 : 1;
240}
241
242int
244 coap_socket_t *new_client,
245 coap_address_t *local_addr,
246 coap_address_t *remote_addr,
247 void *extra) {
248 struct tcp_pcb *tcp_pcb = (struct tcp_pcb *)extra;
249
250 (void)server;
251
252 new_client->tcp_pcb = tcp_pcb;
253 local_addr->addr = tcp_pcb->local_ip;
254 local_addr->port = tcp_pcb->local_port;
255 remote_addr->addr = tcp_pcb->remote_ip;
256 remote_addr->port = tcp_pcb->remote_port;
257 return 1;
258}
259#endif /* COAP_SERVER_SUPPORT */
260
261/*
262 * strm
263 * return +ve Number of bytes written.
264 * -1 Error error in errno).
265 */
266ssize_t
267coap_socket_write(coap_socket_t *sock, const uint8_t *data, size_t data_len) {
268 struct pbuf *pbuf;
269 int err;
270
271 pbuf = pbuf_alloc(PBUF_TRANSPORT, data_len, PBUF_RAM);
272 if (pbuf == NULL)
273 return -1;
274 memcpy(pbuf->payload, data, data_len);
275
276 coap_lock_invert(LOCK_TCPIP_CORE(),
277 UNLOCK_TCPIP_CORE(); return 0);
278
279 err = tcp_write(sock->tcp_pcb, pbuf->payload, pbuf->len, 1);
280
281 UNLOCK_TCPIP_CORE();
282
283 pbuf_free(pbuf);
284 if (err < 0)
285 return -1;
286 return data_len;
287}
288
289/*
290 * strm
291 * return >=0 Number of bytes read.
292 * -1 Error error in errno).
293 */
294ssize_t
295coap_socket_read(coap_socket_t *sock, uint8_t *data, size_t data_len) {
296 if (sock->p) {
297 if (data_len < sock->p->len) {
298 uint8_t *ptr = (uint8_t *)sock->p->payload;
299
300 /* Handle partial read of data request */
301 memcpy(data, sock->p->payload, data_len);
302 sock->p->payload = &ptr[data_len];
303 sock->p->len -= data_len;
304 return data_len;
305 } else {
306 data_len = sock->p->len;
307 memcpy(data, sock->p->payload, sock->p->len);
308 pbuf_free(sock->p);
309 sock->p = NULL;
310 return data_len;
311 }
312 }
313 return 0;
314}
315
316void
318 if (sock->tcp_pcb) {
319 tcp_arg(sock->tcp_pcb, NULL);
320#if COAP_SERVER_SUPPORT
321 if (!sock->endpoint)
322#endif /* COAP_SERVER_SUPPORT */
323 tcp_recv(sock->tcp_pcb, NULL);
324 if (sock->session) {
325 coap_lock_invert(LOCK_TCPIP_CORE(),
326 UNLOCK_TCPIP_CORE(); return);
327 } else {
328 LOCK_TCPIP_CORE();
329 }
330 tcp_close(sock->tcp_pcb);
331 UNLOCK_TCPIP_CORE();
332 sock->tcp_pcb = NULL;
333 }
334 return;
335}
336#endif /* !COAP_DISABLE_TCP */
337
338#else /* ! WITH_LWIP */
339
340#ifdef __clang__
341/* Make compilers happy that do not like empty modules. As this function is
342 * never used, we ignore -Wunused-function at the end of compiling this file
343 */
344#pragma GCC diagnostic ignored "-Wunused-function"
345#endif
346static inline void
347dummy(void) {
348}
349
350#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.
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:4904
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.
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:2437
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