libcoap 4.3.5-develop-490e4e0
Loading...
Searching...
No Matches
coap_strm_posix.c
Go to the documentation of this file.
1/*
2 * coap_strm_posix.c -- Stream (TCP) functions for libcoap
3 *
4 * Copyright (C) 2019-2025 Olaf Bergmann <bergmann@tzi.org> and others
5 *
6 * SPDX-License-Identifier: BSD-2-Clause
7 *
8 * This file is part of the CoAP library libcoap. Please see README for terms
9 * of use.
10 */
11
18
19#if ! defined(WITH_LWIP) && ! defined(WITH_CONTIKI) && ! defined (RIOT_VERSION)
20
21#if COAP_AF_UNIX_SUPPORT
22#ifdef HAVE_UNISTD_H
23#include <unistd.h>
24#endif /* HAVE_UNISTD_H */
25#ifdef _WIN32
26#include <stdio.h>
27#endif /* _WIN32 */
28#endif /* COAP_AF_UNIX_SUPPORT */
29#ifdef COAP_EPOLL_SUPPORT
30#include <sys/epoll.h>
31#include <sys/timerfd.h>
32#endif /* COAP_EPOLL_SUPPORT */
33
34int
36 return !COAP_DISABLE_TCP;
37}
38
39#if !COAP_DISABLE_TCP
40#include <sys/types.h>
41#ifdef HAVE_SYS_SOCKET_H
42# include <sys/socket.h>
43# define OPTVAL_T(t) (t)
44# define OPTVAL_GT(t) (t)
45#endif
46#ifdef HAVE_SYS_IOCTL_H
47#include <sys/ioctl.h>
48#endif
49#ifdef HAVE_WS2TCPIP_H
50#include <ws2tcpip.h>
51# define OPTVAL_T(t) (const char*)(t)
52# define OPTVAL_GT(t) (char*)(t)
53# undef CMSG_DATA
54# define CMSG_DATA WSA_CMSG_DATA
55#endif
56
57#if defined(__ZEPHYR__)
58# include <zephyr/posix/sys/ioctl.h>
59# ifndef OPTVAL_T
60# define OPTVAL_T(t) (t)
61# endif
62# ifndef OPTVAL_GT
63# define OPTVAL_GT(t) (t)
64# endif
65# ifndef FIONBIO
66# define FIONBIO 0x5421
67# endif
68#endif /* __ZEPHYR__ */
69
70int
72 const coap_address_t *local_if,
73 const coap_address_t *server,
74 int default_port,
75 coap_address_t *local_addr,
76 coap_address_t *remote_addr) {
77 int on = 1;
78#if COAP_IPV6_SUPPORT
79 int off = 0;
80#endif /* COAP_IPV6_SUPPORT */
81#ifdef _WIN32
82 u_long u_on = 1;
83#endif
84 coap_address_t connect_addr;
85 coap_address_copy(&connect_addr, server);
86
87 sock->flags &= ~COAP_SOCKET_CONNECTED;
88 sock->fd = socket(server->addr.sa.sa_family, SOCK_STREAM, 0);
89
90 if (sock->fd == COAP_INVALID_SOCKET) {
91 coap_log_warn("coap_socket_connect_tcp1: socket: %s\n",
93 goto error;
94 }
95
96#ifdef _WIN32
97 if (ioctlsocket(sock->fd, FIONBIO, &u_on) == COAP_SOCKET_ERROR) {
98#else
99 if (ioctl(sock->fd, FIONBIO, &on) == COAP_SOCKET_ERROR) {
100#endif
101 coap_log_warn("coap_socket_connect_tcp1: ioctl FIONBIO: %s\n",
103 }
104
105 switch (server->addr.sa.sa_family) {
106#if COAP_IPV4_SUPPORT
107 case AF_INET:
108 if (connect_addr.addr.sin.sin_port == 0)
109 connect_addr.addr.sin.sin_port = htons(default_port);
110 break;
111#endif /* COAP_IPV4_SUPPORT */
112#if COAP_IPV6_SUPPORT
113 case AF_INET6:
114 if (connect_addr.addr.sin6.sin6_port == 0)
115 connect_addr.addr.sin6.sin6_port = htons(default_port);
116 /* Configure the socket as dual-stacked */
117 if (setsockopt(sock->fd, IPPROTO_IPV6, IPV6_V6ONLY, OPTVAL_T(&off),
118 sizeof(off)) == COAP_SOCKET_ERROR)
119 coap_log_warn("coap_socket_connect_tcp1: setsockopt IPV6_V6ONLY: %s\n",
121 break;
122#endif /* COAP_IPV6_SUPPORT */
123#if COAP_AF_UNIX_SUPPORT
124 case AF_UNIX:
125 break;
126#endif /* COAP_AF_UNIX_SUPPORT */
127 default:
128 coap_log_alert("coap_socket_connect_tcp1: unsupported sa_family\n");
129 break;
130 }
131
132 if (local_if && local_if->addr.sa.sa_family) {
133 coap_address_copy(local_addr, local_if);
134 if (setsockopt(sock->fd, SOL_SOCKET, SO_REUSEADDR, OPTVAL_T(&on), sizeof(on)) == COAP_SOCKET_ERROR)
135 coap_log_warn("coap_socket_connect_tcp1: setsockopt SO_REUSEADDR: %s\n",
137 if (bind(sock->fd, &local_if->addr.sa,
139 local_if->addr.sa.sa_family == AF_INET ?
140 (socklen_t)sizeof(struct sockaddr_in) :
141#endif /* COAP_IPV4_SUPPORT */
142 (socklen_t)local_if->size) == COAP_SOCKET_ERROR) {
143 coap_log_warn("coap_socket_connect_tcp1: bind: %s\n",
145 goto error;
146 }
147 } else {
148 local_addr->addr.sa.sa_family = server->addr.sa.sa_family;
149 }
150
151 if (connect(sock->fd, &connect_addr.addr.sa, connect_addr.size) == COAP_SOCKET_ERROR) {
152#ifdef _WIN32
153 if (WSAGetLastError() == WSAEWOULDBLOCK) {
154#else
155 if (errno == EINPROGRESS) {
156#endif
157 /*
158 * COAP_SOCKET_CONNECTED needs to be set here as there will be reads/writes
159 * by underlying TLS libraries during connect() and we do not want to
160 * assert() in coap_read_session() or coap_write_session() when called by coap_read()
161 */
163 return 1;
164 }
165 coap_log_warn("coap_socket_connect_tcp1: connect: %s\n",
167 goto error;
168 }
169
170 if (getsockname(sock->fd, &local_addr->addr.sa, &local_addr->size) == COAP_SOCKET_ERROR) {
171 coap_log_warn("coap_socket_connect_tcp1: getsockname: %s\n",
173 }
174
175 if (getpeername(sock->fd, &remote_addr->addr.sa, &remote_addr->size) == COAP_SOCKET_ERROR) {
176 coap_log_warn("coap_socket_connect_tcp1: getpeername: %s\n",
178 }
179
181 return 1;
182
183error:
184#if COAP_AF_UNIX_SUPPORT
185 if (local_if && local_if->addr.sa.sa_family == AF_UNIX) {
186#ifdef _WIN32
187 _unlink(local_if->addr.cun.sun_path);
188#else /* ! _WIN32 */
189 unlink(local_if->addr.cun.sun_path);
190#endif /* ! _WIN32 */
191 }
192#endif /* COAP_AF_UNIX_SUPPORT */
194 return 0;
195}
196
197int
199 coap_address_t *local_addr,
200 coap_address_t *remote_addr) {
201 int error = 0;
202#ifdef _WIN32
203 int optlen = (int)sizeof(error);
204#else
205 socklen_t optlen = (socklen_t)sizeof(error);
206#endif
207
209
210 if (getsockopt(sock->fd, SOL_SOCKET, SO_ERROR, OPTVAL_GT(&error),
211 &optlen) == COAP_SOCKET_ERROR) {
212 coap_log_warn("coap_socket_connect_tcp2: getsockopt: %s\n",
214 }
215
216 if (error) {
217 coap_log_warn("coap_socket_connect_tcp2: connect failed: %s\n",
220 return 0;
221 }
222
223 if (getsockname(sock->fd, &local_addr->addr.sa, &local_addr->size) == COAP_SOCKET_ERROR) {
224 coap_log_warn("coap_socket_connect_tcp: getsockname: %s\n",
226 }
227
228 if (getpeername(sock->fd, &remote_addr->addr.sa, &remote_addr->size) == COAP_SOCKET_ERROR) {
229 coap_log_warn("coap_socket_connect_tcp: getpeername: %s\n",
231 }
232
233 return 1;
234}
235
236int
238 const coap_address_t *listen_addr,
239 coap_address_t *bound_addr) {
240 int on = 1;
241#if COAP_IPV6_SUPPORT
242 int off = 0;
243#endif /* COAP_IPV6_SUPPORT */
244#ifdef _WIN32
245 u_long u_on = 1;
246#endif
247
248 sock->fd = socket(listen_addr->addr.sa.sa_family, SOCK_STREAM, 0);
249
250 if (sock->fd == COAP_INVALID_SOCKET) {
251 coap_log_warn("coap_socket_bind_tcp: socket: %s\n",
253 goto error;
254 }
255
256#ifdef _WIN32
257 if (ioctlsocket(sock->fd, FIONBIO, &u_on) == COAP_SOCKET_ERROR) {
258#else
259 if (ioctl(sock->fd, FIONBIO, &on) == COAP_SOCKET_ERROR) {
260#endif
261 coap_log_warn("coap_socket_bind_tcp: ioctl FIONBIO: %s\n",
263 }
264 if (setsockopt(sock->fd, SOL_SOCKET, SO_KEEPALIVE, OPTVAL_T(&on),
265 sizeof(on)) == COAP_SOCKET_ERROR)
266 coap_log_warn("coap_socket_bind_tcp: setsockopt SO_KEEPALIVE: %s\n",
268
269 if (setsockopt(sock->fd, SOL_SOCKET, SO_REUSEADDR, OPTVAL_T(&on),
270 sizeof(on)) == COAP_SOCKET_ERROR)
271 coap_log_warn("coap_socket_bind_tcp: setsockopt SO_REUSEADDR: %s\n",
273
274 switch (listen_addr->addr.sa.sa_family) {
275#if COAP_IPV4_SUPPORT
276 case AF_INET:
277 break;
278#endif /* COAP_IPV4_SUPPORT */
279#if COAP_IPV6_SUPPORT
280 case AF_INET6:
281 /* Configure the socket as dual-stacked */
282 if (setsockopt(sock->fd, IPPROTO_IPV6, IPV6_V6ONLY, OPTVAL_T(&off),
283 sizeof(off)) == COAP_SOCKET_ERROR)
284 coap_log_alert("coap_socket_bind_tcp: setsockopt IPV6_V6ONLY: %s\n",
286 break;
287#endif /* COAP_IPV6_SUPPORT */
288#if COAP_AF_UNIX_SUPPORT
289 case AF_UNIX:
290 break;
291#endif /* COAP_AF_UNIX_SUPPORT */
292 default:
293 coap_log_alert("coap_socket_bind_tcp: unsupported sa_family\n");
294 }
295
296 if (bind(sock->fd, &listen_addr->addr.sa,
298 listen_addr->addr.sa.sa_family == AF_INET ?
299 (socklen_t)sizeof(struct sockaddr_in) :
300#endif /* COAP_IPV4_SUPPORT */
301 (socklen_t)listen_addr->size) == COAP_SOCKET_ERROR) {
302 coap_log_alert("coap_socket_bind_tcp: bind: %s\n",
304 goto error;
305 }
306
307 bound_addr->size = (socklen_t)sizeof(*bound_addr);
308 if (getsockname(sock->fd, &bound_addr->addr.sa, &bound_addr->size) < 0) {
309 coap_log_warn("coap_socket_bind_tcp: getsockname: %s\n",
311 goto error;
312 }
313
314 if (listen(sock->fd, 5) == COAP_SOCKET_ERROR) {
315 coap_log_alert("coap_socket_bind_tcp: listen: %s\n",
317 goto error;
318 }
319
320 return 1;
321
322error:
324 return 0;
325}
326
327int
329 coap_socket_t *new_client,
330 coap_address_t *local_addr,
331 coap_address_t *remote_addr,
332 void *extra) {
333#ifdef _WIN32
334 u_long u_on = 1;
335#else
336 int on = 1;
337#endif
338 (void)extra;
339
340 new_client->fd = accept(server->fd, &remote_addr->addr.sa,
341 &remote_addr->size);
342 if (new_client->fd == COAP_INVALID_SOCKET) {
343 if (errno != EAGAIN) {
344 coap_log_warn("coap_socket_accept_tcp: accept: %s\n",
346 }
347 return 0;
348 }
349
350 server->flags &= ~COAP_SOCKET_CAN_ACCEPT;
351
352 if (getsockname(new_client->fd, &local_addr->addr.sa, &local_addr->size) < 0)
353 coap_log_warn("coap_socket_accept_tcp: getsockname: %s\n",
355
356#ifdef _WIN32
357 if (ioctlsocket(new_client->fd, FIONBIO, &u_on) == COAP_SOCKET_ERROR) {
358#else
359 if (ioctl(new_client->fd, FIONBIO, &on) == COAP_SOCKET_ERROR) {
360#endif
361 coap_log_warn("coap_socket_accept_tcp: ioctl FIONBIO: %s\n",
363 }
364 return 1;
365}
366
367/*
368 * strm
369 * return +ve Number of bytes written.
370 * 0 No data written.
371 * -1 Error (error in errno).
372 */
373ssize_t
374coap_socket_write(coap_socket_t *sock, const uint8_t *data, size_t data_len) {
375 ssize_t r;
376
378#ifdef _WIN32
379 r = send(sock->fd, (const char *)data, (int)data_len, 0);
380#else
381#ifndef MSG_NOSIGNAL
382#define MSG_NOSIGNAL 0
383#endif /* MSG_NOSIGNAL */
384 r = send(sock->fd, data, data_len, MSG_NOSIGNAL);
385#endif
386 if (r == COAP_SOCKET_ERROR) {
387#ifdef _WIN32
388 coap_win_error_to_errno();
389#endif /* _WIN32 */
390 if (errno==EAGAIN ||
391#if EAGAIN != EWOULDBLOCK
392 errno == EWOULDBLOCK ||
393#endif
394 errno == EINTR) {
396#ifdef COAP_EPOLL_SUPPORT
398 EPOLLOUT |
399 ((sock->flags & COAP_SOCKET_WANT_READ) ?
400 EPOLLIN : 0),
401 __func__);
402#endif /* COAP_EPOLL_SUPPORT */
403 return 0;
404 }
405 if (errno == EPIPE || errno == ECONNRESET) {
406 coap_log_info("coap_socket_write: send: %s\n",
408 } else {
409 coap_log_warn("coap_socket_write: send: %s\n",
411 }
412 return -1;
413 }
414 if (r < (ssize_t)data_len) {
416#ifdef COAP_EPOLL_SUPPORT
418 EPOLLOUT |
419 ((sock->flags & COAP_SOCKET_WANT_READ) ?
420 EPOLLIN : 0),
421 __func__);
422#endif /* COAP_EPOLL_SUPPORT */
423 }
424 return r;
425}
426
427/*
428 * strm
429 * return >=0 Number of bytes read.
430 * -1 Error (error in errno).
431 */
432ssize_t
433coap_socket_read(coap_socket_t *sock, uint8_t *data, size_t data_len) {
434 ssize_t r;
435
436#ifdef _WIN32
437 r = recv(sock->fd, (char *)data, (int)data_len, 0);
438#else
439 r = recv(sock->fd, data, data_len, 0);
440#endif
441 if (r == 0) {
442 /* graceful shutdown */
443 sock->flags &= ~COAP_SOCKET_CAN_READ;
444 errno = ECONNRESET;
445 return -1;
446 } else if (r == COAP_SOCKET_ERROR) {
447 sock->flags &= ~COAP_SOCKET_CAN_READ;
448#ifdef _WIN32
449 coap_win_error_to_errno();
450#endif /* _WIN32 */
451 if (errno==EAGAIN ||
452#if EAGAIN != EWOULDBLOCK
453 errno == EWOULDBLOCK ||
454#endif
455 errno == EINTR) {
456 return 0;
457 }
458 if (errno != ECONNRESET) {
459 coap_log_warn("coap_socket_read: recv: %s\n",
461 }
462 return -1;
463 }
464 if (r < (ssize_t)data_len)
465 sock->flags &= ~COAP_SOCKET_CAN_READ;
466 return r;
467}
468
469void
471 /* For POSIX, this is the same as the datagram version */
473}
474
475#endif /* !COAP_DISABLE_TCP */
476
477#else /* WITH_LWIP || WITH_CONTIKI || RIOT_VERSION */
478
479#ifdef __clang__
480/* Make compilers happy that do not like empty modules. As this function is
481 * never used, we ignore -Wunused-function at the end of compiling this file
482 */
483#pragma GCC diagnostic ignored "-Wunused-function"
484#endif
485static inline void
486dummy(void) {
487}
488
489#endif /* WITH_LWIP || WITH_CONTIKI || RIOT_VERSION */
void coap_address_copy(coap_address_t *dst, const coap_address_t *src)
static void dummy(void)
#define COAP_IPV4_SUPPORT
const char * coap_socket_format_errno(int error)
Definition coap_io.c:803
const char * coap_socket_strerror(void)
Definition coap_io.c:814
#define COAP_SOCKET_ERROR
Definition coap_io.h:53
#define COAP_INVALID_SOCKET
Definition coap_io.h:54
#define COAP_SOCKET_CAN_WRITE
non blocking socket can now write without blocking
#define COAP_SOCKET_WANT_READ
non blocking socket is waiting for reading
#define COAP_SOCKET_WANT_WRITE
non blocking socket is waiting for writing
#define COAP_SOCKET_CAN_CONNECT
non blocking client socket can now connect without blocking
void coap_epoll_ctl_mod(coap_socket_t *sock, uint32_t events, const char *func)
Epoll specific function to modify the state of events that epoll is tracking on the appropriate file ...
#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.
#define MSG_NOSIGNAL
void coap_socket_dgrm_close(coap_socket_t *sock)
Function interface to close off a datagram socket.
#define coap_log_alert(...)
Definition coap_debug.h:90
#define coap_log_info(...)
Definition coap_debug.h:114
#define coap_log_warn(...)
Definition coap_debug.h:108
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.
Multi-purpose address abstraction.
socklen_t size
size of addr
struct sockaddr_in sin
struct coap_sockaddr_un cun
struct sockaddr_in6 sin6
struct sockaddr sa
union coap_address_t::@0 addr
char sun_path[COAP_UNIX_PATH_MAX]
coap_socket_flags_t flags
1 or more of COAP_SOCKET* flag values