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