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