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