libcoap 4.3.5-develop-5010974
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 default:
165 coap_log_alert("coap_socket_bind_udp: unsupported sa_family\n");
166 break;
167 }
168
169 if (bind(sock->fd, &listen_addr->addr.sa,
171 listen_addr->addr.sa.sa_family == AF_INET ?
172 (socklen_t)sizeof(struct sockaddr_in) :
173#endif /* COAP_IPV4_SUPPORT */
174 (socklen_t)listen_addr->size) == COAP_SOCKET_ERROR) {
175 coap_log_warn("coap_socket_bind_udp: bind: %s\n",
177 goto error;
178 }
179
180 bound_addr->size = (socklen_t)sizeof(*bound_addr);
181 if (getsockname(sock->fd, &bound_addr->addr.sa, &bound_addr->size) < 0) {
182 coap_log_warn("coap_socket_bind_udp: getsockname: %s\n",
184 goto error;
185 }
186 return 1;
187
188error:
190 return 0;
191}
192#endif /* COAP_SERVER_SUPPORT */
193
194#if COAP_CLIENT_SUPPORT
195int
196coap_socket_connect_udp(coap_socket_t *sock,
197 const coap_address_t *local_if,
198 const coap_address_t *server,
199 int default_port,
200 coap_address_t *local_addr,
201 coap_address_t *remote_addr) {
202 int on = 1;
203#if COAP_IPV6_SUPPORT
204 int off = 0;
205#endif /* COAP_IPV6_SUPPORT */
206#ifdef _WIN32
207 u_long u_on = 1;
208#endif
209 coap_address_t connect_addr;
210 int is_mcast = coap_is_mcast(server);
211 coap_address_copy(&connect_addr, server);
212
214 sock->fd = socket(connect_addr.addr.sa.sa_family, SOCK_DGRAM, 0);
215
216 if (sock->fd == COAP_INVALID_SOCKET) {
217 coap_log_warn("coap_socket_connect_udp: socket: %s\n",
219 goto error;
220 }
221
222#ifdef _WIN32
223 if (ioctlsocket(sock->fd, FIONBIO, &u_on) == COAP_SOCKET_ERROR)
224#else
225 if (ioctl(sock->fd, FIONBIO, &on) == COAP_SOCKET_ERROR)
226#endif
227 {
228 /* Ignore Zephyr unexpected Success response */
229 if (errno != 0) {
230 int keep_errno = errno;
231
232 coap_log_warn("coap_socket_connect_udp: ioctl FIONBIO: %s (%d)\n",
233 coap_socket_strerror(), keep_errno);
234 }
235 }
236
237 switch (connect_addr.addr.sa.sa_family) {
238#if COAP_IPV4_SUPPORT
239 case AF_INET:
240 if (connect_addr.addr.sin.sin_port == 0)
241 connect_addr.addr.sin.sin_port = htons(default_port);
242 break;
243#endif /* COAP_IPV4_SUPPORT */
244#if COAP_IPV6_SUPPORT
245 case AF_INET6:
246 if (connect_addr.addr.sin6.sin6_port == 0)
247 connect_addr.addr.sin6.sin6_port = htons(default_port);
248 /* Configure the socket as dual-stacked */
249 if (setsockopt(sock->fd, IPPROTO_IPV6, IPV6_V6ONLY, OPTVAL_T(&off),
250 sizeof(off)) == COAP_SOCKET_ERROR)
251 if (errno != ENOSYS) {
252 coap_log_warn("coap_socket_connect_udp: setsockopt IPV6_V6ONLY: %s\n",
254 }
255#endif /* COAP_IPV6_SUPPORT */
256 break;
257#if COAP_AF_UNIX_SUPPORT
258 case AF_UNIX:
259 break;
260#endif /* COAP_AF_UNIX_SUPPORT */
261 default:
262 coap_log_alert("coap_socket_connect_udp: unsupported sa_family %d\n",
263 connect_addr.addr.sa.sa_family);
264 goto error;;
265 }
266
267 if (local_if && local_if->addr.sa.sa_family) {
268 if (local_if->addr.sa.sa_family != connect_addr.addr.sa.sa_family) {
269 coap_log_warn("coap_socket_connect_udp: local address family != "
270 "remote address family\n");
271 goto error;
272 }
273 if (setsockopt(sock->fd, SOL_SOCKET, SO_REUSEADDR, OPTVAL_T(&on), sizeof(on)) == COAP_SOCKET_ERROR)
274 coap_log_warn("coap_socket_connect_udp: setsockopt SO_REUSEADDR: %s\n",
276 if (bind(sock->fd, &local_if->addr.sa,
278 local_if->addr.sa.sa_family == AF_INET ?
279 (socklen_t)sizeof(struct sockaddr_in) :
280#endif /* COAP_IPV4_SUPPORT */
281 (socklen_t)local_if->size) == COAP_SOCKET_ERROR) {
282 coap_log_warn("coap_socket_connect_udp: bind: %s\n",
284 goto error;
285 }
286#if COAP_AF_UNIX_SUPPORT
287 } else if (connect_addr.addr.sa.sa_family == AF_UNIX) {
288 /* Need to bind to a local address for clarity over endpoints */
289 coap_log_warn("coap_socket_connect_udp: local address required\n");
290 goto error;
291#endif /* COAP_AF_UNIX_SUPPORT */
292 }
293
294 /* special treatment for sockets that are used for multicast communication */
295 if (is_mcast) {
296 if (!(local_if && local_if->addr.sa.sa_family)) {
297 /* Bind to a (unused) port to simplify logging */
298 coap_address_t bind_addr;
299
300 coap_address_init(&bind_addr);
301 bind_addr.addr.sa.sa_family = connect_addr.addr.sa.sa_family;
302 if (bind(sock->fd, &bind_addr.addr.sa,
304 bind_addr.addr.sa.sa_family == AF_INET ?
305 (socklen_t)sizeof(struct sockaddr_in) :
306#endif /* COAP_IPV4_SUPPORT */
307 (socklen_t)bind_addr.size) == COAP_SOCKET_ERROR) {
308 coap_log_warn("coap_socket_connect_udp: bind: %s\n",
310 goto error;
311 }
312 }
313 if (getsockname(sock->fd, &local_addr->addr.sa, &local_addr->size) == COAP_SOCKET_ERROR) {
314 coap_log_warn("coap_socket_connect_udp: getsockname for multicast socket: %s\n",
316 }
317 coap_address_copy(remote_addr, &connect_addr);
318 coap_address_copy(&sock->mcast_addr, &connect_addr);
320 if (coap_is_bcast(server) &&
321 setsockopt(sock->fd, SOL_SOCKET, SO_BROADCAST, OPTVAL_T(&on),
322 sizeof(on)) == COAP_SOCKET_ERROR)
323 coap_log_warn("coap_socket_connect_udp: setsockopt SO_BROADCAST: %s\n",
325 return 1;
326 }
327
328 if (connect(sock->fd, &connect_addr.addr.sa, connect_addr.size) == COAP_SOCKET_ERROR) {
329#if COAP_AF_UNIX_SUPPORT
330 if (connect_addr.addr.sa.sa_family == AF_UNIX) {
331 coap_log_warn("coap_socket_connect_udp: connect: %s: %s\n",
332 connect_addr.addr.cun.sun_path, coap_socket_strerror());
333 } else
334#endif /* COAP_AF_UNIX_SUPPORT */
335 {
336 coap_log_warn("coap_socket_connect_udp: connect: %s (%d)\n",
337 coap_socket_strerror(), connect_addr.addr.sa.sa_family);
338 }
339 goto error;
340 }
341
342 if (getsockname(sock->fd, &local_addr->addr.sa, &local_addr->size) == COAP_SOCKET_ERROR) {
343 coap_log_warn("coap_socket_connect_udp: getsockname: %s\n",
345 }
346
347 if (getpeername(sock->fd, &remote_addr->addr.sa, &remote_addr->size) == COAP_SOCKET_ERROR) {
348 coap_log_warn("coap_socket_connect_udp: getpeername: %s\n",
350 }
351
353 return 1;
354
355error:
357 return 0;
358}
359#endif /* COAP_CLIENT_SUPPORT */
360
361#if !defined(__ZEPHYR__)
362#if 0 == ( defined(HAVE_NETINET_IN_H) || defined(HAVE_WS2TCPIP_H) )
363/* define struct in6_pktinfo and struct in_pktinfo if not available
364 FIXME: check with configure
365*/
366#if !defined(__MINGW32__)
368 struct in6_addr ipi6_addr; /* src/dst IPv6 address */
369 unsigned int ipi6_ifindex; /* send/recv interface index */
370};
371
374 struct in_addr ipi_spec_dst;
375 struct in_addr ipi_addr;
376};
377#endif /* ! __MINGW32__ */
378#endif
379#endif /* ! __ZEPHYR__ */
380
381#if !defined(SOL_IP)
382/* Solaris expects level IPPROTO_IP for ancillary data. */
383#define SOL_IP IPPROTO_IP
384#endif
385#ifdef _WIN32
386#define COAP_SOL_IP IPPROTO_IP
387#else /* ! _WIN32 */
388#define COAP_SOL_IP SOL_IP
389#endif /* ! _WIN32 */
390
391#if defined(_WIN32)
392#include <mswsock.h>
393#if defined(__MINGW32__)
394static __thread LPFN_WSARECVMSG lpWSARecvMsg = NULL;
395#if(_WIN32_WINNT >= 0x0600)
396#define CMSG_FIRSTHDR WSA_CMSG_FIRSTHDR
397#define CMSG_NXTHDR WSA_CMSG_NXTHDR
398#define CMSG_LEN WSA_CMSG_LEN
399#define CMSG_SPACE WSA_CMSG_SPACE
400#if(_WIN32_WINNT < 0x0603 || _WIN32_WINNT == 0x0a00)
401#define cmsghdr _WSACMSGHDR
402#endif /* (_WIN32_WINNT<0x0603 || _WIN32_WINNT == 0x0a00) */
403#endif /* (_WIN32_WINNT>=0x0600) */
404#else /* ! __MINGW32__ */
405static __declspec(thread) LPFN_WSARECVMSG lpWSARecvMsg = NULL;
406#endif /* ! __MINGW32__ */
407/* Map struct WSABUF fields to their posix counterpart */
408#define msghdr _WSAMSG
409#define msg_name name
410#define msg_namelen namelen
411#define msg_iov lpBuffers
412#define msg_iovlen dwBufferCount
413#define msg_control Control.buf
414#define msg_controllen Control.len
415#define iovec _WSABUF
416#define iov_base buf
417#define iov_len len
418#define iov_len_t u_long
419#undef CMSG_DATA
420#define CMSG_DATA WSA_CMSG_DATA
421#define ipi_spec_dst ipi_addr
422#if !defined(__MINGW32__)
423#pragma warning( disable : 4116 )
424#endif /* ! __MINGW32__ */
425#else
426#define iov_len_t size_t
427#endif
428
429#if defined(_CYGWIN_ENV) || defined(__QNXNTO__)
430#define ipi_spec_dst ipi_addr
431#endif
432
433#if COAP_CLIENT_SUPPORT
434static uint32_t cid_track_counter;
435
436static void
437coap_test_cid_tuple_change(coap_session_t *session) {
438 if (session->type == COAP_SESSION_TYPE_CLIENT &&
439 session->negotiated_cid &&
441 session->proto == COAP_PROTO_DTLS && session->context->testing_cids) {
442 if ((++cid_track_counter) % session->context->testing_cids == 0) {
443 coap_address_t local_if = session->addr_info.local;
444 uint16_t port = coap_address_get_port(&local_if);
445
446 port++;
447 coap_address_set_port(&local_if, port);
448
449 coap_socket_dgrm_close(&session->sock);
450 session->sock.session = session;
451 if (!coap_socket_connect_udp(&session->sock, &local_if, &session->addr_info.remote,
452 port,
453 &session->addr_info.local,
454 &session->addr_info.remote)) {
455 coap_log_err("Tuple change for CID failed\n");
456 return;
457#ifdef COAP_EPOLL_SUPPORT
458 } else {
459 coap_epoll_ctl_add(&session->sock,
460 EPOLLIN |
461 ((session->sock.flags & COAP_SOCKET_WANT_CONNECT) ?
462 EPOLLOUT : 0),
463 __func__);
464#endif /* COAP_EPOLL_SUPPORT */
465 }
467 }
468 }
469}
470#endif /* COAP_CLIENT_SUPPORT */
471
472/*
473 * dgram
474 * return +ve Number of bytes written.
475 * -1 Error error in errno).
476 */
477ssize_t
479 const uint8_t *data, size_t datalen) {
480 ssize_t bytes_written = 0;
481 int r;
482
483#if COAP_CLIENT_SUPPORT
484 coap_test_cid_tuple_change(session);
485#endif /* COAP_CLIENT_SUPPORT */
486
487 if ((r = coap_debug_send_packet()) != 1) {
488 if (r)
489 bytes_written = -1;
490 else
491 bytes_written = (ssize_t)datalen;
492 } else if (sock->flags & COAP_SOCKET_CONNECTED) {
493#ifdef _WIN32
494 bytes_written = send(sock->fd, (const char *)data, (int)datalen, 0);
495#else
496 bytes_written = send(sock->fd, data, datalen, 0);
497#endif
498 } else {
499#if defined(_WIN32)
500 DWORD dwNumberOfBytesSent = 0;
501#endif /* _WIN32 */
502#ifdef HAVE_STRUCT_CMSGHDR
503 /* a buffer large enough to hold all packet info types, ipv6 is the largest */
504 char buf[CMSG_SPACE(sizeof(struct in6_pktinfo))];
505 struct msghdr mhdr;
506 struct iovec iov[1];
507 const void *addr = &session->addr_info.remote.addr;
508
509 assert(session);
510
511 memcpy(&iov[0].iov_base, &data, sizeof(iov[0].iov_base));
512 iov[0].iov_len = (iov_len_t)datalen;
513
514 memset(buf, 0, sizeof(buf));
515
516 memset(&mhdr, 0, sizeof(struct msghdr));
517 memcpy(&mhdr.msg_name, &addr, sizeof(mhdr.msg_name));
518 mhdr.msg_namelen = session->addr_info.remote.addr.sa.sa_family == AF_INET ?
519 (socklen_t)sizeof(struct sockaddr_in) :
520 session->addr_info.remote.size;
521
522 mhdr.msg_iov = iov;
523 mhdr.msg_iovlen = 1;
524
525 if (!coap_address_isany(&session->addr_info.local) &&
526 !coap_is_mcast(&session->addr_info.local)) {
527 switch (session->addr_info.local.addr.sa.sa_family) {
528#if COAP_IPV6_SUPPORT
529 case AF_INET6: {
530 struct cmsghdr *cmsg;
531
532#if COAP_IPV4_SUPPORT
533 if (IN6_IS_ADDR_V4MAPPED(&session->addr_info.local.addr.sin6.sin6_addr)) {
534#if defined(IP_PKTINFO)
535 struct in_pktinfo *pktinfo;
536 mhdr.msg_control = buf;
537 mhdr.msg_controllen = CMSG_SPACE(sizeof(struct in_pktinfo));
538
539 cmsg = CMSG_FIRSTHDR(&mhdr);
540 cmsg->cmsg_level = COAP_SOL_IP;
541 cmsg->cmsg_type = IP_PKTINFO;
542 cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo));
543
544 pktinfo = (struct in_pktinfo *)CMSG_DATA(cmsg);
545
546 pktinfo->ipi_ifindex = session->ifindex;
547 memcpy(&pktinfo->ipi_spec_dst,
548 session->addr_info.local.addr.sin6.sin6_addr.s6_addr + 12,
549 sizeof(pktinfo->ipi_spec_dst));
550#elif defined(IP_SENDSRCADDR)
551 mhdr.msg_control = buf;
552 mhdr.msg_controllen = CMSG_SPACE(sizeof(struct in_addr));
553
554 cmsg = CMSG_FIRSTHDR(&mhdr);
555 cmsg->cmsg_level = IPPROTO_IP;
556 cmsg->cmsg_type = IP_SENDSRCADDR;
557 cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_addr));
558
559 memcpy(CMSG_DATA(cmsg),
560 session->addr_info.local.addr.sin6.sin6_addr.s6_addr + 12,
561 sizeof(struct in_addr));
562#endif /* IP_PKTINFO */
563 } else {
564#endif /* COAP_IPV4_SUPPORT */
565 struct in6_pktinfo *pktinfo;
566 mhdr.msg_control = buf;
567 mhdr.msg_controllen = CMSG_SPACE(sizeof(struct in6_pktinfo));
568
569 cmsg = CMSG_FIRSTHDR(&mhdr);
570 cmsg->cmsg_level = IPPROTO_IPV6;
571 cmsg->cmsg_type = IPV6_PKTINFO;
572 cmsg->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo));
573
574 pktinfo = (struct in6_pktinfo *)CMSG_DATA(cmsg);
575
576 if (coap_is_mcast(&session->addr_info.remote)) {
577 pktinfo->ipi6_ifindex = session->addr_info.remote.addr.sin6.sin6_scope_id;
578 } else {
579 pktinfo->ipi6_ifindex = session->ifindex;
580 }
581 memcpy(&pktinfo->ipi6_addr,
582 &session->addr_info.local.addr.sin6.sin6_addr,
583 sizeof(pktinfo->ipi6_addr));
584#if COAP_IPV4_SUPPORT
585 }
586#endif /* COAP_IPV4_SUPPORT */
587 break;
588 }
589#endif /* COAP_IPV6_SUPPORT */
590#if COAP_IPV4_SUPPORT
591 case AF_INET: {
592#if defined(IP_PKTINFO)
593 struct cmsghdr *cmsg;
594 struct in_pktinfo *pktinfo;
595
596 mhdr.msg_control = buf;
597 mhdr.msg_controllen = CMSG_SPACE(sizeof(struct in_pktinfo));
598
599 cmsg = CMSG_FIRSTHDR(&mhdr);
600 cmsg->cmsg_level = COAP_SOL_IP;
601 cmsg->cmsg_type = IP_PKTINFO;
602 cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo));
603
604 pktinfo = (struct in_pktinfo *)CMSG_DATA(cmsg);
605
606 pktinfo->ipi_ifindex = session->ifindex;
607 memcpy(&pktinfo->ipi_spec_dst,
608 &session->addr_info.local.addr.sin.sin_addr,
609 sizeof(pktinfo->ipi_spec_dst));
610#elif defined(IP_SENDSRCADDR)
611 struct cmsghdr *cmsg;
612 mhdr.msg_control = buf;
613 mhdr.msg_controllen = CMSG_SPACE(sizeof(struct in_addr));
614
615 cmsg = CMSG_FIRSTHDR(&mhdr);
616 cmsg->cmsg_level = IPPROTO_IP;
617 cmsg->cmsg_type = IP_SENDSRCADDR;
618 cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_addr));
619
620 memcpy(CMSG_DATA(cmsg),
621 &session->addr_info.local.addr.sin.sin_addr,
622 sizeof(struct in_addr));
623#endif /* IP_PKTINFO */
624 break;
625 }
626#endif /* COAP_IPV4_SUPPORT */
627#if COAP_AF_UNIX_SUPPORT
628 case AF_UNIX:
629 break;
630#endif /* COAP_AF_UNIX_SUPPORT */
631 default:
632 /* error */
633 coap_log_warn("protocol not supported\n");
634 return -1;
635 }
636 }
637#endif /* HAVE_STRUCT_CMSGHDR */
638
639#if defined(_WIN32)
640 r = WSASendMsg(sock->fd, &mhdr, 0 /*dwFlags*/, &dwNumberOfBytesSent, NULL /*lpOverlapped*/,
641 NULL /*lpCompletionRoutine*/);
642 if (r == 0)
643 bytes_written = (ssize_t)dwNumberOfBytesSent;
644 else {
645 bytes_written = -1;
646 coap_win_error_to_errno();
647 }
648#else /* !_WIN32 */
649#ifdef HAVE_STRUCT_CMSGHDR
650 bytes_written = sendmsg(sock->fd, &mhdr, 0);
651#else /* ! HAVE_STRUCT_CMSGHDR */
652 bytes_written = sendto(sock->fd, (const void *)data, datalen, 0,
653 &session->addr_info.remote.addr.sa,
654 session->addr_info.remote.size);
655#endif /* ! HAVE_STRUCT_CMSGHDR */
656#endif /* !_WIN32 */
657 }
658
659 if (bytes_written < 0)
660 coap_log_crit("coap_socket_send: %s\n", coap_socket_strerror());
661
662 return bytes_written;
663}
664
665#define SIN6(A) ((struct sockaddr_in6 *)(A))
666
667/*
668 * dgram
669 * return +ve Number of bytes written.
670 * -1 Error error in errno).
671 * -2 ICMP error response
672 */
673ssize_t
675 ssize_t len = -1;
676
677 assert(sock);
678 assert(packet);
679
680 if ((sock->flags & COAP_SOCKET_CAN_READ) == 0) {
681 return -1;
682 } else {
683 /* clear has-data flag */
684 sock->flags &= ~COAP_SOCKET_CAN_READ;
685 }
686
687 if (sock->flags & COAP_SOCKET_CONNECTED) {
688#ifdef _WIN32
689 len = recv(sock->fd, (char *)packet->payload, COAP_RXBUFFER_SIZE, 0);
690#else
691 len = recv(sock->fd, packet->payload, COAP_RXBUFFER_SIZE, 0);
692#endif
693 if (len < 0) {
694#ifdef _WIN32
695 coap_win_error_to_errno();
696#endif /* _WIN32 */
697 if (errno == ECONNREFUSED || errno == EHOSTUNREACH || errno == ECONNRESET) {
698 /* client-side ICMP destination unreachable, ignore it */
699 coap_log_warn("** %s: coap_socket_recv: ICMP: %s\n",
700 sock->session ?
701 coap_session_str(sock->session) : "",
703 return -2;
704 }
705 if (errno != EAGAIN) {
706 coap_log_warn("** %s: coap_socket_recv: %s\n",
707 sock->session ?
708 coap_session_str(sock->session) : "",
710 }
711 goto error;
712 } else if (len > 0) {
713 packet->length = (size_t)len;
714 }
715 } else {
716#if defined(_WIN32)
717 DWORD dwNumberOfBytesRecvd = 0;
718 int r;
719#endif /* _WIN32 */
720#ifdef HAVE_STRUCT_CMSGHDR
721 /* a buffer large enough to hold all packet info types, ipv6 is the largest */
722 char buf[CMSG_SPACE(sizeof(struct in6_pktinfo))];
723 struct cmsghdr *cmsg;
724 struct msghdr mhdr;
725 struct iovec iov[1];
726
727#if defined(__MINGW32__)
728 iov[0].iov_base = (char *) packet->payload;
729#else /* ! __MINGW32__ */
730 iov[0].iov_base = packet->payload;
731#endif /* defined(__MINGW32__) */
732 iov[0].iov_len = (iov_len_t)COAP_RXBUFFER_SIZE;
733
734 memset(&mhdr, 0, sizeof(struct msghdr));
735
736 mhdr.msg_name = (struct sockaddr *)&packet->addr_info.remote.addr;
737 mhdr.msg_namelen = sizeof(packet->addr_info.remote.addr);
738
739 mhdr.msg_iov = iov;
740 mhdr.msg_iovlen = 1;
741
742 mhdr.msg_control = buf;
743 mhdr.msg_controllen = sizeof(buf);
744 /* set a big first length incase recvmsg() does not implement updating
745 msg_control as well as preset the first cmsg with bad data */
746 cmsg = (struct cmsghdr *)buf;
747 cmsg->cmsg_len = CMSG_LEN(sizeof(buf));
748 cmsg->cmsg_level = -1;
749 cmsg->cmsg_type = -1;
750
751#if defined(_WIN32)
752 if (!lpWSARecvMsg) {
753 GUID wsaid = WSAID_WSARECVMSG;
754 DWORD cbBytesReturned = 0;
755 if (WSAIoctl(sock->fd, SIO_GET_EXTENSION_FUNCTION_POINTER, &wsaid, sizeof(wsaid), &lpWSARecvMsg,
756 sizeof(lpWSARecvMsg), &cbBytesReturned, NULL, NULL) != 0) {
757 coap_log_warn("coap_socket_recv: no WSARecvMsg\n");
758 return -1;
759 }
760 }
761 r = lpWSARecvMsg(sock->fd, &mhdr, &dwNumberOfBytesRecvd, NULL /* LPWSAOVERLAPPED */,
762 NULL /* LPWSAOVERLAPPED_COMPLETION_ROUTINE */);
763 if (r == 0)
764 len = (ssize_t)dwNumberOfBytesRecvd;
765 else if (r == COAP_SOCKET_ERROR)
766 coap_win_error_to_errno();
767#else
768 len = recvmsg(sock->fd, &mhdr, 0);
769#endif
770
771#else /* ! HAVE_STRUCT_CMSGHDR */
772 len = recvfrom(sock->fd, (void *)packet->payload, COAP_RXBUFFER_SIZE, 0,
773 &packet->addr_info.remote.addr.sa,
774 &packet->addr_info.remote.size);
775#endif /* ! HAVE_STRUCT_CMSGHDR */
776
777 if (len < 0) {
778#ifdef _WIN32
779 coap_win_error_to_errno();
780#endif /* _WIN32 */
781 if (errno == ECONNREFUSED || errno == EHOSTUNREACH || errno == ECONNRESET) {
782 /* server-side ICMP destination unreachable, ignore it. The destination address is in msg_name. */
783 coap_log_warn("** %s: coap_socket_recv: ICMP: %s\n",
784 sock->session ?
785 coap_session_str(sock->session) : "",
787 return 0;
788 }
789 if (errno != EAGAIN) {
790 coap_log_warn("coap_socket_recv: %s\n", coap_socket_strerror());
791 }
792 goto error;
793 } else {
794#ifdef HAVE_STRUCT_CMSGHDR
795 int dst_found = 0;
796
797 packet->addr_info.remote.size = mhdr.msg_namelen;
798 packet->length = (size_t)len;
799
800 /* Walk through ancillary data records until the local interface
801 * is found where the data was received. */
802 for (cmsg = CMSG_FIRSTHDR(&mhdr); cmsg; cmsg = CMSG_NXTHDR(&mhdr, cmsg)) {
803
804#if COAP_IPV6_SUPPORT
805 /* get the local interface for IPv6 */
806 if (cmsg->cmsg_level == IPPROTO_IPV6 && cmsg->cmsg_type == IPV6_PKTINFO) {
807 union {
808 uint8_t *c;
809 struct in6_pktinfo *p;
810 } u;
811 u.c = CMSG_DATA(cmsg);
812 packet->ifindex = (int)(u.p->ipi6_ifindex);
813 memcpy(&packet->addr_info.local.addr.sin6.sin6_addr,
814 &u.p->ipi6_addr, sizeof(struct in6_addr));
815 dst_found = 1;
816 break;
817 }
818#endif /* COAP_IPV6_SUPPORT */
819
820#if COAP_IPV4_SUPPORT
821 /* local interface for IPv4 */
822#if defined(IP_PKTINFO)
823 if (cmsg->cmsg_level == COAP_SOL_IP && cmsg->cmsg_type == IP_PKTINFO) {
824 union {
825 uint8_t *c;
826 struct in_pktinfo *p;
827 } u;
828 u.c = CMSG_DATA(cmsg);
829 packet->ifindex = u.p->ipi_ifindex;
830#if COAP_IPV6_SUPPORT
831 if (packet->addr_info.local.addr.sa.sa_family == AF_INET6) {
832 memset(packet->addr_info.local.addr.sin6.sin6_addr.s6_addr, 0, 10);
833 packet->addr_info.local.addr.sin6.sin6_addr.s6_addr[10] = 0xff;
834 packet->addr_info.local.addr.sin6.sin6_addr.s6_addr[11] = 0xff;
835 memcpy(packet->addr_info.local.addr.sin6.sin6_addr.s6_addr + 12,
836 &u.p->ipi_addr, sizeof(struct in_addr));
837 } else
838#endif /* COAP_IPV6_SUPPORT */
839 {
840 memcpy(&packet->addr_info.local.addr.sin.sin_addr,
841 &u.p->ipi_addr, sizeof(struct in_addr));
842 }
843 dst_found = 1;
844 break;
845 }
846#endif /* IP_PKTINFO */
847#if defined(IP_RECVDSTADDR)
848 if (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_RECVDSTADDR) {
849 packet->ifindex = (int)sock->fd;
850 memcpy(&packet->addr_info.local.addr.sin.sin_addr,
851 CMSG_DATA(cmsg), sizeof(struct in_addr));
852 dst_found = 1;
853 break;
854 }
855#endif /* IP_RECVDSTADDR */
856#endif /* COAP_IPV4_SUPPORT */
857 if (!dst_found) {
858 /* cmsg_level / cmsg_type combination we do not understand
859 (ignore preset case for bad recvmsg() not updating cmsg) */
860 if (cmsg->cmsg_level != -1 && cmsg->cmsg_type != -1) {
861 coap_log_debug("cmsg_level = %d and cmsg_type = %d not supported - fix\n",
862 cmsg->cmsg_level, cmsg->cmsg_type);
863 }
864 }
865 }
866 if (!dst_found) {
867 /* Not expected, but cmsg_level and cmsg_type don't match above and
868 may need a new case */
869 packet->ifindex = (int)sock->fd;
870 if (getsockname(sock->fd, &packet->addr_info.local.addr.sa,
871 &packet->addr_info.local.size) < 0) {
872 coap_log_debug("Cannot determine local port\n");
873 }
874 }
875#else /* ! HAVE_STRUCT_CMSGHDR */
876 packet->length = (size_t)len;
877 packet->ifindex = 0;
878 if (getsockname(sock->fd, &packet->addr_info.local.addr.sa,
879 &packet->addr_info.local.size) < 0) {
880 coap_log_debug("Cannot determine local port\n");
881 goto error;
882 }
883#endif /* ! HAVE_STRUCT_CMSGHDR */
884 }
885 }
886
887 if (len >= 0)
888 return len;
889error:
890 return -1;
891}
892
893void
895 if (sock->fd != COAP_INVALID_SOCKET && !(sock->flags & COAP_SOCKET_SLAVE)) {
896#ifdef COAP_EPOLL_SUPPORT
897#if COAP_SERVER_SUPPORT
898 coap_context_t *context = sock->session ? sock->session->context :
899 sock->endpoint ? sock->endpoint->context : NULL;
900#else /* COAP_SERVER_SUPPORT */
901 coap_context_t *context = sock->session ? sock->session->context : NULL;
902#endif /* COAP_SERVER_SUPPORT */
903 if (context != NULL) {
904 int ret;
905 struct epoll_event event;
906
907 /* Kernels prior to 2.6.9 expect non NULL event parameter */
908 ret = epoll_ctl(context->epfd, EPOLL_CTL_DEL, sock->fd, &event);
909 if (ret == -1 && errno != ENOENT) {
910 coap_log_err("%s: epoll_ctl DEL failed: %d: %s (%d)\n",
911 "coap_socket_close",
912 sock->fd,
913 coap_socket_strerror(), errno);
914 }
915 }
916#endif /* COAP_EPOLL_SUPPORT */
917#if COAP_SERVER_SUPPORT
918#if COAP_AF_UNIX_SUPPORT
919 if (sock->endpoint &&
920 sock->endpoint->bind_addr.addr.sa.sa_family == AF_UNIX) {
921 /* Clean up Unix endpoint */
922#ifdef _WIN32
923 _unlink(sock->endpoint->bind_addr.addr.cun.sun_path);
924#else /* ! _WIN32 */
925 unlink(sock->endpoint->bind_addr.addr.cun.sun_path);
926#endif /* ! _WIN32 */
927 }
928#endif /* COAP_AF_UNIX_SUPPORT */
929 sock->endpoint = NULL;
930#endif /* COAP_SERVER_SUPPORT */
931#if COAP_CLIENT_SUPPORT
932#if COAP_AF_UNIX_SUPPORT
933 if (sock->session && sock->session->type == COAP_SESSION_TYPE_CLIENT &&
934 sock->session->addr_info.local.addr.sa.sa_family == AF_UNIX) {
935 /* Clean up Unix endpoint */
936#ifdef _WIN32
937 _unlink(sock->session->addr_info.local.addr.cun.sun_path);
938#else /* ! _WIN32 */
939 unlink(sock->session->addr_info.local.addr.cun.sun_path);
940#endif /* ! _WIN32 */
941 }
942#endif /* COAP_AF_UNIX_SUPPORT */
943#endif /* COAP_CLIENT_SUPPORT */
944 sock->session = NULL;
945 coap_closesocket(sock->fd);
946 sock->fd = COAP_INVALID_SOCKET;
947 sock->flags = COAP_SOCKET_EMPTY;
948 }
949}
950
951#else /* WITH_LWIP || WITH_CONTIKI || RIOT_VERSION */
952
953#ifdef __clang__
954/* Make compilers happy that do not like empty modules. As this function is
955 * never used, we ignore -Wunused-function at the end of compiling this file
956 */
957#pragma GCC diagnostic ignored "-Wunused-function"
958#endif
959static inline void
960dummy(void) {
961}
962
963#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.
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:935
#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_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
#define coap_log_crit(...)
Definition coap_debug.h:96
@ COAP_PROTO_DTLS
Definition coap_pdu.h:319
@ 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