Stroika Library 3.0d16
 
Loading...
Searching...
No Matches
Interface.cpp
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2025. 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);
87 char buf[100]; // intentionally uninitalized
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) < NEltsOf (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 << "Internal-Interface-ID: "sv << fInternalInterfaceID;
144#if qStroika_Foundation_Common_Platform_POSIX
145 sb << ", InterfaceName: "sv << GetInterfaceName ();
146#endif
147 sb << ", Friendly-Name: "sv << fFriendlyName;
148 if (fDescription) {
149 sb << ", Description: "sv << *fDescription;
150 }
151 if (fNetworkGUID) {
152 sb << ", Network-GUID: "sv << *fNetworkGUID;
153 }
154 if (fType) {
155 sb << ", Type: "sv << *fType;
156 }
157 if (fHardwareAddress) {
158 sb << ", Hardware-Address: "sv << *fHardwareAddress;
159 }
160 if (fTransmitSpeedBaud) {
161 sb << ", Transmit-Speed-Baud: "sv << *fTransmitSpeedBaud;
162 }
164 sb << ", Receive-Link-Speed-Baud: "sv << *fReceiveLinkSpeedBaud;
165 }
166 if (fWirelessInfo) {
167 sb << ", Wireless-Info: "sv << fWirelessInfo;
168 }
169 sb << ", Bindings: "sv << fBindings;
170
171 sb << ", Gateways: "sv << fGateways;
172 sb << ", DNS-Servers: "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
195 GetInterfaces_POSIX_mkInterface_ (int sd, const ifreq* i, optional<Interface> prevInterfaceObject2Update)
196 {
197 Interface newInterface = prevInterfaceObject2Update.value_or (Interface{});
198 newInterface.fInternalInterfaceID = String::FromSDKString (i->ifr_name);
199 newInterface.fFriendlyName = newInterface.fInternalInterfaceID; // not great - maybe find better name - but this will do for now...
200 auto getFlags = [] (int sd, const char* name) {
201 ifreq ifreq{};
202 CString::Copy (ifreq.ifr_name, NEltsOf (ifreq.ifr_name), name);
203 int r = ::ioctl (sd, SIOCGIFFLAGS, (char*)&ifreq);
204 Assert (r == 0 or errno == ENXIO); // ENXIO happens on MacOS sometimes, but never seen on linux
205 return r == 0 ? ifreq.ifr_flags : 0;
206 };
207 int flags = getFlags (sd, i->ifr_name);
208#if qStroika_Foundation_Common_Platform_Linux
209 auto getWirelessFlag = [] (int sd, const char* name) -> bool {
210#if defined(SIOCGIWNAME)
211 iwreq pwrq{};
212 CString::Copy (pwrq.ifr_name, NEltsOf (pwrq.ifr_name), name);
213 int r = ::ioctl (sd, SIOCGIWNAME, (char*)&pwrq);
214 return r == 0;
215#else
216 return false;
217#endif
218 };
219#endif
220 if (flags & IFF_LOOPBACK) {
221 newInterface.fType = Interface::Type::eLoopback;
222 }
223#if qStroika_Foundation_Common_Platform_Linux
224 else if (getWirelessFlag (sd, i->ifr_name)) {
225 newInterface.fType = Interface::Type::eWIFI;
226 }
227#endif
228 else {
229 // NYI
230 newInterface.fType = Interface::Type::eWiredEthernet; // WAG - not the right way to tell!
231 }
232
233#if qStroika_Foundation_Common_Platform_Linux
234 {
235 ifreq tmp = *i;
236 if (::ioctl (sd, SIOCGIFHWADDR, &tmp) == 0 and tmp.ifr_hwaddr.sa_family == ARPHRD_ETHER) {
237 newInterface.fHardwareAddress = PrintMacAddr_ (reinterpret_cast<const uint8_t*> (tmp.ifr_hwaddr.sa_data),
238 reinterpret_cast<const uint8_t*> (tmp.ifr_hwaddr.sa_data) + 6);
239 }
240 }
241#endif
242
243#if qStroika_Foundation_Common_Platform_Linux || qStroika_Foundation_Common_Platform_MacOS
244 auto getNetMaskAsPrefix = [] (int sd, const char* name) -> optional<unsigned int> {
245 ifreq ifreq{};
246 CString::Copy (ifreq.ifr_name, NEltsOf (ifreq.ifr_name), name);
247 int r = ::ioctl (sd, SIOCGIFNETMASK, (char*)&ifreq);
248 // On MacOS this often fails, but I've never seen it fail on Linux
249 if (r == 0) {
250#if qStroika_Foundation_Common_Platform_Linux
251 SocketAddress sa{ifreq.ifr_netmask};
252#elif qStroika_Foundation_Common_Platform_MacOS
253 SocketAddress sa{ifreq.ifr_addr};
254#endif
255 if (sa.IsInternetAddress ()) {
256 InternetAddress ia = sa.GetInternetAddress ();
257 size_t prefixLen{};
258 for (bool b : ia.As<vector<bool>> ()) {
259 if (b) {
260 prefixLen++;
261 }
262 else {
263 break;
264 }
265 }
266 return prefixLen;
267 }
268 }
269 return nullopt;
270 };
271#endif
272
273#if qStroika_Foundation_Common_Platform_Linux || qStroika_Foundation_Common_Platform_MacOS
274 auto getDefaultGateway = [] (const char* name) -> optional<InternetAddress> {
275 try {
276#if qStroika_Foundation_Common_Platform_Linux
278 static const filesystem::path kFileName_{"/proc/net/route"};
279 /*
280 * EXAMPLE OUTPUT:
281 * cat /proc/net/route
282 * Iface Destination Gateway Flags RefCnt Use Metric Mask MTU Window IRTT
283 * eth0 00000000 010011AC 0003 0 0 0 00000000 0 0 0
284 * eth0 000011AC 00000000 0001 0 0 0 0000FFFF 0 0 0
285 */
286 // Note - /procfs files always unseekable
287 for (const Sequence<String>& line :
288 reader.ReadMatrix (FileInputStream::New (kFileName_, IO::FileSystem::FileInputStream::eNotSeekable))) {
289 if (line.size () >= 3 and line[0] == String::FromNarrowSDKString (name) and line[1] == "00000000"sv) {
290 //
291 int tmp[4]{};
292 swscanf (line[2].As<wstring> ().c_str (), L"%02x%02x%02x%02x", &tmp[3], &tmp[2], &tmp[1], &tmp[0]);
293 return InternetAddress{static_cast<byte> (tmp[0]), static_cast<byte> (tmp[1]), static_cast<byte> (tmp[2]),
294 static_cast<byte> (tmp[3])};
295 }
296 }
297#elif qStroika_Foundation_Common_Platform_MacOS
298 /*
299 * NOTE: Could ALSO use netstat -nr - https://unh.edu/it/kb/article/how-to-route-print-mac-os-x.html
300 *
301 * EXAMPLE OUTPUT:
302 * >route get default
303 * route to: default
304 * destination: default
305 * mask: default
306 * gateway: router.asus.com
307 * interface: en0
308 * flags: <UP,GATEWAY,DONE,STATIC,PRCLONING>
309 * recvpipe sendpipe ssthresh rtt,msec rttvar hopcount mtu expire
310 * 0 0 0 0 0 0 1500 0
311 */
312 ProcessRunner pr{"route get default"sv};
313 Streams::MemoryStream::Ptr<byte> useStdOut = Streams::MemoryStream::New<byte> ();
314 pr.Run (nullptr, useStdOut);
316 optional<String> forInterface;
317 optional<String> gateway;
318 for (const Sequence<String>& line : reader.ReadMatrix (useStdOut)) {
319 if (line.size () == 2 and line[0] == "interface"sv) {
320 forInterface = line[1];
321 }
322 else if (line.size () == 2 and line[0] == "gateway"sv) {
323 gateway = line[1];
324 }
325 }
326 if (forInterface == String::FromNarrowSDKString (name) and gateway) {
327 try {
328 return InternetAddress{*gateway};
329 }
330 catch (...) {
331 // frequently fails - cuz its a dns name
332 }
333 try {
334 auto s = IO::Network::DNS::kThe.GetHostAddresses (*gateway);
335 if (not s.empty ()) {
336 return InternetAddress{s.Nth (0)};
337 }
338 }
339 catch (...) {
340 DbgTrace ("got exception converting gateway to address (dns): {}"_f, current_exception ()); // should work...
341 }
342 }
343#endif
344 }
345 catch (...) {
346 // lot's of reasons this could fail, including running WSL on Windows (2018-12-03)
347 }
348 return nullopt;
349 };
350 if (auto gw = getDefaultGateway (i->ifr_name)) {
351 auto gws = newInterface.fGateways.value_or (Containers::Sequence<InternetAddress>{});
352 if (not gws.Contains (*gw)) {
353 gws += *gw;
354 newInterface.fGateways = gws;
355 }
356 }
357#endif
358
359#if qStroika_Foundation_Common_Platform_Linux
360 auto getSpeed = [] (int sd, const char* name) -> optional<uint64_t> {
361 ifreq ifreq{};
362 CString::Copy (ifreq.ifr_name, NEltsOf (ifreq.ifr_name), name);
363 ethtool_cmd edata{};
364 ifreq.ifr_data = reinterpret_cast<caddr_t> (&edata);
365 edata.cmd = ETHTOOL_GSET;
366 int r = ioctl (sd, SIOCETHTOOL, &ifreq);
367 if (r != 0) {
368#if USE_NOISY_TRACE_IN_THIS_MODULE_
369 DbgTrace ("No speed for interface {}, errno={}"_f, name, errno); // typicall errno=22{EINVAL} on linux
370#endif
371 return nullopt;
372 }
373 constexpr uint64_t kMegabit_ = 1000 * 1000;
374 switch (ethtool_cmd_speed (&edata)) {
375 case SPEED_10:
376 return 10 * kMegabit_;
377 case SPEED_100:
378 return 100 * kMegabit_;
379 case SPEED_1000:
380 return 1000 * kMegabit_;
381 case SPEED_2500:
382 return 2500 * kMegabit_;
383 case SPEED_10000:
384 return 10000 * kMegabit_;
385 default:
386 return nullopt;
387 }
388 };
389 newInterface.fTransmitSpeedBaud = getSpeed (sd, i->ifr_name);
390 newInterface.fReceiveLinkSpeedBaud = newInterface.fTransmitSpeedBaud;
391#endif
392
393 {
394 Containers::Set<Interface::Status> status = Memory::NullCoalesce (newInterface.fStatus);
395 if (flags & IFF_RUNNING) {
396 // see https://stackoverflow.com/questions/11679514/what-is-the-difference-between-iff-up-and-iff-running for difference between IFF_UP and IFF_RUNNING
399 }
400 else {
401 // 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
402#if qStroika_Foundation_Common_Platform_Linux
403 auto checkCarrierKnownSet = [] (const char* id) -> bool {
404#if !USE_NOISY_TRACE_IN_THIS_MODULE_
405 Debug::TraceContextSuppressor suppressTraceInThisBlock; // needlessly noisy on linux systems (due to frequent throw), and more heat than light
406#endif
407 try {
408 auto fs = FileInputStream::New (filesystem::path{"/sys/class/net"} / id, IO::FileSystem::FileInputStream::eNotSeekable);
409 Memory::BLOB b = fs.ReadAll ();
410 if (b.size () >= 1 and b[0] == static_cast<byte> ('1')) {
411 return true;
412 }
413 }
414 catch (...) {
415 }
416 return false; // unknown if this fails
417 };
418 if (checkCarrierKnownSet (i->ifr_name)) {
420 }
421#endif
422 }
423 newInterface.fStatus = status;
424 }
425 {
426 SocketAddress sa{i->ifr_addr};
427 if (sa.IsInternetAddress ()) {
428#if qStroika_Foundation_Common_Platform_Linux || qStroika_Foundation_Common_Platform_MacOS
429 newInterface.fBindings.fAddressRanges.Add (CIDR{sa.GetInternetAddress (), getNetMaskAsPrefix (sd, i->ifr_name)});
430#else
431 newInterface.fBindings.fAddressRanges.Add (sa.GetInternetAddress ());
432#endif
433 newInterface.fBindings.fAddresses.Add (sa.GetInternetAddress ());
434 }
435 }
436#if USE_NOISY_TRACE_IN_THIS_MODULE_
437 DbgTrace ("GetInterfaces_POSIX_mkInterface_ returns {}"_f, newInterface);
438#endif
439 return newInterface;
440 }
441#if qMacUBSanitizerifreqAlignmentIssue_Buggy
442 Stroika_Foundation_Debug_ATTRIBUTE_NO_SANITIZE_UNDEFINED
443#endif
445 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 {
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_IEEE80211:
818 newInterface.fType = Interface::Type::eWIFI;
819 break;
820 case IF_TYPE_ETHERNET_CSMACD:
821 if (newInterface.fDescription->Contains ("VirtualBox Host-Only Ethernet Adapter"sv) or
822 newInterface.fDescription->Contains ("Hyper-V Virtual Ethernet Adapter"sv)) {
823 // a fairly good guess - not sure how to tell for sure
824 newInterface.fType = Interface::Type::eDeviceVirtualInternalNetwork;
825 }
826 else {
827 newInterface.fType = Interface::Type::eWiredEthernet;
828 }
829 break;
830 default:
831 DbgTrace ("Treating unknown currAddresses->IfType = {} type as eOther"_f, currAddresses->IfType);
832 newInterface.fType = Interface::Type::eOther;
833 break;
834 }
835 if (currAddresses->TunnelType != TUNNEL_TYPE_NONE) {
836 if (newInterface.fType) {
837 DbgTrace ("overwriting type {} with tunneltype"_f, newInterface.fType);
838 }
839 newInterface.fType = Interface::Type::eTunnel;
840 }
841 switch (currAddresses->OperStatus) {
842 case IfOperStatusUp:
843 newInterface.fStatus = Memory::NullCoalesce (newInterface.fStatus) +
845 break;
846 case IfOperStatusDown:
847 newInterface.fStatus = Memory::NullCoalesce (newInterface.fStatus); // keep any existing status values, but don't leave unknown
848 break;
849 case IfOperStatusDormant:
850 case IfOperStatusLowerLayerDown:
851 // 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
852 newInterface.fStatus = Memory::NullCoalesce (newInterface.fStatus) + Set<Interface::Status> ({Interface::Status::eConnected});
853 break;
854 default:
855 // Don't know how to interpret the other status states
856 DbgTrace ("ignoring unrecognized status: {}"_f, static_cast<int> (currAddresses->OperStatus));
857 break;
858 }
859 for (PIP_ADAPTER_UNICAST_ADDRESS pu = currAddresses->FirstUnicastAddress; pu != nullptr; pu = pu->Next) {
860 SocketAddress sa{pu->Address};
861 if (sa.IsInternetAddress ()) {
862 newInterface.fBindings.fAddressRanges.Add (
863 pu->OnLinkPrefixLength == 255 ? sa.GetInternetAddress () : CIDR{sa.GetInternetAddress (), pu->OnLinkPrefixLength});
864 newInterface.fBindings.fAddresses.Add (sa.GetInternetAddress ());
865 }
866 }
867 for (PIP_ADAPTER_ANYCAST_ADDRESS pa = currAddresses->FirstAnycastAddress; pa != nullptr; pa = pa->Next) {
868 SocketAddress sa{pa->Address};
869 if (sa.IsInternetAddress ()) {
870 newInterface.fBindings.fAddressRanges.Add (sa.GetInternetAddress ());
871 newInterface.fBindings.fAddresses.Add (sa.GetInternetAddress ());
872 }
873 }
874 for (PIP_ADAPTER_MULTICAST_ADDRESS pm = currAddresses->FirstMulticastAddress; pm != nullptr; pm = pm->Next) {
875 SocketAddress sa{pm->Address};
876 if (sa.IsInternetAddress ()) {
877 newInterface.fBindings.fAddressRanges.Add (sa.GetInternetAddress ());
878 newInterface.fBindings.fAddresses.Add (sa.GetInternetAddress ());
879 }
880 }
881 for (PIP_ADAPTER_GATEWAY_ADDRESS_LH pa = currAddresses->FirstGatewayAddress; pa != nullptr; pa = pa->Next) {
882 SocketAddress sa{pa->Address};
883 if (sa.IsInternetAddress ()) {
884 auto gws = newInterface.fGateways.value_or (Containers::Sequence<InternetAddress>{});
885 if (not gws.Contains (sa.GetInternetAddress ())) {
886 gws += sa.GetInternetAddress ();
887 newInterface.fGateways = gws;
888 }
889 }
890 }
891 for (PIP_ADAPTER_DNS_SERVER_ADDRESS_XP pa = currAddresses->FirstDnsServerAddress; pa != nullptr; pa = pa->Next) {
892 SocketAddress sa{pa->Address};
893 if (sa.IsInternetAddress ()) {
894 auto ds = newInterface.fDNSServers.value_or (Containers::Sequence<InternetAddress>{});
895 if (not ds.Contains (sa.GetInternetAddress ())) {
896 ds += sa.GetInternetAddress ();
897 newInterface.fDNSServers = ds;
898 }
899 }
900 }
901 if (currAddresses->PhysicalAddressLength == 6) {
902 newInterface.fHardwareAddress = PrintMacAddr_ (currAddresses->PhysicalAddress, currAddresses->PhysicalAddress + 6);
903 }
904
905#if (NTDDI_VERSION >= NTDDI_WIN6)
906 newInterface.fTransmitSpeedBaud = currAddresses->TransmitLinkSpeed;
907 newInterface.fReceiveLinkSpeedBaud = currAddresses->ReceiveLinkSpeed;
908#endif
909
910 if (newInterface.fType == Interface::Type::eWIFI) {
911 if (auto owinfo = wirelessInfo2Merge.Lookup (newInterface.fInternalInterfaceID)) {
912 newInterface.fWirelessInfo = *owinfo;
913 WeakAssert (not newInterface.fTransmitSpeedBaud.has_value () or newInterface.fTransmitSpeedBaud == owinfo->fTransmitSpeedBaud);
914 WeakAssert (not newInterface.fReceiveLinkSpeedBaud.has_value () or newInterface.fReceiveLinkSpeedBaud == owinfo->fReceiveLinkSpeedBaud);
915 newInterface.fTransmitSpeedBaud = Memory::NullCoalesce (newInterface.fTransmitSpeedBaud, owinfo->fTransmitSpeedBaud);
916 newInterface.fReceiveLinkSpeedBaud = Memory::NullCoalesce (newInterface.fReceiveLinkSpeedBaud, owinfo->fReceiveLinkSpeedBaud);
917 }
918 else {
919 // This happens for down/wifi-direct interfaces
920 // no biggie.
921 // DbgTrace ("Oops - didn't find wireless interface we should have: {}, avail-keys={}"_f, newInterface.fInternalInterfaceID, wirelessInfo2Merge.Keys ());
922 }
923 }
924 else {
925 WeakAssert (not wirelessInfo2Merge.ContainsKey (newInterface.fInternalInterfaceID));
926 }
927#if USE_NOISY_TRACE_IN_THIS_MODULE_
928 DbgTrace (L"newInterface={}"_f, newInterface);
929#endif
930 results.Add (newInterface);
931 }
932 }
933 else if (dwRetVal == ERROR_BUFFER_OVERFLOW) {
934 buf.GrowToSize_uninitialized (ulOutBufLen);
935 goto Again;
936 }
937 else if (dwRetVal == ERROR_NO_DATA) {
938 DbgTrace ("There are no network adapters enabled on the local system"_f);
939 }
940 else {
941 Execution::ThrowSystemErrNo (dwRetVal);
942 }
943
944#if USE_NOISY_TRACE_IN_THIS_MODULE_
945 DbgTrace ("returning {}"_f, results.MappedValues ());
946#endif
947 return results;
948 }
949}
950#endif
951
952/*
953 ********************************************************************************
954 ************************* Network::SystemInterfacesMgr *************************
955 ********************************************************************************
956 */
958{
959#if USE_NOISY_TRACE_IN_THIS_MODULE_
960 Debug::TraceContextBumper ctx{"SystemInterfacesMgr::GetAll"};
961#endif
962#if qStroika_Foundation_Common_Platform_POSIX
963 Traversal::Iterable<Interface> results = GetInterfaces_POSIX_ ();
964#elif qStroika_Foundation_Common_Platform_Windows
965 Traversal::Iterable<Interface> results = GetInterfaces_Windows_ ();
966#else
968#endif
969#if USE_NOISY_TRACE_IN_THIS_MODULE_
970 DbgTrace ("returning {}"_f, results);
971#endif
972 return results;
973}
974
975optional<Interface> SystemInterfacesMgr::GetById (const Interface::SystemIDType& internalInterfaceID)
976{
977 // 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).
978#if USE_NOISY_TRACE_IN_THIS_MODULE_
979 Debug::TraceContextBumper ctx{"Network::GetById"};
980#endif
981 // @todo - a much more efficent implementation - maybe good enuf to use caller staleness cache with a few seconds staleness
982 for (const Interface& i : GetAll ()) {
983 if (i.fInternalInterfaceID == internalInterfaceID) {
984#if USE_NOISY_TRACE_IN_THIS_MODULE_
985 DbgTrace (L"found interface %s", internalInterfaceID.c_str ());
986#endif
987 return i;
988 }
989 }
990#if USE_NOISY_TRACE_IN_THIS_MODULE_
991 DbgTrace (L"interface %s not found", internalInterfaceID.c_str ());
992#endif
993 return nullopt;
994}
995
997{
998#if USE_NOISY_TRACE_IN_THIS_MODULE_
999 Debug::TraceContextBumper ctx{"Network::GetContainingAddress"};
1000#endif
1001 // @todo - a much more efficent implementation - maybe good enuf to use caller staleness cache with a few seconds staleness
1002 for (const Interface& i : GetAll ()) {
1003 if (i.fBindings.fAddressRanges.Any ([&ia] (CIDR c) { return c.GetRange ().Contains (ia); })) {
1004#if USE_NOISY_TRACE_IN_THIS_MODULE_
1005 DbgTrace (L"found interface %s", internalInterfaceID.c_str ());
1006#endif
1007 return i;
1008 }
1009 }
1010#if USE_NOISY_TRACE_IN_THIS_MODULE_
1011 DbgTrace (L"interface %s not found", internalInterfaceID.c_str ());
1012#endif
1013 return nullopt;
1014}
#define AssertNotImplemented()
Definition Assertions.h:401
#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:438
#define Verify(c)
Definition Assertions.h:419
#define DbgTrace
Definition Trace.h:309
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:1049
static String FromSDKString(const SDKChar *from)
Definition String.inl:447
static String FromNarrowSDKString(const char *from)
Definition String.inl:470
a cross between Mapping<KEY, T> and Collection<T> and Set<T>
nonvirtual bool Add(ArgByValueType< key_type > key, ArgByValueType< mapped_type > newElt, AddReplaceMode addReplaceMode=AddReplaceMode::eAddReplaces)
Definition Mapping.inl:190
nonvirtual Iterable< mapped_type > MappedValues() const
Definition Mapping.inl:118
nonvirtual bool ContainsKey(ArgByValueType< key_type > key) const
Definition Mapping.inl:179
nonvirtual optional< mapped_type > Lookup(ArgByValueType< key_type > key) const
Definition Mapping.inl:144
nonvirtual mapped_type LookupValue(ArgByValueType< key_type > key, ArgByValueType< mapped_type > defaultValue=mapped_type{}) const
Definition Mapping.inl:168
A generalization of a vector: a container whose elements are keyed by the natural numbers.
Definition Sequence.h:187
Set<T> is a container of T, where once an item is added, additionally adds () do nothing.
Definition Set.h:105
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 the given command, and optionally support stdin/stdout/stderr as streams (either sync with Run,...
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:237
void ThrowIfNull(const Private_::ConstVoidStar &p, const HRESULT &hr)
Template specialization for ThrowIfNull (), for thing being thrown HRESULT - really throw HRESULTErro...
auto Finally(FUNCTION &&f) -> Private_::FinallySentry< FUNCTION >
Definition Finally.inl:31
optional< Containers::Set< Status > > fStatus
Definition Interface.h:328
optional< Containers::Sequence< InternetAddress > > fDNSServers
Definition Interface.h:297
optional< Containers::Sequence< InternetAddress > > fGateways
Definition Interface.h:292
optional< Common::GUID > fNetworkGUID
Definition Interface.h:98