Stroika Library 3.0d24
 
Loading...
Searching...
No Matches
Interface.cpp
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2026. All rights reserved
3 */
4#include "Stroika/Foundation/StroikaPreComp.h"
5
6#include <cstdio>
7#include <filesystem>
8#include <random>
9
10#if qStroika_Foundation_Common_Platform_POSIX
11#include <arpa/inet.h>
12#include <net/if_arp.h>
13#include <netdb.h>
14#include <netinet/in.h>
15#include <sys/ioctl.h>
16#include <sys/socket.h>
17#include <unistd.h>
18#if qStroika_Foundation_Common_Platform_Linux
19#include <linux/ethtool.h>
20#include <linux/sockios.h>
21#include <linux/wireless.h>
22#elif qStroika_Foundation_Common_Platform_MacOS
23#include <net/if.h>
24#endif
25#elif qStroika_Foundation_Common_Platform_Windows
26#include <WinSock2.h>
27
28#include <Windot11.h> // for DOT11_SSID struct
29#include <wlanapi.h>
30
31#include <Iphlpapi.h>
32#include <WS2tcpip.h>
33#include <netioapi.h>
34#endif
35
40#include "Stroika/Foundation/Containers/Collection.h"
41#include "Stroika/Foundation/Containers/KeyedCollection.h"
42#include "Stroika/Foundation/Containers/Mapping.h"
45#include "Stroika/Foundation/Execution/Exceptions.h"
47#if qStroika_Foundation_Common_Platform_Windows
48#include "Stroika/Foundation/../Foundation/Execution/Platform/Windows/Exception.h"
49#endif
50#include "Stroika/Foundation/Execution/ProcessRunner.h"
55#include "Stroika/Foundation/Streams/MemoryStream.h"
56
57#include "Socket.h"
58
59#include "Interface.h"
60
61using std::byte;
62
63using namespace Stroika::Foundation;
66using namespace Stroika::Foundation::Execution;
67using namespace Stroika::Foundation::Memory;
68using namespace Stroika::Foundation::IO;
71
72// Comment this in to turn on aggressive noisy DbgTrace in this module
73// #define USE_NOISY_TRACE_IN_THIS_MODULE_ 1
74
75#if defined(_MSC_VER)
76// support use of Iphlpapi - but better to reference here than in lib entry of project file cuz
77// easier to see/modularize (and only pulled in if this module is referenced)
78#pragma comment(lib, "Iphlpapi.lib")
79#endif
80
81namespace {
82 // Windows uses '-' as separator, and linux ':'. Pick arbitrarily (more linux machines
83 // than windows, or soon will be)
84 auto PrintMacAddr_ (const uint8_t* macaddrBytes, [[maybe_unused]] const uint8_t* macaddrBytesEnd) -> String
85 {
86 Require (macaddrBytesEnd - macaddrBytes == 6);
88 (void)snprintf (buf, sizeof (buf), "%02x:%02x:%02x:%02x:%02x:%02x", macaddrBytes[0], macaddrBytes[1], macaddrBytes[2],
89 macaddrBytes[3], macaddrBytes[4], macaddrBytes[5]);
90 Assert (::strlen (buf) < std::size (buf)); // else we must patch in '\0' but I think snprintf always works here
91 return String{buf};
92 };
93}
94
95/*
96 ********************************************************************************
97 ********************** Interface::WirelessInfo *********************************
98 ********************************************************************************
99 */
101{
102 StringBuilder sb;
103 sb << "{"sv;
104 sb << "SSID: "sv << fSSID;
105 sb << ", state: "sv << fState;
106 sb << ", connectionMode: "sv << fConnectionMode;
107 sb << ", profileName: "sv << fProfileName;
108 sb << ", BSSType: "sv << fBSSType;
109 sb << ", MACAddress: "sv << fMACAddress;
110 sb << ", physicalConnectionType: "sv << fPhysicalConnectionType;
111 sb << ", signalQuality: "sv << fSignalQuality;
112 sb << ", securityEnabled: "sv << fSecurityEnabled;
113 sb << ", 8021XEnabled: "sv << f8021XEnabled;
114 sb << ", authAlgorithm: "sv << fAuthAlgorithm;
115 sb << ", cipher: "sv << fCipher;
116 sb << "}"sv;
117 return sb;
118}
119
120/*
121 ********************************************************************************
122 ******************************* Interface::Bindings ****************************
123 ********************************************************************************
124 */
125String Interface::Bindings::ToString () const
126{
127 StringBuilder sb;
128 sb << "{"sv;
129 sb << "boundAddressRanges: "sv << fAddressRanges;
130 sb << ", boundAddresses: "sv << fAddresses;
131 sb << "}"sv;
132 return sb;
133}
134/*
135 ********************************************************************************
136 *********************************** Interface **********************************
137 ********************************************************************************
138 */
140{
141 StringBuilder sb;
142 sb << "{"sv;
143 sb << "internalInterfaceID: "sv << fInternalInterfaceID;
144#if qStroika_Foundation_Common_Platform_POSIX
145 sb << ", interfaceName: "sv << GetInterfaceName ();
146#endif
147 sb << ", friendlyName: "sv << fFriendlyName;
148 if (fDescription) {
149 sb << ", description: "sv << *fDescription;
150 }
151 if (fNetworkGUID) {
152 sb << ", networkGUID: "sv << *fNetworkGUID;
153 }
154 if (fType) {
155 sb << ", type: "sv << *fType;
156 }
157 if (fHardwareAddress) {
158 sb << ", hardwareAddress: "sv << *fHardwareAddress;
159 }
160 if (fTransmitSpeedBaud) {
161 sb << ", transmitSpeedBaud: "sv << *fTransmitSpeedBaud;
162 }
164 sb << ", receiveLinkSpeedBaud: "sv << *fReceiveLinkSpeedBaud;
165 }
166 if (fWirelessInfo) {
167 sb << ", wirelessInfo: "sv << fWirelessInfo;
168 }
169 sb << ", bindings: "sv << fBindings;
170
171 sb << ", gateways: "sv << fGateways;
172 sb << ", DNSServers: "sv << fDNSServers;
173 if (fStatus) {
174 sb << ", status: "sv << *fStatus;
175 }
176 sb << "}"sv;
177 return sb;
178}
179
180/*
181 ********************************************************************************
182 ************************** Network::GetInterfaces ******************************
183 ********************************************************************************
184 */
185#if qStroika_Foundation_Common_Platform_POSIX
186namespace {
187
188 // NB: On macos, we get:
189 // Interface.cpp:210:71: runtime error: member access within misaligned address 0x70000a774c74 for type 'const ifreq', which requires 8 byte alignment
190 // 0x70000a774c74: note: pointer points here
191#if qMacUBSanitizerifreqAlignmentIssue_Buggy
192 Stroika_Foundation_Debug_ATTRIBUTE_NO_SANITIZE_UNDEFINED
193#endif
194 Interface GetInterfaces_POSIX_mkInterface_ (int sd, const ifreq* i, optional<Interface> prevInterfaceObject2Update)
195 {
196 Interface newInterface = prevInterfaceObject2Update.value_or (Interface{});
197 newInterface.fInternalInterfaceID = String::FromSDKString (i->ifr_name);
198 newInterface.fFriendlyName = newInterface.fInternalInterfaceID; // not great - maybe find better name - but this will do for now...
199 auto getFlags = [] (int sd, const char* name) {
200 ifreq ifreq{};
201 CString::Copy (ifreq.ifr_name, std::size (ifreq.ifr_name), name);
202 int r = ::ioctl (sd, SIOCGIFFLAGS, (char*)&ifreq);
203 Assert (r == 0 or errno == ENXIO); // ENXIO happens on MacOS sometimes, but never seen on linux
204 return r == 0 ? ifreq.ifr_flags : 0;
205 };
206 int flags = getFlags (sd, i->ifr_name);
207#if qStroika_Foundation_Common_Platform_Linux
208 auto getWirelessFlag = [] (int sd, const char* name) -> bool {
209#if defined(SIOCGIWNAME)
210 iwreq pwrq{};
211 CString::Copy (pwrq.ifr_name, std::size (pwrq.ifr_name), name);
212 int r = ::ioctl (sd, SIOCGIWNAME, (char*)&pwrq);
213 return r == 0;
214#else
215 return false;
216#endif
217 };
218#endif
219 if (flags & IFF_LOOPBACK) {
220 newInterface.fType = Interface::Type::eLoopback;
221 }
222#if qStroika_Foundation_Common_Platform_Linux
223 else if (getWirelessFlag (sd, i->ifr_name)) {
224 newInterface.fType = Interface::Type::eWIFI;
225 }
226#endif
227 else {
228 // NYI
229 newInterface.fType = Interface::Type::eWiredEthernet; // WAG - not the right way to tell!
230 }
231
232#if qStroika_Foundation_Common_Platform_Linux
233 {
234 ifreq tmp = *i;
235 if (::ioctl (sd, SIOCGIFHWADDR, &tmp) == 0 and tmp.ifr_hwaddr.sa_family == ARPHRD_ETHER) {
236 newInterface.fHardwareAddress = PrintMacAddr_ (reinterpret_cast<const uint8_t*> (tmp.ifr_hwaddr.sa_data),
237 reinterpret_cast<const uint8_t*> (tmp.ifr_hwaddr.sa_data) + 6);
238 }
239 }
240#endif
241
242#if qStroika_Foundation_Common_Platform_Linux || qStroika_Foundation_Common_Platform_MacOS
243 auto getNetMaskAsPrefix = [] (int sd, const char* name) -> optional<unsigned int> {
244 ifreq ifreq{};
245 CString::Copy (ifreq.ifr_name, std::size (ifreq.ifr_name), name);
246 int r = ::ioctl (sd, SIOCGIFNETMASK, (char*)&ifreq);
247 // On MacOS this often fails, but I've never seen it fail on Linux
248 if (r == 0) {
249#if qStroika_Foundation_Common_Platform_Linux
250 SocketAddress sa{ifreq.ifr_netmask};
251#elif qStroika_Foundation_Common_Platform_MacOS
252 SocketAddress sa{ifreq.ifr_addr};
253#endif
254 if (sa.IsInternetAddress ()) {
255 InternetAddress ia = sa.GetInternetAddress ();
256 size_t prefixLen{};
257 for (bool b : ia.As<vector<bool>> ()) {
258 if (b) {
259 prefixLen++;
260 }
261 else {
262 break;
263 }
264 }
265 return prefixLen;
266 }
267 }
268 return nullopt;
269 };
270#endif
271
272#if qStroika_Foundation_Common_Platform_Linux || qStroika_Foundation_Common_Platform_MacOS
273 auto getDefaultGateway = [] (const char* name) -> optional<InternetAddress> {
274 try {
275#if qStroika_Foundation_Common_Platform_Linux
277 static const filesystem::path kFileName_{"/proc/net/route"};
278 /*
279 * EXAMPLE OUTPUT:
280 * cat /proc/net/route
281 * Iface Destination Gateway Flags RefCnt Use Metric Mask MTU Window IRTT
282 * eth0 00000000 010011AC 0003 0 0 0 00000000 0 0 0
283 * eth0 000011AC 00000000 0001 0 0 0 0000FFFF 0 0 0
284 */
285 // Note - /procfs files always unseekable
286 for (const Sequence<String>& line :
287 reader.ReadMatrix (FileInputStream::New (kFileName_, IO::FileSystem::FileInputStream::eNotSeekable))) {
288 if (line.size () >= 3 and line[0] == String::FromNarrowSDKString (name) and line[1] == "00000000"sv) {
289 //
290 int tmp[4]{};
291 swscanf (line[2].As<wstring> ().c_str (), L"%02x%02x%02x%02x", &tmp[3], &tmp[2], &tmp[1], &tmp[0]);
292 return InternetAddress{static_cast<byte> (tmp[0]), static_cast<byte> (tmp[1]), static_cast<byte> (tmp[2]),
293 static_cast<byte> (tmp[3])};
294 }
295 }
296#elif qStroika_Foundation_Common_Platform_MacOS
297 /*
298 * NOTE: Could ALSO use netstat -nr - https://unh.edu/it/kb/article/how-to-route-print-mac-os-x.html
299 *
300 * EXAMPLE OUTPUT:
301 * >route get default
302 * route to: default
303 * destination: default
304 * mask: default
305 * gateway: router.asus.com
306 * interface: en0
307 * flags: <UP,GATEWAY,DONE,STATIC,PRCLONING>
308 * recvpipe sendpipe ssthresh rtt,msec rttvar hopcount mtu expire
309 * 0 0 0 0 0 0 1500 0
310 */
311 ProcessRunner pr{"route get default"sv};
312 Streams::MemoryStream::Ptr<byte> useStdOut = Streams::MemoryStream::New<byte> ();
313 pr.Run (nullptr, useStdOut);
315 optional<String> forInterface;
316 optional<String> gateway;
317 for (const Sequence<String>& line : reader.ReadMatrix (useStdOut)) {
318 if (line.size () == 2 and line[0] == "interface"sv) {
319 forInterface = line[1];
320 }
321 else if (line.size () == 2 and line[0] == "gateway"sv) {
322 gateway = line[1];
323 }
324 }
325 if (forInterface == String::FromNarrowSDKString (name) and gateway) {
326 try {
327 return InternetAddress{*gateway};
328 }
329 catch (...) {
330 // frequently fails - cuz its a dns name
331 }
332 try {
333 auto s = IO::Network::DNS::kThe.GetHostAddresses (*gateway);
334 if (not s.empty ()) {
335 return InternetAddress{s.Nth (0)};
336 }
337 }
338 catch (...) {
339 DbgTrace ("got exception converting gateway to address (dns): {}"_f, current_exception ()); // should work...
340 }
341 }
342#endif
343 }
344 catch (...) {
345 // lot's of reasons this could fail, including running WSL on Windows (2018-12-03)
346 }
347 return nullopt;
348 };
349 if (auto gw = getDefaultGateway (i->ifr_name)) {
350 auto gws = newInterface.fGateways.value_or (Containers::Sequence<InternetAddress>{});
351 if (not gws.Contains (*gw)) {
352 gws += *gw;
353 newInterface.fGateways = gws;
354 }
355 }
356#endif
357
358#if qStroika_Foundation_Common_Platform_Linux
359 auto getSpeed = [] (int sd, const char* name) -> optional<uint64_t> {
360 ifreq ifreq{};
361 CString::Copy (ifreq.ifr_name, std::size (ifreq.ifr_name), name);
362 ethtool_cmd edata{};
363 ifreq.ifr_data = reinterpret_cast<caddr_t> (&edata);
364 edata.cmd = ETHTOOL_GSET;
365 int r = ioctl (sd, SIOCETHTOOL, &ifreq);
366 if (r != 0) {
367#if USE_NOISY_TRACE_IN_THIS_MODULE_
368 DbgTrace ("No speed for interface {}, errno={}"_f, name, errno); // typicall errno=22{EINVAL} on linux
369#endif
370 return nullopt;
371 }
372 constexpr uint64_t kMegabit_ = 1000 * 1000;
373 switch (ethtool_cmd_speed (&edata)) {
374 case SPEED_10:
375 return 10 * kMegabit_;
376 case SPEED_100:
377 return 100 * kMegabit_;
378 case SPEED_1000:
379 return 1000 * kMegabit_;
380 case SPEED_2500:
381 return 2500 * kMegabit_;
382 case SPEED_10000:
383 return 10000 * kMegabit_;
384 default:
385 return nullopt;
386 }
387 };
388 newInterface.fTransmitSpeedBaud = getSpeed (sd, i->ifr_name);
389 newInterface.fReceiveLinkSpeedBaud = newInterface.fTransmitSpeedBaud;
390#endif
391
392 {
393 Containers::Set<Interface::Status> status = Memory::NullCoalesce (newInterface.fStatus);
394 if (flags & IFF_RUNNING) {
395 // see https://stackoverflow.com/questions/11679514/what-is-the-difference-between-iff-up-and-iff-running for difference between IFF_UP and IFF_RUNNING
398 }
399 else {
400 // see if we can check if physical cable plugged in - https://stackoverflow.com/questions/808560/how-to-detect-the-physical-connected-state-of-a-network-cable-connector
401#if qStroika_Foundation_Common_Platform_Linux
402 auto checkCarrierKnownSet = [] (const char* id) -> bool {
403#if !USE_NOISY_TRACE_IN_THIS_MODULE_
404 Debug::TraceContextSuppressor suppressTraceInThisBlock; // needlessly noisy on linux systems (due to frequent throw), and more heat than light
405#endif
406 try {
407 auto fs = FileInputStream::New (filesystem::path{"/sys/class/net"} / id, IO::FileSystem::FileInputStream::eNotSeekable);
408 Memory::BLOB b = fs.ReadAll ();
409 if (b.size () >= 1 and b[0] == static_cast<byte> ('1')) {
410 return true;
411 }
412 }
413 catch (...) {
414 }
415 return false; // unknown if this fails
416 };
417 if (checkCarrierKnownSet (i->ifr_name)) {
419 }
420#endif
421 }
422 newInterface.fStatus = status;
423 }
424 {
425 SocketAddress sa{i->ifr_addr};
426 if (sa.IsInternetAddress ()) {
427#if qStroika_Foundation_Common_Platform_Linux || qStroika_Foundation_Common_Platform_MacOS
428 DISABLE_COMPILER_GCC_WARNING_START ("GCC diagnostic ignored \"-Wfree-nonheap-object\"");
429 newInterface.fBindings.fAddressRanges.Add (CIDR{sa.GetInternetAddress (), getNetMaskAsPrefix (sd, i->ifr_name)});
430 DISABLE_COMPILER_GCC_WARNING_END ("GCC diagnostic ignored \"-Wfree-nonheap-object\"");
431#else
432 newInterface.fBindings.fAddressRanges.Add (sa.GetInternetAddress ());
433#endif
434 newInterface.fBindings.fAddresses.Add (sa.GetInternetAddress ());
435 }
436 }
437#if USE_NOISY_TRACE_IN_THIS_MODULE_
438 DbgTrace ("GetInterfaces_POSIX_mkInterface_ returns {}"_f, newInterface);
439#endif
440 return newInterface;
441 }
442#if qMacUBSanitizerifreqAlignmentIssue_Buggy
443 Stroika_Foundation_Debug_ATTRIBUTE_NO_SANITIZE_UNDEFINED
444#endif
445 Traversal::Iterable<Interface> GetInterfaces_POSIX_ ()
446 {
447 KeyedCollection<Interface, String> results{[] (const Interface& i) { return i.fInternalInterfaceID; }};
448
449 ifreq ifreqs[128]{};
450 ifconf ifconf{sizeof (ifreqs), {reinterpret_cast<char*> (ifreqs)}};
451
452 int sd = ::socket (PF_INET, SOCK_STREAM, 0);
453 Assert (sd >= 0);
454 [[maybe_unused]] auto&& cleanup = Execution::Finally ([sd] () noexcept { ::close (sd); });
455
456 [[maybe_unused]] int r = ::ioctl (sd, SIOCGIFCONF, (char*)&ifconf);
457 Assert (r == 0);
458
459 for (const ifreq* i = std::begin (ifreqs);
460 reinterpret_cast<const char*> (i) - reinterpret_cast<const char*> (std::begin (ifreqs)) < ifconf.ifc_len;) {
461#if USE_NOISY_TRACE_IN_THIS_MODULE_
462 DbgTrace ("interface: ifr_name={}; ifr_addr.sa_family = {}"_f, i->ifr_name, i->ifr_addr.sa_family);
463#endif
464 String interfaceName{String::FromSDKString (i->ifr_name)};
465
466 // @todo - On MacOS - we get multiple copies of the same interface (one for each address family on that interface). Redo this code
467 // to be smarter about merging these
468 Interface newInterface = GetInterfaces_POSIX_mkInterface_ (sd, i, results.Lookup (interfaceName));
469
470 results.Add (newInterface);
471
472 // On MacOS (at least) I needed to use IFNAMESIZ + addr.size - as suggested
473 // in https://gist.githubusercontent.com/OrangeTide/909204/raw/ed097cf0fc73eb0c44de1b26118f041a36424e3f/showif.c
474 //
475 // https://linux.die.net/man/7/netdevice strongly suggests ("array of structures" to treat as array - fixed offset per element
476 //
477 // We'll have to see what other OSes require... --LGP 2018-09-27
478#if qStroika_Foundation_Common_Platform_Linux
479 size_t len = sizeof (*i);
480#else
481 size_t len = IFNAMSIZ + i->ifr_addr.sa_len;
482#endif
483 i = reinterpret_cast<const ifreq*> (reinterpret_cast<const byte*> (i) + len);
484 }
485 return move (results);
486 }
487}
488#endif
489
490#if qStroika_Foundation_Common_Platform_Windows
491namespace {
492 /*
493 * This DLL is apparently only available if the Wireless Lan Service feature is installed
494 * (see https://forum.kodi.tv/showthread.php?tid=333721&pid=2751888#pid2751888)
495 * It is also missing from the docker image mcr.microsoft.com/windows/servercore:ltsc2019.
496 * So don't statically link since that caused the whole app to not load. Instead, load the DLL conditionally.
497 *
498 * \note switched to this helper class from #pragma comment(lib, "wlanapi.lib") in Stroika v2.1a6, to avoid dependency on
499 * that DLL (not met by simple docker containers).
500 */
501 struct WLANAPI_ {
502 WLANAPI_ ()
503 : fDLL{::LoadLibrary (_T ("wlanapi.dll"))}
504 {
505 Execution::ThrowIfNull (fDLL);
506 fWlanOpenHandle = (DWORD (WINAPI*) (_In_ DWORD dwClientVersion, _Reserved_ PVOID pReserved, _Out_ PDWORD pdwNegotiatedVersion,
507 _Out_ PHANDLE phClientHandle)) (::GetProcAddress (fDLL, "WlanOpenHandle"));
508 fWlanCloseHandle =
509 (DWORD (WINAPI*) (_In_ HANDLE hClientHandle, _Reserved_ PVOID pReserved)) (::GetProcAddress (fDLL, "WlanCloseHandle"));
510 fWlanFreeMemory = (DWORD (WINAPI*) (_In_ PVOID pMemory)) (::GetProcAddress (fDLL, "WlanFreeMemory"));
511 fWlanEnumInterfaces =
512 (DWORD (WINAPI*) (_In_ HANDLE hClientHandle, _Reserved_ PVOID pReserved,
513 _Outptr_ PWLAN_INTERFACE_INFO_LIST * ppInterfaceList)) (::GetProcAddress (fDLL, "WlanEnumInterfaces"));
514 fWlanQueryInterface =
515 (DWORD (WINAPI*) (_In_ HANDLE hClientHandle, _In_ CONST ::GUID * pInterfaceGuid, _In_ WLAN_INTF_OPCODE OpCode,
516 _Reserved_ PVOID pReserved, _Out_ PDWORD pdwDataSize, _Outptr_result_bytebuffer_ (*pdwDataSize) PVOID * ppData,
517 _Out_opt_ PWLAN_OPCODE_VALUE_TYPE pWlanOpcodeValueType)) (::GetProcAddress (fDLL, "WlanQueryInterface"));
518 }
519 WLANAPI_ (const WLANAPI_&) = delete;
520 ~WLANAPI_ ()
521 {
522 if (fDLL != nullptr) {
523 Verify (::FreeLibrary (fDLL));
524 }
525 }
526 HINSTANCE fDLL;
527 DWORD (WINAPI* fWlanOpenHandle)
528 (_In_ DWORD dwClientVersion, _Reserved_ PVOID pReserved, _Out_ PDWORD pdwNegotiatedVersion, _Out_ PHANDLE phClientHandle) = nullptr;
529 DWORD (WINAPI* fWlanCloseHandle)
530 (_In_ HANDLE hClientHandle, _Reserved_ PVOID pReserved) = nullptr;
531 DWORD (WINAPI* fWlanFreeMemory)
532 (_In_ PVOID pMemory) = nullptr;
533 DWORD (WINAPI* fWlanEnumInterfaces)
534 (_In_ HANDLE hClientHandle, _Reserved_ PVOID pReserved, _Outptr_ PWLAN_INTERFACE_INFO_LIST* ppInterfaceList) = nullptr;
535 DWORD (WINAPI* fWlanQueryInterface)
536 (_In_ HANDLE hClientHandle, _In_ CONST ::GUID* pInterfaceGuid, _In_ WLAN_INTF_OPCODE OpCode, _Reserved_ PVOID pReserved, _Out_ PDWORD pdwDataSize,
537 _Outptr_result_bytebuffer_ (*pdwDataSize) PVOID* ppData, _Out_opt_ PWLAN_OPCODE_VALUE_TYPE pWlanOpcodeValueType) = nullptr;
538 };
539 struct WirelessInfoPlus_ : Interface::WirelessInfo {
540 // extra info so can be patched into Interface
541 optional<uint64_t> fTransmitSpeedBaud;
542 optional<uint64_t> fReceiveLinkSpeedBaud;
543 };
544 Mapping<Common::GUID, WirelessInfoPlus_> GetInterfaces_Windows_WirelessInfo_ ()
545 {
546 using WirelessInfo = Interface::WirelessInfo;
548
549 static unique_ptr<WLANAPI_> sWlanAPI_;
550 static once_flag sOnceFlag_;
551 call_once (sOnceFlag_, [&] () { IgnoreExceptionsForCall (sWlanAPI_ = make_unique<WLANAPI_> ()) });
552
553 if (sWlanAPI_) {
554 HANDLE hClient = nullptr;
555 {
556 DWORD dwCurVersion = 0;
557 Execution::Platform::Windows::ThrowIfNotERROR_SUCCESS (sWlanAPI_->fWlanOpenHandle (2, NULL, &dwCurVersion, &hClient));
558 }
559 [[maybe_unused]] auto&& cleanup1 = Execution::Finally ([&] () noexcept {
560 if (hClient != nullptr) {
561 sWlanAPI_->fWlanCloseHandle (hClient, nullptr);
562 }
563 });
564
565 PWLAN_INTERFACE_INFO_LIST pIfList = nullptr;
566 Execution::Platform::Windows::ThrowIfNotERROR_SUCCESS (sWlanAPI_->fWlanEnumInterfaces (hClient, nullptr, &pIfList));
567 [[maybe_unused]] auto&& cleanup2 = Execution::Finally ([&] () noexcept {
568 if (pIfList != nullptr) {
569 sWlanAPI_->fWlanFreeMemory (pIfList);
570 }
571 });
572
573 //
574 // makes more sense for pConnectionInfo to be scoped inside loop, but the example docs in:
575 // https://docs.microsoft.com/en-us/windows/win32/api/wlanapi/nf-wlanapi-wlanqueryinterface
576 // keep it out here (so gets re-used in each QueryInterface call) and just deleted once
577 // at the end.
578 //
579 // Scoped inside the loop, we get ASAN errors under windows, so presume the docs / example was right
580 // any my intuitions wrong.
581 //
582 PWLAN_CONNECTION_ATTRIBUTES pConnectInfo{};
583 [[maybe_unused]] auto&& cleanup3 = Execution::Finally ([&] () noexcept {
584 if (pConnectInfo != nullptr) {
585 sWlanAPI_->fWlanFreeMemory (pConnectInfo);
586 }
587 });
588
589 for (DWORD i = 0; i < pIfList->dwNumberOfItems; ++i) {
590 PWLAN_INTERFACE_INFO pIfInfo = (WLAN_INTERFACE_INFO*)&pIfList->InterfaceInfo[i];
591 WirelessInfoPlus_ wInfo;
592 Common::GUID interfaceGUID{pIfInfo->InterfaceGuid};
593
594 auto mapState = [] (WLAN_INTERFACE_STATE s) -> WirelessInfo::State {
595 switch (s) {
596 case wlan_interface_state_not_ready:
597 return WirelessInfo::State::eNotReady;
598 case wlan_interface_state_connected:
599 return WirelessInfo::State::eConnected;
600 case wlan_interface_state_ad_hoc_network_formed:
601 return WirelessInfo::State::eAdHocNetworkFormed;
602 case wlan_interface_state_disconnecting:
603 return WirelessInfo::State::eDisconnecting;
604 case wlan_interface_state_disconnected:
605 return WirelessInfo::State::eDisconnected;
606 case wlan_interface_state_associating:
607 return WirelessInfo::State::eAssociating;
608 case wlan_interface_state_discovering:
609 return WirelessInfo::State::eDiscovering;
610 case wlan_interface_state_authenticating:
611 return WirelessInfo::State::eAuthenticating;
612 default:
613 DbgTrace ("Unknown state {}"_f, static_cast<int> (s));
614 return WirelessInfo::State::eUnknown;
615 }
616 };
617
618 wInfo.fState = mapState (pIfInfo->isState);
619
620 // If interface state is connected, call WlanQueryInterface
621 // to get current connection attributes
622 if (pIfInfo->isState == wlan_interface_state_connected) {
623 {
624 DWORD connectInfoSize = sizeof (WLAN_CONNECTION_ATTRIBUTES);
625 WLAN_OPCODE_VALUE_TYPE opCode = wlan_opcode_value_type_invalid;
627 sWlanAPI_->fWlanQueryInterface (hClient, &pIfInfo->InterfaceGuid, wlan_intf_opcode_current_connection, nullptr,
628 &connectInfoSize, (PVOID*)&pConnectInfo, &opCode));
629 }
630
631 if (pConnectInfo->isState != pIfInfo->isState) {
632 DbgTrace ("Not sure how these can differ (except for race condition) - but if they do, maybe worth looking into"_f);
633 }
634
635 auto mapConnectionMode = [] (WLAN_CONNECTION_MODE s) -> WirelessInfo::ConnectionMode {
636 switch (s) {
637 case wlan_connection_mode_profile:
638 return WirelessInfo::ConnectionMode::eProfile;
639 case wlan_connection_mode_temporary_profile:
640 return WirelessInfo::ConnectionMode::eTemporaryProfile;
641 case wlan_connection_mode_discovery_secure:
642 return WirelessInfo::ConnectionMode::eDiscoverSecrure;
643 case wlan_connection_mode_discovery_unsecure:
644 return WirelessInfo::ConnectionMode::eDiscoverInsecure;
645 case wlan_connection_mode_auto:
646 return WirelessInfo::ConnectionMode::eAuto;
647 case wlan_connection_mode_invalid:
648 return WirelessInfo::ConnectionMode::eInvalid;
649 default:
650 DbgTrace ("Unknown connection mode {}"_f, static_cast<int> (s));
651 return WirelessInfo::ConnectionMode::eUnknown;
652 }
653 };
654 wInfo.fConnectionMode = mapConnectionMode (pConnectInfo->wlanConnectionMode);
655 wInfo.fProfileName = pConnectInfo->strProfileName;
656
657 //Association Attributes for this connection
658 if (pConnectInfo->wlanAssociationAttributes.dot11Ssid.uSSIDLength != 0) {
659 wInfo.fSSID = String::FromNarrowSDKString (
660 span{reinterpret_cast<const char*> (pConnectInfo->wlanAssociationAttributes.dot11Ssid.ucSSID),
661 pConnectInfo->wlanAssociationAttributes.dot11Ssid.uSSIDLength});
662 }
663
664 auto mapBSSType = [] (DOT11_BSS_TYPE s) -> WirelessInfo::BSSType {
665 switch (s) {
666 case dot11_BSS_type_infrastructure:
667 return WirelessInfo::BSSType::eInfrastructure;
668 case dot11_BSS_type_independent:
669 return WirelessInfo::BSSType::eIndependent;
670 case dot11_BSS_type_any:
671 return WirelessInfo::BSSType::eAny;
672 default:
673 DbgTrace ("Unknown BSS mode {}"_f, static_cast<int> (s));
674 return WirelessInfo::BSSType::eUnknown;
675 }
676 };
677 wInfo.fBSSType = mapBSSType (pConnectInfo->wlanAssociationAttributes.dot11BssType);
678
679 wInfo.fMACAddress = PrintMacAddr_ (std::begin (pConnectInfo->wlanAssociationAttributes.dot11Bssid),
680 std::end (pConnectInfo->wlanAssociationAttributes.dot11Bssid));
681
682 auto mapPhysicalConnectionType = [] (DOT11_PHY_TYPE s) -> WirelessInfo::PhysicalConnectionType {
683 switch (s) {
684 case dot11_phy_type_unknown:
685 return WirelessInfo::PhysicalConnectionType::eUnknown;
686 case dot11_phy_type_fhss:
687 return WirelessInfo::PhysicalConnectionType::eFHSS;
688 case dot11_phy_type_dsss:
689 return WirelessInfo::PhysicalConnectionType::eDSSS;
690 case dot11_phy_type_irbaseband:
691 return WirelessInfo::PhysicalConnectionType::eIRBaseBand;
692 case dot11_phy_type_ofdm:
693 return WirelessInfo::PhysicalConnectionType::e80211a;
694 case dot11_phy_type_hrdsss:
695 return WirelessInfo::PhysicalConnectionType::e80211b;
696 case dot11_phy_type_erp:
697 return WirelessInfo::PhysicalConnectionType::e80211g;
698 case dot11_phy_type_ht:
699 return WirelessInfo::PhysicalConnectionType::e80211n;
700 case dot11_phy_type_vht:
701 return WirelessInfo::PhysicalConnectionType::e80211ac;
702 case dot11_phy_type_dmg:
703 return WirelessInfo::PhysicalConnectionType::e80211ad;
704 case dot11_phy_type_he:
705 return WirelessInfo::PhysicalConnectionType::e80211ax;
706 default:
707 DbgTrace (L"Unknown DOT11_PHY_TYPE {}"_f, static_cast<int> (s));
708 return WirelessInfo::PhysicalConnectionType::eUnknown;
709 }
710 };
711 wInfo.fPhysicalConnectionType = mapPhysicalConnectionType (pConnectInfo->wlanAssociationAttributes.dot11PhyType);
712
713 wInfo.fSignalQuality = pConnectInfo->wlanAssociationAttributes.wlanSignalQuality;
714
715 {
716 const ULONG kMagicFactor_{1000}; // empirical, so we get same answers from IP_ADAPTER_ADDRESSES_LH::TransmitLinkSpeed
717 wInfo.fTransmitSpeedBaud = pConnectInfo->wlanAssociationAttributes.ulTxRate * kMagicFactor_;
718 wInfo.fReceiveLinkSpeedBaud = pConnectInfo->wlanAssociationAttributes.ulRxRate * kMagicFactor_;
719 }
720
721 wInfo.fSecurityEnabled = pConnectInfo->wlanSecurityAttributes.bSecurityEnabled;
722 wInfo.f8021XEnabled = pConnectInfo->wlanSecurityAttributes.bOneXEnabled;
723 auto mapAuthAlgorithm = [] (DOT11_AUTH_ALGORITHM s) -> WirelessInfo::AuthAlgorithm {
724 switch (s) {
725 case DOT11_AUTH_ALGO_80211_OPEN:
726 return WirelessInfo::AuthAlgorithm::eOpen;
727 case DOT11_AUTH_ALGO_80211_SHARED_KEY:
728 return WirelessInfo::AuthAlgorithm::ePresharedKey;
729 case DOT11_AUTH_ALGO_WPA:
730 return WirelessInfo::AuthAlgorithm::eWPA;
731 case DOT11_AUTH_ALGO_WPA_PSK:
732 return WirelessInfo::AuthAlgorithm::eWPA_PSK;
733 case DOT11_AUTH_ALGO_WPA_NONE:
734 return WirelessInfo::AuthAlgorithm::eWPA_NONE;
735 case DOT11_AUTH_ALGO_RSNA:
736 return WirelessInfo::AuthAlgorithm::eRSNA;
737 case DOT11_AUTH_ALGO_RSNA_PSK:
738 return WirelessInfo::AuthAlgorithm::eRSNA_PSK;
739 default:
740 DbgTrace (L"Unknown AuthAlgorithm {}"_f, static_cast<int> (s));
741 return WirelessInfo::AuthAlgorithm::eUnknown;
742 }
743 };
744 wInfo.fAuthAlgorithm = mapAuthAlgorithm (pConnectInfo->wlanSecurityAttributes.dot11AuthAlgorithm);
745
746 auto mapCipher = [] (DOT11_CIPHER_ALGORITHM s) -> String {
747 switch (s) {
748 case DOT11_CIPHER_ALGO_NONE:
749 return "None"sv;
750 case DOT11_CIPHER_ALGO_WEP40:
751 return "WEP-40"sv;
752 case DOT11_CIPHER_ALGO_TKIP:
753 return "TKIP"sv;
754 case DOT11_CIPHER_ALGO_CCMP:
755 return "CCMP"sv;
756 case DOT11_CIPHER_ALGO_WEP104:
757 return "WEP-104"sv;
758 case DOT11_CIPHER_ALGO_WEP:
759 return "WEP"sv;
760 default:
761 return "{}"_f(static_cast<int> (s));
762 }
763 };
764 wInfo.fCipher = mapCipher (pConnectInfo->wlanSecurityAttributes.dot11CipherAlgorithm);
765 results.Add (interfaceGUID, wInfo);
766 }
767 }
768 }
769
770 return results;
771 }
772
773 Traversal::Iterable<Interface> GetInterfaces_Windows_ ()
774 {
776 try {
777 wirelessInfo2Merge = GetInterfaces_Windows_WirelessInfo_ ();
778 }
779 catch (const std::system_error& e) {
780 if (e.code () == error_code{ERROR_SERVICE_NOT_ACTIVE, system_category ()}) {
781 // this just means no wireless services active, so leave wirelessInfo2Merge empty
782 }
783 else {
784 // but other errors it makes sense to propagate
786 }
787 }
788 KeyedCollection<Interface, String> results{[] (const Interface& i) { return i.fInternalInterfaceID; }};
789 ULONG flags = GAA_FLAG_INCLUDE_PREFIX | GAA_FLAG_INCLUDE_GATEWAYS;
790 ULONG family = AF_UNSPEC; // Both IPv4 and IPv6 addresses
792 Again:
793 ULONG ulOutBufLen = static_cast<ULONG> (buf.GetSize ());
794 PIP_ADAPTER_ADDRESSES pAddresses = reinterpret_cast<PIP_ADAPTER_ADDRESSES> (buf.begin ());
795 // NB: we use GetAdapaterAddresses () instead of GetInterfaceInfo () so we get non-ipv4 addresses
796 //
797 // Note also: calling GetAdaptersAddresses () produces about 10 "Invalid parameter passed to C runtime function" lines in the debugger.
798 // @see https://developercommunity.visualstudio.com/content/problem/363323/getadaptersaddresses-invalid-parameter-passed-to-c.html
799 //
800 DWORD dwRetVal = ::GetAdaptersAddresses (family, flags, nullptr, pAddresses, &ulOutBufLen);
801 if (dwRetVal == NO_ERROR) {
802 for (PIP_ADAPTER_ADDRESSES currAddresses = pAddresses; currAddresses != nullptr; currAddresses = currAddresses->Next) {
803 String adapterName{String::FromNarrowSDKString (currAddresses->AdapterName)};
804 Interface newInterface = results.LookupValue (adapterName);
805 newInterface.fInternalInterfaceID = adapterName;
806 newInterface.fFriendlyName = currAddresses->FriendlyName;
807 newInterface.fDescription = currAddresses->Description;
808
809 static constexpr Common::GUID kZeroGUID_{};
810 if (memcmp (&currAddresses->NetworkGuid, &kZeroGUID_, sizeof (kZeroGUID_)) != 0) {
811 newInterface.fNetworkGUID = currAddresses->NetworkGuid;
812 }
813 switch (currAddresses->IfType) {
814 case IF_TYPE_SOFTWARE_LOOPBACK:
815 newInterface.fType = Interface::Type::eLoopback;
816 break;
817 case IF_TYPE_PROP_VIRTUAL:
819 break;
820 case IF_TYPE_IEEE80211:
821 newInterface.fType = Interface::Type::eWIFI;
822 break;
823 case IF_TYPE_ETHERNET_CSMACD:
824 if (newInterface.fDescription->Contains ("VirtualBox Host-Only Ethernet Adapter"sv) or
825 newInterface.fDescription->Contains ("Hyper-V Virtual Ethernet Adapter"sv)) {
826 // a fairly good guess - not sure how to tell for sure
828 }
829 else {
830 newInterface.fType = Interface::Type::eWiredEthernet;
831 }
832 break;
833 default:
834 DbgTrace ("Treating unknown currAddresses->IfType = {} type as eOther"_f, currAddresses->IfType);
835 newInterface.fType = Interface::Type::eOther;
836 break;
837 }
838 if (currAddresses->TunnelType != TUNNEL_TYPE_NONE) {
839 if (newInterface.fType) {
840 DbgTrace ("overwriting type {} with tunneltype"_f, newInterface.fType);
841 }
842 newInterface.fType = Interface::Type::eTunnel;
843 }
844 switch (currAddresses->OperStatus) {
845 case IfOperStatusUp:
846 newInterface.fStatus = Memory::NullCoalesce (newInterface.fStatus) +
848 break;
849 case IfOperStatusDown:
850 newInterface.fStatus = Memory::NullCoalesce (newInterface.fStatus); // keep any existing status values, but don't leave unknown
851 break;
852 case IfOperStatusDormant:
853 case IfOperStatusLowerLayerDown:
854 // Not sure about either of these - based on docs in https://msdn.microsoft.com/en-us/library/windows/hardware/ff553790(v=vs.85).aspx - not super clear
855 newInterface.fStatus = Memory::NullCoalesce (newInterface.fStatus) + Set<Interface::Status> ({Interface::Status::eConnected});
856 break;
857 default:
858 // Don't know how to interpret the other status states
859 DbgTrace ("ignoring unrecognized status: {}"_f, static_cast<int> (currAddresses->OperStatus));
860 break;
861 }
862 for (PIP_ADAPTER_UNICAST_ADDRESS pu = currAddresses->FirstUnicastAddress; pu != nullptr; pu = pu->Next) {
863 SocketAddress sa{pu->Address};
864 if (sa.IsInternetAddress ()) {
865 newInterface.fBindings.fAddressRanges.Add (
866 pu->OnLinkPrefixLength == 255 ? sa.GetInternetAddress () : CIDR{sa.GetInternetAddress (), pu->OnLinkPrefixLength});
867 newInterface.fBindings.fAddresses.Add (sa.GetInternetAddress ());
868 }
869 }
870 for (PIP_ADAPTER_ANYCAST_ADDRESS pa = currAddresses->FirstAnycastAddress; pa != nullptr; pa = pa->Next) {
871 SocketAddress sa{pa->Address};
872 if (sa.IsInternetAddress ()) {
873 newInterface.fBindings.fAddressRanges.Add (sa.GetInternetAddress ());
874 newInterface.fBindings.fAddresses.Add (sa.GetInternetAddress ());
875 }
876 }
877 for (PIP_ADAPTER_MULTICAST_ADDRESS pm = currAddresses->FirstMulticastAddress; pm != nullptr; pm = pm->Next) {
878 SocketAddress sa{pm->Address};
879 if (sa.IsInternetAddress ()) {
880 newInterface.fBindings.fAddressRanges.Add (sa.GetInternetAddress ());
881 newInterface.fBindings.fAddresses.Add (sa.GetInternetAddress ());
882 }
883 }
884 for (PIP_ADAPTER_GATEWAY_ADDRESS_LH pa = currAddresses->FirstGatewayAddress; pa != nullptr; pa = pa->Next) {
885 SocketAddress sa{pa->Address};
886 if (sa.IsInternetAddress ()) {
887 auto gws = newInterface.fGateways.value_or (Containers::Sequence<InternetAddress>{});
888 if (not gws.Contains (sa.GetInternetAddress ())) {
889 gws += sa.GetInternetAddress ();
890 newInterface.fGateways = gws;
891 }
892 }
893 }
894 for (PIP_ADAPTER_DNS_SERVER_ADDRESS_XP pa = currAddresses->FirstDnsServerAddress; pa != nullptr; pa = pa->Next) {
895 SocketAddress sa{pa->Address};
896 if (sa.IsInternetAddress ()) {
897 auto ds = newInterface.fDNSServers.value_or (Containers::Sequence<InternetAddress>{});
898 if (not ds.Contains (sa.GetInternetAddress ())) {
899 ds += sa.GetInternetAddress ();
900 newInterface.fDNSServers = ds;
901 }
902 }
903 }
904 if (currAddresses->PhysicalAddressLength == 6) {
905 newInterface.fHardwareAddress = PrintMacAddr_ (currAddresses->PhysicalAddress, currAddresses->PhysicalAddress + 6);
906 }
907
908#if (NTDDI_VERSION >= NTDDI_WIN6)
909 newInterface.fTransmitSpeedBaud = currAddresses->TransmitLinkSpeed;
910 newInterface.fReceiveLinkSpeedBaud = currAddresses->ReceiveLinkSpeed;
911#endif
912
913 if (newInterface.fType == Interface::Type::eWIFI) {
914 if (auto owinfo = wirelessInfo2Merge.Lookup (newInterface.fInternalInterfaceID)) {
915 newInterface.fWirelessInfo = *owinfo;
916 WeakAssert (not newInterface.fTransmitSpeedBaud.has_value () or newInterface.fTransmitSpeedBaud == owinfo->fTransmitSpeedBaud);
917 WeakAssert (not newInterface.fReceiveLinkSpeedBaud.has_value () or newInterface.fReceiveLinkSpeedBaud == owinfo->fReceiveLinkSpeedBaud);
918 newInterface.fTransmitSpeedBaud = Memory::NullCoalesce (newInterface.fTransmitSpeedBaud, owinfo->fTransmitSpeedBaud);
919 newInterface.fReceiveLinkSpeedBaud = Memory::NullCoalesce (newInterface.fReceiveLinkSpeedBaud, owinfo->fReceiveLinkSpeedBaud);
920 }
921 else {
922 // This happens for down/wifi-direct interfaces
923 // no biggie.
924 // DbgTrace ("Oops - didn't find wireless interface we should have: {}, avail-keys={}"_f, newInterface.fInternalInterfaceID, wirelessInfo2Merge.Keys ());
925 }
926 }
927 else {
928 WeakAssert (not wirelessInfo2Merge.ContainsKey (newInterface.fInternalInterfaceID));
929 }
930#if USE_NOISY_TRACE_IN_THIS_MODULE_
931 DbgTrace (L"newInterface={}"_f, newInterface);
932#endif
933 results.Add (newInterface);
934 }
935 }
936 else if (dwRetVal == ERROR_BUFFER_OVERFLOW) {
937 buf.GrowToSize_uninitialized (ulOutBufLen);
938 goto Again;
939 }
940 else if (dwRetVal == ERROR_NO_DATA) {
941 DbgTrace ("There are no network adapters enabled on the local system"_f);
942 }
943 else {
944 Execution::ThrowSystemErrNo (dwRetVal);
945 }
946
947#if USE_NOISY_TRACE_IN_THIS_MODULE_
948 DbgTrace ("returning {}"_f, results.MappedValues ());
949#endif
950 return results;
951 }
952}
953#endif
954
955/*
956 ********************************************************************************
957 ************************* Network::SystemInterfacesMgr *************************
958 ********************************************************************************
959 */
961{
962#if USE_NOISY_TRACE_IN_THIS_MODULE_
963 Debug::TraceContextBumper ctx{"SystemInterfacesMgr::GetAll"};
964#endif
965#if qStroika_Foundation_Common_Platform_POSIX
966 Traversal::Iterable<Interface> results = GetInterfaces_POSIX_ ();
967#elif qStroika_Foundation_Common_Platform_Windows
968 Traversal::Iterable<Interface> results = GetInterfaces_Windows_ ();
969#else
971#endif
972#if USE_NOISY_TRACE_IN_THIS_MODULE_
973 DbgTrace ("returning {}"_f, results);
974#endif
975 return results;
976}
977
978optional<Interface> SystemInterfacesMgr::GetById (const Interface::SystemIDType& internalInterfaceID)
979{
980 // Made some progress but must refactor the above a little more to be able avoid iterating and just fetch the desired interface (esp on macos).
981#if USE_NOISY_TRACE_IN_THIS_MODULE_
982 Debug::TraceContextBumper ctx{"Network::GetById"};
983#endif
984 // @todo - a much more efficent implementation - maybe good enuf to use caller staleness cache with a few seconds staleness
985 for (const Interface& i : GetAll ()) {
986 if (i.fInternalInterfaceID == internalInterfaceID) {
987#if USE_NOISY_TRACE_IN_THIS_MODULE_
988 DbgTrace (L"found interface %s", internalInterfaceID.c_str ());
989#endif
990 return i;
991 }
992 }
993#if USE_NOISY_TRACE_IN_THIS_MODULE_
994 DbgTrace (L"interface %s not found", internalInterfaceID.c_str ());
995#endif
996 return nullopt;
997}
998
1000{
1001#if USE_NOISY_TRACE_IN_THIS_MODULE_
1002 Debug::TraceContextBumper ctx{"Network::GetContainingAddress"};
1003#endif
1004 // @todo - a much more efficent implementation - maybe good enuf to use caller staleness cache with a few seconds staleness
1005 for (const Interface& i : GetAll ()) {
1006 if (i.fBindings.fAddressRanges.Any ([&ia] (CIDR c) { return c.GetRange ().Contains (ia); })) {
1007#if USE_NOISY_TRACE_IN_THIS_MODULE_
1008 DbgTrace (L"found interface %s", internalInterfaceID.c_str ());
1009#endif
1010 return i;
1011 }
1012 }
1013#if USE_NOISY_TRACE_IN_THIS_MODULE_
1014 DbgTrace (L"interface %s not found", internalInterfaceID.c_str ());
1015#endif
1016 return nullopt;
1017}
#define AssertNotImplemented()
Definition Assertions.h:402
#define WeakAssert(c)
A WeakAssert() is for things that aren't guaranteed to be true, but are overwhelmingly likely to be t...
Definition Assertions.h:439
#define Verify(c)
Definition Assertions.h:420
#define qStroika_ATTRIBUTE_INDETERMINATE
qStroika_ATTRIBUTE_INDETERMINATE is used where you would use a C++ attribute for a variable that is i...
Definition StdCompat.h:393
#define DbgTrace
Definition Trace.h:317
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 tuple< const wchar_t *, wstring_view > c_str(Memory::StackBuffer< wchar_t > *possibleBackingStore) const
Definition String.inl:1057
static String FromSDKString(const SDKChar *from)
Definition String.inl:449
static String FromNarrowSDKString(const char *from)
Definition String.inl:472
a cross between Mapping<KEY, T> and Collection<T> and Set<T>
A Mapping uniquely associates two elements: a key and a value (use Assocation to allow multiple value...
nonvirtual bool Add(ArgByValueType< key_type > key, ArgByValueType< mapped_type > newElt, AddReplaceMode addReplaceMode=AddReplaceMode::eAddReplaces)
Definition Mapping.inl:188
nonvirtual Iterable< mapped_type > MappedValues() const
Definition Mapping.inl:116
nonvirtual bool ContainsKey(ArgByValueType< key_type > key) const
Definition Mapping.inl:177
nonvirtual optional< mapped_type > Lookup(ArgByValueType< key_type > key) const
Definition Mapping.inl:142
nonvirtual mapped_type LookupValue(ArgByValueType< key_type > key, ArgByValueType< mapped_type > defaultValue=mapped_type{}) const
Definition Mapping.inl:166
A generalization of a vector: a container whose elements are keyed by the natural numbers.
Set<T> is a container of T, where once an item is added, additionally adds () do nothing.
nonvirtual void Add(ArgByValueType< value_type > item)
Definition Set.inl:138
This COULD be easily used to read CSV files, or tab-delimited files, for example.
Run an external command, with stdin/stdout/stderr as strings or as streams - like perl backticks.
nonvirtual Sequence< InternetAddress > GetHostAddresses(const String &hostNameOrAddress) const
simple wrapper on GetHostEntry - looking up the hostname/ip address and returning the list of associa...
Definition DNS.cpp:238
nonvirtual optional< Interface > GetContainingAddress(const InternetAddress &ia)
nonvirtual Traversal::Iterable< Interface > GetAll()
nonvirtual optional< Interface > GetById(const Interface::SystemIDType &internalInterfaceID)
Logically halfway between std::array and std::vector; Smart 'direct memory array' - which when needed...
nonvirtual void GrowToSize_uninitialized(size_t nElements)
same as GrowToSize (), except leaves newly created elements uninitialized (requires is_trivially_copy...
nonvirtual size_t GetSize() const noexcept
Iterable<T> is a base class for containers which easily produce an Iterator<T> to traverse them.
Definition Iterable.h:238
auto Finally(FUNCTION &&f) -> Private_::FinallySentry< FUNCTION >
Definition Finally.inl:31
optional< Containers::Set< Status > > fStatus
Definition Interface.h:333
optional< Containers::Sequence< InternetAddress > > fDNSServers
Definition Interface.h:302
optional< Containers::Sequence< InternetAddress > > fGateways
Definition Interface.h:297
optional< Common::GUID > fNetworkGUID
Definition Interface.h:98