AuroraRuntime/Source/IO/Net/AuNetAdapter.NT.cpp
2023-12-01 13:26:44 +00:00

359 lines
10 KiB
C++

/***
Copyright (C) 2022 J Reece Wilson (a/k/a "Reece"). All rights reserved.
File: AuNetAdapter.NT.cpp
Date: 2022-11-14
Author: Reece
***/
#include "Networking.hpp"
#include "AuNetAdapter.hpp"
#include "AuNetEndpoint.hpp"
#include <winsock2.h>
#include <iphlpapi.h>
namespace Aurora::IO::Net
{
static AuList<AuSPtr<INetAdapter>> gIpv4Adapters;
static AuList<AuSPtr<INetAdapter>> gIpv6Adapters;
AuString NetAdapter::GetHostname()
{
wchar_t buffer[256];
#if defined(AURORA_PLATFORM_WIN32)
DWORD dwLength { AuArraySize(buffer) };
if (!::GetComputerNameExW(ComputerNameDnsHostname, buffer, &dwLength))
{
return {};
}
return AuLocale::ConvertFromWChar(buffer, dwLength);
#else
int iLength { AuArraySize(buffer) };
if (!(iLength = ::GetHostNameW(buffer, iLength)))
{
return {};
}
return AuLocale::ConvertFromWChar(buffer, iLength);
#endif
}
#if defined(AURORA_PLATFORM_WIN32)
AuSPtr<IP_ADAPTER_ADDRESSES_XP> GetAddressesForFamilyXP(ULONG uFamily)
{
static const auto kDefSize = 15 * 1024;
if (!pGetAdaptersAddresses)
{
return {};
}
AuSPtr<IP_ADAPTER_ADDRESSES_XP> pAddresses;
DWORD outBufLen { kDefSize };
DWORD dwRetVal;
DWORD dwFlags = AuSwInfo::IsWindowsXPSP1OrGreater() ? GAA_FLAG_INCLUDE_PREFIX : 0;
DWORD dwIterations {};
do
{
pAddresses = AuReinterpretCast<IP_ADAPTER_ADDRESSES_XP>(AuMakeSharedArray<AuUInt8>(kDefSize));
SysAssert(pAddresses);
dwRetVal = pGetAdaptersAddresses(uFamily, dwFlags, NULL, (IP_ADAPTER_ADDRESSES *)(pAddresses.get()), &outBufLen);
if (dwRetVal == 0)
{
break;
}
else if (dwRetVal != ERROR_BUFFER_OVERFLOW)
{
}
else
{
SysPushErrorNet("Fail");
break;
}
dwIterations++;
}
while ((dwRetVal == ERROR_BUFFER_OVERFLOW) && (dwIterations < 5));
return dwRetVal == NO_ERROR ? pAddresses : AuSPtr<IP_ADAPTER_ADDRESSES_XP> {};
}
AuSPtr<IP_ADAPTER_ADDRESSES_LH> GetAddressesForFamilyLH(ULONG uFamily)
{
static const auto kDefSize = 15 * 1024;
if (!pGetAdaptersAddresses)
{
return {};
}
AuSPtr<IP_ADAPTER_ADDRESSES_LH> pAddresses;
DWORD outBufLen { kDefSize };
DWORD dwRetVal;
DWORD dwFlags = GAA_FLAG_INCLUDE_PREFIX | GAA_FLAG_INCLUDE_GATEWAYS;
DWORD dwIterations {};
do
{
pAddresses = AuReinterpretCast<IP_ADAPTER_ADDRESSES_LH>(AuMakeSharedArray<AuUInt8>(kDefSize));
SysAssert(pAddresses);
dwRetVal = pGetAdaptersAddresses(uFamily, dwFlags, NULL, (IP_ADAPTER_ADDRESSES *)(pAddresses.get()), &outBufLen);
if (dwRetVal == 0)
{
break;
}
else if (dwRetVal != ERROR_BUFFER_OVERFLOW)
{
}
else
{
SysPushErrorNet("Fail");
break;
}
dwIterations++;
}
while ((dwRetVal == ERROR_BUFFER_OVERFLOW) && (dwIterations < 5));
return dwRetVal == NO_ERROR ? pAddresses : AuSPtr<IP_ADAPTER_ADDRESSES_LH> {};
}
template <typename T>
AU_NOINLINE void AddressesToList(AuSPtr<T> pAddresses, AuList<AuSPtr<INetAdapter>> &adaptersOut)
{
auto pCurrAddresses = pAddresses.get();
adaptersOut.clear();
while (pCurrAddresses)
{
if (pCurrAddresses->Length != sizeof(T))
{
return;
}
auto pAdapter = AuMakeSharedThrow<NetAdapter>();
auto &adapter = *pAdapter.get();
adapter.device = pCurrAddresses->AdapterName;
adapter.name = AuLocale::ConvertFromWChar(pCurrAddresses->FriendlyName);
AuMemcpy(adapter.mac.begin(), pCurrAddresses->PhysicalAddress, pCurrAddresses->PhysicalAddressLength);
adapter.mtu = pCurrAddresses->Mtu;
auto pUnicast = pCurrAddresses->FirstUnicastAddress;
if (pUnicast)
{
NetEndpoint ep;
if (pUnicast->Address.iSockaddrLength <= sizeof(ep.hint))
{
AuMemcpy(ep.hint, pUnicast->Address.lpSockaddr, pUnicast->Address.iSockaddrLength);
DeoptimizeEndpoint(ep);
}
adapter.address = ep.ip;
}
auto pMulticast = pCurrAddresses->FirstMulticastAddress;
if (pMulticast)
{
NetEndpoint ep;
if (pMulticast->Address.iSockaddrLength <= sizeof(ep.hint))
{
AuMemcpy(ep.hint, pMulticast->Address.lpSockaddr, pMulticast->Address.iSockaddrLength);
DeoptimizeEndpoint(ep);
}
adapter.broadcast = ep.ip;
}
{
switch (pCurrAddresses->IfType)
{
case IF_TYPE_OTHER:
adapter.eNetworkType = ENetworkAdapterType::eOther;
break;
case IF_TYPE_ETHERNET_CSMACD:
adapter.eNetworkType = ENetworkAdapterType::eEthernet;
break;
case IF_TYPE_IEEE80211:
adapter.eNetworkType = ENetworkAdapterType::eIEEE80211;
break;
case IF_TYPE_ISO88025_TOKENRING:
adapter.eNetworkType = ENetworkAdapterType::eTokenRing;
break;
case IF_TYPE_SOFTWARE_LOOPBACK:
adapter.eNetworkType = ENetworkAdapterType::eLoopback;
break;
case IF_TYPE_TUNNEL:
adapter.eNetworkType = ENetworkAdapterType::eTunnel;
break;
}
}
adapter.eNetworkStatus = pCurrAddresses->OperStatus == IfOperStatusUp ?
ENetworkAdapterStatus::eUp :
ENetworkAdapterStatus::eDown;
auto pAnycast = pCurrAddresses->FirstAnycastAddress;
if (pAnycast)
{
NetEndpoint ep;
if (pAnycast->Address.iSockaddrLength <= sizeof(ep.hint))
{
AuMemcpy(ep.hint, pAnycast->Address.lpSockaddr, pAnycast->Address.iSockaddrLength);
DeoptimizeEndpoint(ep);
}
adapter.anycast = ep.ip;
}
if constexpr (!AuIsSame_v<T, _IP_ADAPTER_ADDRESSES_XP>)
{
auto pGateway = pCurrAddresses->FirstGatewayAddress;
if (pGateway)
{
NetEndpoint ep;
if (pGateway->Address.iSockaddrLength <= sizeof(ep.hint))
{
AuMemcpy(ep.hint, pGateway->Address.lpSockaddr, pGateway->Address.iSockaddrLength);
DeoptimizeEndpoint(ep);
}
adapter.gateway = ep.ip;
}
adapter.dhcp = pCurrAddresses->Dhcpv4Enabled;
adapter.uTransmitBytesPerSec = pCurrAddresses->TransmitLinkSpeed / 8;
adapter.uReceiveBytesPerSec = pCurrAddresses->ReceiveLinkSpeed / 8;
}
if (adapter.address.ip == EIPProtocol::eIPProtocolV4)
{
adapter.index = pCurrAddresses->IfIndex;
}
else
{
adapter.index = adaptersOut.size();
}
auto pDnsServers = pCurrAddresses->FirstDnsServerAddress;
if (pDnsServers)
{
do
{
NetEndpoint ep;
if (pDnsServers->Address.iSockaddrLength <= sizeof(ep.hint))
{
AuMemcpy(ep.hint, pDnsServers->Address.lpSockaddr, pDnsServers->Address.iSockaddrLength);
DeoptimizeEndpoint(ep);
}
adapter.dns.push_back(ep.ip);
}
while ((pDnsServers = pDnsServers->Next));
}
adaptersOut.push_back(pAdapter);
pCurrAddresses = pCurrAddresses->Next;
}
}
static void FixupXPGatewaysIPv4()
{
IP_ADAPTER_INFO info[16];
DWORD size { sizeof(info) };
IP_ADAPTER_INFO *pAdapter {};
if (!pGetAdaptersInfo)
{
return;
}
ULONG error;
if ((error = pGetAdaptersInfo(info, &size)) != NO_ERROR)
{
if (error == ERROR_NO_DATA)
{
return;
}
SysPushErrorNet("GetAdaptersInfo failed");
return;
}
if (size == 0)
{
return;
}
auto pIPAddrTable = info;
while (pIPAddrTable)
{
for (auto &pAdapter : gIpv4Adapters)
{
auto pAdapterEx = AuStaticCast<NetAdapter>(pAdapter);
if (pAdapterEx->device == AuString(pIPAddrTable->AdapterName))
{
pAdapterEx->dhcp = pIPAddrTable->DhcpEnabled;
pAdapterEx->gateway = IPAddress(pIPAddrTable->GatewayList.IpAddress.String);
}
}
pIPAddrTable = pIPAddrTable->Next;
}
}
static void PrecacheAdapters()
{
if (AuSwInfo::IsWindowsXPAny())
{
auto pAdaptersV4 = GetAddressesForFamilyXP(AF_INET);
auto pAdaptersV6 = GetAddressesForFamilyXP(AF_INET6);
AddressesToList(pAdaptersV4, gIpv4Adapters);
AddressesToList(pAdaptersV6, gIpv6Adapters);
FixupXPGatewaysIPv4();
}
else
{
auto pAdaptersV4 = GetAddressesForFamilyLH(AF_INET);
auto pAdaptersV6 = GetAddressesForFamilyLH(AF_INET6);
AddressesToList(pAdaptersV4, gIpv4Adapters);
AddressesToList(pAdaptersV6, gIpv6Adapters);
}
}
#endif
AuList<AuSPtr<INetAdapter>> NetAdapter::GetIPv4s()
{
#if defined(AURORA_PLATFORM_WIN32)
PrecacheAdapters();
return gIpv4Adapters;
#else
return {};
#endif
}
AuList<AuSPtr<INetAdapter>> NetAdapter::GetIPv6s()
{
#if defined(AURORA_PLATFORM_WIN32)
return gIpv6Adapters;
#else
return {};
#endif
}
}