Stroika Library 3.0d25
 
Loading...
Searching...
No Matches
ConnectionlessSocket.cpp
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2026. All rights reserved
3 */
4#if defined(__APPLE__)
5// before any system header: so macOS has RFC 3542's IPV6_RECVPKTINFO (else its IPV6_PKTINFO is the older RFC 2292's option)
6#define __APPLE_USE_RFC_3542 1
7#endif
8
9#include "Stroika/Foundation/StroikaPreComp.h"
10
11#if qStroika_Platform_POSIX
12#include <net/if.h>
13#include <netinet/in.h>
14#include <sys/socket.h>
15#elif qStroika_Platform_Windows
16#include <WinSock2.h>
17
18#include <Iphlpapi.h>
19#include <mswsock.h>
20#include <netioapi.h>
21#include <ws2tcpip.h>
22#endif
23
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"
34
35#include "ConnectionlessSocket.h"
36
37// Comment this in to turn on aggressive noisy DbgTrace in this module
38//#define USE_NOISY_TRACE_IN_THIS_MODULE_ 1
39using std::byte;
40
41using namespace Stroika::Foundation;
43using namespace Stroika::Foundation::Execution;
44using namespace Stroika::Foundation::Memory;
45using namespace Stroika::Foundation::IO;
47
48using namespace Stroika::Foundation::IO::Network::PRIVATE_;
49
51
53
54#if defined(_MSC_VER)
55#pragma comment(lib, "Iphlpapi.lib") // ConvertInterfaceGuidToLuid ()
56#endif
57
58namespace {
59 // the index of the interface with this address (an exact match - not merely on its subnet); kAddrAny: kAnyIndex
60 unsigned int InterfaceIndexOf_ (const InternetAddress& interfaceAddress)
61 {
62 if (interfaceAddress == V4::kAddrAny or interfaceAddress == V6::kAddrAny) {
64 }
65 for (const Interface& i : SystemInterfacesMgr{}.GetAll ()) {
66 if (i.fIndex and i.fBindings.fAddresses.Contains (interfaceAddress)) {
67 return *i.fIndex;
68 }
69 }
70 Execution::Throw (SystemErrorException{make_error_code (errc::no_such_device)}); // what IP_ADD_MEMBERSHIP reported for this (on Linux)
71 }
72 // i's index now - looked up by i.fInterfaceID, not i.fIndex, which may be stale (@see Interface::fIndex) - or ENODEV if it is
73 // gone (as IP_ADD_MEMBERSHIP reports for a missing interface, on Linux)
74 unsigned int CurrentIndexOf_ (const Interface& i)
75 {
76#if qStroika_Platform_POSIX
77 // fInterfaceID is the device's name
78 if (unsigned int index = ::if_nametoindex (i.fInterfaceID.AsNarrowSDKString ().c_str ()); index != Interface::kAnyIndex) {
79 return index;
80 }
81#elif qStroika_Platform_Windows
82 // fInterfaceID is the adapter's GUID (IP_ADAPTER_ADDRESSES::AdapterName) - so one that is not a GUID is no adapter's
83 optional<::GUID> guid;
84 try {
86 static_assert (sizeof (::GUID) == sizeof (g));
87 guid.emplace ();
88 ::memcpy (&*guid, &g, sizeof (::GUID));
89 }
91 }
92 NET_LUID luid{};
93 NET_IFINDEX index{};
94 if (guid and ::ConvertInterfaceGuidToLuid (&*guid, &luid) == NO_ERROR and
95 ::ConvertInterfaceLuidToIndex (&luid, &index) == NO_ERROR and index != Interface::kAnyIndex) {
96 return index;
97 }
98#else
100#endif
101 Execution::Throw (SystemErrorException{make_error_code (errc::no_such_device)});
102 }
103 // the interface as IPv6 names it: by index (an address is looked up)
104 unsigned int InterfaceIndex_ (const variant<InternetAddress, unsigned int>& onInterface)
105 {
106 if (const unsigned int* index = get_if<unsigned int> (&onInterface)) {
107 return *index;
108 }
109 return InterfaceIndexOf_ (get<InternetAddress> (onInterface));
110 }
111}
112
113namespace {
114 struct Rep_ : BackSocketImpl_<ConnectionlessSocket::_IRep> {
115 using inherited = BackSocketImpl_<ConnectionlessSocket::_IRep>;
117 : inherited{sd}
118 {
119 }
120 virtual void SendTo (const byte* start, const byte* end, const SocketAddress& sockAddr) override
121 {
122#if USE_NOISY_TRACE_IN_THIS_MODULE_
123 Debug::TraceContextBumper ctx{"IO::Network::Socket...rep...::SendTo", "end-start={}, sockAddr={}"_f,
124 static_cast<long long> (end - start), sockAddr};
125#endif
126 AssertExternallySynchronizedChecker::WriteContext declareContext{this->fThisAssertExternallySynchronized};
127 sockaddr_storage sa = sockAddr.As<sockaddr_storage> ();
128#if qStroika_Platform_POSIX
129 Handle_ErrNoResultInterruption ([this, &start, &end, &sa, &sockAddr] () -> int {
130 return ::sendto (fSD_, reinterpret_cast<const char*> (start), end - start, 0, reinterpret_cast<sockaddr*> (&sa),
131 sockAddr.GetRequiredSize ());
132 });
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 ())));
137#else
139#endif
140 }
141 virtual bool GetReceivePacketInfo () const override
142 {
143 AssertExternallySynchronizedChecker::ReadContext declareContext{this->fThisAssertExternallySynchronized};
144 switch (GetAddressFamily ()) {
145 case SocketAddress::INET: {
146 return getsockopt<int> (IPPROTO_IP, IP_PKTINFO) != 0;
147 }
148 case SocketAddress::INET6: {
149#if qStroika_Platform_Windows
150 return getsockopt<int> (IPPROTO_IPV6, IPV6_PKTINFO) != 0;
151#else
152 return getsockopt<int> (IPPROTO_IPV6, IPV6_RECVPKTINFO) != 0;
153#endif
154 }
155 default:
156 RequireNotReached (); // only legal for IP sockets
157 return false;
158 }
159 }
160 virtual void SetReceivePacketInfo (bool on) override
161 {
162 AssertExternallySynchronizedChecker::WriteContext declareContext{fThisAssertExternallySynchronized};
163 switch (GetAddressFamily ()) {
164 case SocketAddress::INET: {
165 setsockopt<int> (IPPROTO_IP, IP_PKTINFO, on); // (macOS: the same as its IP_RECVPKTINFO)
166 } break;
167 case SocketAddress::INET6: {
168#if qStroika_Platform_Windows
169 setsockopt<int> (IPPROTO_IPV6, IPV6_PKTINFO, on);
170#else
171 setsockopt<int> (IPPROTO_IPV6, IPV6_RECVPKTINFO, on); // RFC 3542
172#endif
173 } break;
174 default:
175 RequireNotReached (); // only legal for IP sockets
176 }
177 }
178 virtual size_t ReceiveFrom (byte* intoStart, byte* intoEnd, int flag, SocketAddress* fromAddress, optional<PacketInfo>* packetInfo,
179 Time::DurationSeconds timeout) override
180 {
181 AssertExternallySynchronizedChecker::WriteContext declareContext{fThisAssertExternallySynchronized};
182
183 if constexpr (qStroika_Platform_Windows) {
184 // TMPHACK for - https://github.com/SophistSolutions/Stroika/issues/1096 (STK-964)
185 auto s = Execution::WaitForIOReady{fSD_}.WaitQuietly (timeout);
187 if (s.empty ()) {
188 Execution::ThrowError (errc::timed_out);
189 }
190 }
191
192 // Note - COULD have implemented timeout with SO_RCVTIMEO, but that would risk statefulness, and confusion setting/resetting the parameter. Could be done, but this seems
193 // cleaner...
194 constexpr Time::DurationSeconds kMaxPolltime_{numeric_limits<int>::max () / 1000.0};
195 if (timeout < kMaxPolltime_) {
196 int timeout_millisecs = Math::Round<int> (timeout.count () * 1000);
197 pollfd pollData{};
198 pollData.fd = fSD_;
199 pollData.events = POLLIN;
200#if qStroika_Platform_Windows
201 int nresults;
202 if ((nresults = ::WSAPoll (&pollData, 1, timeout_millisecs)) == SOCKET_ERROR) {
203 Execution::ThrowSystemErrNo (::WSAGetLastError ());
204 }
205#else
206 int nresults = Handle_ErrNoResultInterruption ([&] () { return ::poll (&pollData, 1, timeout_millisecs); });
207#endif
208 if (nresults == 0) [[unlikely]] {
209 Execution::ThrowError (errc::timed_out);
210 }
211 }
212
213 if (packetInfo != nullptr) {
214 return ReceiveMsg_ (intoStart, intoEnd, flag, fromAddress, packetInfo);
215 }
216 struct sockaddr_storage sa;
217 socklen_t salen = sizeof (sa);
218#if qStroika_Platform_POSIX
219 size_t result = static_cast<size_t> (Handle_ErrNoResultInterruption ([&] () -> int {
220 return ::recvfrom (fSD_, reinterpret_cast<char*> (intoStart), intoEnd - intoStart, flag,
221 fromAddress == nullptr ? nullptr : reinterpret_cast<sockaddr*> (&sa), fromAddress == nullptr ? nullptr : &salen);
222 }));
223 if (fromAddress != nullptr) {
224 *fromAddress = sa;
225 }
226 return result;
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) {
233 *fromAddress = sa;
234 }
235 return result;
236#else
238#endif
239 }
240 // as recvfrom, but with recvmsg (Windows: WSARecvMsg) - which also reads the IP_PKTINFO / IPV6_PKTINFO the OS sends with
241 // the datagram, once SetReceivePacketInfo (true) - into *packetInfo (else nullopt)
242 size_t ReceiveMsg_ (byte* intoStart, byte* intoEnd, int flag, SocketAddress* fromAddress, optional<PacketInfo>* packetInfo)
243 {
244 RequireNotNull (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))];
250 msghdr m{};
251 m.msg_name = &sa;
252 m.msg_namelen = sizeof (sa);
253 m.msg_iov = &iov;
254 m.msg_iovlen = 1;
255 m.msg_control = control;
256 m.msg_controllen = sizeof (control);
257 size_t result = static_cast<size_t> (Handle_ErrNoResultInterruption ([&] () -> int { return ::recvmsg (fSD_, &m, flag); }));
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) {
260 in_pktinfo i;
261 ::memcpy (&i, CMSG_DATA (c), sizeof (i));
262 *packetInfo = PacketInfo{static_cast<unsigned int> (i.ipi_ifindex), InternetAddress { i.ipi_addr }};
263 }
264 else if (c->cmsg_level == IPPROTO_IPV6 and c->cmsg_type == IPV6_PKTINFO) {
265 in6_pktinfo i;
266 ::memcpy (&i, CMSG_DATA (c), sizeof (i));
267 *packetInfo = PacketInfo{static_cast<unsigned int> (i.ipi6_ifindex), InternetAddress { i.ipi6_addr }};
268 }
269 }
270#elif qStroika_Platform_Windows
271 Require (cmp_less (intoEnd - intoStart, numeric_limits<ULONG>::max ()));
272 if (fWSARecvMsg_ == nullptr) {
273 // not exported: fetched for the socket
274 GUID id = WSAID_WSARECVMSG;
275 DWORD bytes = 0;
276 ThrowWSASystemErrorIfSOCKET_ERROR (::WSAIoctl (fSD_, SIO_GET_EXTENSION_FUNCTION_POINTER, &id, sizeof (id), &fWSARecvMsg_,
277 sizeof (fWSARecvMsg_), &bytes, nullptr, nullptr));
278 }
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))];
281 WSAMSG m{};
282 m.name = reinterpret_cast<sockaddr*> (&sa);
283 m.namelen = sizeof (sa);
284 m.lpBuffers = &buf;
285 m.dwBufferCount = 1;
286 m.Control = WSABUF{sizeof (control), control};
287 m.dwFlags = static_cast<ULONG> (flag);
288 DWORD nBytes = 0;
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) {
293 IN_PKTINFO i;
294 ::memcpy (&i, WSA_CMSG_DATA (c), sizeof (i));
295 *packetInfo = PacketInfo{static_cast<unsigned int> (i.ipi_ifindex), InternetAddress { i.ipi_addr }};
296 }
297 else if (c->cmsg_level == IPPROTO_IPV6 and c->cmsg_type == IPV6_PKTINFO) {
298 IN6_PKTINFO i;
299 ::memcpy (&i, WSA_CMSG_DATA (c), sizeof (i));
300 *packetInfo = PacketInfo{static_cast<unsigned int> (i.ipi6_ifindex), InternetAddress { i.ipi6_addr }};
301 }
302 }
303#else
305 size_t result = 0;
306#endif
307 if (fromAddress != nullptr) {
308 *fromAddress = sa;
309 }
310 return result;
311 }
312#if qStroika_Platform_Windows
313 LPFN_WSARECVMSG fWSARecvMsg_{nullptr}; // (fThisAssertExternallySynchronized) fetched as first needed
314#endif
315 virtual void JoinMulticastGroup (const InternetAddress& iaddr, const variant<InternetAddress, unsigned int>& onInterface) override
316 {
318 "IO::Network::Socket::JoinMulticastGroup",
319 Stroika_Foundation_Debug_OptionalizeTraceArgs ("iaddr={} onInterface={}"_f, iaddr, Characters::ToString (onInterface))};
320 AssertExternallySynchronizedChecker::WriteContext declareContext{fThisAssertExternallySynchronized};
321 auto activity = Execution::LazyEvalActivity{[&] () -> Characters::String {
322 return "joining multicast group "sv + Characters::ToString (iaddr) + " on interface "sv + Characters::ToString (onInterface);
323 }};
324 Execution::DeclareActivity activityDeclare{&activity};
325 SetMembership_ (iaddr, onInterface, true);
326 }
327 virtual void LeaveMulticastGroup (const InternetAddress& iaddr, const variant<InternetAddress, unsigned int>& onInterface) override
328 {
329 Debug::TraceContextBumper ctx{"IO::Network::Socket::LeaveMulticastGroup", "iaddr={} onInterface={}"_f, iaddr,
330 Characters::ToString (onInterface)};
331 AssertExternallySynchronizedChecker::WriteContext declareContext{fThisAssertExternallySynchronized};
332 SetMembership_ (iaddr, onInterface, false);
333 }
334 /*
335 * Each case names the interface as the OS's own call does, so the kernel resolves it: IPv4's classic join takes an
336 * interface ADDRESS (INADDR_ANY: the OS picks one), IPv6's an index (kAnyIndex: the OS picks one). An IPv4 interface
337 * given by index uses RFC 3678's MCAST_JOIN_GROUP - but BSD/macOS refuse kAnyIndex there (EADDRNOTAVAIL), so that is
338 * INADDR_ANY.
339 * IPv6 has no join by address, so an address is looked up.
340 */
341 void SetMembership_ (const InternetAddress& group, const variant<InternetAddress, unsigned int>& onInterface, bool join)
342 {
343 switch (group.GetAddressFamily ()) {
344 case InternetAddress::AddressFamily::V4: {
345 if (const unsigned int* index = get_if<unsigned int> (&onInterface); index != nullptr and *index != Interface::kAnyIndex) {
346 // RFC 3678's protocol-independent form: the group as a sockaddr, the interface by index.
347 ::group_req r{};
348 r.gr_interface = *index;
349 const SocketAddress sa{group};
350 const sockaddr_storage ss = sa.As<sockaddr_storage> ();
351 ::memcpy (&r.gr_group, &ss, sa.GetRequiredSize ());
352#if qStroika_Platform_MacOS
353 // a BSD sockaddr carries its own length - which bind () and sendto () take from their argument instead,
354 // but which MCAST_JOIN_GROUP checks (EINVAL)
355 r.gr_group.ss_len = static_cast<uint8_t> (sa.GetRequiredSize ());
356#endif
357 setsockopt (IPPROTO_IP, join ? MCAST_JOIN_GROUP : MCAST_LEAVE_GROUP, &r, static_cast<socklen_t> (sizeof (r)));
358 }
359 else {
360 ::ip_mreq m{};
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> (); // else INADDR_ANY: the OS picks
364 }
365 setsockopt (IPPROTO_IP, join ? IP_ADD_MEMBERSHIP : IP_DROP_MEMBERSHIP, m);
366 }
367 } break;
368 case InternetAddress::AddressFamily::V6: {
369 ::ipv6_mreq m{};
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);
373 } break;
374 default:
376 }
377 }
378 virtual void SetMulticastInterface (const variant<InternetAddress, unsigned int>& onInterface) override
379 {
380 AssertExternallySynchronizedChecker::WriteContext declareContext{fThisAssertExternallySynchronized};
381 switch (GetAddressFamily ()) {
382 case SocketAddress::INET: {
383 // by one of its addresses - there is no portable way to choose an IPv4 interface by index
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> ());
387 } break;
388 case SocketAddress::INET6: {
389 setsockopt<unsigned int> (IPPROTO_IPV6, IPV6_MULTICAST_IF, InterfaceIndex_ (onInterface)); // an unsigned int (a DWORD on Windows)
390 } break;
391 default:
393 }
394 }
395 virtual uint8_t GetMulticastTTL () const override
396 {
397 AssertExternallySynchronizedChecker::ReadContext declareContext{this->fThisAssertExternallySynchronized};
398 switch (GetAddressFamily ()) {
399 case SocketAddress::INET: {
400 return getsockopt<uint8_t> (IPPROTO_IP, IP_MULTICAST_TTL);
401 }
402 case SocketAddress::INET6: {
403 // IPv6's multicast options are an int (hops) and an unsigned int (loop) - RFC 3493 5.2 - and Linux and macOS
404 // reject anything shorter, with EINVAL. (IPv4's traditionally take a byte, which all accept.)
405 return static_cast<uint8_t> (getsockopt<int> (IPPROTO_IPV6, IPV6_MULTICAST_HOPS));
406 }
407 default:
408 RequireNotReached (); // only legal for IP sockets
409 return 0;
410 }
411 }
412 virtual void SetMulticastTTL (uint8_t ttl) override
413 {
414 static constexpr Execution::Activity kSettingMulticastTTL{"setting multicast TTL"sv};
415 Execution::DeclareActivity activityDeclare{&kSettingMulticastTTL};
416 AssertExternallySynchronizedChecker::WriteContext declareContext{this->fThisAssertExternallySynchronized};
417 switch (GetAddressFamily ()) {
418 case SocketAddress::INET: {
419 setsockopt<uint8_t> (IPPROTO_IP, IP_MULTICAST_TTL, ttl);
420 break;
421 }
422 case SocketAddress::INET6: {
423 setsockopt<int> (IPPROTO_IPV6, IPV6_MULTICAST_HOPS, ttl); // an int: @see GetMulticastTTL
424 break;
425 }
426 default:
427 RequireNotReached (); // only legal for IP sockets
428 }
429 }
430 virtual bool GetMulticastLoopMode () const override
431 {
432 AssertExternallySynchronizedChecker::ReadContext declareContext{this->fThisAssertExternallySynchronized};
433 switch (GetAddressFamily ()) {
434 case SocketAddress::INET: {
435 return !!getsockopt<char> (IPPROTO_IP, IP_MULTICAST_LOOP);
436 }
437 case SocketAddress::INET6: {
438 return !!getsockopt<unsigned int> (IPPROTO_IPV6, IPV6_MULTICAST_LOOP); // an unsigned int: @see GetMulticastTTL
439 }
440 default:
441 RequireNotReached (); // only legal for IP sockets
442 return false;
443 }
444 }
445 virtual void SetMulticastLoopMode (bool loopMode) override
446 {
447 static constexpr Execution::Activity kSettingMulticastLoopMode{"setting multicast loop mode"sv};
448 Execution::DeclareActivity activityDeclare{&kSettingMulticastLoopMode};
449 AssertExternallySynchronizedChecker::WriteContext declareContext{this->fThisAssertExternallySynchronized};
450 switch (GetAddressFamily ()) {
451 case SocketAddress::INET: {
452 setsockopt<char> (IPPROTO_IP, IP_MULTICAST_LOOP, loopMode);
453 break;
454 }
455 case SocketAddress::INET6: {
456 setsockopt<unsigned int> (IPPROTO_IPV6, IPV6_MULTICAST_LOOP, loopMode); // an unsigned int: @see GetMulticastTTL
457 break;
458 }
459 default:
460 RequireNotReached (); // only legal for IP sockets
461 }
462 }
463 };
464}
465
466/*
467 ********************************************************************************
468 *********************** ConnectionlessSocket::PacketInfo ***********************
469 ********************************************************************************
470 */
472{
473 StringBuilder sb;
474 sb << "{"sv;
475 sb << "interfaceIndex : "sv << fInterfaceIndex;
476 sb << ", destination : "sv << fDestination;
477 sb << "}"sv;
478 return sb;
479}
480
481/*
482 ********************************************************************************
483 ************************** ConnectionlessSocket::Ptr ***************************
484 ********************************************************************************
485 */
487{
488 JoinMulticastGroup (iaddr, CurrentIndexOf_ (onInterface));
489}
490
492{
493 LeaveMulticastGroup (iaddr, CurrentIndexOf_ (onInterface));
494}
495
497{
498 if (GetAddressFamily () == SocketAddress::INET) {
499 // by one of its addresses - there is no portable way to choose an IPv4 interface by index
500 optional<InternetAddress> a = i.fBindings.fAddresses.First (
501 [] (const InternetAddress& ia) { return ia.GetAddressFamily () == InternetAddress::AddressFamily::V4; });
502 if (not a) {
503 Execution::Throw (SystemErrorException{make_error_code (errc::address_not_available)}); // no IPv4 on that interface
504 }
505 SetMulticastInterface (*a);
506 }
507 else {
508 SetMulticastInterface (CurrentIndexOf_ (i));
509 }
510}
511
512/*
513 ********************************************************************************
514 ************************** ConnectionlessSocket ********************************
515 ********************************************************************************
516 */
517ConnectionlessSocket::Ptr ConnectionlessSocket::New (SocketAddress::FamilyType family, Type socketKind, const optional<IPPROTO>& protocol)
518{
519 Require (socketKind != Type::STREAM); // use ConnectionOrientedStreamSocket or ConnectionOrientedMasterSocket
520 return Ptr{Memory::MakeSharedPtr<Rep_> (_Protected::mkLowLevelSocket_ (family, socketKind, protocol))};
521}
522
524{
525 return Ptr{Memory::MakeSharedPtr<Rep_> (sd)};
526}
#define AssertNotImplemented()
Definition Assertions.h:404
#define RequireNotReached()
Definition Assertions.h:388
#define RequireNotNull(p)
Definition Assertions.h:350
chrono::duration< double > DurationSeconds
chrono::duration<double> - a time span (length of time) measured in seconds, but high precision.
Definition Realtime.h:57
#define Stroika_Foundation_Debug_OptionalizeTraceArgs(...)
Definition Trace.h:288
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,...
Definition String.h:201
nonvirtual string AsNarrowSDKString() const
Definition String.inl:832
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
FamilyType
Socket address family - also sometimes referred to as domain (argument to ::socket calls it domain)
void Throw(T &&e2Throw)
like the builtin C++ 'throw', but stamps in the current Activity context and does helpful,...
Definition Throw.inl:54
auto Handle_ErrNoResultInterruption(CALL call) -> decltype(call())
Handle UNIX EINTR system call behavior - fairly transparently - just effectively removes them from th...
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 ...
static constexpr unsigned int kAnyIndex
Definition Interface.h:84