6#define __APPLE_USE_RFC_3542 1
9#include "Stroika/Foundation/StroikaPreComp.h"
11#if qStroika_Platform_POSIX
13#include <netinet/in.h>
14#include <sys/socket.h>
15#elif qStroika_Platform_Windows
26#include "Stroika/Foundation/Common/GUID.h"
27#include "Stroika/Foundation/DataExchange/BadFormatException.h"
28#include "Stroika/Foundation/Execution/Activity.h"
32#include "Stroika/Foundation/IO/Network/Socket-Private_.h"
35#include "ConnectionlessSocket.h"
44using namespace Stroika::Foundation::Memory;
45using namespace Stroika::Foundation::IO;
48using namespace Stroika::Foundation::IO::Network::PRIVATE_;
55#pragma comment(lib, "Iphlpapi.lib")
60 unsigned int InterfaceIndexOf_ (
const InternetAddress& interfaceAddress)
62 if (interfaceAddress == V4::kAddrAny or interfaceAddress == V6::kAddrAny) {
66 if (i.fIndex and i.fBindings.fAddresses.Contains (interfaceAddress)) {
74 unsigned int CurrentIndexOf_ (
const Interface& i)
76#if qStroika_Platform_POSIX
81#elif qStroika_Platform_Windows
83 optional<::GUID> guid;
86 static_assert (
sizeof (
::GUID) ==
sizeof (g));
88 ::memcpy (&*guid, &g,
sizeof (
::GUID));
94 if (guid and ::ConvertInterfaceGuidToLuid (&*guid, &luid) == NO_ERROR and
104 unsigned int InterfaceIndex_ (
const variant<InternetAddress, unsigned int>& onInterface)
106 if (
const unsigned int* index = get_if<unsigned int> (&onInterface)) {
109 return InterfaceIndexOf_ (get<InternetAddress> (onInterface));
114 struct Rep_ : BackSocketImpl_<ConnectionlessSocket::_IRep> {
115 using inherited = BackSocketImpl_<ConnectionlessSocket::_IRep>;
120 virtual void SendTo (
const byte* start,
const byte* end,
const SocketAddress& sockAddr)
override
122#if USE_NOISY_TRACE_IN_THIS_MODULE_
124 static_cast<long long> (end - start), sockAddr};
127 sockaddr_storage sa = sockAddr.
As<sockaddr_storage> ();
128#if qStroika_Platform_POSIX
130 return ::sendto (fSD_,
reinterpret_cast<const char*
> (start), end - start, 0,
reinterpret_cast<sockaddr*
> (&sa),
133#elif qStroika_Platform_Windows
134 Require (end - start < numeric_limits<int>::max ());
135 ThrowWSASystemErrorIfSOCKET_ERROR (::sendto (fSD_,
reinterpret_cast<const char*
> (start),
static_cast<int> (end - start), 0,
136 reinterpret_cast<sockaddr*
> (&sa),
static_cast<int> (sockAddr.
GetRequiredSize ())));
141 virtual bool GetReceivePacketInfo ()
const override
144 switch (GetAddressFamily ()) {
145 case SocketAddress::INET: {
146 return getsockopt<int> (IPPROTO_IP, IP_PKTINFO) != 0;
148 case SocketAddress::INET6: {
149#if qStroika_Platform_Windows
150 return getsockopt<int> (IPPROTO_IPV6, IPV6_PKTINFO) != 0;
152 return getsockopt<int> (IPPROTO_IPV6, IPV6_RECVPKTINFO) != 0;
160 virtual void SetReceivePacketInfo (
bool on)
override
163 switch (GetAddressFamily ()) {
164 case SocketAddress::INET: {
165 setsockopt<int> (IPPROTO_IP, IP_PKTINFO, on);
167 case SocketAddress::INET6: {
168#if qStroika_Platform_Windows
169 setsockopt<int> (IPPROTO_IPV6, IPV6_PKTINFO, on);
171 setsockopt<int> (IPPROTO_IPV6, IPV6_RECVPKTINFO, on);
178 virtual size_t ReceiveFrom (
byte* intoStart,
byte* intoEnd,
int flag,
SocketAddress* fromAddress, optional<PacketInfo>* packetInfo,
183 if constexpr (qStroika_Platform_Windows) {
188 Execution::ThrowError (errc::timed_out);
195 if (timeout < kMaxPolltime_) {
196 int timeout_millisecs = Math::Round<int> (timeout.count () * 1000);
199 pollData.events = POLLIN;
200#if qStroika_Platform_Windows
202 if ((nresults = ::WSAPoll (&pollData, 1, timeout_millisecs)) == SOCKET_ERROR) {
203 Execution::ThrowSystemErrNo (::WSAGetLastError ());
208 if (nresults == 0) [[unlikely]] {
209 Execution::ThrowError (errc::timed_out);
213 if (packetInfo !=
nullptr) {
214 return ReceiveMsg_ (intoStart, intoEnd, flag, fromAddress, packetInfo);
216 struct sockaddr_storage sa;
217 socklen_t salen =
sizeof (sa);
218#if qStroika_Platform_POSIX
220 return ::recvfrom (fSD_,
reinterpret_cast<char*
> (intoStart), intoEnd - intoStart, flag,
221 fromAddress ==
nullptr ?
nullptr : reinterpret_cast<sockaddr*> (&sa), fromAddress == nullptr ? nullptr : &salen);
223 if (fromAddress !=
nullptr) {
227#elif qStroika_Platform_Windows
228 Require (intoEnd - intoStart < numeric_limits<int>::max ());
229 size_t result =
static_cast<size_t> (ThrowWSASystemErrorIfSOCKET_ERROR (
230 ::recvfrom (fSD_,
reinterpret_cast<char*
> (intoStart),
static_cast<int> (intoEnd - intoStart), flag,
231 fromAddress ==
nullptr ?
nullptr : reinterpret_cast<sockaddr*> (&sa), fromAddress == nullptr ? nullptr : &salen)));
232 if (fromAddress !=
nullptr) {
242 size_t ReceiveMsg_ (
byte* intoStart,
byte* intoEnd,
int flag,
SocketAddress* fromAddress, optional<PacketInfo>* packetInfo)
245 sockaddr_storage sa{};
246 *packetInfo = nullopt;
247#if qStroika_Platform_POSIX
248 iovec iov{intoStart,
static_cast<size_t> (intoEnd - intoStart)};
249 alignas (cmsghdr)
byte control[CMSG_SPACE (
sizeof (in6_pktinfo)) + CMSG_SPACE (
sizeof (in_pktinfo))];
252 m.msg_namelen =
sizeof (sa);
255 m.msg_control = control;
256 m.msg_controllen =
sizeof (control);
258 for (cmsghdr* c = CMSG_FIRSTHDR (&m); c !=
nullptr; c = CMSG_NXTHDR (&m, c)) {
259 if (c->cmsg_level == IPPROTO_IP and c->cmsg_type == IP_PKTINFO) {
261 ::memcpy (&i, CMSG_DATA (c),
sizeof (i));
264 else if (c->cmsg_level == IPPROTO_IPV6 and c->cmsg_type == IPV6_PKTINFO) {
266 ::memcpy (&i, CMSG_DATA (c),
sizeof (i));
270#elif qStroika_Platform_Windows
271 Require (cmp_less (intoEnd - intoStart, numeric_limits<ULONG>::max ()));
272 if (fWSARecvMsg_ ==
nullptr) {
274 GUID id = WSAID_WSARECVMSG;
276 ThrowWSASystemErrorIfSOCKET_ERROR (::WSAIoctl (fSD_, SIO_GET_EXTENSION_FUNCTION_POINTER, &
id,
sizeof (
id), &fWSARecvMsg_,
277 sizeof (fWSARecvMsg_), &bytes,
nullptr,
nullptr));
279 WSABUF buf{
static_cast<ULONG
> (intoEnd - intoStart),
reinterpret_cast<CHAR*
> (intoStart)};
280 alignas (WSACMSGHDR)
char control[WSA_CMSG_SPACE (
sizeof (IN6_PKTINFO)) + WSA_CMSG_SPACE (
sizeof (IN_PKTINFO))];
282 m.name =
reinterpret_cast<sockaddr*
> (&sa);
283 m.namelen =
sizeof (sa);
286 m.Control = WSABUF{
sizeof (control), control};
287 m.dwFlags =
static_cast<ULONG
> (flag);
289 ThrowWSASystemErrorIfSOCKET_ERROR (fWSARecvMsg_ (fSD_, &m, &nBytes,
nullptr,
nullptr));
290 size_t result = nBytes;
291 for (WSACMSGHDR* c = WSA_CMSG_FIRSTHDR (&m); c !=
nullptr; c = WSA_CMSG_NXTHDR (&m, c)) {
292 if (c->cmsg_level == IPPROTO_IP and c->cmsg_type == IP_PKTINFO) {
294 ::memcpy (&i, WSA_CMSG_DATA (c),
sizeof (i));
297 else if (c->cmsg_level == IPPROTO_IPV6 and c->cmsg_type == IPV6_PKTINFO) {
299 ::memcpy (&i, WSA_CMSG_DATA (c),
sizeof (i));
307 if (fromAddress !=
nullptr) {
312#if qStroika_Platform_Windows
313 LPFN_WSARECVMSG fWSARecvMsg_{
nullptr};
315 virtual void JoinMulticastGroup (
const InternetAddress& iaddr,
const variant<InternetAddress, unsigned int>& onInterface)
override
318 "IO::Network::Socket::JoinMulticastGroup",
322 return "joining multicast group "sv + Characters::ToString (iaddr) +
" on interface "sv + Characters::ToString (onInterface);
325 SetMembership_ (iaddr, onInterface,
true);
327 virtual void LeaveMulticastGroup (
const InternetAddress& iaddr,
const variant<InternetAddress, unsigned int>& onInterface)
override
330 Characters::ToString (onInterface)};
332 SetMembership_ (iaddr, onInterface,
false);
341 void SetMembership_ (
const InternetAddress& group,
const variant<InternetAddress, unsigned int>& onInterface,
bool join)
344 case InternetAddress::AddressFamily::V4: {
345 if (
const unsigned int* index = get_if<unsigned int> (&onInterface); index !=
nullptr and *index !=
Interface::kAnyIndex) {
348 r.gr_interface = *index;
350 const sockaddr_storage ss = sa.
As<sockaddr_storage> ();
351 ::memcpy (&r.gr_group, &ss, sa.GetRequiredSize ());
352#if qStroika_Platform_MacOS
355 r.gr_group.ss_len =
static_cast<uint8_t
> (sa.GetRequiredSize ());
357 setsockopt (IPPROTO_IP, join ? MCAST_JOIN_GROUP : MCAST_LEAVE_GROUP, &r, static_cast<socklen_t> (sizeof (r)));
361 m.imr_multiaddr = group.
As<in_addr> ();
362 if (
const InternetAddress* address = get_if<InternetAddress> (&onInterface); address !=
nullptr and *address != V6::kAddrAny) {
363 m.imr_interface = address->As<in_addr> ();
365 setsockopt (IPPROTO_IP, join ? IP_ADD_MEMBERSHIP : IP_DROP_MEMBERSHIP, m);
368 case InternetAddress::AddressFamily::V6: {
370 m.ipv6mr_multiaddr = group.
As<in6_addr> ();
371 m.ipv6mr_interface = InterfaceIndex_ (onInterface);
372 setsockopt (IPPROTO_IPV6, join ? IPV6_JOIN_GROUP : IPV6_LEAVE_GROUP, m);
378 virtual void SetMulticastInterface (
const variant<InternetAddress, unsigned int>& onInterface)
override
381 switch (GetAddressFamily ()) {
382 case SocketAddress::INET: {
384 const InternetAddress* address = get_if<InternetAddress> (&onInterface);
385 Require (address !=
nullptr and address->
GetAddressFamily () == InternetAddress::AddressFamily::V4);
386 setsockopt (IPPROTO_IP, IP_MULTICAST_IF, address->
As<in_addr> ());
388 case SocketAddress::INET6: {
389 setsockopt<unsigned int> (IPPROTO_IPV6, IPV6_MULTICAST_IF, InterfaceIndex_ (onInterface));
395 virtual uint8_t GetMulticastTTL ()
const override
398 switch (GetAddressFamily ()) {
399 case SocketAddress::INET: {
400 return getsockopt<uint8_t> (IPPROTO_IP, IP_MULTICAST_TTL);
402 case SocketAddress::INET6: {
405 return static_cast<uint8_t
> (getsockopt<int> (IPPROTO_IPV6, IPV6_MULTICAST_HOPS));
412 virtual void SetMulticastTTL (uint8_t ttl)
override
417 switch (GetAddressFamily ()) {
418 case SocketAddress::INET: {
419 setsockopt<uint8_t> (IPPROTO_IP, IP_MULTICAST_TTL, ttl);
422 case SocketAddress::INET6: {
423 setsockopt<int> (IPPROTO_IPV6, IPV6_MULTICAST_HOPS, ttl);
430 virtual bool GetMulticastLoopMode ()
const override
433 switch (GetAddressFamily ()) {
434 case SocketAddress::INET: {
435 return !!getsockopt<char> (IPPROTO_IP, IP_MULTICAST_LOOP);
437 case SocketAddress::INET6: {
438 return !!getsockopt<unsigned int> (IPPROTO_IPV6, IPV6_MULTICAST_LOOP);
445 virtual void SetMulticastLoopMode (
bool loopMode)
override
447 static constexpr Execution::Activity kSettingMulticastLoopMode{
"setting multicast loop mode"sv};
450 switch (GetAddressFamily ()) {
451 case SocketAddress::INET: {
452 setsockopt<char> (IPPROTO_IP, IP_MULTICAST_LOOP, loopMode);
455 case SocketAddress::INET6: {
456 setsockopt<unsigned int> (IPPROTO_IPV6, IPV6_MULTICAST_LOOP, loopMode);
488 JoinMulticastGroup (iaddr, CurrentIndexOf_ (onInterface));
493 LeaveMulticastGroup (iaddr, CurrentIndexOf_ (onInterface));
498 if (GetAddressFamily () == SocketAddress::INET) {
500 optional<InternetAddress> a = i.
fBindings.fAddresses.First (
505 SetMulticastInterface (*a);
508 SetMulticastInterface (CurrentIndexOf_ (i));
519 Require (socketKind != Type::STREAM);
520 return Ptr{Memory::MakeSharedPtr<Rep_> (_Protected::mkLowLevelSocket_ (family, socketKind, protocol))};
525 return Ptr{Memory::MakeSharedPtr<Rep_> (sd)};
#define AssertNotImplemented()
#define RequireNotReached()
#define RequireNotNull(p)
chrono::duration< double > DurationSeconds
chrono::duration<double> - a time span (length of time) measured in seconds, but high precision.
#define Stroika_Foundation_Debug_OptionalizeTraceArgs(...)
Similar to String, but intended to more efficiently construct a String. Mutable type (String is large...
String is like std::u32string, except it is much easier to use, often much more space efficient,...
nonvirtual string AsNarrowSDKString() const
NOT a real mutex - just a debugging infrastructure support tool so in debug builds can be assured thr...
unique_lock< AssertExternallySynchronizedChecker > WriteContext
Instantiate AssertExternallySynchronizedChecker::WriteContext to designate an area of code where prot...
shared_lock< const AssertExternallySynchronizedChecker > ReadContext
Instantiate AssertExternallySynchronizedChecker::ReadContext to designate an area of code where prote...
nonvirtual Containers::Set< T > WaitQuietly(Time::DurationSeconds waitFor=Time::kInfinity)
nonvirtual void LeaveMulticastGroup(const InternetAddress &iaddr, const Interface &onInterface) const
nonvirtual void SetMulticastInterface(const Interface &i) const
nonvirtual void JoinMulticastGroup(const InternetAddress &iaddr, const Interface &onInterface) const
nonvirtual constexpr AddressFamily GetAddressFamily() const
nonvirtual size_t GetRequiredSize() const
FamilyType
Socket address family - also sometimes referred to as domain (argument to ::socket calls it domain)
void CheckForInterruption()
void Throw(T &&e2Throw)
like the builtin C++ 'throw', but stamps in the current Activity context and does helpful,...
auto Handle_ErrNoResultInterruption(CALL call) -> decltype(call())
Handle UNIX EINTR system call behavior - fairly transparently - just effectively removes them from th...
Ptr Attach(PlatformNativeHandle sd)
ConnectionlessSocket::Ptr New(SocketAddress::FamilyType family, Type socketKind, const optional< IPPROTO > &protocol=nullopt)
Where a datagram arrived (from ReceiveFrom, once SetReceivePacketInfo (true)): the interface it came ...
nonvirtual Characters::String ToString() const
unsigned int fInterfaceIndex
InternetAddress fDestination
static constexpr unsigned int kAnyIndex
SystemIDType fInterfaceID