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