/src/libcoap/src/coap_dgrm_posix.c
Line | Count | Source |
1 | | /* |
2 | | * coap_dgrm_posix.c -- Datagram (UDP) functions for libcoap |
3 | | * |
4 | | * Copyright (C) 2019-2026 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_dgrm_posix.c |
14 | | * @brief Posix CoAP Datagram (UDP) handling functions |
15 | | */ |
16 | | |
17 | | #include "coap3/coap_libcoap_build.h" |
18 | | |
19 | | #if ! defined(WITH_LWIP) && ! defined(WITH_CONTIKI) && ! defined (RIOT_VERSION) |
20 | | |
21 | | #ifdef HAVE_STDIO_H |
22 | | # include <stdio.h> |
23 | | #endif |
24 | | #ifdef HAVE_UNISTD_H |
25 | | # include <unistd.h> |
26 | | #endif |
27 | | |
28 | | #ifndef __ZEPHYR__ |
29 | | #ifdef HAVE_SYS_SELECT_H |
30 | | # include <sys/select.h> |
31 | | #endif |
32 | | #ifdef HAVE_SYS_SOCKET_H |
33 | | # include <sys/socket.h> |
34 | 26 | # define OPTVAL_T(t) (t) |
35 | | # define OPTVAL_GT(t) (t) |
36 | | #endif |
37 | | #ifdef HAVE_SYS_IOCTL_H |
38 | | #include <sys/ioctl.h> |
39 | | #endif |
40 | | #ifdef HAVE_NETINET_IN_H |
41 | | # include <netinet/in.h> |
42 | | #endif |
43 | | #ifdef HAVE_NETINET_IP_ICMP_H |
44 | | # include <netinet/ip_icmp.h> |
45 | | #endif |
46 | | #ifdef HAVE_NETINET_ICMP6_H |
47 | | # include <netinet/icmp6.h> |
48 | | #endif |
49 | | #ifdef HAVE_LINUX_ERRQUEUE_H |
50 | | # include <linux/errqueue.h> |
51 | | #endif |
52 | | #ifdef HAVE_SYS_SELECT_H |
53 | | # include <sys/select.h> |
54 | | #endif |
55 | | #ifdef HAVE_WS2TCPIP_H |
56 | | #include <ws2tcpip.h> |
57 | | # define OPTVAL_T(t) (const char*)(t) |
58 | | # define OPTVAL_GT(t) (char*)(t) |
59 | | # undef CMSG_DATA |
60 | | # define CMSG_DATA WSA_CMSG_DATA |
61 | | #endif |
62 | | #ifdef HAVE_SYS_UIO_H |
63 | | # include <sys/uio.h> |
64 | | #endif |
65 | | #ifdef _WIN32 |
66 | | #include <stdio.h> |
67 | | #endif /* _WIN32 */ |
68 | | #ifdef COAP_EPOLL_SUPPORT |
69 | | #include <sys/epoll.h> |
70 | | #include <sys/timerfd.h> |
71 | | #ifdef HAVE_LIMITS_H |
72 | | #include <limits.h> |
73 | | #endif |
74 | | #endif /* COAP_EPOLL_SUPPORT */ |
75 | | #else /* __ZEPHYR__ */ |
76 | | #include <sys/ioctl.h> |
77 | | #include <sys/select.h> |
78 | | #define OPTVAL_T(t) (const void*)(t) |
79 | | #define OPTVAL_GT(t) (void*)(t) |
80 | | |
81 | | #ifndef IPV6_PKTINFO |
82 | | #ifdef IPV6_RECVPKTINFO |
83 | | #define IPV6_PKTINFO IPV6_RECVPKTINFO |
84 | | #else |
85 | | #define IPV6_PKTINFO IP_PKTINFO |
86 | | #endif |
87 | | #ifndef IN6_IS_ADDR_V4MAPPED |
88 | | #define IN6_IS_ADDR_V4MAPPED(a) \ |
89 | | ((((a)->s6_addr32[0]) == 0) && (((a)->s6_addr32[1]) == 0) && \ |
90 | | (((a)->s6_addr32[2]) == htonl(0xffff))) |
91 | | #endif |
92 | | #endif |
93 | | #endif /* __ZEPHYR__ */ |
94 | | |
95 | | /* define generic PKTINFO for IPv4 */ |
96 | | #if defined(IP_PKTINFO) |
97 | 0 | # define GEN_IP_PKTINFO IP_PKTINFO |
98 | | #elif defined(IP_RECVDSTADDR) |
99 | | # define GEN_IP_PKTINFO IP_RECVDSTADDR |
100 | | #else |
101 | | # error "Need IP_PKTINFO or IP_RECVDSTADDR to request ancillary data from OS." |
102 | | #endif /* IP_PKTINFO */ |
103 | | |
104 | | /* define generic PKTINFO for IPv6 */ |
105 | | #ifdef IPV6_RECVPKTINFO |
106 | 0 | # define GEN_IPV6_PKTINFO IPV6_RECVPKTINFO |
107 | | #elif defined(IPV6_PKTINFO) |
108 | | # define GEN_IPV6_PKTINFO IPV6_PKTINFO |
109 | | #else |
110 | | # error "Need IPV6_PKTINFO or IPV6_RECVPKTINFO to request ancillary data from OS." |
111 | | #endif /* IPV6_RECVPKTINFO */ |
112 | | |
113 | | #if COAP_SERVER_SUPPORT |
114 | | int |
115 | | coap_socket_bind_udp(coap_socket_t *sock, |
116 | | const coap_address_t *listen_addr, |
117 | 0 | coap_address_t *bound_addr) { |
118 | 0 | int on = 1; |
119 | 0 | #if COAP_IPV6_SUPPORT |
120 | 0 | int off = 0; |
121 | 0 | #endif /* COAP_IPV6_SUPPORT */ |
122 | | #ifdef _WIN32 |
123 | | u_long u_on = 1; |
124 | | #endif |
125 | |
|
126 | 0 | sock->fd = socket(listen_addr->addr.sa.sa_family, SOCK_DGRAM, 0); |
127 | |
|
128 | 0 | if (sock->fd == COAP_INVALID_SOCKET) { |
129 | 0 | coap_log_warn("coap_socket_bind_udp: socket: %s\n", coap_socket_strerror()); |
130 | 0 | goto error; |
131 | 0 | } |
132 | | #ifdef _WIN32 |
133 | | if (ioctlsocket(sock->fd, FIONBIO, &u_on) == COAP_SOCKET_ERROR) |
134 | | #else |
135 | 0 | if (ioctl(sock->fd, FIONBIO, &on) == COAP_SOCKET_ERROR) |
136 | 0 | #endif |
137 | 0 | { |
138 | 0 | coap_log_warn("coap_socket_bind_udp: ioctl FIONBIO: %s\n", coap_socket_strerror()); |
139 | 0 | } |
140 | |
|
141 | 0 | if (setsockopt(sock->fd, SOL_SOCKET, SO_REUSEADDR, OPTVAL_T(&on), sizeof(on)) == COAP_SOCKET_ERROR) |
142 | 0 | coap_log_warn("coap_socket_bind_udp: setsockopt SO_REUSEADDR: %s\n", |
143 | 0 | coap_socket_strerror()); |
144 | |
|
145 | 0 | switch (listen_addr->addr.sa.sa_family) { |
146 | 0 | #if COAP_IPV4_SUPPORT |
147 | 0 | case AF_INET: |
148 | 0 | if (setsockopt(sock->fd, IPPROTO_IP, GEN_IP_PKTINFO, OPTVAL_T(&on), |
149 | 0 | sizeof(on)) == COAP_SOCKET_ERROR) |
150 | 0 | coap_log_alert("coap_socket_bind_udp: setsockopt IP_PKTINFO: %s\n", |
151 | 0 | coap_socket_strerror()); |
152 | 0 | break; |
153 | 0 | #endif /* COAP_IPV4_SUPPORT */ |
154 | 0 | #if COAP_IPV6_SUPPORT |
155 | 0 | case AF_INET6: |
156 | | /* Configure the socket as dual-stacked */ |
157 | 0 | if (setsockopt(sock->fd, IPPROTO_IPV6, IPV6_V6ONLY, OPTVAL_T(&off), |
158 | 0 | sizeof(off)) == COAP_SOCKET_ERROR) |
159 | 0 | coap_log_alert("coap_socket_bind_udp: setsockopt IPV6_V6ONLY: %s\n", |
160 | 0 | coap_socket_strerror()); |
161 | 0 | #if !defined(ESPIDF_VERSION) |
162 | 0 | if (setsockopt(sock->fd, IPPROTO_IPV6, GEN_IPV6_PKTINFO, OPTVAL_T(&on), |
163 | 0 | sizeof(on)) == COAP_SOCKET_ERROR) |
164 | 0 | coap_log_alert("coap_socket_bind_udp: setsockopt IPV6_PKTINFO: %s\n", |
165 | 0 | coap_socket_strerror()); |
166 | 0 | #endif /* !defined(ESPIDF_VERSION) */ |
167 | 0 | #endif /* COAP_IPV6_SUPPORT */ |
168 | 0 | setsockopt(sock->fd, IPPROTO_IP, GEN_IP_PKTINFO, OPTVAL_T(&on), sizeof(on)); |
169 | | /* ignore error, because likely cause is that IPv4 is disabled at the os |
170 | | level */ |
171 | 0 | break; |
172 | 0 | #if COAP_AF_UNIX_SUPPORT |
173 | 0 | case AF_UNIX: |
174 | 0 | break; |
175 | 0 | #endif /* COAP_AF_UNIX_SUPPORT */ |
176 | 0 | #if COAP_AF_LLC_SUPPORT |
177 | 0 | case AF_LLC: |
178 | 0 | break; |
179 | 0 | #endif /* COAP_AF_LLC_SUPPORT */ |
180 | 0 | default: |
181 | 0 | coap_log_alert("coap_socket_bind_udp: unsupported sa_family\n"); |
182 | 0 | break; |
183 | 0 | } |
184 | | |
185 | 0 | if (bind(sock->fd, &listen_addr->addr.sa, |
186 | 0 | #if COAP_IPV4_SUPPORT |
187 | 0 | listen_addr->addr.sa.sa_family == AF_INET ? |
188 | 0 | (socklen_t)sizeof(struct sockaddr_in) : |
189 | 0 | #endif /* COAP_IPV4_SUPPORT */ |
190 | 0 | (socklen_t)listen_addr->size) == COAP_SOCKET_ERROR) { |
191 | 0 | coap_log_warn("coap_socket_bind_udp: bind: %s\n", |
192 | 0 | coap_socket_strerror()); |
193 | 0 | goto error; |
194 | 0 | } |
195 | | |
196 | 0 | bound_addr->size = (socklen_t)sizeof(*bound_addr); |
197 | 0 | if (getsockname(sock->fd, &bound_addr->addr.sa, &bound_addr->size) < 0) { |
198 | 0 | coap_log_warn("coap_socket_bind_udp: getsockname: %s\n", |
199 | 0 | coap_socket_strerror()); |
200 | 0 | goto error; |
201 | 0 | } |
202 | 0 | return 1; |
203 | | |
204 | 0 | error: |
205 | 0 | coap_socket_dgrm_close(sock); |
206 | 0 | return 0; |
207 | 0 | } |
208 | | #endif /* COAP_SERVER_SUPPORT */ |
209 | | |
210 | | #if COAP_CLIENT_SUPPORT |
211 | | int |
212 | | coap_socket_connect_udp(coap_socket_t *sock, |
213 | | const coap_address_t *local_if, |
214 | | const coap_address_t *server, |
215 | | int default_port, |
216 | | coap_address_t *local_addr, |
217 | 26 | coap_address_t *remote_addr) { |
218 | 26 | int on = 1; |
219 | 26 | #if COAP_IPV6_SUPPORT |
220 | 26 | int off = 0; |
221 | 26 | #endif /* COAP_IPV6_SUPPORT */ |
222 | | #ifdef _WIN32 |
223 | | u_long u_on = 1; |
224 | | #endif |
225 | 26 | coap_address_t connect_addr; |
226 | 26 | int is_mcast = coap_is_mcast(server); |
227 | 26 | coap_address_copy(&connect_addr, server); |
228 | | |
229 | 26 | sock->flags &= ~(COAP_SOCKET_CONNECTED | COAP_SOCKET_MULTICAST); |
230 | 26 | sock->fd = socket(connect_addr.addr.sa.sa_family, SOCK_DGRAM, 0); |
231 | | |
232 | 26 | if (sock->fd == COAP_INVALID_SOCKET) { |
233 | 0 | coap_log_warn("coap_socket_connect_udp: socket: %s\n", |
234 | 0 | coap_socket_strerror()); |
235 | 0 | goto error; |
236 | 0 | } |
237 | | |
238 | | #ifdef _WIN32 |
239 | | if (ioctlsocket(sock->fd, FIONBIO, &u_on) == COAP_SOCKET_ERROR) |
240 | | #else |
241 | 26 | if (ioctl(sock->fd, FIONBIO, &on) == COAP_SOCKET_ERROR) |
242 | 0 | #endif |
243 | 0 | { |
244 | | /* Ignore Zephyr unexpected Success response */ |
245 | 0 | if (errno != 0) { |
246 | 0 | int keep_errno = errno; |
247 | |
|
248 | 0 | coap_log_warn("coap_socket_connect_udp: ioctl FIONBIO: %s (%d)\n", |
249 | 0 | coap_socket_strerror(), keep_errno); |
250 | 0 | } |
251 | 0 | } |
252 | | |
253 | 26 | switch (connect_addr.addr.sa.sa_family) { |
254 | 0 | #if COAP_IPV4_SUPPORT |
255 | 26 | case AF_INET: |
256 | 26 | if (connect_addr.addr.sin.sin_port == 0) |
257 | 26 | connect_addr.addr.sin.sin_port = htons(default_port); |
258 | 26 | #if IP_RECVERR && defined(HAVE_STRUCT_CMSGHDR) && defined(HAVE_LINUX_ERRQUEUE_H) |
259 | 26 | if (setsockopt(sock->fd, IPPROTO_IP, IP_RECVERR, OPTVAL_T(&on), |
260 | 26 | sizeof(on)) == COAP_SOCKET_ERROR) |
261 | 26 | coap_log_alert("coap_socket_connect_udp: setsockopt IP_RECVERR: %s\n", |
262 | 26 | coap_socket_strerror()); |
263 | 26 | #endif /* IP_RECVERR && HAVE_STRUCT_CMSGHDR && HAVE_LINUX_ERRQUEUE_H */ |
264 | 26 | break; |
265 | 0 | #endif /* COAP_IPV4_SUPPORT */ |
266 | 0 | #if COAP_IPV6_SUPPORT |
267 | 0 | case AF_INET6: |
268 | 0 | if (connect_addr.addr.sin6.sin6_port == 0) |
269 | 0 | connect_addr.addr.sin6.sin6_port = htons(default_port); |
270 | | /* Configure the socket as dual-stacked */ |
271 | 0 | if (setsockopt(sock->fd, IPPROTO_IPV6, IPV6_V6ONLY, OPTVAL_T(&off), |
272 | 0 | sizeof(off)) == COAP_SOCKET_ERROR) |
273 | 0 | if (errno != ENOSYS) { |
274 | 0 | coap_log_warn("coap_socket_connect_udp: setsockopt IPV6_V6ONLY: %s\n", |
275 | 0 | coap_socket_strerror()); |
276 | 0 | } |
277 | 0 | #if IPV6_RECVERR && defined(HAVE_STRUCT_CMSGHDR) && defined(HAVE_LINUX_ERRQUEUE_H) |
278 | 0 | if (setsockopt(sock->fd, IPPROTO_IPV6, IPV6_RECVERR, OPTVAL_T(&on), |
279 | 0 | sizeof(on)) == COAP_SOCKET_ERROR) |
280 | 0 | coap_log_alert("coap_socket_connect_udp: setsockopt IPV6_RECVERR: %s\n", |
281 | 0 | coap_socket_strerror()); |
282 | 0 | #endif /* IPV6_RECVERR && HAVE_STRUCT_CMSGHDR && HAVE_LINUX_ERRQUEUE_H */ |
283 | 0 | #endif /* COAP_IPV6_SUPPORT */ |
284 | 0 | break; |
285 | 0 | #if COAP_AF_UNIX_SUPPORT |
286 | 0 | case AF_UNIX: |
287 | 0 | break; |
288 | 0 | #endif /* COAP_AF_UNIX_SUPPORT */ |
289 | 0 | #if COAP_AF_LLC_SUPPORT |
290 | 0 | case AF_LLC: |
291 | 0 | break; |
292 | 0 | #endif /* COAP_AF_LLC_SUPPORT */ |
293 | 0 | default: |
294 | 0 | coap_log_alert("coap_socket_connect_udp: unsupported sa_family %d\n", |
295 | 0 | connect_addr.addr.sa.sa_family); |
296 | 0 | goto error;; |
297 | 26 | } |
298 | | |
299 | 26 | if (local_if && local_if->addr.sa.sa_family) { |
300 | 0 | if (local_if->addr.sa.sa_family != connect_addr.addr.sa.sa_family) { |
301 | 0 | coap_log_warn("coap_socket_connect_udp: local address family != " |
302 | 0 | "remote address family\n"); |
303 | 0 | goto error; |
304 | 0 | } |
305 | 0 | if (setsockopt(sock->fd, SOL_SOCKET, SO_REUSEADDR, OPTVAL_T(&on), sizeof(on)) == COAP_SOCKET_ERROR) |
306 | 0 | coap_log_warn("coap_socket_connect_udp: setsockopt SO_REUSEADDR: %s\n", |
307 | 0 | coap_socket_strerror()); |
308 | 0 | if (bind(sock->fd, &local_if->addr.sa, |
309 | 0 | #if COAP_IPV4_SUPPORT |
310 | 0 | local_if->addr.sa.sa_family == AF_INET ? |
311 | 0 | (socklen_t)sizeof(struct sockaddr_in) : |
312 | 0 | #endif /* COAP_IPV4_SUPPORT */ |
313 | 0 | (socklen_t)local_if->size) == COAP_SOCKET_ERROR) { |
314 | 0 | coap_log_warn("coap_socket_connect_udp: bind: %s\n", |
315 | 0 | coap_socket_strerror()); |
316 | 0 | goto error; |
317 | 0 | } |
318 | 0 | #if COAP_AF_UNIX_SUPPORT |
319 | 26 | } else if (connect_addr.addr.sa.sa_family == AF_UNIX) { |
320 | | /* Need to bind to a local address for clarity over endpoints */ |
321 | 0 | coap_log_warn("coap_socket_connect_udp: local address required\n"); |
322 | 0 | goto error; |
323 | 0 | #endif /* COAP_AF_UNIX_SUPPORT */ |
324 | 0 | } |
325 | | |
326 | | /* special treatment for sockets that are used for multicast communication */ |
327 | 26 | if (is_mcast) { |
328 | 0 | if (!(local_if && local_if->addr.sa.sa_family)) { |
329 | | /* Bind to a (unused) port to simplify logging */ |
330 | 0 | coap_address_t bind_addr; |
331 | |
|
332 | 0 | coap_address_init(&bind_addr); |
333 | 0 | bind_addr.addr.sa.sa_family = connect_addr.addr.sa.sa_family; |
334 | 0 | if (bind(sock->fd, &bind_addr.addr.sa, |
335 | 0 | #if COAP_IPV4_SUPPORT |
336 | 0 | bind_addr.addr.sa.sa_family == AF_INET ? |
337 | 0 | (socklen_t)sizeof(struct sockaddr_in) : |
338 | 0 | #endif /* COAP_IPV4_SUPPORT */ |
339 | 0 | (socklen_t)bind_addr.size) == COAP_SOCKET_ERROR) { |
340 | 0 | coap_log_warn("coap_socket_connect_udp: bind: %s\n", |
341 | 0 | coap_socket_strerror()); |
342 | 0 | goto error; |
343 | 0 | } |
344 | 0 | } |
345 | 0 | if (getsockname(sock->fd, &local_addr->addr.sa, &local_addr->size) == COAP_SOCKET_ERROR) { |
346 | 0 | coap_log_warn("coap_socket_connect_udp: getsockname for multicast socket: %s\n", |
347 | 0 | coap_socket_strerror()); |
348 | 0 | } |
349 | 0 | coap_address_copy(remote_addr, &connect_addr); |
350 | 0 | coap_address_copy(&sock->mcast_addr, &connect_addr); |
351 | 0 | sock->flags |= COAP_SOCKET_MULTICAST; |
352 | 0 | if (coap_is_bcast(server) && |
353 | 0 | setsockopt(sock->fd, SOL_SOCKET, SO_BROADCAST, OPTVAL_T(&on), |
354 | 0 | sizeof(on)) == COAP_SOCKET_ERROR) |
355 | 0 | coap_log_warn("coap_socket_connect_udp: setsockopt SO_BROADCAST: %s\n", |
356 | 0 | coap_socket_strerror()); |
357 | 0 | return 1; |
358 | 0 | } |
359 | | |
360 | 26 | switch (connect_addr.addr.sa.sa_family) { |
361 | 0 | #if COAP_AF_LLC_SUPPORT |
362 | | /* Only SOCK_STREAM type AF_LLC sockets can be connect()ed. */ |
363 | 0 | case AF_LLC: |
364 | 0 | break; |
365 | 0 | #endif /* COAP_AF_LLC_SUPPORT */ |
366 | 0 | #if COAP_AF_UNIX_SUPPORT |
367 | 0 | case AF_UNIX: |
368 | 0 | #endif /* COAP_AF_UNIX_SUPPORT */ |
369 | 26 | default: |
370 | 26 | if (connect(sock->fd, &connect_addr.addr.sa, connect_addr.size) == COAP_SOCKET_ERROR) { |
371 | 0 | #if COAP_AF_UNIX_SUPPORT |
372 | 0 | if (connect_addr.addr.sa.sa_family == AF_UNIX) { |
373 | 0 | coap_log_warn("coap_socket_connect_udp: connect: %s: %s\n", |
374 | 0 | connect_addr.addr.cun.sun_path, coap_socket_strerror()); |
375 | 0 | } else |
376 | 0 | #endif /* COAP_AF_UNIX_SUPPORT */ |
377 | 0 | { |
378 | 0 | coap_log_warn("coap_socket_connect_udp: connect: %s (%d)\n", |
379 | 0 | coap_socket_strerror(), connect_addr.addr.sa.sa_family); |
380 | 0 | } |
381 | 0 | goto error; |
382 | 0 | } |
383 | | |
384 | 26 | if (getpeername(sock->fd, &remote_addr->addr.sa, &remote_addr->size) == COAP_SOCKET_ERROR) { |
385 | 0 | coap_log_warn("coap_socket_connect_udp: getpeername: %s\n", |
386 | 0 | coap_socket_strerror()); |
387 | 0 | } |
388 | 26 | break; |
389 | 26 | } |
390 | | |
391 | 26 | if (getsockname(sock->fd, &local_addr->addr.sa, &local_addr->size) == COAP_SOCKET_ERROR) { |
392 | 0 | coap_log_warn("coap_socket_connect_udp: getsockname: %s\n", |
393 | 0 | coap_socket_strerror()); |
394 | 0 | } |
395 | | |
396 | 26 | #if COAP_AF_LLC_SUPPORT |
397 | | /* AF_LLC sockets send to the local address without an explicit destination. */ |
398 | 26 | if (coap_is_af_llc(&connect_addr)) |
399 | 0 | sock->flags |= COAP_SOCKET_WANT_SENDTO; |
400 | 26 | #endif /* COAP_AF_LLC_SUPPORT */ |
401 | | |
402 | 26 | sock->flags |= COAP_SOCKET_CONNECTED; |
403 | 26 | return 1; |
404 | | |
405 | 0 | error: |
406 | 0 | coap_socket_dgrm_close(sock); |
407 | 0 | return 0; |
408 | 26 | } |
409 | | #endif /* COAP_CLIENT_SUPPORT */ |
410 | | |
411 | | #if !defined(__ZEPHYR__) |
412 | | #if 0 == ( defined(HAVE_NETINET_IN_H) || defined(HAVE_WS2TCPIP_H) ) |
413 | | /* define struct in6_pktinfo and struct in_pktinfo if not available |
414 | | FIXME: check with configure |
415 | | */ |
416 | | #if !defined(__MINGW32__) |
417 | | struct in6_pktinfo { |
418 | | struct in6_addr ipi6_addr; /* src/dst IPv6 address */ |
419 | | unsigned int ipi6_ifindex; /* send/recv interface index */ |
420 | | }; |
421 | | |
422 | | struct in_pktinfo { |
423 | | int ipi_ifindex; |
424 | | struct in_addr ipi_spec_dst; |
425 | | struct in_addr ipi_addr; |
426 | | }; |
427 | | #endif /* ! __MINGW32__ */ |
428 | | #endif |
429 | | #endif /* ! __ZEPHYR__ */ |
430 | | |
431 | | #if !defined(SOL_IP) |
432 | | /* Solaris expects level IPPROTO_IP for ancillary data. */ |
433 | | #define SOL_IP IPPROTO_IP |
434 | | #endif |
435 | | #ifdef _WIN32 |
436 | | #define COAP_SOL_IP IPPROTO_IP |
437 | | #else /* ! _WIN32 */ |
438 | 0 | #define COAP_SOL_IP SOL_IP |
439 | | #endif /* ! _WIN32 */ |
440 | | |
441 | | #if defined(_WIN32) |
442 | | #include <mswsock.h> |
443 | | #if defined(__MINGW32__) |
444 | | static __thread LPFN_WSARECVMSG lpWSARecvMsg = NULL; |
445 | | #if(_WIN32_WINNT >= 0x0600) |
446 | | #define CMSG_FIRSTHDR WSA_CMSG_FIRSTHDR |
447 | | #define CMSG_NXTHDR WSA_CMSG_NXTHDR |
448 | | #define CMSG_LEN WSA_CMSG_LEN |
449 | | #define CMSG_SPACE WSA_CMSG_SPACE |
450 | | #if(_WIN32_WINNT < 0x0603 || _WIN32_WINNT == 0x0a00) |
451 | | #define cmsghdr _WSACMSGHDR |
452 | | #endif /* (_WIN32_WINNT<0x0603 || _WIN32_WINNT == 0x0a00) */ |
453 | | #endif /* (_WIN32_WINNT>=0x0600) */ |
454 | | #else /* ! __MINGW32__ */ |
455 | | static __declspec(thread) LPFN_WSARECVMSG lpWSARecvMsg = NULL; |
456 | | #endif /* ! __MINGW32__ */ |
457 | | /* Map struct WSABUF fields to their posix counterpart */ |
458 | | #define msghdr _WSAMSG |
459 | | #define msg_name name |
460 | | #define msg_namelen namelen |
461 | | #define msg_iov lpBuffers |
462 | | #define msg_iovlen dwBufferCount |
463 | | #define msg_control Control.buf |
464 | | #define msg_controllen Control.len |
465 | | #define iovec _WSABUF |
466 | | #define iov_base buf |
467 | | #define iov_len len |
468 | | #define iov_len_t u_long |
469 | | #undef CMSG_DATA |
470 | | #define CMSG_DATA WSA_CMSG_DATA |
471 | | #define ipi_spec_dst ipi_addr |
472 | | #if !defined(__MINGW32__) |
473 | | #pragma warning( disable : 4116 ) |
474 | | #endif /* ! __MINGW32__ */ |
475 | | #else |
476 | | #define iov_len_t size_t |
477 | | #endif |
478 | | |
479 | | #if defined(_CYGWIN_ENV) || defined(__QNXNTO__) |
480 | | #define ipi_spec_dst ipi_addr |
481 | | #endif |
482 | | |
483 | | #if COAP_CLIENT_SUPPORT |
484 | | static uint32_t cid_track_counter; |
485 | | |
486 | | static void |
487 | 187 | coap_test_cid_tuple_change(coap_session_t *session) { |
488 | 187 | if (session->type == COAP_SESSION_TYPE_CLIENT && |
489 | 187 | session->negotiated_cid && |
490 | 0 | session->state == COAP_SESSION_STATE_ESTABLISHED && |
491 | 0 | session->proto == COAP_PROTO_DTLS && session->context->testing_cids) { |
492 | 0 | if ((++cid_track_counter) % session->context->testing_cids == 0) { |
493 | 0 | coap_address_t local_if = session->addr_info.local; |
494 | 0 | uint16_t port = coap_address_get_port(&local_if); |
495 | |
|
496 | 0 | port++; |
497 | 0 | coap_address_set_port(&local_if, port); |
498 | |
|
499 | 0 | coap_socket_dgrm_close(&session->sock); |
500 | 0 | session->sock.session = session; |
501 | 0 | if (!coap_socket_connect_udp(&session->sock, &local_if, &session->addr_info.remote, |
502 | 0 | port, |
503 | 0 | &session->addr_info.local, |
504 | 0 | &session->addr_info.remote)) { |
505 | 0 | coap_log_err("Tuple change for CID failed\n"); |
506 | 0 | return; |
507 | 0 | #ifdef COAP_EPOLL_SUPPORT |
508 | 0 | } else { |
509 | 0 | coap_epoll_ctl_add(&session->sock, |
510 | 0 | EPOLLIN | |
511 | 0 | ((session->sock.flags & COAP_SOCKET_WANT_CONNECT) ? |
512 | 0 | EPOLLOUT : 0), |
513 | 0 | __func__); |
514 | 0 | #endif /* COAP_EPOLL_SUPPORT */ |
515 | 0 | } |
516 | 0 | session->sock.flags |= COAP_SOCKET_NOT_EMPTY | COAP_SOCKET_WANT_READ | COAP_SOCKET_BOUND; |
517 | 0 | } |
518 | 0 | } |
519 | 187 | } |
520 | | #endif /* COAP_CLIENT_SUPPORT */ |
521 | | |
522 | | #if defined(HAVE_LINUX_ERRQUEUE_H) && defined(HAVE_STRUCT_CMSGHDR) |
523 | | static ssize_t |
524 | 0 | coap_get_icmp_data(coap_socket_t *sock, uint8_t *data, size_t length) { |
525 | 0 | coap_fd_t fd = sock->fd; |
526 | 0 | char control_buf[1024]; |
527 | 0 | struct iovec iov; |
528 | 0 | struct msghdr msg; |
529 | 0 | struct cmsghdr *cmsg; |
530 | 0 | int kerrno = errno; |
531 | 0 | int ret; |
532 | |
|
533 | 0 | if (length < sizeof(coap_icmp_info_t)) { |
534 | 0 | return -1; |
535 | 0 | } |
536 | 0 | iov.iov_base = data + sizeof(coap_icmp_info_t); |
537 | 0 | iov.iov_len = length - sizeof(coap_icmp_info_t);; |
538 | |
|
539 | 0 | memset(&msg, 0, sizeof(msg)); |
540 | 0 | msg.msg_name = NULL; |
541 | 0 | msg.msg_namelen = 0; |
542 | 0 | msg.msg_iov = &iov; |
543 | 0 | msg.msg_iovlen = 1; |
544 | 0 | msg.msg_control = control_buf; |
545 | 0 | msg.msg_controllen = sizeof(control_buf); |
546 | | |
547 | | /* Poll for errors using MSG_ERRQUEUE */ |
548 | 0 | ret = recvmsg(fd, &msg, MSG_ERRQUEUE); |
549 | 0 | errno = kerrno; |
550 | 0 | if (ret < 0) { |
551 | 0 | return -1; |
552 | 0 | } |
553 | | |
554 | | /* Parse control messages, looking for ICMP error messages */ |
555 | 0 | for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; cmsg = CMSG_NXTHDR(&msg, cmsg)) { |
556 | 0 | #if COAP_IPV4_SUPPORT && IP_RECVERR |
557 | 0 | if (cmsg->cmsg_level == SOL_IP && cmsg->cmsg_type == IP_RECVERR) { |
558 | 0 | struct sock_extended_err *err = (struct sock_extended_err *)CMSG_DATA(cmsg); |
559 | 0 | coap_icmp_info_t *info = (coap_icmp_info_t *)data; |
560 | |
|
561 | 0 | if (err->ee_origin == SO_EE_ORIGIN_ICMP) { |
562 | 0 | info->length = (uint32_t)ret; |
563 | 0 | info->cmsg_level = cmsg->cmsg_level; |
564 | 0 | info->ee_origin = err->ee_origin; |
565 | 0 | info->ee_type = err->ee_type; |
566 | 0 | info->ee_code = err->ee_code; |
567 | 0 | #if COAP_SERVER_SUPPORT |
568 | 0 | coap_log_debug("* %s: ICMP Error Type: %d, Code: %d\n", |
569 | 0 | sock->endpoint ? coap_endpoint_str(sock->endpoint) : |
570 | 0 | coap_session_str(sock->session), |
571 | 0 | err->ee_type, err->ee_code); |
572 | | #else /* ! COAP_SERVER_SUPPORT */ |
573 | | coap_log_debug("* %s: ICMP Error Type: %d, Code: %d\n", |
574 | | coap_session_str(sock->session), |
575 | | err->ee_type, err->ee_code); |
576 | | #endif /* ! COAP_SERVER_SUPPORT */ |
577 | 0 | return ret; |
578 | 0 | } |
579 | 0 | } |
580 | 0 | #endif /* COAP_IPV4_SUPPORT && IP_RECVERR */ |
581 | 0 | #if COAP_IPV6_SUPPORT && IPV6_RECVERR |
582 | 0 | if (cmsg->cmsg_level == SOL_IPV6 && cmsg->cmsg_type == IPV6_RECVERR) { |
583 | 0 | struct sock_extended_err *err = (struct sock_extended_err *)CMSG_DATA(cmsg); |
584 | 0 | coap_icmp_info_t *info = (coap_icmp_info_t *)data; |
585 | |
|
586 | 0 | if (err->ee_origin == SO_EE_ORIGIN_ICMP6 || err->ee_origin == SO_EE_ORIGIN_ICMP) { |
587 | 0 | info->length = (uint32_t)ret; |
588 | 0 | info->cmsg_level = cmsg->cmsg_level; |
589 | 0 | info->ee_origin = err->ee_origin; |
590 | 0 | info->ee_type = err->ee_type; |
591 | 0 | info->ee_code = err->ee_code; |
592 | 0 | #if COAP_SERVER_SUPPORT |
593 | 0 | coap_log_debug("* %s: ICMP%s Error Type: %d, Code: %d\n", |
594 | 0 | sock->endpoint ? coap_endpoint_str(sock->endpoint) : |
595 | 0 | coap_session_str(sock->session), |
596 | 0 | err->ee_origin == SO_EE_ORIGIN_ICMP6 ? "6" : "", |
597 | 0 | err->ee_type, err->ee_code); |
598 | | #else /* ! COAP_SERVER_SUPPORT */ |
599 | | coap_log_debug("* %s: ICMP%s Error Type: %d, Code: %d\n", |
600 | | coap_session_str(sock->session), |
601 | | err->ee_origin == SO_EE_ORIGIN_ICMP6 ? "6" : "", |
602 | | err->ee_type, err->ee_code); |
603 | | #endif /* ! COAP_SERVER_SUPPORT */ |
604 | 0 | return ret; |
605 | 0 | } |
606 | 0 | } |
607 | 0 | #endif /* COAP_IPV6_SUPPORT && IPV6_RECVERR */ |
608 | 0 | coap_log_debug("MSG_ERRQUEUE: Level %d, Type: %d\n", cmsg->cmsg_level, cmsg->cmsg_type); |
609 | 0 | } |
610 | 0 | return -1; |
611 | 0 | } |
612 | | #endif /* HAVE_LINUX_ERRQUEUE_H && HAVE_STRUCT_CMSGHDR */ |
613 | | |
614 | | /* |
615 | | * dgram |
616 | | * return +ve Number of bytes written. |
617 | | * -1 Error error in errno). |
618 | | */ |
619 | | ssize_t |
620 | | coap_socket_send(coap_socket_t *sock, coap_session_t *session, |
621 | 187 | const uint8_t *data, size_t datalen) { |
622 | 187 | ssize_t bytes_written = 0; |
623 | 187 | int r; |
624 | | |
625 | 187 | #if COAP_CLIENT_SUPPORT |
626 | 187 | coap_test_cid_tuple_change(session); |
627 | 187 | #endif /* COAP_CLIENT_SUPPORT */ |
628 | | |
629 | 187 | if ((r = coap_debug_send_packet()) != 1) { |
630 | 187 | if (r) |
631 | 92 | bytes_written = -1; |
632 | 95 | else |
633 | 95 | bytes_written = (ssize_t)datalen; |
634 | 187 | } else if (sock->flags & COAP_SOCKET_CONNECTED && |
635 | 0 | !(sock->flags & COAP_SOCKET_WANT_SENDTO)) { |
636 | | #ifdef _WIN32 |
637 | | bytes_written = send(sock->fd, (const char *)data, (int)datalen, 0); |
638 | | #else |
639 | 0 | bytes_written = send(sock->fd, data, datalen, 0); |
640 | 0 | #endif |
641 | 0 | } else { |
642 | | #if defined(_WIN32) |
643 | | DWORD dwNumberOfBytesSent = 0; |
644 | | #endif /* _WIN32 */ |
645 | 0 | #ifdef HAVE_STRUCT_CMSGHDR |
646 | | /* a buffer large enough to hold all packet info types, ipv6 is the largest */ |
647 | 0 | char buf[CMSG_SPACE(sizeof(struct in6_pktinfo))]; |
648 | 0 | struct msghdr mhdr; |
649 | 0 | struct iovec iov[1]; |
650 | 0 | const void *addr = &session->addr_info.remote.addr; |
651 | |
|
652 | 0 | assert(session); |
653 | | |
654 | 0 | memcpy(&iov[0].iov_base, &data, sizeof(iov[0].iov_base)); |
655 | 0 | iov[0].iov_len = (iov_len_t)datalen; |
656 | |
|
657 | 0 | memset(buf, 0, sizeof(buf)); |
658 | |
|
659 | 0 | memset(&mhdr, 0, sizeof(struct msghdr)); |
660 | 0 | memcpy(&mhdr.msg_name, &addr, sizeof(mhdr.msg_name)); |
661 | 0 | mhdr.msg_namelen = session->addr_info.remote.addr.sa.sa_family == AF_INET ? |
662 | 0 | (socklen_t)sizeof(struct sockaddr_in) : |
663 | 0 | session->addr_info.remote.size; |
664 | |
|
665 | 0 | mhdr.msg_iov = iov; |
666 | 0 | mhdr.msg_iovlen = 1; |
667 | |
|
668 | 0 | if (!coap_address_isany(&session->addr_info.local) && |
669 | 0 | !coap_is_mcast(&session->addr_info.local)) { |
670 | 0 | switch (session->addr_info.local.addr.sa.sa_family) { |
671 | 0 | #if COAP_IPV6_SUPPORT |
672 | 0 | case AF_INET6: { |
673 | 0 | struct cmsghdr *cmsg; |
674 | |
|
675 | 0 | #if COAP_IPV4_SUPPORT |
676 | 0 | if (IN6_IS_ADDR_V4MAPPED(&session->addr_info.local.addr.sin6.sin6_addr)) { |
677 | 0 | #if defined(IP_PKTINFO) |
678 | 0 | struct in_pktinfo *pktinfo; |
679 | 0 | mhdr.msg_control = buf; |
680 | 0 | mhdr.msg_controllen = CMSG_SPACE(sizeof(struct in_pktinfo)); |
681 | |
|
682 | 0 | cmsg = CMSG_FIRSTHDR(&mhdr); |
683 | 0 | cmsg->cmsg_level = COAP_SOL_IP; |
684 | 0 | cmsg->cmsg_type = IP_PKTINFO; |
685 | 0 | cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo)); |
686 | |
|
687 | 0 | pktinfo = (struct in_pktinfo *)CMSG_DATA(cmsg); |
688 | |
|
689 | 0 | pktinfo->ipi_ifindex = session->ifindex; |
690 | 0 | memcpy(&pktinfo->ipi_spec_dst, |
691 | 0 | session->addr_info.local.addr.sin6.sin6_addr.s6_addr + 12, |
692 | 0 | sizeof(pktinfo->ipi_spec_dst)); |
693 | | #elif defined(IP_SENDSRCADDR) |
694 | | mhdr.msg_control = buf; |
695 | | mhdr.msg_controllen = CMSG_SPACE(sizeof(struct in_addr)); |
696 | | |
697 | | cmsg = CMSG_FIRSTHDR(&mhdr); |
698 | | cmsg->cmsg_level = IPPROTO_IP; |
699 | | cmsg->cmsg_type = IP_SENDSRCADDR; |
700 | | cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_addr)); |
701 | | |
702 | | memcpy(CMSG_DATA(cmsg), |
703 | | session->addr_info.local.addr.sin6.sin6_addr.s6_addr + 12, |
704 | | sizeof(struct in_addr)); |
705 | | #endif /* IP_PKTINFO */ |
706 | 0 | } else { |
707 | 0 | #endif /* COAP_IPV4_SUPPORT */ |
708 | 0 | struct in6_pktinfo *pktinfo; |
709 | 0 | mhdr.msg_control = buf; |
710 | 0 | mhdr.msg_controllen = CMSG_SPACE(sizeof(struct in6_pktinfo)); |
711 | |
|
712 | 0 | cmsg = CMSG_FIRSTHDR(&mhdr); |
713 | 0 | cmsg->cmsg_level = IPPROTO_IPV6; |
714 | 0 | cmsg->cmsg_type = IPV6_PKTINFO; |
715 | 0 | cmsg->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo)); |
716 | |
|
717 | 0 | pktinfo = (struct in6_pktinfo *)CMSG_DATA(cmsg); |
718 | |
|
719 | 0 | if (coap_is_mcast(&session->addr_info.remote)) { |
720 | 0 | pktinfo->ipi6_ifindex = session->addr_info.remote.addr.sin6.sin6_scope_id; |
721 | 0 | } else { |
722 | 0 | pktinfo->ipi6_ifindex = session->ifindex; |
723 | 0 | } |
724 | 0 | memcpy(&pktinfo->ipi6_addr, |
725 | 0 | &session->addr_info.local.addr.sin6.sin6_addr, |
726 | 0 | sizeof(pktinfo->ipi6_addr)); |
727 | 0 | #if COAP_IPV4_SUPPORT |
728 | 0 | } |
729 | 0 | #endif /* COAP_IPV4_SUPPORT */ |
730 | 0 | break; |
731 | 0 | } |
732 | 0 | #endif /* COAP_IPV6_SUPPORT */ |
733 | 0 | #if COAP_IPV4_SUPPORT |
734 | 0 | case AF_INET: { |
735 | 0 | #if defined(IP_PKTINFO) |
736 | 0 | struct cmsghdr *cmsg; |
737 | 0 | struct in_pktinfo *pktinfo; |
738 | |
|
739 | 0 | mhdr.msg_control = buf; |
740 | 0 | mhdr.msg_controllen = CMSG_SPACE(sizeof(struct in_pktinfo)); |
741 | |
|
742 | 0 | cmsg = CMSG_FIRSTHDR(&mhdr); |
743 | 0 | cmsg->cmsg_level = COAP_SOL_IP; |
744 | 0 | cmsg->cmsg_type = IP_PKTINFO; |
745 | 0 | cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo)); |
746 | |
|
747 | 0 | pktinfo = (struct in_pktinfo *)CMSG_DATA(cmsg); |
748 | |
|
749 | 0 | pktinfo->ipi_ifindex = session->ifindex; |
750 | 0 | memcpy(&pktinfo->ipi_spec_dst, |
751 | 0 | &session->addr_info.local.addr.sin.sin_addr, |
752 | 0 | sizeof(pktinfo->ipi_spec_dst)); |
753 | | #elif defined(IP_SENDSRCADDR) |
754 | | struct cmsghdr *cmsg; |
755 | | mhdr.msg_control = buf; |
756 | | mhdr.msg_controllen = CMSG_SPACE(sizeof(struct in_addr)); |
757 | | |
758 | | cmsg = CMSG_FIRSTHDR(&mhdr); |
759 | | cmsg->cmsg_level = IPPROTO_IP; |
760 | | cmsg->cmsg_type = IP_SENDSRCADDR; |
761 | | cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_addr)); |
762 | | |
763 | | memcpy(CMSG_DATA(cmsg), |
764 | | &session->addr_info.local.addr.sin.sin_addr, |
765 | | sizeof(struct in_addr)); |
766 | | #endif /* IP_PKTINFO */ |
767 | 0 | break; |
768 | 0 | } |
769 | 0 | #endif /* COAP_IPV4_SUPPORT */ |
770 | 0 | #if COAP_AF_UNIX_SUPPORT |
771 | 0 | case AF_UNIX: |
772 | 0 | break; |
773 | 0 | #endif /* COAP_AF_UNIX_SUPPORT */ |
774 | 0 | #if COAP_AF_LLC_SUPPORT |
775 | 0 | case AF_LLC: |
776 | 0 | break; |
777 | 0 | #endif /* COAP_AF_LLC_SUPPORT */ |
778 | 0 | default: |
779 | | /* error */ |
780 | 0 | coap_log_warn("protocol not supported\n"); |
781 | 0 | return -1; |
782 | 0 | } |
783 | 0 | } |
784 | 0 | #endif /* HAVE_STRUCT_CMSGHDR */ |
785 | | |
786 | | #if defined(_WIN32) |
787 | | r = WSASendMsg(sock->fd, &mhdr, 0 /*dwFlags*/, &dwNumberOfBytesSent, NULL /*lpOverlapped*/, |
788 | | NULL /*lpCompletionRoutine*/); |
789 | | if (r == 0) |
790 | | bytes_written = (ssize_t)dwNumberOfBytesSent; |
791 | | else { |
792 | | bytes_written = -1; |
793 | | coap_win_error_to_errno(); |
794 | | } |
795 | | #else /* !_WIN32 */ |
796 | 0 | #ifdef HAVE_STRUCT_CMSGHDR |
797 | 0 | bytes_written = sendmsg(sock->fd, &mhdr, 0); |
798 | | #else /* ! HAVE_STRUCT_CMSGHDR */ |
799 | | bytes_written = sendto(sock->fd, (const void *)data, datalen, 0, |
800 | | &session->addr_info.remote.addr.sa, |
801 | | session->addr_info.remote.size); |
802 | | #endif /* ! HAVE_STRUCT_CMSGHDR */ |
803 | 0 | #endif /* !_WIN32 */ |
804 | 0 | } |
805 | | |
806 | 187 | if (bytes_written < 0) |
807 | 187 | coap_log_warn("coap_socket_send: %s\n", coap_socket_strerror()); |
808 | | |
809 | 187 | return bytes_written; |
810 | 187 | } |
811 | | |
812 | | #define SIN6(A) ((struct sockaddr_in6 *)(A)) |
813 | | |
814 | | /* |
815 | | * dgram |
816 | | * return +ve Number of bytes written. |
817 | | * -1 Error error in errno). |
818 | | * -2 ICMP error response |
819 | | */ |
820 | | ssize_t |
821 | 0 | coap_socket_recv(coap_socket_t *sock, coap_packet_t *packet) { |
822 | 0 | ssize_t len = -1; |
823 | 0 | #if defined(HAVE_LINUX_ERRQUEUE_H) && defined(HAVE_STRUCT_CMSGHDR) |
824 | 0 | ssize_t ret; |
825 | 0 | #endif /* HAVE_LINUX_ERRQUEUE_H && HAVE_STRUCT_CMSGHDR */ |
826 | |
|
827 | 0 | assert(sock); |
828 | 0 | assert(packet); |
829 | | |
830 | 0 | if ((sock->flags & COAP_SOCKET_CAN_READ) == 0) { |
831 | 0 | return -1; |
832 | 0 | } else { |
833 | | /* clear has-data flag */ |
834 | 0 | sock->flags &= ~COAP_SOCKET_CAN_READ; |
835 | 0 | } |
836 | | |
837 | 0 | if (sock->flags & COAP_SOCKET_CONNECTED) { |
838 | | #ifdef _WIN32 |
839 | | len = recv(sock->fd, (char *)packet->payload, COAP_RXBUFFER_SIZE, 0); |
840 | | #else |
841 | 0 | len = recv(sock->fd, packet->payload, COAP_RXBUFFER_SIZE, 0); |
842 | 0 | #endif |
843 | 0 | if (len < 0) { |
844 | | #ifdef _WIN32 |
845 | | coap_win_error_to_errno(); |
846 | | #endif /* _WIN32 */ |
847 | |
|
848 | 0 | #if defined(HAVE_LINUX_ERRQUEUE_H) && defined(HAVE_STRUCT_CMSGHDR) |
849 | 0 | ret = coap_get_icmp_data(sock, packet->payload, COAP_RXBUFFER_SIZE); |
850 | 0 | if (ret >= 4) { |
851 | | /* Information about the ICMP diagnostic packet is in packet->payload */ |
852 | 0 | return -2; |
853 | 0 | } |
854 | 0 | #endif /* HAVE_LINUX_ERRQUEUE_H && HAVE_STRUCT_CMSGHDR */ |
855 | 0 | if (errno == ECONNREFUSED || errno == EHOSTUNREACH || errno == ECONNRESET) { |
856 | | /* client-side ICMP destination unreachable, ignore it */ |
857 | 0 | coap_log_warn("** %s: coap_socket_recv: ICMP: %s\n", |
858 | 0 | sock->session ? |
859 | 0 | coap_session_str(sock->session) : "", |
860 | 0 | coap_socket_strerror()); |
861 | 0 | goto error; |
862 | 0 | } |
863 | 0 | if (errno != EAGAIN) { |
864 | 0 | coap_log_warn("** %s: coap_socket_recv: %s\n", |
865 | 0 | sock->session ? |
866 | 0 | coap_session_str(sock->session) : "", |
867 | 0 | coap_socket_strerror()); |
868 | 0 | } |
869 | 0 | goto error; |
870 | 0 | } else if (len > 0) { |
871 | 0 | packet->length = (size_t)len; |
872 | 0 | } |
873 | 0 | } else { |
874 | | #if defined(_WIN32) |
875 | | DWORD dwNumberOfBytesRecvd = 0; |
876 | | int r; |
877 | | #endif /* _WIN32 */ |
878 | 0 | #ifdef HAVE_STRUCT_CMSGHDR |
879 | | /* a buffer large enough to hold all packet info types, ipv6 is the largest */ |
880 | 0 | char buf[CMSG_SPACE(sizeof(struct in6_pktinfo))]; |
881 | 0 | struct cmsghdr *cmsg; |
882 | 0 | struct msghdr mhdr; |
883 | 0 | struct iovec iov[1]; |
884 | |
|
885 | | #if defined(__MINGW32__) |
886 | | iov[0].iov_base = (char *) packet->payload; |
887 | | #else /* ! __MINGW32__ */ |
888 | 0 | iov[0].iov_base = packet->payload; |
889 | 0 | #endif /* defined(__MINGW32__) */ |
890 | 0 | iov[0].iov_len = (iov_len_t)COAP_RXBUFFER_SIZE; |
891 | |
|
892 | 0 | memset(&mhdr, 0, sizeof(struct msghdr)); |
893 | |
|
894 | 0 | mhdr.msg_name = (struct sockaddr *)&packet->addr_info.remote.addr; |
895 | 0 | mhdr.msg_namelen = sizeof(packet->addr_info.remote.addr); |
896 | |
|
897 | 0 | mhdr.msg_iov = iov; |
898 | 0 | mhdr.msg_iovlen = 1; |
899 | |
|
900 | 0 | mhdr.msg_control = buf; |
901 | 0 | mhdr.msg_controllen = sizeof(buf); |
902 | | /* set a big first length incase recvmsg() does not implement updating |
903 | | msg_control as well as preset the first cmsg with bad data */ |
904 | 0 | cmsg = (struct cmsghdr *)buf; |
905 | 0 | cmsg->cmsg_len = CMSG_LEN(sizeof(buf)); |
906 | 0 | cmsg->cmsg_level = -1; |
907 | 0 | cmsg->cmsg_type = -1; |
908 | |
|
909 | | #if defined(_WIN32) |
910 | | if (!lpWSARecvMsg) { |
911 | | GUID wsaid = WSAID_WSARECVMSG; |
912 | | DWORD cbBytesReturned = 0; |
913 | | if (WSAIoctl(sock->fd, SIO_GET_EXTENSION_FUNCTION_POINTER, &wsaid, sizeof(wsaid), &lpWSARecvMsg, |
914 | | sizeof(lpWSARecvMsg), &cbBytesReturned, NULL, NULL) != 0) { |
915 | | coap_log_warn("coap_socket_recv: no WSARecvMsg\n"); |
916 | | return -1; |
917 | | } |
918 | | } |
919 | | r = lpWSARecvMsg(sock->fd, &mhdr, &dwNumberOfBytesRecvd, NULL /* LPWSAOVERLAPPED */, |
920 | | NULL /* LPWSAOVERLAPPED_COMPLETION_ROUTINE */); |
921 | | if (r == 0) |
922 | | len = (ssize_t)dwNumberOfBytesRecvd; |
923 | | else if (r == COAP_SOCKET_ERROR) |
924 | | coap_win_error_to_errno(); |
925 | | #else |
926 | 0 | len = recvmsg(sock->fd, &mhdr, 0); |
927 | 0 | #endif |
928 | |
|
929 | | #else /* ! HAVE_STRUCT_CMSGHDR */ |
930 | | len = recvfrom(sock->fd, (void *)packet->payload, COAP_RXBUFFER_SIZE, 0, |
931 | | &packet->addr_info.remote.addr.sa, |
932 | | &packet->addr_info.remote.size); |
933 | | #endif /* ! HAVE_STRUCT_CMSGHDR */ |
934 | |
|
935 | 0 | if (len < 0) { |
936 | | #ifdef _WIN32 |
937 | | coap_win_error_to_errno(); |
938 | | #endif /* _WIN32 */ |
939 | 0 | if (errno == ECONNREFUSED || errno == EHOSTUNREACH || errno == ECONNRESET) { |
940 | | /* server-side ICMP destination unreachable, ignore it. The destination address is in msg_name. */ |
941 | 0 | coap_log_warn("** %s: coap_socket_recv: ICMP: %s\n", |
942 | 0 | sock->session ? |
943 | 0 | coap_session_str(sock->session) : "", |
944 | 0 | coap_socket_strerror()); |
945 | 0 | return 0; |
946 | 0 | } |
947 | 0 | if (errno != EAGAIN) { |
948 | 0 | coap_log_warn("coap_socket_recv: %s\n", coap_socket_strerror()); |
949 | 0 | } |
950 | 0 | goto error; |
951 | 0 | } else { |
952 | 0 | #ifdef HAVE_STRUCT_CMSGHDR |
953 | 0 | int dst_found = 0; |
954 | |
|
955 | 0 | packet->addr_info.remote.size = mhdr.msg_namelen; |
956 | 0 | packet->length = (size_t)len; |
957 | | |
958 | | /* Walk through ancillary data records until the local interface |
959 | | * is found where the data was received. */ |
960 | 0 | for (cmsg = CMSG_FIRSTHDR(&mhdr); cmsg; cmsg = CMSG_NXTHDR(&mhdr, cmsg)) { |
961 | |
|
962 | 0 | #if COAP_IPV6_SUPPORT |
963 | | /* get the local interface for IPv6 */ |
964 | 0 | if (cmsg->cmsg_level == IPPROTO_IPV6 && cmsg->cmsg_type == IPV6_PKTINFO) { |
965 | 0 | union { |
966 | 0 | uint8_t *c; |
967 | 0 | struct in6_pktinfo *p; |
968 | 0 | } u; |
969 | 0 | u.c = CMSG_DATA(cmsg); |
970 | 0 | packet->ifindex = (int)(u.p->ipi6_ifindex); |
971 | 0 | memcpy(&packet->addr_info.local.addr.sin6.sin6_addr, |
972 | 0 | &u.p->ipi6_addr, sizeof(struct in6_addr)); |
973 | 0 | dst_found = 1; |
974 | 0 | break; |
975 | 0 | } |
976 | 0 | #endif /* COAP_IPV6_SUPPORT */ |
977 | | |
978 | 0 | #if COAP_IPV4_SUPPORT |
979 | | /* local interface for IPv4 */ |
980 | 0 | #if defined(IP_PKTINFO) |
981 | 0 | if (cmsg->cmsg_level == COAP_SOL_IP && cmsg->cmsg_type == IP_PKTINFO) { |
982 | 0 | union { |
983 | 0 | uint8_t *c; |
984 | 0 | struct in_pktinfo *p; |
985 | 0 | } u; |
986 | 0 | u.c = CMSG_DATA(cmsg); |
987 | 0 | packet->ifindex = u.p->ipi_ifindex; |
988 | 0 | #if COAP_IPV6_SUPPORT |
989 | 0 | if (packet->addr_info.local.addr.sa.sa_family == AF_INET6) { |
990 | 0 | memset(packet->addr_info.local.addr.sin6.sin6_addr.s6_addr, 0, 10); |
991 | 0 | packet->addr_info.local.addr.sin6.sin6_addr.s6_addr[10] = 0xff; |
992 | 0 | packet->addr_info.local.addr.sin6.sin6_addr.s6_addr[11] = 0xff; |
993 | 0 | memcpy(packet->addr_info.local.addr.sin6.sin6_addr.s6_addr + 12, |
994 | 0 | &u.p->ipi_addr, sizeof(struct in_addr)); |
995 | 0 | } else |
996 | 0 | #endif /* COAP_IPV6_SUPPORT */ |
997 | 0 | { |
998 | 0 | memcpy(&packet->addr_info.local.addr.sin.sin_addr, |
999 | 0 | &u.p->ipi_addr, sizeof(struct in_addr)); |
1000 | 0 | } |
1001 | 0 | dst_found = 1; |
1002 | 0 | break; |
1003 | 0 | } |
1004 | 0 | #endif /* IP_PKTINFO */ |
1005 | | #if defined(IP_RECVDSTADDR) |
1006 | | if (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_RECVDSTADDR) { |
1007 | | packet->ifindex = (int)sock->fd; |
1008 | | memcpy(&packet->addr_info.local.addr.sin.sin_addr, |
1009 | | CMSG_DATA(cmsg), sizeof(struct in_addr)); |
1010 | | dst_found = 1; |
1011 | | break; |
1012 | | } |
1013 | | #endif /* IP_RECVDSTADDR */ |
1014 | 0 | #endif /* COAP_IPV4_SUPPORT */ |
1015 | 0 | if (!dst_found) { |
1016 | | /* cmsg_level / cmsg_type combination we do not understand |
1017 | | (ignore preset case for bad recvmsg() not updating cmsg) */ |
1018 | 0 | if (cmsg->cmsg_level != -1 && cmsg->cmsg_type != -1) { |
1019 | 0 | coap_log_debug("cmsg_level = %d and cmsg_type = %d not supported - fix\n", |
1020 | 0 | cmsg->cmsg_level, cmsg->cmsg_type); |
1021 | 0 | } |
1022 | 0 | } |
1023 | 0 | } |
1024 | 0 | if (!dst_found) { |
1025 | | /* Not expected, but cmsg_level and cmsg_type don't match above and |
1026 | | may need a new case */ |
1027 | 0 | packet->ifindex = (int)sock->fd; |
1028 | 0 | if (getsockname(sock->fd, &packet->addr_info.local.addr.sa, |
1029 | 0 | &packet->addr_info.local.size) < 0) { |
1030 | 0 | coap_log_debug("Cannot determine local port\n"); |
1031 | 0 | } |
1032 | 0 | } |
1033 | | #else /* ! HAVE_STRUCT_CMSGHDR */ |
1034 | | packet->length = (size_t)len; |
1035 | | packet->ifindex = 0; |
1036 | | if (getsockname(sock->fd, &packet->addr_info.local.addr.sa, |
1037 | | &packet->addr_info.local.size) < 0) { |
1038 | | coap_log_debug("Cannot determine local port\n"); |
1039 | | goto error; |
1040 | | } |
1041 | | #endif /* ! HAVE_STRUCT_CMSGHDR */ |
1042 | 0 | } |
1043 | 0 | } |
1044 | | |
1045 | 0 | if (len >= 0) |
1046 | 0 | return len; |
1047 | 0 | error: |
1048 | 0 | return -1; |
1049 | 0 | } |
1050 | | |
1051 | | void |
1052 | 26 | coap_socket_dgrm_close(coap_socket_t *sock) { |
1053 | 26 | if (sock->fd != COAP_INVALID_SOCKET && !(sock->flags & COAP_SOCKET_SLAVE)) { |
1054 | 26 | #ifdef COAP_EPOLL_SUPPORT |
1055 | 26 | #if COAP_SERVER_SUPPORT |
1056 | 26 | coap_context_t *context = sock->session ? sock->session->context : |
1057 | 26 | sock->endpoint ? sock->endpoint->context : NULL; |
1058 | | #else /* ! COAP_SERVER_SUPPORT */ |
1059 | | coap_context_t *context = sock->session ? sock->session->context : NULL; |
1060 | | #endif /* ! COAP_SERVER_SUPPORT */ |
1061 | 26 | if (context != NULL) { |
1062 | 26 | int ret; |
1063 | 26 | struct epoll_event event; |
1064 | | |
1065 | | /* Kernels prior to 2.6.9 expect non NULL event parameter */ |
1066 | 26 | ret = epoll_ctl(context->epfd, EPOLL_CTL_DEL, sock->fd, &event); |
1067 | 26 | if (ret == -1 && errno != ENOENT) { |
1068 | 0 | coap_log_err("%s: epoll_ctl DEL failed: %d: %s (%d)\n", |
1069 | 0 | "coap_socket_close", |
1070 | 0 | sock->fd, |
1071 | 0 | coap_socket_strerror(), errno); |
1072 | 0 | } |
1073 | 26 | } |
1074 | 26 | #endif /* COAP_EPOLL_SUPPORT */ |
1075 | 26 | #if COAP_SERVER_SUPPORT |
1076 | 26 | #if COAP_AF_UNIX_SUPPORT |
1077 | 26 | if (sock->endpoint && |
1078 | 0 | sock->endpoint->bind_addr.addr.sa.sa_family == AF_UNIX) { |
1079 | | /* Clean up Unix endpoint */ |
1080 | | #ifdef _WIN32 |
1081 | | _unlink(sock->endpoint->bind_addr.addr.cun.sun_path); |
1082 | | #else /* ! _WIN32 */ |
1083 | 0 | unlink(sock->endpoint->bind_addr.addr.cun.sun_path); |
1084 | 0 | #endif /* ! _WIN32 */ |
1085 | 0 | } |
1086 | 26 | #endif /* COAP_AF_UNIX_SUPPORT */ |
1087 | 26 | sock->endpoint = NULL; |
1088 | 26 | #endif /* COAP_SERVER_SUPPORT */ |
1089 | 26 | #if COAP_CLIENT_SUPPORT |
1090 | 26 | #if COAP_AF_UNIX_SUPPORT |
1091 | 26 | if (sock->session && sock->session->type == COAP_SESSION_TYPE_CLIENT && |
1092 | 26 | sock->session->addr_info.local.addr.sa.sa_family == AF_UNIX) { |
1093 | | /* Clean up Unix endpoint */ |
1094 | | #ifdef _WIN32 |
1095 | | _unlink(sock->session->addr_info.local.addr.cun.sun_path); |
1096 | | #else /* ! _WIN32 */ |
1097 | 0 | unlink(sock->session->addr_info.local.addr.cun.sun_path); |
1098 | 0 | #endif /* ! _WIN32 */ |
1099 | 0 | } |
1100 | 26 | #endif /* COAP_AF_UNIX_SUPPORT */ |
1101 | 26 | #endif /* COAP_CLIENT_SUPPORT */ |
1102 | 26 | sock->session = NULL; |
1103 | 26 | coap_closesocket(sock->fd); |
1104 | 26 | sock->fd = COAP_INVALID_SOCKET; |
1105 | 26 | sock->flags = COAP_SOCKET_EMPTY; |
1106 | 26 | } |
1107 | 26 | } |
1108 | | |
1109 | | #else /* WITH_LWIP || WITH_CONTIKI || RIOT_VERSION */ |
1110 | | |
1111 | | #ifdef __clang__ |
1112 | | /* Make compilers happy that do not like empty modules. As this function is |
1113 | | * never used, we ignore -Wunused-function at the end of compiling this file |
1114 | | */ |
1115 | | #pragma GCC diagnostic ignored "-Wunused-function" |
1116 | | #endif |
1117 | | static inline void |
1118 | | dummy(void) { |
1119 | | } |
1120 | | |
1121 | | #endif /* WITH_LWIP || WITH_CONTIKI || RIOT_VERSION */ |