libcoap 4.3.5-develop-19cef11
coap_tcp.c
Go to the documentation of this file.
1/*
2 * coap_tcp.c -- TCP functions for libcoap
3 *
4 * Copyright (C) 2019-2024 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
19int
21 return !COAP_DISABLE_TCP;
22}
23
24#if !COAP_DISABLE_TCP && !defined(WITH_LWIP) && !defined(WITH_CONTIKI) && !defined(RIOT_VERSION)
25#include <sys/types.h>
26#ifdef HAVE_SYS_SOCKET_H
27# include <sys/socket.h>
28# define OPTVAL_T(t) (t)
29# define OPTVAL_GT(t) (t)
30#endif
31#ifdef HAVE_SYS_IOCTL_H
32#include <sys/ioctl.h>
33#endif
34#ifdef HAVE_WS2TCPIP_H
35#include <ws2tcpip.h>
36# define OPTVAL_T(t) (const char*)(t)
37# define OPTVAL_GT(t) (char*)(t)
38# undef CMSG_DATA
39# define CMSG_DATA WSA_CMSG_DATA
40#endif
41
42int
44 const coap_address_t *local_if,
45 const coap_address_t *server,
46 int default_port,
47 coap_address_t *local_addr,
48 coap_address_t *remote_addr) {
49 int on = 1;
50#if COAP_IPV6_SUPPORT
51 int off = 0;
52#endif /* COAP_IPV6_SUPPORT */
53#ifdef _WIN32
54 u_long u_on = 1;
55#endif
56 coap_address_t connect_addr;
57 coap_address_copy(&connect_addr, server);
58
59 sock->flags &= ~COAP_SOCKET_CONNECTED;
60 sock->fd = socket(server->addr.sa.sa_family, SOCK_STREAM, 0);
61
62 if (sock->fd == COAP_INVALID_SOCKET) {
63 coap_log_warn("coap_socket_connect_tcp1: socket: %s\n",
65 goto error;
66 }
67
68#ifdef _WIN32
69 if (ioctlsocket(sock->fd, FIONBIO, &u_on) == COAP_SOCKET_ERROR) {
70#else
71 if (ioctl(sock->fd, FIONBIO, &on) == COAP_SOCKET_ERROR) {
72#endif
73 coap_log_warn("coap_socket_connect_tcp1: ioctl FIONBIO: %s\n",
75 }
76
77 switch (server->addr.sa.sa_family) {
78#if COAP_IPV4_SUPPORT
79 case AF_INET:
80 if (connect_addr.addr.sin.sin_port == 0)
81 connect_addr.addr.sin.sin_port = htons(default_port);
82 break;
83#endif /* COAP_IPV4_SUPPORT */
84#if COAP_IPV6_SUPPORT
85 case AF_INET6:
86 if (connect_addr.addr.sin6.sin6_port == 0)
87 connect_addr.addr.sin6.sin6_port = htons(default_port);
88 /* Configure the socket as dual-stacked */
89 if (setsockopt(sock->fd, IPPROTO_IPV6, IPV6_V6ONLY, OPTVAL_T(&off),
90 sizeof(off)) == COAP_SOCKET_ERROR)
91 coap_log_warn("coap_socket_connect_tcp1: setsockopt IPV6_V6ONLY: %s\n",
93 break;
94#endif /* COAP_IPV6_SUPPORT */
95#if COAP_AF_UNIX_SUPPORT
96 case AF_UNIX:
97 break;
98#endif /* COAP_AF_UNIX_SUPPORT */
99 default:
100 coap_log_alert("coap_socket_connect_tcp1: unsupported sa_family\n");
101 break;
102 }
103
104 if (local_if && local_if->addr.sa.sa_family) {
105 coap_address_copy(local_addr, local_if);
106 if (setsockopt(sock->fd, SOL_SOCKET, SO_REUSEADDR, OPTVAL_T(&on), sizeof(on)) == COAP_SOCKET_ERROR)
107 coap_log_warn("coap_socket_connect_tcp1: setsockopt SO_REUSEADDR: %s\n",
109 if (bind(sock->fd, &local_if->addr.sa,
111 local_if->addr.sa.sa_family == AF_INET ?
112 (socklen_t)sizeof(struct sockaddr_in) :
113#endif /* COAP_IPV4_SUPPORT */
114 (socklen_t)local_if->size) == COAP_SOCKET_ERROR) {
115 coap_log_warn("coap_socket_connect_tcp1: bind: %s\n",
117 goto error;
118 }
119 } else {
120 local_addr->addr.sa.sa_family = server->addr.sa.sa_family;
121 }
122
123 if (connect(sock->fd, &connect_addr.addr.sa, connect_addr.size) == COAP_SOCKET_ERROR) {
124#ifdef _WIN32
125 if (WSAGetLastError() == WSAEWOULDBLOCK) {
126#else
127 if (errno == EINPROGRESS) {
128#endif
129 /*
130 * COAP_SOCKET_CONNECTED needs to be set here as there will be reads/writes
131 * by underlying TLS libraries during connect() and we do not want to
132 * assert() in coap_read_session() or coap_write_session() when called by coap_read()
133 */
135 return 1;
136 }
137 coap_log_warn("coap_socket_connect_tcp1: connect: %s\n",
139 goto error;
140 }
141
142 if (getsockname(sock->fd, &local_addr->addr.sa, &local_addr->size) == COAP_SOCKET_ERROR) {
143 coap_log_warn("coap_socket_connect_tcp1: getsockname: %s\n",
145 }
146
147 if (getpeername(sock->fd, &remote_addr->addr.sa, &remote_addr->size) == COAP_SOCKET_ERROR) {
148 coap_log_warn("coap_socket_connect_tcp1: getpeername: %s\n",
150 }
151
153 return 1;
154
155error:
156 coap_socket_close(sock);
157 return 0;
158}
159
160int
162 coap_address_t *local_addr,
163 coap_address_t *remote_addr) {
164 int error = 0;
165#ifdef _WIN32
166 int optlen = (int)sizeof(error);
167#else
168 socklen_t optlen = (socklen_t)sizeof(error);
169#endif
170
172
173 if (getsockopt(sock->fd, SOL_SOCKET, SO_ERROR, OPTVAL_GT(&error),
174 &optlen) == COAP_SOCKET_ERROR) {
175 coap_log_warn("coap_socket_connect_tcp2: getsockopt: %s\n",
177 }
178
179 if (error) {
180 coap_log_warn("coap_socket_connect_tcp2: connect failed: %s\n",
182 coap_socket_close(sock);
183 return 0;
184 }
185
186 if (getsockname(sock->fd, &local_addr->addr.sa, &local_addr->size) == COAP_SOCKET_ERROR) {
187 coap_log_warn("coap_socket_connect_tcp: getsockname: %s\n",
189 }
190
191 if (getpeername(sock->fd, &remote_addr->addr.sa, &remote_addr->size) == COAP_SOCKET_ERROR) {
192 coap_log_warn("coap_socket_connect_tcp: getpeername: %s\n",
194 }
195
196 return 1;
197}
198
199int
201 const coap_address_t *listen_addr,
202 coap_address_t *bound_addr) {
203 int on = 1;
204#if COAP_IPV6_SUPPORT
205 int off = 0;
206#endif /* COAP_IPV6_SUPPORT */
207#ifdef _WIN32
208 u_long u_on = 1;
209#endif
210
211 sock->fd = socket(listen_addr->addr.sa.sa_family, SOCK_STREAM, 0);
212
213 if (sock->fd == COAP_INVALID_SOCKET) {
214 coap_log_warn("coap_socket_bind_tcp: socket: %s\n",
216 goto error;
217 }
218
219#ifdef _WIN32
220 if (ioctlsocket(sock->fd, FIONBIO, &u_on) == COAP_SOCKET_ERROR) {
221#else
222 if (ioctl(sock->fd, FIONBIO, &on) == COAP_SOCKET_ERROR) {
223#endif
224 coap_log_warn("coap_socket_bind_tcp: ioctl FIONBIO: %s\n",
226 }
227 if (setsockopt(sock->fd, SOL_SOCKET, SO_KEEPALIVE, OPTVAL_T(&on),
228 sizeof(on)) == COAP_SOCKET_ERROR)
229 coap_log_warn("coap_socket_bind_tcp: setsockopt SO_KEEPALIVE: %s\n",
231
232 if (setsockopt(sock->fd, SOL_SOCKET, SO_REUSEADDR, OPTVAL_T(&on),
233 sizeof(on)) == COAP_SOCKET_ERROR)
234 coap_log_warn("coap_socket_bind_tcp: setsockopt SO_REUSEADDR: %s\n",
236
237 switch (listen_addr->addr.sa.sa_family) {
238#if COAP_IPV4_SUPPORT
239 case AF_INET:
240 break;
241#endif /* COAP_IPV4_SUPPORT */
242#if COAP_IPV6_SUPPORT
243 case AF_INET6:
244 /* Configure the socket as dual-stacked */
245 if (setsockopt(sock->fd, IPPROTO_IPV6, IPV6_V6ONLY, OPTVAL_T(&off),
246 sizeof(off)) == COAP_SOCKET_ERROR)
247 coap_log_alert("coap_socket_bind_tcp: setsockopt IPV6_V6ONLY: %s\n",
249 break;
250#endif /* COAP_IPV6_SUPPORT */
251#if COAP_AF_UNIX_SUPPORT
252 case AF_UNIX:
253 break;
254#endif /* COAP_AF_UNIX_SUPPORT */
255 default:
256 coap_log_alert("coap_socket_bind_tcp: unsupported sa_family\n");
257 }
258
259 if (bind(sock->fd, &listen_addr->addr.sa,
261 listen_addr->addr.sa.sa_family == AF_INET ?
262 (socklen_t)sizeof(struct sockaddr_in) :
263#endif /* COAP_IPV4_SUPPORT */
264 (socklen_t)listen_addr->size) == COAP_SOCKET_ERROR) {
265 coap_log_alert("coap_socket_bind_tcp: bind: %s\n",
267 goto error;
268 }
269
270 bound_addr->size = (socklen_t)sizeof(*bound_addr);
271 if (getsockname(sock->fd, &bound_addr->addr.sa, &bound_addr->size) < 0) {
272 coap_log_warn("coap_socket_bind_tcp: getsockname: %s\n",
274 goto error;
275 }
276
277 if (listen(sock->fd, 5) == COAP_SOCKET_ERROR) {
278 coap_log_alert("coap_socket_bind_tcp: listen: %s\n",
280 goto error;
281 }
282
283 return 1;
284
285error:
286 coap_socket_close(sock);
287 return 0;
288}
289
290int
292 coap_socket_t *new_client,
293 coap_address_t *local_addr,
294 coap_address_t *remote_addr,
295 void *extra) {
296#ifdef _WIN32
297 u_long u_on = 1;
298#else
299 int on = 1;
300#endif
301 (void)extra;
302
303 server->flags &= ~COAP_SOCKET_CAN_ACCEPT;
304
305 new_client->fd = accept(server->fd, &remote_addr->addr.sa,
306 &remote_addr->size);
307 if (new_client->fd == COAP_INVALID_SOCKET) {
308 coap_log_warn("coap_socket_accept_tcp: accept: %s\n",
310 return 0;
311 }
312
313 if (getsockname(new_client->fd, &local_addr->addr.sa, &local_addr->size) < 0)
314 coap_log_warn("coap_socket_accept_tcp: getsockname: %s\n",
316
317#ifdef _WIN32
318 if (ioctlsocket(new_client->fd, FIONBIO, &u_on) == COAP_SOCKET_ERROR) {
319#else
320 if (ioctl(new_client->fd, FIONBIO, &on) == COAP_SOCKET_ERROR) {
321#endif
322 coap_log_warn("coap_socket_accept_tcp: ioctl FIONBIO: %s\n",
324 }
325 return 1;
326}
327#endif /* !COAP_DISABLE_TCP */
void coap_address_copy(coap_address_t *dst, const coap_address_t *src)
Definition: coap_address.c:801
#define COAP_IPV4_SUPPORT
Definition: coap_internal.h:70
const char * coap_socket_format_errno(int error)
Definition: coap_io.c:1896
void coap_socket_close(coap_socket_t *sock)
Function interface to close off a socket.
Definition: coap_io.c:375
const char * coap_socket_strerror(void)
Definition: coap_io.c:1900
#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:20
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:200
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:43
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:291
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:161
Multi-purpose address abstraction.
Definition: coap_address.h:148
socklen_t size
size of addr
Definition: coap_address.h:149
struct sockaddr_in sin
Definition: coap_address.h:152
struct sockaddr_in6 sin6
Definition: coap_address.h:153
struct sockaddr sa
Definition: coap_address.h:151
union coap_address_t::@0 addr
coap_socket_flags_t flags
1 or more of COAP_SOCKET* flag values