Stroika Library 3.0d20
 
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 DISABLE_COMPILER_GCC_WARNING_START ("GCC diagnostic ignored \"-Wfree-nonheap-object\"");
430 newInterface.fBindings.fAddressRanges.Add (CIDR{sa.GetInternetAddress (), getNetMaskAsPrefix (sd, i->ifr_name)});
431 DISABLE_COMPILER_GCC_WARNING_END ("GCC diagnostic ignored \"-Wfree-nonheap-object\"");
432#else
433 newInterface.fBindings.fAddressRanges.Add (sa.GetInternetAddress ());
434#endif
435 newInterface.fBindings.fAddresses.Add (sa.GetInternetAddress ());
436 }
437 }
438#if USE_NOISY_TRACE_IN_THIS_MODULE_
439 DbgTrace ("GetInterfaces_POSIX_mkInterface_ returns {}"_f, newInterface);
440#endif
441 return newInterface;
442 }
443#if qMacUBSanitizerifreqAlignmentIssue_Buggy
444 Stroika_Foundation_Debug_ATTRIBUTE_NO_SANITIZE_UNDEFINED
445#endif
447 GetInterfaces_POSIX_ ()
448 {
449 KeyedCollection<Interface, String> results{[] (const Interface& i) { return i.fInternalInterfaceID; }};
450
451 ifreq ifreqs[128]{};
452 ifconf ifconf{sizeof (ifreqs), {reinterpret_cast<char*> (ifreqs)}};
453
454 int sd = ::socket (PF_INET, SOCK_STREAM, 0);
455 Assert (sd >= 0);
456 [[maybe_unused]] auto&& cleanup = Execution::Finally ([sd] () noexcept { ::close (sd); });
457
458 [[maybe_unused]] int r = ::ioctl (sd, SIOCGIFCONF, (char*)&ifconf);
459 Assert (r == 0);
460
461 for (const ifreq* i = std::begin (ifreqs);
462 reinterpret_cast<const char*> (i) - reinterpret_cast<const char*> (std::begin (ifreqs)) < ifconf.ifc_len;) {
463#if USE_NOISY_TRACE_IN_THIS_MODULE_
464 DbgTrace ("interface: ifr_name={}; ifr_addr.sa_family = {}"_f, i->ifr_name, i->ifr_addr.sa_family);
465#endif
466 String interfaceName{String::FromSDKString (i->ifr_name)};
467
468 // @todo - On MacOS - we get multiple copies of the same interface (one for each address family on that interface). Redo this code
469 // to be smarter about merging these
470 Interface newInterface = GetInterfaces_POSIX_mkInterface_ (sd, i, results.Lookup (interfaceName));
471
472 results.Add (newInterface);
473
474 // On MacOS (at least) I needed to use IFNAMESIZ + addr.size - as suggested
475 // in https://gist.githubusercontent.com/OrangeTide/909204/raw/ed097cf0fc73eb0c44de1b26118f041a36424e3f/showif.c
476 //
477 // https://linux.die.net/man/7/netdevice strongly suggests ("array of structures" to treat as array - fixed offset per element
478 //
479 // We'll have to see what other OSes require... --LGP 2018-09-27
480#if qStroika_Foundation_Common_Platform_Linux
481 size_t len = sizeof (*i);
482#else
483 size_t len = IFNAMSIZ + i->ifr_addr.sa_len;
484#endif
485 i = reinterpret_cast<const ifreq*> (reinterpret_cast<const byte*> (i) + len);
486 }
487 return move (results);
488 }
489}
490#endif
491
492#if qStroika_Foundation_Common_Platform_Windows
493namespace {
494 /*
495 * This DLL is apparently only available if the Wireless Lan Service feature is installed
496 * (see https://forum.kodi.tv/showthread.php?tid=333721&pid=2751888#pid2751888)
497 * It is also missing from the docker image mcr.microsoft.com/windows/servercore:ltsc2019.
498 * So don't statically link since that caused the whole app to not load. Instead, load the DLL conditionally.
499 *
500 * \note switched to this helper class from #pragma comment(lib, "wlanapi.lib") in Stroika v2.1a6, to avoid dependency on
501 * that DLL (not met by simple docker containers).
502 */
503 struct WLANAPI_ {
504 WLANAPI_ ()
505 : fDLL{::LoadLibrary (_T ("wlanapi.dll"))}
506 {
507 Execution::ThrowIfNull (fDLL);
508 fWlanOpenHandle = (DWORD (WINAPI*) (_In_ DWORD dwClientVersion, _Reserved_ PVOID pReserved, _Out_ PDWORD pdwNegotiatedVersion,
509 _Out_ PHANDLE phClientHandle)) (::GetProcAddress (fDLL, "WlanOpenHandle"));
510 fWlanCloseHandle =
511 (DWORD (WINAPI*) (_In_ HANDLE hClientHandle, _Reserved_ PVOID pReserved)) (::GetProcAddress (fDLL, "WlanCloseHandle"));
512 fWlanFreeMemory = (DWORD (WINAPI*) (_In_ PVOID pMemory)) (::GetProcAddress (fDLL, "WlanFreeMemory"));
513 fWlanEnumInterfaces =
514 (DWORD (WINAPI*) (_In_ HANDLE hClientHandle, _Reserved_ PVOID pReserved,
515 _Outptr_ PWLAN_INTERFACE_INFO_LIST * ppInterfaceList)) (::GetProcAddress (fDLL, "WlanEnumInterfaces"));
516 fWlanQueryInterface =
517 (DWORD (WINAPI*) (_In_ HANDLE hClientHandle, _In_ CONST ::GUID * pInterfaceGuid, _In_ WLAN_INTF_OPCODE OpCode,
518 _Reserved_ PVOID pReserved, _Out_ PDWORD pdwDataSize, _Outptr_result_bytebuffer_ (*pdwDataSize) PVOID * ppData,
519 _Out_opt_ PWLAN_OPCODE_VALUE_TYPE pWlanOpcodeValueType)) (::GetProcAddress (fDLL, "WlanQueryInterface"));
520 }
521 WLANAPI_ (const WLANAPI_&) = delete;
522 ~WLANAPI_ ()
523 {
524 if (fDLL != nullptr) {
525 Verify (::FreeLibrary (fDLL));
526 }
527 }
528 HINSTANCE fDLL;
529 DWORD (WINAPI* fWlanOpenHandle)
530 (_In_ DWORD dwClientVersion, _Reserved_ PVOID pReserved, _Out_ PDWORD pdwNegotiatedVersion, _Out_ PHANDLE phClientHandle) = nullptr;
531 DWORD (WINAPI* fWlanCloseHandle)
532 (_In_ HANDLE hClientHandle, _Reserved_ PVOID pReserved) = nullptr;
533 DWORD (WINAPI* fWlanFreeMemory)
534 (_In_ PVOID pMemory) = nullptr;
535 DWORD (WINAPI* fWlanEnumInterfaces)
536 (_In_ HANDLE hClientHandle, _Reserved_ PVOID pReserved, _Outptr_ PWLAN_INTERFACE_INFO_LIST* ppInterfaceList) = nullptr;
537 DWORD (WINAPI* fWlanQueryInterface)
538 (_In_ HANDLE hClientHandle, _In_ CONST ::GUID* pInterfaceGuid, _In_ WLAN_INTF_OPCODE OpCode, _Reserved_ PVOID pReserved, _Out_ PDWORD pdwDataSize,
539 _Outptr_result_bytebuffer_ (*pdwDataSize) PVOID* ppData, _Out_opt_ PWLAN_OPCODE_VALUE_TYPE pWlanOpcodeValueType) = nullptr;
540 };
541 struct WirelessInfoPlus_ : Interface::WirelessInfo {
542 // extra info so can be patched into Interface
543 optional<uint64_t> fTransmitSpeedBaud;
544 optional<uint64_t> fReceiveLinkSpeedBaud;
545 };
546 Mapping<Common::GUID, WirelessInfoPlus_> GetInterfaces_Windows_WirelessInfo_ ()
547 {
548 using WirelessInfo = Interface::WirelessInfo;
550
551 static unique_ptr<WLANAPI_> sWlanAPI_;
552 static once_flag sOnceFlag_;
553 call_once (sOnceFlag_, [&] () { IgnoreExceptionsForCall (sWlanAPI_ = make_unique<WLANAPI_> ()) });
554
555 if (sWlanAPI_) {
556 HANDLE hClient = nullptr;
557 {
558 DWORD dwCurVersion = 0;
559 Execution::Platform::Windows::ThrowIfNotERROR_SUCCESS (sWlanAPI_->fWlanOpenHandle (2, NULL, &dwCurVersion, &hClient));
560 }
561 [[maybe_unused]] auto&& cleanup1 = Execution::Finally ([&] () noexcept {
562 if (hClient != nullptr) {
563 sWlanAPI_->fWlanCloseHandle (hClient, nullptr);
564 }
565 });
566
567 PWLAN_INTERFACE_INFO_LIST pIfList = nullptr;
568 Execution::Platform::Windows::ThrowIfNotERROR_SUCCESS (sWlanAPI_->fWlanEnumInterfaces (hClient, nullptr, &pIfList));
569 [[maybe_unused]] auto&& cleanup2 = Execution::Finally ([&] () noexcept {
570 if (pIfList != nullptr) {
571 sWlanAPI_->fWlanFreeMemory (pIfList);
572 }
573 });
574
575 //
576 // makes more sense for pConnectionInfo to be scoped inside loop, but the example docs in:
577 // https://docs.microsoft.com/en-us/windows/win32/api/wlanapi/nf-wlanapi-wlanqueryinterface
578 // keep it out here (so gets re-used in each QueryInterface call) and just deleted once
579 // at the end.
580 //
581 // Scoped inside the loop, we get ASAN errors under windows, so presume the docs / example was right
582 // any my intuitions wrong.
583 //
584 PWLAN_CONNECTION_ATTRIBUTES pConnectInfo{};
585 [[maybe_unused]] auto&& cleanup3 = Execution::Finally ([&] () noexcept {
586 if (pConnectInfo != nullptr) {
587 sWlanAPI_->fWlanFreeMemory (pConnectInfo);
588 }
589 });
590
591 for (DWORD i = 0; i < pIfList->dwNumberOfItems; ++i) {
592 PWLAN_INTERFACE_INFO pIfInfo = (WLAN_INTERFACE_INFO*)&pIfList->InterfaceInfo[i];
593 WirelessInfoPlus_ wInfo;
594 Common::GUID interfaceGUID{pIfInfo->InterfaceGuid};
595
596 auto mapState = [] (WLAN_INTERFACE_STATE s) -> WirelessInfo::State {
597 switch (s) {
598 case wlan_interface_state_not_ready:
599 return WirelessInfo::State::eNotReady;
600 case wlan_interface_state_connected:
601 return WirelessInfo::State::eConnected;
602 case wlan_interface_state_ad_hoc_network_formed:
603 return WirelessInfo::State::eAdHocNetworkFormed;
604 case wlan_interface_state_disconnecting:
605 return WirelessInfo::State::eDisconnecting;
606 case wlan_interface_state_disconnected:
607 return WirelessInfo::State::eDisconnected;
608 case wlan_interface_state_associating:
609 return WirelessInfo::State::eAssociating;
610 case wlan_interface_state_discovering:
611 return WirelessInfo::State::eDiscovering;
612 case wlan_interface_state_authenticating:
613 return WirelessInfo::State::eAuthenticating;
614 default:
615 DbgTrace ("Unknown state {}"_f, static_cast<int> (s));
616 return WirelessInfo::State::eUnknown;
617 }
618 };
619
620 wInfo.fState = mapState (pIfInfo->isState);
621
622 // If interface state is connected, call WlanQueryInterface
623 // to get current connection attributes
624 if (pIfInfo->isState == wlan_interface_state_connected) {
625 {
626 DWORD connectInfoSize = sizeof (WLAN_CONNECTION_ATTRIBUTES);
627 WLAN_OPCODE_VALUE_TYPE opCode = wlan_opcode_value_type_invalid;
629 sWlanAPI_->fWlanQueryInterface (hClient, &pIfInfo->InterfaceGuid, wlan_intf_opcode_current_connection, nullptr,
630 &connectInfoSize, (PVOID*)&pConnectInfo, &opCode));
631 }
632
633 if (pConnectInfo->isState != pIfInfo->isState) {
634 DbgTrace ("Not sure how these can differ (except for race condition) - but if they do, maybe worth looking into"_f);
635 }
636
637 auto mapConnectionMode = [] (WLAN_CONNECTION_MODE s) -> WirelessInfo::ConnectionMode {
638 switch (s) {
639 case wlan_connection_mode_profile:
640 return WirelessInfo::ConnectionMode::eProfile;
641 case wlan_connection_mode_temporary_profile:
642 return WirelessInfo::ConnectionMode::eTemporaryProfile;
643 case wlan_connection_mode_discovery_secure:
644 return WirelessInfo::ConnectionMode::eDiscoverSecrure;
645 case wlan_connection_mode_discovery_unsecure:
646 return WirelessInfo::ConnectionMode::eDiscoverInsecure;
647 case wlan_connection_mode_auto:
648 return WirelessInfo::ConnectionMode::eAuto;
649 case wlan_connection_mode_invalid:
650 return WirelessInfo::ConnectionMode::eInvalid;
651 default:
652 DbgTrace ("Unknown connection mode {}"_f, static_cast<int> (s));
653 return WirelessInfo::ConnectionMode::eUnknown;
654 }
655 };
656 wInfo.fConnectionMode = mapConnectionMode (pConnectInfo->wlanConnectionMode);
657 wInfo.fProfileName = pConnectInfo->strProfileName;
658
659 //Association Attributes for this connection
660 if (pConnectInfo->wlanAssociationAttributes.dot11Ssid.uSSIDLength != 0) {
661 wInfo.fSSID = String::FromNarrowSDKString (
662 span{reinterpret_cast<const char*> (pConnectInfo->wlanAssociationAttributes.dot11Ssid.ucSSID),
663 pConnectInfo->wlanAssociationAttributes.dot11Ssid.uSSIDLength});
664 }
665
666 auto mapBSSType = [] (DOT11_BSS_TYPE s) -> WirelessInfo::BSSType {
667 switch (s) {
668 case dot11_BSS_type_infrastructure:
669 return WirelessInfo::BSSType::eInfrastructure;
670 case dot11_BSS_type_independent:
671 return WirelessInfo::BSSType::eIndependent;
672 case dot11_BSS_type_any:
673 return WirelessInfo::BSSType::eAny;
674 default:
675 DbgTrace ("Unknown BSS mode {}"_f, static_cast<int> (s));
676 return WirelessInfo::BSSType::eUnknown;
677 }
678 };
679 wInfo.fBSSType = mapBSSType (pConnectInfo->wlanAssociationAttributes.dot11BssType);
680
681 wInfo.fMACAddress = PrintMacAddr_ (std::begin (pConnectInfo->wlanAssociationAttributes.dot11Bssid),
682 std::end (pConnectInfo->wlanAssociationAttributes.dot11Bssid));
683
684 auto mapPhysicalConnectionType = [] (DOT11_PHY_TYPE s) -> WirelessInfo::PhysicalConnectionType {
685 switch (s) {
686 case dot11_phy_type_unknown:
687 return WirelessInfo::PhysicalConnectionType::eUnknown;
688 case dot11_phy_type_fhss:
689 return WirelessInfo::PhysicalConnectionType::eFHSS;
690 case dot11_phy_type_dsss:
691 return WirelessInfo::PhysicalConnectionType::eDSSS;
692 case dot11_phy_type_irbaseband:
693 return WirelessInfo::PhysicalConnectionType::eIRBaseBand;
694 case dot11_phy_type_ofdm:
695 return WirelessInfo::PhysicalConnectionType::e80211a;
696 case dot11_phy_type_hrdsss:
697 return WirelessInfo::PhysicalConnectionType::e80211b;
698 case dot11_phy_type_erp:
699 return WirelessInfo::PhysicalConnectionType::e80211g;
700 case dot11_phy_type_ht:
701 return WirelessInfo::PhysicalConnectionType::e80211n;
702 case dot11_phy_type_vht:
703 return WirelessInfo::PhysicalConnectionType::e80211ac;
704 case dot11_phy_type_dmg:
705 return WirelessInfo::PhysicalConnectionType::e80211ad;
706 case dot11_phy_type_he:
707 return WirelessInfo::PhysicalConnectionType::e80211ax;
708 default:
709 DbgTrace (L"Unknown DOT11_PHY_TYPE {}"_f, static_cast<int> (s));
710 return WirelessInfo::PhysicalConnectionType::eUnknown;
711 }
712 };
713 wInfo.fPhysicalConnectionType = mapPhysicalConnectionType (pConnectInfo->wlanAssociationAttributes.dot11PhyType);
714
715 wInfo.fSignalQuality = pConnectInfo->wlanAssociationAttributes.wlanSignalQuality;
716
717 {
718 const ULONG kMagicFactor_{1000}; // empirical, so we get same answers from IP_ADAPTER_ADDRESSES_LH::TransmitLinkSpeed
719 wInfo.fTransmitSpeedBaud = pConnectInfo->wlanAssociationAttributes.ulTxRate * kMagicFactor_;
720 wInfo.fReceiveLinkSpeedBaud = pConnectInfo->wlanAssociationAttributes.ulRxRate * kMagicFactor_;
721 }
722
723 wInfo.fSecurityEnabled = pConnectInfo->wlanSecurityAttributes.bSecurityEnabled;
724 wInfo.f8021XEnabled = pConnectInfo->wlanSecurityAttributes.bOneXEnabled;
725 auto mapAuthAlgorithm = [] (DOT11_AUTH_ALGORITHM s) -> WirelessInfo::AuthAlgorithm {
726 switch (s) {
727 case DOT11_AUTH_ALGO_80211_OPEN:
728 return WirelessInfo::AuthAlgorithm::eOpen;
729 case DOT11_AUTH_ALGO_80211_SHARED_KEY:
730 return WirelessInfo::AuthAlgorithm::ePresharedKey;
731 case DOT11_AUTH_ALGO_WPA:
732 return WirelessInfo::AuthAlgorithm::eWPA;
733 case DOT11_AUTH_ALGO_WPA_PSK:
734 return WirelessInfo::AuthAlgorithm::eWPA_PSK;
735 case DOT11_AUTH_ALGO_WPA_NONE:
736 return WirelessInfo::AuthAlgorithm::eWPA_NONE;
737 case DOT11_AUTH_ALGO_RSNA:
738 return WirelessInfo::AuthAlgorithm::eRSNA;
739 case DOT11_AUTH_ALGO_RSNA_PSK:
740 return WirelessInfo::AuthAlgorithm::eRSNA_PSK;
741 default:
742 DbgTrace (L"Unknown AuthAlgorithm {}"_f, static_cast<int> (s));
743 return WirelessInfo::AuthAlgorithm::eUnknown;
744 }
745 };
746 wInfo.fAuthAlgorithm = mapAuthAlgorithm (pConnectInfo->wlanSecurityAttributes.dot11AuthAlgorithm);
747
748 auto mapCipher = [] (DOT11_CIPHER_ALGORITHM s) -> String {
749 switch (s) {
750 case DOT11_CIPHER_ALGO_NONE:
751 return "None"sv;
752 case DOT11_CIPHER_ALGO_WEP40:
753 return "WEP-40"sv;
754 case DOT11_CIPHER_ALGO_TKIP:
755 return "TKIP"sv;
756 case DOT11_CIPHER_ALGO_CCMP:
757 return "CCMP"sv;
758 case DOT11_CIPHER_ALGO_WEP104:
759 return "WEP-104"sv;
760 case DOT11_CIPHER_ALGO_WEP:
761 return "WEP"sv;
762 default:
763 return "{}"_f(static_cast<int> (s));
764 }
765 };
766 wInfo.fCipher = mapCipher (pConnectInfo->wlanSecurityAttributes.dot11CipherAlgorithm);
767 results.Add (interfaceGUID, wInfo);
768 }
769 }
770 }
771
772 return results;
773 }
774
775 Traversal::Iterable<Interface> GetInterfaces_Windows_ ()
776 {
778 try {
779 wirelessInfo2Merge = GetInterfaces_Windows_WirelessInfo_ ();
780 }
781 catch (const std::system_error& e) {
782 if (e.code () == error_code{ERROR_SERVICE_NOT_ACTIVE, system_category ()}) {
783 // this just means no wireless services active, so leave wirelessInfo2Merge empty
784 }
785 else {
786 // but other errors it makes sense to propagate
788 }
789 }
790 KeyedCollection<Interface, String> results{[] (const Interface& i) { return i.fInternalInterfaceID; }};
791 ULONG flags = GAA_FLAG_INCLUDE_PREFIX | GAA_FLAG_INCLUDE_GATEWAYS;
792 ULONG family = AF_UNSPEC; // Both IPv4 and IPv6 addresses
794 Again:
795 ULONG ulOutBufLen = static_cast<ULONG> (buf.GetSize ());
796 PIP_ADAPTER_ADDRESSES pAddresses = reinterpret_cast<PIP_ADAPTER_ADDRESSES> (buf.begin ());
797 // NB: we use GetAdapaterAddresses () instead of GetInterfaceInfo () so we get non-ipv4 addresses
798 //
799 // Note also: calling GetAdaptersAddresses () produces about 10 "Invalid parameter passed to C runtime function" lines in the debugger.
800 // @see https://developercommunity.visualstudio.com/content/problem/363323/getadaptersaddresses-invalid-parameter-passed-to-c.html
801 //
802 DWORD dwRetVal = ::GetAdaptersAddresses (family, flags, nullptr, pAddresses, &ulOutBufLen);
803 if (dwRetVal == NO_ERROR) {
804 for (PIP_ADAPTER_ADDRESSES currAddresses = pAddresses; currAddresses != nullptr; currAddresses = currAddresses->Next) {
805 String adapterName{String::FromNarrowSDKString (currAddresses->AdapterName)};
806 Interface newInterface = results.LookupValue (adapterName);
807 newInterface.fInternalInterfaceID = adapterName;
808 newInterface.fFriendlyName = currAddresses->FriendlyName;
809 newInterface.fDescription = currAddresses->Description;
810
811 static constexpr Common::GUID kZeroGUID_{};
812 if (memcmp (&currAddresses->NetworkGuid, &kZeroGUID_, sizeof (kZeroGUID_)) != 0) {
813 newInterface.fNetworkGUID = currAddresses->NetworkGuid;
814 }
815 switch (currAddresses->IfType) {
816 case IF_TYPE_SOFTWARE_LOOPBACK:
817 newInterface.fType = Interface::Type::eLoopback;
818 break;
819 case IF_TYPE_IEEE80211:
820 newInterface.fType = Interface::Type::eWIFI;
821 break;
822 case IF_TYPE_ETHERNET_CSMACD:
823 if (newInterface.fDescription->Contains ("VirtualBox Host-Only Ethernet Adapter"sv) or
824 newInterface.fDescription->Contains ("Hyper-V Virtual Ethernet Adapter"sv)) {
825 // a fairly good guess - not sure how to tell for sure
826 newInterface.fType = Interface::Type::eDeviceVirtualInternalNetwork;
827 }
828 else {
829 newInterface.fType = Interface::Type::eWiredEthernet;
830 }
831 break;
832 default:
833 DbgTrace ("Treating unknown currAddresses->IfType = {} type as eOther"_f, currAddresses->IfType);
834 newInterface.fType = Interface::Type::eOther;
835 break;
836 }
837 if (currAddresses->TunnelType != TUNNEL_TYPE_NONE) {
838 if (newInterface.fType) {
839 DbgTrace ("overwriting type {} with tunneltype"_f, newInterface.fType);
840 }
841 newInterface.fType = Interface::Type::eTunnel;
842 }
843 switch (currAddresses->OperStatus) {
844 case IfOperStatusUp:
845 newInterface.fStatus = Memory::NullCoalesce (newInterface.fStatus) +
847 break;
848 case IfOperStatusDown:
849 newInterface.fStatus = Memory::NullCoalesce (newInterface.fStatus); // keep any existing status values, but don't leave unknown
850 break;
851 case IfOperStatusDormant:
852 case IfOperStatusLowerLayerDown:
853 // 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
854 newInterface.fStatus = Memory::NullCoalesce (newInterface.fStatus) + Set<Interface::Status> ({Interface::Status::eConnected});
855 break;
856 default:
857 // Don't know how to interpret the other status states
858 DbgTrace ("ignoring unrecognized status: {}"_f, static_cast<int> (currAddresses->OperStatus));
859 break;
860 }
861 for (PIP_ADAPTER_UNICAST_ADDRESS pu = currAddresses->FirstUnicastAddress; pu != nullptr; pu = pu->Next) {
862 SocketAddress sa{pu->Address};
863 if (sa.IsInternetAddress ()) {
864 newInterface.fBindings.fAddressRanges.Add (
865 pu->OnLinkPrefixLength == 255 ? sa.GetInternetAddress () : CIDR{sa.GetInternetAddress (), pu->OnLinkPrefixLength});
866 newInterface.fBindings.fAddresses.Add (sa.GetInternetAddress ());
867 }
868 }
869 for (PIP_ADAPTER_ANYCAST_ADDRESS pa = currAddresses->FirstAnycastAddress; pa != nullptr; pa = pa->Next) {
870 SocketAddress sa{pa->Address};
871 if (sa.IsInternetAddress ()) {
872 newInterface.fBindings.fAddressRanges.Add (sa.GetInternetAddress ());
873 newInterface.fBindings.fAddresses.Add (sa.GetInternetAddress ());
874 }
875 }
876 for (PIP_ADAPTER_MULTICAST_ADDRESS pm = currAddresses->FirstMulticastAddress; pm != nullptr; pm = pm->Next) {
877 SocketAddress sa{pm->Address};
878 if (sa.IsInternetAddress ()) {
879 newInterface.fBindings.fAddressRanges.Add (sa.GetInternetAddress ());
880 newInterface.fBindings.fAddresses.Add (sa.GetInternetAddress ());
881 }
882 }
883 for (PIP_ADAPTER_GATEWAY_ADDRESS_LH pa = currAddresses->FirstGatewayAddress; pa != nullptr; pa = pa->Next) {
884 SocketAddress sa{pa->Address};
885 if (sa.IsInternetAddress ()) {
886 auto gws = newInterface.fGateways.value_or (Containers::Sequence<InternetAddress>{});
887 if (not gws.Contains (sa.GetInternetAddress ())) {
888 gws += sa.GetInternetAddress ();
889 newInterface.fGateways = gws;
890 }
891 }
892 }
893 for (PIP_ADAPTER_DNS_SERVER_ADDRESS_XP pa = currAddresses->FirstDnsServerAddress; pa != nullptr; pa = pa->Next) {
894 SocketAddress sa{pa->Address};
895 if (sa.IsInternetAddress ()) {
896 auto ds = newInterface.fDNSServers.value_or (Containers::Sequence<InternetAddress>{});
897 if (not ds.Contains (sa.GetInternetAddress ())) {
898 ds += sa.GetInternetAddress ();
899 newInterface.fDNSServers = ds;
900 }
901 }
902 }
903 if (currAddresses->PhysicalAddressLength == 6) {
904 newInterface.fHardwareAddress = PrintMacAddr_ (currAddresses->PhysicalAddress, currAddresses->PhysicalAddress + 6);
905 }
906
907#if (NTDDI_VERSION >= NTDDI_WIN6)
908 newInterface.fTransmitSpeedBaud = currAddresses->TransmitLinkSpeed;
909 newInterface.fReceiveLinkSpeedBaud = currAddresses->ReceiveLinkSpeed;
910#endif
911
912 if (newInterface.fType == Interface::Type::eWIFI) {
913 if (auto owinfo = wirelessInfo2Merge.Lookup (newInterface.fInternalInterfaceID)) {
914 newInterface.fWirelessInfo = *owinfo;
915 WeakAssert (not newInterface.fTransmitSpeedBaud.has_value () or newInterface.fTransmitSpeedBaud == owinfo->fTransmitSpeedBaud);
916 WeakAssert (not newInterface.fReceiveLinkSpeedBaud.has_value () or newInterface.fReceiveLinkSpeedBaud == owinfo->fReceiveLinkSpeedBaud);
917 newInterface.fTransmitSpeedBaud = Memory::NullCoalesce (newInterface.fTransmitSpeedBaud, owinfo->fTransmitSpeedBaud);
918 newInterface.fReceiveLinkSpeedBaud = Memory::NullCoalesce (newInterface.fReceiveLinkSpeedBaud, owinfo->fReceiveLinkSpeedBaud);
919 }
920 else {
921 // This happens for down/wifi-direct interfaces
922 // no biggie.
923 // DbgTrace ("Oops - didn't find wireless interface we should have: {}, avail-keys={}"_f, newInterface.fInternalInterfaceID, wirelessInfo2Merge.Keys ());
924 }
925 }
926 else {
927 WeakAssert (not wirelessInfo2Merge.ContainsKey (newInterface.fInternalInterfaceID));
928 }
929#if USE_NOISY_TRACE_IN_THIS_MODULE_
930 DbgTrace (L"newInterface={}"_f, newInterface);
931#endif
932 results.Add (newInterface);
933 }
934 }
935 else if (dwRetVal == ERROR_BUFFER_OVERFLOW) {
936 buf.GrowToSize_uninitialized (ulOutBufLen);
937 goto Again;
938 }
939 else if (dwRetVal == ERROR_NO_DATA) {
940 DbgTrace ("There are no network adapters enabled on the local system"_f);
941 }
942 else {
943 Execution::ThrowSystemErrNo (dwRetVal);
944 }
945
946#if USE_NOISY_TRACE_IN_THIS_MODULE_
947 DbgTrace ("returning {}"_f, results.MappedValues ());
948#endif
949 return results;
950 }
951}
952#endif
953
954/*
955 ********************************************************************************
956 ************************* Network::SystemInterfacesMgr *************************
957 ********************************************************************************
958 */
960{
961#if USE_NOISY_TRACE_IN_THIS_MODULE_
962 Debug::TraceContextBumper ctx{"SystemInterfacesMgr::GetAll"};
963#endif
964#if qStroika_Foundation_Common_Platform_POSIX
965 Traversal::Iterable<Interface> results = GetInterfaces_POSIX_ ();
966#elif qStroika_Foundation_Common_Platform_Windows
967 Traversal::Iterable<Interface> results = GetInterfaces_Windows_ ();
968#else
970#endif
971#if USE_NOISY_TRACE_IN_THIS_MODULE_
972 DbgTrace ("returning {}"_f, results);
973#endif
974 return results;
975}
976
977optional<Interface> SystemInterfacesMgr::GetById (const Interface::SystemIDType& internalInterfaceID)
978{
979 // 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).
980#if USE_NOISY_TRACE_IN_THIS_MODULE_
981 Debug::TraceContextBumper ctx{"Network::GetById"};
982#endif
983 // @todo - a much more efficent implementation - maybe good enuf to use caller staleness cache with a few seconds staleness
984 for (const Interface& i : GetAll ()) {
985 if (i.fInternalInterfaceID == internalInterfaceID) {
986#if USE_NOISY_TRACE_IN_THIS_MODULE_
987 DbgTrace (L"found interface %s", internalInterfaceID.c_str ());
988#endif
989 return i;
990 }
991 }
992#if USE_NOISY_TRACE_IN_THIS_MODULE_
993 DbgTrace (L"interface %s not found", internalInterfaceID.c_str ());
994#endif
995 return nullopt;
996}
997
999{
1000#if USE_NOISY_TRACE_IN_THIS_MODULE_
1001 Debug::TraceContextBumper ctx{"Network::GetContainingAddress"};
1002#endif
1003 // @todo - a much more efficent implementation - maybe good enuf to use caller staleness cache with a few seconds staleness
1004 for (const Interface& i : GetAll ()) {
1005 if (i.fBindings.fAddressRanges.Any ([&ia] (CIDR c) { return c.GetRange ().Contains (ia); })) {
1006#if USE_NOISY_TRACE_IN_THIS_MODULE_
1007 DbgTrace (L"found interface %s", internalInterfaceID.c_str ());
1008#endif
1009 return i;
1010 }
1011 }
1012#if USE_NOISY_TRACE_IN_THIS_MODULE_
1013 DbgTrace (L"interface %s not found", internalInterfaceID.c_str ());
1014#endif
1015 return nullopt;
1016}
#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:1055
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: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 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
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