libcoap 4.3.5-develop-783b531
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#if COAP_AF_LLC_SUPPORT
128 case AF_LLC:
129 break;
130#endif /* COAP_AF_LLC_SUPPORT */
131 default:
132 coap_log_alert("coap_socket_connect_tcp1: unsupported sa_family\n");
133 break;
134 }
135
136 if (local_if && local_if->addr.sa.sa_family) {
137 coap_address_copy(local_addr, local_if);
138 if (setsockopt(sock->fd, SOL_SOCKET, SO_REUSEADDR, OPTVAL_T(&on), sizeof(on)) == COAP_SOCKET_ERROR)
139 coap_log_warn("coap_socket_connect_tcp1: setsockopt SO_REUSEADDR: %s\n",
141 if (bind(sock->fd, &local_if->addr.sa,
143 local_if->addr.sa.sa_family == AF_INET ?
144 (socklen_t)sizeof(struct sockaddr_in) :
145#endif /* COAP_IPV4_SUPPORT */
146 (socklen_t)local_if->size) == COAP_SOCKET_ERROR) {
147 coap_log_warn("coap_socket_connect_tcp1: bind: %s\n",
149 goto error;
150 }
151 } else {
152 local_addr->addr.sa.sa_family = server->addr.sa.sa_family;
153 }
154
155 if (connect(sock->fd, &connect_addr.addr.sa, connect_addr.size) == COAP_SOCKET_ERROR) {
156#ifdef _WIN32
157 if (WSAGetLastError() == WSAEWOULDBLOCK) {
158#else
159 if (errno == EINPROGRESS) {
160#endif
161 /*
162 * COAP_SOCKET_CONNECTED needs to be set here as there will be reads/writes
163 * by underlying TLS libraries during connect() and we do not want to
164 * assert() in coap_read_session() or coap_write_session() when called by coap_read()
165 */
167 return 1;
168 }
169 coap_log_warn("coap_socket_connect_tcp1: connect: %s\n",
171 goto error;
172 }
173
174 if (getsockname(sock->fd, &local_addr->addr.sa, &local_addr->size) == COAP_SOCKET_ERROR) {
175 coap_log_warn("coap_socket_connect_tcp1: getsockname: %s\n",
177 }
178
179#if COAP_AF_LLC_SUPPORT
180 /* Current (7.0, 6.19.12 and similar vintage) Linux kernels do not return
181 * -EINPROGRESS for AF_LLC sockets with zero timeouts, as with AF_INET, so
182 * let’s assume we are connected, because getpeername() will error out.
183 */
184 if (coap_is_af_llc(remote_addr)) {
186 return 1;
187 }
188#endif /* COAP_AF_LLC_SUPPORT */
189
190 if (getpeername(sock->fd, &remote_addr->addr.sa, &remote_addr->size) == COAP_SOCKET_ERROR) {
191 coap_log_warn("coap_socket_connect_tcp1: getpeername: %s\n",
193 }
194
196 return 1;
197
198error:
199#if COAP_AF_UNIX_SUPPORT
200 if (local_if && local_if->addr.sa.sa_family == AF_UNIX) {
201#ifdef _WIN32
202 _unlink(local_if->addr.cun.sun_path);
203#else /* ! _WIN32 */
204 unlink(local_if->addr.cun.sun_path);
205#endif /* ! _WIN32 */
206 }
207#endif /* COAP_AF_UNIX_SUPPORT */
209 return 0;
210}
211
212int
214 coap_address_t *local_addr,
215 coap_address_t *remote_addr) {
216 int error = 0;
217#ifdef _WIN32
218 int optlen = (int)sizeof(error);
219#else
220 socklen_t optlen = (socklen_t)sizeof(error);
221#endif
222
224
225 if (getsockopt(sock->fd, SOL_SOCKET, SO_ERROR, OPTVAL_GT(&error),
226 &optlen) == COAP_SOCKET_ERROR) {
227 coap_log_warn("coap_socket_connect_tcp2: getsockopt: %s\n",
229 }
230
231 if (error) {
232 coap_log_warn("coap_socket_connect_tcp2: connect failed: %s\n",
235 return 0;
236 }
237
238 if (getsockname(sock->fd, &local_addr->addr.sa, &local_addr->size) == COAP_SOCKET_ERROR) {
239 coap_log_warn("coap_socket_connect_tcp: getsockname: %s\n",
241 }
242
243 if (getpeername(sock->fd, &remote_addr->addr.sa, &remote_addr->size) == COAP_SOCKET_ERROR) {
244 coap_log_warn("coap_socket_connect_tcp: getpeername: %s\n",
246 }
247
248 return 1;
249}
250
251int
253 const coap_address_t *listen_addr,
254 coap_address_t *bound_addr) {
255 int on = 1;
256#if COAP_IPV6_SUPPORT
257 int off = 0;
258#endif /* COAP_IPV6_SUPPORT */
259#ifdef _WIN32
260 u_long u_on = 1;
261#endif
262
263 sock->fd = socket(listen_addr->addr.sa.sa_family, SOCK_STREAM, 0);
264
265 if (sock->fd == COAP_INVALID_SOCKET) {
266 coap_log_warn("coap_socket_bind_tcp: socket: %s\n",
268 goto error;
269 }
270
271#ifdef _WIN32
272 if (ioctlsocket(sock->fd, FIONBIO, &u_on) == COAP_SOCKET_ERROR) {
273#else
274 if (ioctl(sock->fd, FIONBIO, &on) == COAP_SOCKET_ERROR) {
275#endif
276 coap_log_warn("coap_socket_bind_tcp: ioctl FIONBIO: %s\n",
278 }
279 if (setsockopt(sock->fd, SOL_SOCKET, SO_KEEPALIVE, OPTVAL_T(&on),
280 sizeof(on)) == COAP_SOCKET_ERROR)
281 coap_log_warn("coap_socket_bind_tcp: setsockopt SO_KEEPALIVE: %s\n",
283
284 if (setsockopt(sock->fd, SOL_SOCKET, SO_REUSEADDR, OPTVAL_T(&on),
285 sizeof(on)) == COAP_SOCKET_ERROR)
286 coap_log_warn("coap_socket_bind_tcp: setsockopt SO_REUSEADDR: %s\n",
288
289 switch (listen_addr->addr.sa.sa_family) {
290#if COAP_IPV4_SUPPORT
291 case AF_INET:
292 break;
293#endif /* COAP_IPV4_SUPPORT */
294#if COAP_IPV6_SUPPORT
295 case AF_INET6:
296 /* Configure the socket as dual-stacked */
297 if (setsockopt(sock->fd, IPPROTO_IPV6, IPV6_V6ONLY, OPTVAL_T(&off),
298 sizeof(off)) == COAP_SOCKET_ERROR)
299 coap_log_alert("coap_socket_bind_tcp: setsockopt IPV6_V6ONLY: %s\n",
301 break;
302#endif /* COAP_IPV6_SUPPORT */
303#if COAP_AF_UNIX_SUPPORT
304 case AF_UNIX:
305 break;
306#endif /* COAP_AF_UNIX_SUPPORT */
307#if COAP_AF_LLC_SUPPORT
308 case AF_LLC:
309 break;
310#endif /* COAP_AF_LLC_SUPPORT */
311 default:
312 coap_log_alert("coap_socket_bind_tcp: unsupported sa_family\n");
313 }
314
315 if (bind(sock->fd, &listen_addr->addr.sa,
317 listen_addr->addr.sa.sa_family == AF_INET ?
318 (socklen_t)sizeof(struct sockaddr_in) :
319#endif /* COAP_IPV4_SUPPORT */
320 (socklen_t)listen_addr->size) == COAP_SOCKET_ERROR) {
321 coap_log_alert("coap_socket_bind_tcp: bind: %s\n",
323 goto error;
324 }
325
326 bound_addr->size = (socklen_t)sizeof(*bound_addr);
327 if (getsockname(sock->fd, &bound_addr->addr.sa, &bound_addr->size) < 0) {
328 coap_log_warn("coap_socket_bind_tcp: getsockname: %s\n",
330 goto error;
331 }
332
333 if (listen(sock->fd, 5) == COAP_SOCKET_ERROR) {
334 coap_log_alert("coap_socket_bind_tcp: listen: %s\n",
336 goto error;
337 }
338
339 return 1;
340
341error:
343 return 0;
344}
345
346int
348 coap_socket_t *new_client,
349 coap_address_t *local_addr,
350 coap_address_t *remote_addr,
351 void *extra) {
352#ifdef _WIN32
353 u_long u_on = 1;
354#else
355 int on = 1;
356#endif
357 (void)extra;
358
359 new_client->fd = accept(server->fd, &remote_addr->addr.sa,
360 &remote_addr->size);
361 if (new_client->fd == COAP_INVALID_SOCKET) {
362 if (errno != EAGAIN
363#if EAGAIN != EWOULDBLOCK
364 && errno != EWOULDBLOCK
365#endif
366 ) {
367 coap_log_warn("coap_socket_accept_tcp: accept: %s\n",
369 }
370 return 0;
371 }
372
373 server->flags &= ~COAP_SOCKET_CAN_ACCEPT;
374
375 if (getsockname(new_client->fd, &local_addr->addr.sa, &local_addr->size) < 0)
376 coap_log_warn("coap_socket_accept_tcp: getsockname: %s\n",
378
379#ifdef _WIN32
380 if (ioctlsocket(new_client->fd, FIONBIO, &u_on) == COAP_SOCKET_ERROR) {
381#else
382 if (ioctl(new_client->fd, FIONBIO, &on) == COAP_SOCKET_ERROR) {
383#endif
384 coap_log_warn("coap_socket_accept_tcp: ioctl FIONBIO: %s\n",
386 }
387 return 1;
388}
389
390/*
391 * strm
392 * return +ve Number of bytes written.
393 * 0 No data written.
394 * -1 Error (error in errno).
395 */
396ssize_t
397coap_socket_write(coap_socket_t *sock, const uint8_t *data, size_t data_len) {
398 ssize_t r;
399
401#ifdef _WIN32
402 r = send(sock->fd, (const char *)data, (int)data_len, 0);
403#else
404#ifndef MSG_NOSIGNAL
405#define MSG_NOSIGNAL 0
406#endif /* MSG_NOSIGNAL */
407 r = send(sock->fd, data, data_len, MSG_NOSIGNAL);
408#endif
409 if (r == COAP_SOCKET_ERROR) {
410#ifdef _WIN32
411 coap_win_error_to_errno();
412#endif /* _WIN32 */
413 if (errno==EAGAIN ||
414#if EAGAIN != EWOULDBLOCK
415 errno == EWOULDBLOCK ||
416#endif
417 errno == EINTR) {
419#ifdef COAP_EPOLL_SUPPORT
421 EPOLLOUT |
422 ((sock->flags & COAP_SOCKET_WANT_READ) ?
423 EPOLLIN : 0),
424 __func__);
425#endif /* COAP_EPOLL_SUPPORT */
426 return 0;
427 }
428 if (errno == EPIPE || errno == ECONNRESET) {
429 coap_log_info("coap_socket_write: send: %s\n",
431 } else {
432 coap_log_warn("coap_socket_write: send: %s\n",
434 }
435 return -1;
436 }
437 if (r < (ssize_t)data_len) {
439#ifdef COAP_EPOLL_SUPPORT
441 EPOLLOUT |
442 ((sock->flags & COAP_SOCKET_WANT_READ) ?
443 EPOLLIN : 0),
444 __func__);
445#endif /* COAP_EPOLL_SUPPORT */
446 }
447 return r;
448}
449
450/*
451 * strm
452 * return >=0 Number of bytes read.
453 * -1 Error (error in errno).
454 */
455ssize_t
456coap_socket_read(coap_socket_t *sock, uint8_t *data, size_t data_len) {
457 ssize_t r;
458
459#ifdef _WIN32
460 r = recv(sock->fd, (char *)data, (int)data_len, 0);
461#else
462 r = recv(sock->fd, data, data_len, 0);
463#endif
464 if (r == 0) {
465 /* graceful shutdown */
466 sock->flags &= ~COAP_SOCKET_CAN_READ;
467 errno = ECONNRESET;
468 return -1;
469 } else if (r == COAP_SOCKET_ERROR) {
470 sock->flags &= ~COAP_SOCKET_CAN_READ;
471#ifdef _WIN32
472 coap_win_error_to_errno();
473#endif /* _WIN32 */
474 if (errno==EAGAIN ||
475#if EAGAIN != EWOULDBLOCK
476 errno == EWOULDBLOCK ||
477#endif
478 errno == EINTR) {
479 return 0;
480 }
481 if (errno != ECONNRESET) {
482 coap_log_warn("coap_socket_read: recv: %s\n",
484 }
485 return -1;
486 }
487 if (r < (ssize_t)data_len)
488 sock->flags &= ~COAP_SOCKET_CAN_READ;
489 return r;
490}
491
492void
494 /* For POSIX, this is the same as the datagram version */
496}
497
498#endif /* !COAP_DISABLE_TCP */
499
500#else /* WITH_LWIP || WITH_CONTIKI || RIOT_VERSION */
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 || WITH_CONTIKI || RIOT_VERSION */
int coap_is_af_llc(const coap_address_t *a)
Checks if given address a denotes an AF_LLC address.
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:952
const char * coap_socket_strerror(void)
Definition coap_io.c:963
#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