libcoap 4.3.5-develop-5010974
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-2026 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#if EAGAIN != EWOULDBLOCK
345 && errno != EWOULDBLOCK
346#endif
347 ) {
348 coap_log_warn("coap_socket_accept_tcp: accept: %s\n",
350 }
351 return 0;
352 }
353
354 server->flags &= ~COAP_SOCKET_CAN_ACCEPT;
355
356 if (getsockname(new_client->fd, &local_addr->addr.sa, &local_addr->size) < 0)
357 coap_log_warn("coap_socket_accept_tcp: getsockname: %s\n",
359
360#ifdef _WIN32
361 if (ioctlsocket(new_client->fd, FIONBIO, &u_on) == COAP_SOCKET_ERROR) {
362#else
363 if (ioctl(new_client->fd, FIONBIO, &on) == COAP_SOCKET_ERROR) {
364#endif
365 coap_log_warn("coap_socket_accept_tcp: ioctl FIONBIO: %s\n",
367 }
368 return 1;
369}
370
371/*
372 * strm
373 * return +ve Number of bytes written.
374 * 0 No data written.
375 * -1 Error (error in errno).
376 */
377ssize_t
378coap_socket_write(coap_socket_t *sock, const uint8_t *data, size_t data_len) {
379 ssize_t r;
380
382#ifdef _WIN32
383 r = send(sock->fd, (const char *)data, (int)data_len, 0);
384#else
385#ifndef MSG_NOSIGNAL
386#define MSG_NOSIGNAL 0
387#endif /* MSG_NOSIGNAL */
388 r = send(sock->fd, data, data_len, MSG_NOSIGNAL);
389#endif
390 if (r == COAP_SOCKET_ERROR) {
391#ifdef _WIN32
392 coap_win_error_to_errno();
393#endif /* _WIN32 */
394 if (errno==EAGAIN ||
395#if EAGAIN != EWOULDBLOCK
396 errno == EWOULDBLOCK ||
397#endif
398 errno == EINTR) {
400#ifdef COAP_EPOLL_SUPPORT
402 EPOLLOUT |
403 ((sock->flags & COAP_SOCKET_WANT_READ) ?
404 EPOLLIN : 0),
405 __func__);
406#endif /* COAP_EPOLL_SUPPORT */
407 return 0;
408 }
409 if (errno == EPIPE || errno == ECONNRESET) {
410 coap_log_info("coap_socket_write: send: %s\n",
412 } else {
413 coap_log_warn("coap_socket_write: send: %s\n",
415 }
416 return -1;
417 }
418 if (r < (ssize_t)data_len) {
420#ifdef COAP_EPOLL_SUPPORT
422 EPOLLOUT |
423 ((sock->flags & COAP_SOCKET_WANT_READ) ?
424 EPOLLIN : 0),
425 __func__);
426#endif /* COAP_EPOLL_SUPPORT */
427 }
428 return r;
429}
430
431/*
432 * strm
433 * return >=0 Number of bytes read.
434 * -1 Error (error in errno).
435 */
436ssize_t
437coap_socket_read(coap_socket_t *sock, uint8_t *data, size_t data_len) {
438 ssize_t r;
439
440#ifdef _WIN32
441 r = recv(sock->fd, (char *)data, (int)data_len, 0);
442#else
443 r = recv(sock->fd, data, data_len, 0);
444#endif
445 if (r == 0) {
446 /* graceful shutdown */
447 sock->flags &= ~COAP_SOCKET_CAN_READ;
448 errno = ECONNRESET;
449 return -1;
450 } else if (r == COAP_SOCKET_ERROR) {
451 sock->flags &= ~COAP_SOCKET_CAN_READ;
452#ifdef _WIN32
453 coap_win_error_to_errno();
454#endif /* _WIN32 */
455 if (errno==EAGAIN ||
456#if EAGAIN != EWOULDBLOCK
457 errno == EWOULDBLOCK ||
458#endif
459 errno == EINTR) {
460 return 0;
461 }
462 if (errno != ECONNRESET) {
463 coap_log_warn("coap_socket_read: recv: %s\n",
465 }
466 return -1;
467 }
468 if (r < (ssize_t)data_len)
469 sock->flags &= ~COAP_SOCKET_CAN_READ;
470 return r;
471}
472
473void
475 /* For POSIX, this is the same as the datagram version */
477}
478
479#endif /* !COAP_DISABLE_TCP */
480
481#else /* WITH_LWIP || WITH_CONTIKI || RIOT_VERSION */
482
483#ifdef __clang__
484/* Make compilers happy that do not like empty modules. As this function is
485 * never used, we ignore -Wunused-function at the end of compiling this file
486 */
487#pragma GCC diagnostic ignored "-Wunused-function"
488#endif
489static inline void
490dummy(void) {
491}
492
493#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:924
const char * coap_socket_strerror(void)
Definition coap_io.c:935
#define COAP_SOCKET_ERROR
Definition coap_io.h:51
#define COAP_INVALID_SOCKET
Definition coap_io.h:52
#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