4#include "Stroika/Foundation/StroikaPreComp.h"
7#include "Stroika/Foundation/Execution/Activity.h"
9#include "Stroika/Foundation/Execution/TimeOutException.h"
13#include "Socket-Private_.h"
15#include "ConnectionlessSocket.h"
24using namespace Stroika::Foundation::Memory;
25using namespace Stroika::Foundation::IO;
28using namespace Stroika::Foundation::IO::Network::PRIVATE_;
33 struct Rep_ : BackSocketImpl_<ConnectionlessSocket::_IRep> {
34 using inherited = BackSocketImpl_<ConnectionlessSocket::_IRep>;
39 virtual void SendTo (
const byte* start,
const byte* end,
const SocketAddress& sockAddr)
override
41#if USE_NOISY_TRACE_IN_THIS_MODULE_
43 static_cast<long long> (end - start), sockAddr};
46 sockaddr_storage sa = sockAddr.
As<sockaddr_storage> ();
47#if qStroika_Foundation_Common_Platform_POSIX
49 return ::sendto (fSD_,
reinterpret_cast<const char*
> (start), end - start, 0,
reinterpret_cast<sockaddr*
> (&sa),
52#elif qStroika_Foundation_Common_Platform_Windows
53 Require (end - start < numeric_limits<int>::max ());
54 ThrowWSASystemErrorIfSOCKET_ERROR (::sendto (fSD_,
reinterpret_cast<const char*
> (start),
static_cast<int> (end - start), 0,
55 reinterpret_cast<sockaddr*
> (&sa),
static_cast<int> (sockAddr.
GetRequiredSize ())));
64 if constexpr (qStroika_Foundation_Common_Platform_Windows) {
76 if (timeout < kMaxPolltime_) {
77 int timeout_millisecs = Math::Round<int> (timeout.count () * 1000);
80 pollData.events = POLLIN;
81#if qStroika_Foundation_Common_Platform_Windows
83 if ((nresults = ::WSAPoll (&pollData, 1, timeout_millisecs)) == SOCKET_ERROR) {
84 Execution::ThrowSystemErrNo (::WSAGetLastError ());
89 if (nresults == 0) [[unlikely]] {
94 struct sockaddr_storage sa;
95 socklen_t salen =
sizeof (sa);
96#if qStroika_Foundation_Common_Platform_POSIX
98 return ::recvfrom (fSD_,
reinterpret_cast<char*
> (intoStart), intoEnd - intoStart, flag,
99 fromAddress ==
nullptr ?
nullptr : reinterpret_cast<sockaddr*> (&sa), fromAddress == nullptr ? nullptr : &salen);
101 if (fromAddress !=
nullptr) {
105#elif qStroika_Foundation_Common_Platform_Windows
106 Require (intoEnd - intoStart < numeric_limits<int>::max ());
107 size_t result =
static_cast<size_t> (ThrowWSASystemErrorIfSOCKET_ERROR (
108 ::recvfrom (fSD_,
reinterpret_cast<char*
> (intoStart),
static_cast<int> (intoEnd - intoStart), flag,
109 fromAddress ==
nullptr ?
nullptr : reinterpret_cast<sockaddr*> (&sa), fromAddress == nullptr ? nullptr : &salen)));
110 if (fromAddress !=
nullptr) {
125 return "joining multicast group "sv + Characters::ToString (iaddr) +
" on interface "sv + Characters::ToString (onInterface);
129 case InternetAddress::AddressFamily::V4: {
131 m.imr_multiaddr = iaddr.
As<in_addr> ();
132 m.imr_interface = onInterface.
As<in_addr> ();
133 setsockopt (IPPROTO_IP, IP_ADD_MEMBERSHIP, m);
135 case InternetAddress::AddressFamily::V6: {
137 m.ipv6mr_multiaddr = iaddr.
As<in6_addr> ();
138 m.ipv6mr_interface = 0;
139 setsockopt (IPPROTO_IPV6, IPV6_JOIN_GROUP, m);
147 Debug::TraceContextBumper ctx{
"IO::Network::Socket::LeaveMulticastGroup",
"iaddr={} onInterface={}"_f, iaddr, onInterface};
150 case InternetAddress::AddressFamily::V4: {
152 m.imr_multiaddr = iaddr.
As<in_addr> ();
153 m.imr_interface = onInterface.
As<in_addr> ();
154 setsockopt (IPPROTO_IP, IP_DROP_MEMBERSHIP, m);
156 case InternetAddress::AddressFamily::V6: {
158 m.ipv6mr_multiaddr = iaddr.
As<in6_addr> ();
159 m.ipv6mr_interface = 0;
160 setsockopt (IPPROTO_IPV6, IPV6_LEAVE_GROUP, m);
166 virtual uint8_t GetMulticastTTL ()
const override
169 switch (GetAddressFamily ()) {
170 case SocketAddress::INET: {
171 return getsockopt<uint8_t> (IPPROTO_IP, IP_MULTICAST_TTL);
173 case SocketAddress::INET6: {
174 return getsockopt<uint8_t> (IPPROTO_IPV6, IPV6_MULTICAST_HOPS);
181 virtual void SetMulticastTTL (uint8_t ttl)
override
186 switch (GetAddressFamily ()) {
187 case SocketAddress::INET: {
188 setsockopt<uint8_t> (IPPROTO_IP, IP_MULTICAST_TTL, ttl);
191 case SocketAddress::INET6: {
192 constexpr bool kIPV6LoophackMulticastTTLLinuxBug_{qStroika_Foundation_Common_Platform_Linux};
193 if (kIPV6LoophackMulticastTTLLinuxBug_) {
195 setsockopt<char> (IPPROTO_IPV6, IPV6_MULTICAST_HOPS, ttl);
197 catch (
const std::system_error& e) {
199 if (e.code () == errc::invalid_argument) {
200 DbgTrace (
"IPV6_MULTICAST_HOPS: For now ignoring what is probably a very small, minor bug, but one "
201 "where I have no idea why this is happening - but I saw reliably on Ubuntu/Linux"_f);
207 setsockopt<char> (IPPROTO_IPV6, IPV6_MULTICAST_HOPS, ttl);
215 virtual bool GetMulticastLoopMode ()
const override
218 switch (GetAddressFamily ()) {
219 case SocketAddress::INET: {
220 return !!getsockopt<char> (IPPROTO_IP, IP_MULTICAST_LOOP);
222 case SocketAddress::INET6: {
223 return !!getsockopt<char> (IPPROTO_IPV6, IP_MULTICAST_LOOP);
230 virtual void SetMulticastLoopMode (
bool loopMode)
override
232 static constexpr Execution::Activity kSettingMulticastLoopMode{
"setting multicast loop mode"sv};
235 switch (GetAddressFamily ()) {
236 case SocketAddress::INET: {
237 setsockopt<char> (IPPROTO_IP, IP_MULTICAST_LOOP, loopMode);
240 case SocketAddress::INET6: {
241 constexpr bool kIPV6LoophackMulticastModeLinuxBug_{qStroika_Foundation_Common_Platform_Linux};
242 if (kIPV6LoophackMulticastModeLinuxBug_) {
244 setsockopt<char> (IPPROTO_IPV6, IPV6_MULTICAST_LOOP, loopMode);
246 catch (
const std::system_error& e) {
248 if (e.code () == errc::invalid_argument) {
249 DbgTrace (
"IPV6_MULTICAST_LOOP: For now ignoring what is probably a very small, minor bug, but one "
250 "where I have no idea why this is happening - but I saw reliably on Ubuntu/Linux"_f);
256 setsockopt<char> (IPPROTO_IPV6, IPV6_MULTICAST_LOOP, loopMode);
274 Require (socketKind != Type::STREAM);
275 return Ptr{Memory::MakeSharedPtr<Rep_> (_Protected::mkLowLevelSocket_ (family, socketKind, protocol))};
280 return Ptr{Memory::MakeSharedPtr<Rep_> (sd)};
#define AssertNotImplemented()
#define RequireNotReached()
chrono::duration< double > DurationSeconds
chrono::duration<double> - a time span (length of time) measured in seconds, but high precision.
#define Stroika_Foundation_Debug_OptionalizeTraceArgs(...)
String is like std::u32string, except it is much easier to use, often much more space efficient,...
NOT a real mutex - just a debugging infrastructure support tool so in debug builds can be assured thr...
shared_lock< const AssertExternallySynchronizedMutex > ReadContext
Instantiate AssertExternallySynchronizedMutex::ReadContext to designate an area of code where protect...
unique_lock< AssertExternallySynchronizedMutex > WriteContext
Instantiate AssertExternallySynchronizedMutex::WriteContext to designate an area of code where protec...
static const TimeOutException kThe
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()
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)