diff options
| author | Garrett D'Amore <garrett@damore.org> | 2024-06-02 23:33:27 -0700 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2024-06-02 23:33:27 -0700 |
| commit | 603282f28e6f2e1b32d3a587d8de761f9f94ad45 (patch) | |
| tree | b90b5f4c057979524e6b4a12a74742c7da25c484 /src/platform/windows | |
| parent | 890d4899138ff497a48ba4aaa2385b3ed2b84ac4 (diff) | |
| download | nng-603282f28e6f2e1b32d3a587d8de761f9f94ad45.tar.gz nng-603282f28e6f2e1b32d3a587d8de761f9f94ad45.tar.bz2 nng-603282f28e6f2e1b32d3a587d8de761f9f94ad45.zip | |
UDP: Introduce an experimental (undocumented for now) public API for UDP. (#1838)
This exposes the UDP methods as nng_ methods, and adds support for Multicast Membership,
which is useful in a variety of situations.
No documentation is provided, and applications should consider thios API experimental.
Diffstat (limited to 'src/platform/windows')
| -rw-r--r-- | src/platform/windows/win_udp.c | 95 |
1 files changed, 95 insertions, 0 deletions
diff --git a/src/platform/windows/win_udp.c b/src/platform/windows/win_udp.c index 09064e2f..c1c7ef21 100644 --- a/src/platform/windows/win_udp.c +++ b/src/platform/windows/win_udp.c @@ -315,6 +315,101 @@ nni_plat_udp_sockname(nni_plat_udp *udp, nni_sockaddr *sa) return (nni_win_sockaddr2nn(sa, &ss)); } +// Joining a multicast group is different than binding to a multicast +// group. This allows to receive both unicast and multicast at the given +// address. +static int +ip4_multicast_member(nni_plat_udp *udp, SOCKADDR *sa, bool join) +{ + IP_MREQ mreq; + SOCKADDR_IN *sin; + SOCKADDR_STORAGE local; + int sz = sizeof(local); + + if (getsockname(udp->s, (SOCKADDR *) &local, &sz) >= 0) { + if (local.ss_family != AF_INET) { + // address families have to match + return (NNG_EADDRINVAL); + } + sin = (SOCKADDR_IN *) &local; + mreq.imr_interface.s_addr = sin->sin_addr.s_addr; + } else { + mreq.imr_interface.s_addr = INADDR_ANY; + } + + // Determine our local interface + sin = (SOCKADDR_IN *) sa; + + mreq.imr_multiaddr.s_addr = sin->sin_addr.s_addr; + if (setsockopt(udp->s, IPPROTO_IP, + join ? IP_ADD_MEMBERSHIP : IP_DROP_MEMBERSHIP, + (const char *) &mreq, sizeof(mreq)) == 0) { + return (0); + } + return (nni_win_error(GetLastError())); +} + +#ifdef NNG_ENABLE_IPV6 +static int +ip6_multicast_member(nni_plat_udp *udp, SOCKADDR *sa, bool join) +{ + IPV6_MREQ mreq; + SOCKADDR_IN6 *sin6; + SOCKADDR_STORAGE local; + int sz = sizeof(local); + + if (getsockname(udp->s, (SOCKADDR *) &local, &sz) >= 0) { + if (local.ss_family != AF_INET6) { + // address families have to match + return (NNG_EADDRINVAL); + } + sin6 = (SOCKADDR_IN6 *) &local; + mreq.ipv6mr_interface = sin6->sin6_scope_id; + } else { + mreq.ipv6mr_interface = 0; + } + + // Determine our local interface + sin6 = (SOCKADDR_IN6 *) sa; + + mreq.ipv6mr_multiaddr = sin6->sin6_addr; + if (setsockopt(udp->s, IPPROTO_IPV6, + join ? IPV6_JOIN_GROUP : IPV6_LEAVE_GROUP, + (const char *) &mreq, sizeof(mreq)) == 0) { + return (0); + } + return (nni_win_error(GetLastError())); +} +#endif + +int +nni_plat_udp_multicast_membership( + nni_plat_udp *udp, nni_sockaddr *sa, bool join) +{ + SOCKADDR_STORAGE ss; + socklen_t sz; + int rv; + + sz = nni_win_nn2sockaddr(&ss, sa); + if (sz < 1) { + return (NNG_EADDRINVAL); + } + switch (ss.ss_family) { + case AF_INET: + rv = ip4_multicast_member(udp, (struct sockaddr *) &ss, join); + break; +#ifdef NNG_ENABLE_IPV6 + case AF_INET6: + rv = ip6_multicast_member(udp, (struct sockaddr *) &ss, join); + break; +#endif + default: + rv = NNG_EADDRINVAL; + } + + return (rv); +} + int nni_win_udp_sysinit(void) { |
