libcoap  4.3.0
coap_tcp.c
Go to the documentation of this file.
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
34  return !COAP_DISABLE_TCP;
35 }
36 
37 #if !COAP_DISABLE_TCP
38 int
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) {
60  "coap_socket_connect_tcp1: socket: %s\n",
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
72  "coap_socket_connect_tcp1: ioctl FIONBIO: %s\n",
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)
89  "coap_socket_connect_tcp1: setsockopt IPV6_V6ONLY: %s\n",
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)
102  "coap_socket_connect_tcp1: setsockopt SO_REUSEADDR: %s\n",
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",
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  */
128  return 1;
129  }
130  coap_log(LOG_WARNING, "coap_socket_connect_tcp1: connect: %s\n",
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",
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",
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
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 
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",
170  }
171 
172  if (error) {
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",
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",
188  }
189 
190  return 1;
191 }
192 
193 int
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",
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",
221  }
222 #endif /* RIOT_VERSION */
223  if (setsockopt (sock->fd, SOL_SOCKET, SO_KEEPALIVE, OPTVAL_T(&on),
224  sizeof (on)) == COAP_SOCKET_ERROR)
226  "coap_socket_bind_tcp: setsockopt SO_KEEPALIVE: %s\n",
228 
229  if (setsockopt(sock->fd, SOL_SOCKET, SO_REUSEADDR, OPTVAL_T(&on),
230  sizeof(on)) == COAP_SOCKET_ERROR)
232  "coap_socket_bind_tcp: setsockopt SO_REUSEADDR: %s\n",
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)
243  "coap_socket_bind_tcp: setsockopt IPV6_V6ONLY: %s\n",
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",
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",
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",
270  goto error;
271  }
272 
273  return 1;
274 
275 error:
276  coap_socket_close(sock);
277  return 0;
278 }
279 
280 int
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",
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",
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",
315  }
316 #endif /* RIOT_VERSION */
317  return 1;
318 }
319 #endif /* !COAP_DISABLE_TCP */
COAP_STATIC_INLINE void coap_address_copy(coap_address_t *dst, const coap_address_t *src)
Definition: address.h:152
Pulls together all the internal only header files.
void coap_socket_close(coap_socket_t *sock)
Definition: coap_io.c:368
const char * coap_socket_strerror(void)
Definition: coap_io.c:1502
const char * coap_socket_format_errno(int error)
Definition: coap_io.c:1499
#define COAP_SOCKET_ERROR
Definition: coap_io.h:44
#define COAP_INVALID_SOCKET
Definition: coap_io.h:45
#define COAP_SOCKET_CAN_ACCEPT
non blocking server socket can now accept without blocking
#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
int coap_tcp_is_supported(void)
Check whether TCP is available.
Definition: coap_tcp.c:33
#define coap_log(level,...)
Logging function.
Definition: coap_debug.h:152
@ LOG_ALERT
Alert.
Definition: coap_debug.h:53
@ LOG_WARNING
Warning.
Definition: coap_debug.h:56
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:194
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:39
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:281
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:154
multi-purpose address abstraction
Definition: address.h:96
socklen_t size
size of addr
Definition: address.h:97
struct sockaddr_in sin
Definition: address.h:100
struct sockaddr_in6 sin6
Definition: address.h:101
struct sockaddr sa
Definition: address.h:99
union coap_address_t::@0 addr
coap_socket_flags_t flags