libcoap 4.3.5-develop-4fa3dfa
Loading...
Searching...
No Matches
coap_tcp.c
Go to the documentation of this file.
1/*
2 * coap_tcp.c -- 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 COAP_AF_UNIX_SUPPORT
20#ifdef HAVE_UNISTD_H
21#include <unistd.h>
22#endif /* HAVE_UNISTD_H */
23#ifdef _WIN32
24#include <stdio.h>
25#endif /* _WIN32 */
26#endif /* COAP_AF_UNIX_SUPPORT */
27
28int
30 return !COAP_DISABLE_TCP;
31}
32
33#if !COAP_DISABLE_TCP && !defined(WITH_LWIP) && !defined(WITH_CONTIKI) && !defined(RIOT_VERSION)
34#include <sys/types.h>
35#ifdef HAVE_SYS_SOCKET_H
36# include <sys/socket.h>
37# define OPTVAL_T(t) (t)
38# define OPTVAL_GT(t) (t)
39#endif
40#ifdef HAVE_SYS_IOCTL_H
41#include <sys/ioctl.h>
42#endif
43#ifdef HAVE_WS2TCPIP_H
44#include <ws2tcpip.h>
45# define OPTVAL_T(t) (const char*)(t)
46# define OPTVAL_GT(t) (char*)(t)
47# undef CMSG_DATA
48# define CMSG_DATA WSA_CMSG_DATA
49#endif
50
51#if defined(__ZEPHYR__)
52# include <zephyr/posix/sys/ioctl.h>
53# ifndef OPTVAL_T
54# define OPTVAL_T(t) (t)
55# endif
56# ifndef OPTVAL_GT
57# define OPTVAL_GT(t) (t)
58# endif
59# ifndef FIONBIO
60# define FIONBIO 0x5421
61# endif
62#endif /* __ZEPHYR__ */
63
64int
66 const coap_address_t *local_if,
67 const coap_address_t *server,
68 int default_port,
69 coap_address_t *local_addr,
70 coap_address_t *remote_addr) {
71 int on = 1;
72#if COAP_IPV6_SUPPORT
73 int off = 0;
74#endif /* COAP_IPV6_SUPPORT */
75#ifdef _WIN32
76 u_long u_on = 1;
77#endif
78 coap_address_t connect_addr;
79 coap_address_copy(&connect_addr, server);
80
81 sock->flags &= ~COAP_SOCKET_CONNECTED;
82 sock->fd = socket(server->addr.sa.sa_family, SOCK_STREAM, 0);
83
84 if (sock->fd == COAP_INVALID_SOCKET) {
85 coap_log_warn("coap_socket_connect_tcp1: socket: %s\n",
87 goto error;
88 }
89
90#ifdef _WIN32
91 if (ioctlsocket(sock->fd, FIONBIO, &u_on) == COAP_SOCKET_ERROR) {
92#else
93 if (ioctl(sock->fd, FIONBIO, &on) == COAP_SOCKET_ERROR) {
94#endif
95 coap_log_warn("coap_socket_connect_tcp1: ioctl FIONBIO: %s\n",
97 }
98
99 switch (server->addr.sa.sa_family) {
100#if COAP_IPV4_SUPPORT
101 case AF_INET:
102 if (connect_addr.addr.sin.sin_port == 0)
103 connect_addr.addr.sin.sin_port = htons(default_port);
104 break;
105#endif /* COAP_IPV4_SUPPORT */
106#if COAP_IPV6_SUPPORT
107 case AF_INET6:
108 if (connect_addr.addr.sin6.sin6_port == 0)
109 connect_addr.addr.sin6.sin6_port = htons(default_port);
110 /* Configure the socket as dual-stacked */
111 if (setsockopt(sock->fd, IPPROTO_IPV6, IPV6_V6ONLY, OPTVAL_T(&off),
112 sizeof(off)) == COAP_SOCKET_ERROR)
113 coap_log_warn("coap_socket_connect_tcp1: setsockopt IPV6_V6ONLY: %s\n",
115 break;
116#endif /* COAP_IPV6_SUPPORT */
117#if COAP_AF_UNIX_SUPPORT
118 case AF_UNIX:
119 break;
120#endif /* COAP_AF_UNIX_SUPPORT */
121 default:
122 coap_log_alert("coap_socket_connect_tcp1: unsupported sa_family\n");
123 break;
124 }
125
126 if (local_if && local_if->addr.sa.sa_family) {
127 coap_address_copy(local_addr, local_if);
128 if (setsockopt(sock->fd, SOL_SOCKET, SO_REUSEADDR, OPTVAL_T(&on), sizeof(on)) == COAP_SOCKET_ERROR)
129 coap_log_warn("coap_socket_connect_tcp1: setsockopt SO_REUSEADDR: %s\n",
131 if (bind(sock->fd, &local_if->addr.sa,
133 local_if->addr.sa.sa_family == AF_INET ?
134 (socklen_t)sizeof(struct sockaddr_in) :
135#endif /* COAP_IPV4_SUPPORT */
136 (socklen_t)local_if->size) == COAP_SOCKET_ERROR) {
137 coap_log_warn("coap_socket_connect_tcp1: bind: %s\n",
139 goto error;
140 }
141 } else {
142 local_addr->addr.sa.sa_family = server->addr.sa.sa_family;
143 }
144
145 if (connect(sock->fd, &connect_addr.addr.sa, connect_addr.size) == COAP_SOCKET_ERROR) {
146#ifdef _WIN32
147 if (WSAGetLastError() == WSAEWOULDBLOCK) {
148#else
149 if (errno == EINPROGRESS) {
150#endif
151 /*
152 * COAP_SOCKET_CONNECTED needs to be set here as there will be reads/writes
153 * by underlying TLS libraries during connect() and we do not want to
154 * assert() in coap_read_session() or coap_write_session() when called by coap_read()
155 */
157 return 1;
158 }
159 coap_log_warn("coap_socket_connect_tcp1: connect: %s\n",
161 goto error;
162 }
163
164 if (getsockname(sock->fd, &local_addr->addr.sa, &local_addr->size) == COAP_SOCKET_ERROR) {
165 coap_log_warn("coap_socket_connect_tcp1: getsockname: %s\n",
167 }
168
169 if (getpeername(sock->fd, &remote_addr->addr.sa, &remote_addr->size) == COAP_SOCKET_ERROR) {
170 coap_log_warn("coap_socket_connect_tcp1: getpeername: %s\n",
172 }
173
175 return 1;
176
177error:
178#if COAP_AF_UNIX_SUPPORT
179 if (local_if && local_if->addr.sa.sa_family == AF_UNIX) {
180#ifdef _WIN32
181 _unlink(local_if->addr.cun.sun_path);
182#else /* ! _WIN32 */
183 unlink(local_if->addr.cun.sun_path);
184#endif /* ! _WIN32 */
185 }
186#endif /* COAP_AF_UNIX_SUPPORT */
187 coap_socket_close(sock);
188 return 0;
189}
190
191int
193 coap_address_t *local_addr,
194 coap_address_t *remote_addr) {
195 int error = 0;
196#ifdef _WIN32
197 int optlen = (int)sizeof(error);
198#else
199 socklen_t optlen = (socklen_t)sizeof(error);
200#endif
201
203
204 if (getsockopt(sock->fd, SOL_SOCKET, SO_ERROR, OPTVAL_GT(&error),
205 &optlen) == COAP_SOCKET_ERROR) {
206 coap_log_warn("coap_socket_connect_tcp2: getsockopt: %s\n",
208 }
209
210 if (error) {
211 coap_log_warn("coap_socket_connect_tcp2: connect failed: %s\n",
213 coap_socket_close(sock);
214 return 0;
215 }
216
217 if (getsockname(sock->fd, &local_addr->addr.sa, &local_addr->size) == COAP_SOCKET_ERROR) {
218 coap_log_warn("coap_socket_connect_tcp: getsockname: %s\n",
220 }
221
222 if (getpeername(sock->fd, &remote_addr->addr.sa, &remote_addr->size) == COAP_SOCKET_ERROR) {
223 coap_log_warn("coap_socket_connect_tcp: getpeername: %s\n",
225 }
226
227 return 1;
228}
229
230int
232 const coap_address_t *listen_addr,
233 coap_address_t *bound_addr) {
234 int on = 1;
235#if COAP_IPV6_SUPPORT
236 int off = 0;
237#endif /* COAP_IPV6_SUPPORT */
238#ifdef _WIN32
239 u_long u_on = 1;
240#endif
241
242 sock->fd = socket(listen_addr->addr.sa.sa_family, SOCK_STREAM, 0);
243
244 if (sock->fd == COAP_INVALID_SOCKET) {
245 coap_log_warn("coap_socket_bind_tcp: socket: %s\n",
247 goto error;
248 }
249
250#ifdef _WIN32
251 if (ioctlsocket(sock->fd, FIONBIO, &u_on) == COAP_SOCKET_ERROR) {
252#else
253 if (ioctl(sock->fd, FIONBIO, &on) == COAP_SOCKET_ERROR) {
254#endif
255 coap_log_warn("coap_socket_bind_tcp: ioctl FIONBIO: %s\n",
257 }
258 if (setsockopt(sock->fd, SOL_SOCKET, SO_KEEPALIVE, OPTVAL_T(&on),
259 sizeof(on)) == COAP_SOCKET_ERROR)
260 coap_log_warn("coap_socket_bind_tcp: setsockopt SO_KEEPALIVE: %s\n",
262
263 if (setsockopt(sock->fd, SOL_SOCKET, SO_REUSEADDR, OPTVAL_T(&on),
264 sizeof(on)) == COAP_SOCKET_ERROR)
265 coap_log_warn("coap_socket_bind_tcp: setsockopt SO_REUSEADDR: %s\n",
267
268 switch (listen_addr->addr.sa.sa_family) {
269#if COAP_IPV4_SUPPORT
270 case AF_INET:
271 break;
272#endif /* COAP_IPV4_SUPPORT */
273#if COAP_IPV6_SUPPORT
274 case AF_INET6:
275 /* Configure the socket as dual-stacked */
276 if (setsockopt(sock->fd, IPPROTO_IPV6, IPV6_V6ONLY, OPTVAL_T(&off),
277 sizeof(off)) == COAP_SOCKET_ERROR)
278 coap_log_alert("coap_socket_bind_tcp: setsockopt IPV6_V6ONLY: %s\n",
280 break;
281#endif /* COAP_IPV6_SUPPORT */
282#if COAP_AF_UNIX_SUPPORT
283 case AF_UNIX:
284 break;
285#endif /* COAP_AF_UNIX_SUPPORT */
286 default:
287 coap_log_alert("coap_socket_bind_tcp: unsupported sa_family\n");
288 }
289
290 if (bind(sock->fd, &listen_addr->addr.sa,
292 listen_addr->addr.sa.sa_family == AF_INET ?
293 (socklen_t)sizeof(struct sockaddr_in) :
294#endif /* COAP_IPV4_SUPPORT */
295 (socklen_t)listen_addr->size) == COAP_SOCKET_ERROR) {
296 coap_log_alert("coap_socket_bind_tcp: bind: %s\n",
298 goto error;
299 }
300
301 bound_addr->size = (socklen_t)sizeof(*bound_addr);
302 if (getsockname(sock->fd, &bound_addr->addr.sa, &bound_addr->size) < 0) {
303 coap_log_warn("coap_socket_bind_tcp: getsockname: %s\n",
305 goto error;
306 }
307
308 if (listen(sock->fd, 5) == COAP_SOCKET_ERROR) {
309 coap_log_alert("coap_socket_bind_tcp: listen: %s\n",
311 goto error;
312 }
313
314 return 1;
315
316error:
317 coap_socket_close(sock);
318 return 0;
319}
320
321int
323 coap_socket_t *new_client,
324 coap_address_t *local_addr,
325 coap_address_t *remote_addr,
326 void *extra) {
327#ifdef _WIN32
328 u_long u_on = 1;
329#else
330 int on = 1;
331#endif
332 (void)extra;
333
334 new_client->fd = accept(server->fd, &remote_addr->addr.sa,
335 &remote_addr->size);
336 if (new_client->fd == COAP_INVALID_SOCKET) {
337 if (errno != EAGAIN) {
338 coap_log_warn("coap_socket_accept_tcp: accept: %s\n",
340 }
341 return 0;
342 }
343
344 server->flags &= ~COAP_SOCKET_CAN_ACCEPT;
345
346 if (getsockname(new_client->fd, &local_addr->addr.sa, &local_addr->size) < 0)
347 coap_log_warn("coap_socket_accept_tcp: getsockname: %s\n",
349
350#ifdef _WIN32
351 if (ioctlsocket(new_client->fd, FIONBIO, &u_on) == COAP_SOCKET_ERROR) {
352#else
353 if (ioctl(new_client->fd, FIONBIO, &on) == COAP_SOCKET_ERROR) {
354#endif
355 coap_log_warn("coap_socket_accept_tcp: ioctl FIONBIO: %s\n",
357 }
358 return 1;
359}
360#endif /* !COAP_DISABLE_TCP */
void coap_address_copy(coap_address_t *dst, const coap_address_t *src)
#define COAP_IPV4_SUPPORT
const char * coap_socket_format_errno(int error)
Definition coap_io.c:2344
void coap_socket_close(coap_socket_t *sock)
Function interface to close off a socket.
Definition coap_io.c:390
const char * coap_socket_strerror(void)
Definition coap_io.c:2355
#define COAP_SOCKET_ERROR
Definition coap_io.h:49
#define COAP_INVALID_SOCKET
Definition coap_io.h:50
#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.
#define coap_log_alert(...)
Definition coap_debug.h:84
#define coap_log_warn(...)
Definition coap_debug.h:102
int coap_tcp_is_supported(void)
Check whether TCP is available.
Definition coap_tcp.c:29
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.
Definition coap_tcp.c:231
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.
Definition coap_tcp.c:65
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.
Definition coap_tcp.c:322
int coap_socket_connect_tcp2(coap_socket_t *sock, coap_address_t *local_addr, coap_address_t *remote_addr)
Complete the TCP Connection.
Definition coap_tcp.c:192
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