4#include "Stroika/Foundation/StroikaPreComp.h"
7#include "Stroika/Foundation/Execution/Activity.h"
9#include "Stroika/Foundation/Execution/TimeOutException.h"
12#include "Socket-Private_.h"
14#include "ConnectionlessSocket.h"
23using namespace Stroika::Foundation::Memory;
24using namespace Stroika::Foundation::IO;
27using namespace Stroika::Foundation::IO::Network::PRIVATE_;
32 struct Rep_ : BackSocketImpl_<ConnectionlessSocket::_IRep> {
33 using inherited = BackSocketImpl_<ConnectionlessSocket::_IRep>;
38 virtual void SendTo (
const byte* start,
const byte* end,
const SocketAddress& sockAddr)
override
40#if USE_NOISY_TRACE_IN_THIS_MODULE_
42 static_cast<long long> (end - start), sockAddr};
45 sockaddr_storage sa = sockAddr.
As<sockaddr_storage> ();
46#if qStroika_Foundation_Common_Platform_POSIX
48 return ::sendto (fSD_,
reinterpret_cast<const char*
> (start), end - start, 0,
reinterpret_cast<sockaddr*
> (&sa),
51#elif qStroika_Foundation_Common_Platform_Windows
52 Require (end - start < numeric_limits<int>::max ());
53 ThrowWSASystemErrorIfSOCKET_ERROR (::sendto (fSD_,
reinterpret_cast<const char*
> (start),
static_cast<int> (end - start), 0,
54 reinterpret_cast<sockaddr*
> (&sa),
static_cast<int> (sockAddr.
GetRequiredSize ())));
63 if constexpr (qStroika_Foundation_Common_Platform_Windows) {
75 if (timeout < kMaxPolltime_) {
76 int timeout_millisecs = Math::Round<int> (timeout.count () * 1000);
79 pollData.events = POLLIN;
80#if qStroika_Foundation_Common_Platform_Windows
82 if ((nresults = ::WSAPoll (&pollData, 1, timeout_millisecs)) == SOCKET_ERROR) {
83 Execution::ThrowSystemErrNo (::WSAGetLastError ());
88 if (nresults == 0) [[unlikely]] {
93 struct sockaddr_storage sa;
94 socklen_t salen =
sizeof (sa);
95#if qStroika_Foundation_Common_Platform_POSIX
97 return ::recvfrom (fSD_,
reinterpret_cast<char*
> (intoStart), intoEnd - intoStart, flag,
98 fromAddress ==
nullptr ?
nullptr : reinterpret_cast<sockaddr*> (&sa), fromAddress == nullptr ? nullptr : &salen);
100 if (fromAddress !=
nullptr) {
104#elif qStroika_Foundation_Common_Platform_Windows
105 Require (intoEnd - intoStart < numeric_limits<int>::max ());
106 size_t result =
static_cast<size_t> (ThrowWSASystemErrorIfSOCKET_ERROR (
107 ::recvfrom (fSD_,
reinterpret_cast<char*
> (intoStart),
static_cast<int> (intoEnd - intoStart), flag,
108 fromAddress ==
nullptr ?
nullptr : reinterpret_cast<sockaddr*> (&sa), fromAddress == nullptr ? nullptr : &salen)));
109 if (fromAddress !=
nullptr) {
124 return "joining multicast group "sv + Characters::ToString (iaddr) +
" on interface "sv + Characters::ToString (onInterface);
128 case InternetAddress::AddressFamily::V4: {
130 m.imr_multiaddr = iaddr.
As<in_addr> ();
131 m.imr_interface = onInterface.
As<in_addr> ();
132 setsockopt (IPPROTO_IP, IP_ADD_MEMBERSHIP, m);
134 case InternetAddress::AddressFamily::V6: {
136 m.ipv6mr_multiaddr = iaddr.
As<in6_addr> ();
137 m.ipv6mr_interface = 0;
138 setsockopt (IPPROTO_IPV6, IPV6_JOIN_GROUP, m);
146 Debug::TraceContextBumper ctx{
"IO::Network::Socket::LeaveMulticastGroup",
"iaddr={} onInterface={}"_f, iaddr, onInterface};
149 case InternetAddress::AddressFamily::V4: {
151 m.imr_multiaddr = iaddr.
As<in_addr> ();
152 m.imr_interface = onInterface.
As<in_addr> ();
153 setsockopt (IPPROTO_IP, IP_DROP_MEMBERSHIP, m);
155 case InternetAddress::AddressFamily::V6: {
157 m.ipv6mr_multiaddr = iaddr.
As<in6_addr> ();
158 m.ipv6mr_interface = 0;
159 setsockopt (IPPROTO_IPV6, IPV6_LEAVE_GROUP, m);
165 virtual uint8_t GetMulticastTTL ()
const override
168 switch (GetAddressFamily ()) {
169 case SocketAddress::INET: {
170 return getsockopt<uint8_t> (IPPROTO_IP, IP_MULTICAST_TTL);
172 case SocketAddress::INET6: {
173 return getsockopt<uint8_t> (IPPROTO_IPV6, IPV6_MULTICAST_HOPS);
180 virtual void SetMulticastTTL (uint8_t ttl)
override
185 switch (GetAddressFamily ()) {
186 case SocketAddress::INET: {
187 setsockopt<uint8_t> (IPPROTO_IP, IP_MULTICAST_TTL, ttl);
190 case SocketAddress::INET6: {
191 constexpr bool kIPV6LoophackMulticastTTLLinuxBug_{qStroika_Foundation_Common_Platform_Linux};
192 if (kIPV6LoophackMulticastTTLLinuxBug_) {
194 setsockopt<char> (IPPROTO_IPV6, IPV6_MULTICAST_HOPS, ttl);
196 catch (
const std::system_error& e) {
198 if (e.code () == errc::invalid_argument) {
199 DbgTrace (
"IPV6_MULTICAST_HOPS: For now ignoring what is probably a very small, minor bug, but one "
200 "where I have no idea why this is happening - but I saw reliably on Ubuntu/Linux"_f);
206 setsockopt<char> (IPPROTO_IPV6, IPV6_MULTICAST_HOPS, ttl);
214 virtual bool GetMulticastLoopMode ()
const override
217 switch (GetAddressFamily ()) {
218 case SocketAddress::INET: {
219 return !!getsockopt<char> (IPPROTO_IP, IP_MULTICAST_LOOP);
221 case SocketAddress::INET6: {
222 return !!getsockopt<char> (IPPROTO_IPV6, IP_MULTICAST_LOOP);
229 virtual void SetMulticastLoopMode (
bool loopMode)
override
231 static constexpr Execution::Activity kSettingMulticastLoopMode{
"setting multicast loop mode"sv};
234 switch (GetAddressFamily ()) {
235 case SocketAddress::INET: {
236 setsockopt<char> (IPPROTO_IP, IP_MULTICAST_LOOP, loopMode);
239 case SocketAddress::INET6: {
240 constexpr bool kIPV6LoophackMulticastModeLinuxBug_{qStroika_Foundation_Common_Platform_Linux};
241 if (kIPV6LoophackMulticastModeLinuxBug_) {
243 setsockopt<char> (IPPROTO_IPV6, IPV6_MULTICAST_LOOP, loopMode);
245 catch (
const std::system_error& e) {
247 if (e.code () == errc::invalid_argument) {
248 DbgTrace (
"IPV6_MULTICAST_LOOP: For now ignoring what is probably a very small, minor bug, but one "
249 "where I have no idea why this is happening - but I saw reliably on Ubuntu/Linux"_f);
255 setsockopt<char> (IPPROTO_IPV6, IPV6_MULTICAST_LOOP, loopMode);
273 Require (socketKind != Type::STREAM);
274 return Ptr{make_shared<Rep_> (_Protected::mkLowLevelSocket_ (family, socketKind, protocol))};
279 return Ptr{make_shared<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()
void Throw(T &&e2Throw)
identical to builtin C++ 'throw' except that it does helpful, type dependent DbgTrace() messages firs...
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)