mirror of
https://github.com/boykopovar/AnyPS5.git
synced 2026-10-22 00:22:57 -05:00
List the host's IPv4 and IPv6 addresses in FreeBSD's struct ifaddrs layout, with FreeBSD sockaddrs and interface flags, from getifaddrs on Linux and GetAdaptersAddresses on Windows. The list is one allocation, so freeifaddrs is a single free.
103 lines
3.8 KiB
C++
103 lines
3.8 KiB
C++
#ifdef _WIN32
|
|
#define WIN32_LEAN_AND_MEAN
|
|
#include <winsock2.h>
|
|
#include <ws2tcpip.h>
|
|
#include <iphlpapi.h>
|
|
#else
|
|
#include <ifaddrs.h>
|
|
#include <netinet/in.h>
|
|
#endif
|
|
#include "prx/libc/include/general/VabiMacros.hpp"
|
|
#include <cstddef>
|
|
#include <cstdint>
|
|
#include <cstdlib>
|
|
#include <cstring>
|
|
#include <stdexcept>
|
|
#include <vector>
|
|
|
|
struct GuestInterfaceAddress {
|
|
GuestInterfaceAddress* next;
|
|
char* name;
|
|
std::uint32_t flags;
|
|
void* address;
|
|
void* netmask;
|
|
void* destination;
|
|
void* data;
|
|
};
|
|
static_assert(sizeof(GuestInterfaceAddress) == 56);
|
|
|
|
extern "C" {
|
|
int APS5_VABI getifaddrs_nid_postfix(GuestInterfaceAddress** list);
|
|
void APS5_VABI freeifaddrs_nid_postfix(GuestInterfaceAddress* list);
|
|
}
|
|
|
|
static void Require(bool value) { if (!value) std::abort(); }
|
|
|
|
static std::size_t HostIpv4Count() {
|
|
std::size_t count = 0;
|
|
#ifdef _WIN32
|
|
const ULONG options = GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_DNS_SERVER;
|
|
ULONG size = 0;
|
|
std::vector<std::uint8_t> storage;
|
|
ULONG result = GetAdaptersAddresses(AF_UNSPEC, options, nullptr, nullptr, &size);
|
|
while (result == ERROR_BUFFER_OVERFLOW) {
|
|
storage.resize(size);
|
|
result = GetAdaptersAddresses(AF_UNSPEC, options, nullptr, reinterpret_cast<IP_ADAPTER_ADDRESSES*>(storage.data()), &size);
|
|
}
|
|
Require(result == NO_ERROR);
|
|
for (auto* adapter = reinterpret_cast<IP_ADAPTER_ADDRESSES*>(storage.data()); adapter; adapter = adapter->Next)
|
|
for (auto* unicast = adapter->FirstUnicastAddress; unicast; unicast = unicast->Next)
|
|
if (unicast->Address.lpSockaddr && unicast->Address.lpSockaddr->sa_family == AF_INET) ++count;
|
|
#else
|
|
ifaddrs* host = nullptr;
|
|
Require(getifaddrs(&host) == 0);
|
|
for (auto* item = host; item; item = item->ifa_next)
|
|
if (item->ifa_addr && item->ifa_addr->sa_family == AF_INET) ++count;
|
|
freeifaddrs(host);
|
|
#endif
|
|
return count;
|
|
}
|
|
|
|
int main() {
|
|
constexpr std::uint32_t knownFlags = 0x1 | 0x2 | 0x8 | 0x10 | 0x40 | 0x80 | 0x100 | 0x200 | 0x8000;
|
|
const std::uint8_t loopbackAddress[] = {127, 0, 0, 1};
|
|
const std::uint8_t loopbackMask[] = {255, 0, 0, 0};
|
|
GuestInterfaceAddress* list = nullptr;
|
|
Require(getifaddrs_nid_postfix(&list) == 0 && list != nullptr);
|
|
std::size_t entries = 0;
|
|
std::size_t ipv4 = 0;
|
|
bool loopback = false;
|
|
for (auto* item = list; item; item = item->next) {
|
|
Require(++entries < 100000);
|
|
Require(item->name != nullptr);
|
|
const std::size_t nameLength = std::strlen(item->name);
|
|
Require(nameLength >= 1 && nameLength <= 15);
|
|
Require((item->flags & ~knownFlags) == 0);
|
|
Require(item->data == nullptr);
|
|
const auto* address = static_cast<const std::uint8_t*>(item->address);
|
|
const auto* netmask = static_cast<const std::uint8_t*>(item->netmask);
|
|
Require(address != nullptr && netmask != nullptr);
|
|
Require((address[0] == 16 && address[1] == 2) || (address[0] == 28 && address[1] == 28));
|
|
Require(netmask[0] == address[0] && netmask[1] == address[1]);
|
|
if (item->destination) {
|
|
const auto* destination = static_cast<const std::uint8_t*>(item->destination);
|
|
Require(destination[0] == address[0] && destination[1] == address[1]);
|
|
}
|
|
if (address[1] != 2) continue;
|
|
++ipv4;
|
|
if (std::memcmp(address + 4, loopbackAddress, 4) == 0)
|
|
loopback = std::memcmp(netmask + 4, loopbackMask, 4) == 0 && (item->flags & 0x9) == 0x9 && !(item->flags & 0x2);
|
|
}
|
|
Require(loopback);
|
|
Require(ipv4 == HostIpv4Count());
|
|
freeifaddrs_nid_postfix(list);
|
|
freeifaddrs_nid_postfix(nullptr);
|
|
bool rejected = false;
|
|
try {
|
|
getifaddrs_nid_postfix(nullptr);
|
|
} catch (const std::invalid_argument&) {
|
|
rejected = true;
|
|
}
|
|
Require(rejected);
|
|
}
|