libcoap 4.3.2rc2

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