• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
12 /**
13  * @file coap_tcp.c
14  * @brief CoAP TCP handling functions
15  */
16 
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
coap_tcp_is_supported(void)37 coap_tcp_is_supported(void) {
38   return !COAP_DISABLE_TCP;
39 }
40 
41 #if !COAP_DISABLE_TCP
42 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)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 
159 error:
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 
293 error:
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 */
338