#ifdef _WIN32 #define WIN32_LEAN_AND_MEAN #include #include #include #else #include #include #include #include #endif #include "prx/libc/include/general/VabiMacros.hpp" #include #include #include #include #include #include #include #include #include extern "C" int* APS5_VABI __error_nid_postfix(); struct GuestInterfaceAddress { GuestInterfaceAddress* next; char* name; std::uint32_t flags; void* address; void* netmask; void* destination; void* data; }; static_assert(sizeof(GuestInterfaceAddress) == 56); static_assert(offsetof(GuestInterfaceAddress, flags) == 16); static_assert(offsetof(GuestInterfaceAddress, address) == 24); static_assert(offsetof(GuestInterfaceAddress, destination) == 40); static_assert(offsetof(GuestInterfaceAddress, data) == 48); namespace { constexpr std::uint32_t guestUp = 0x1; constexpr std::uint32_t guestBroadcast = 0x2; constexpr std::uint32_t guestLoopback = 0x8; constexpr std::uint32_t guestPointToPoint = 0x10; constexpr std::uint32_t guestRunning = 0x40; constexpr std::uint32_t guestMulticast = 0x8000; constexpr std::size_t guestNameCapacity = 16; struct Entry { std::string name; std::uint32_t flags; std::vector address; std::vector netmask; std::vector destination; }; std::vector GuestIpv4(const std::uint8_t* bytes) { std::vector guest(16, 0); guest[0] = 16; guest[1] = 2; std::memcpy(guest.data() + 4, bytes, 4); return guest; } std::vector GuestIpv6(const std::uint8_t* bytes, std::uint32_t scope) { std::vector guest(28, 0); guest[0] = 28; guest[1] = 28; std::memcpy(guest.data() + 8, bytes, 16); std::memcpy(guest.data() + 24, &scope, 4); return guest; } std::vector GuestAddress(const sockaddr* address) { if (address->sa_family == AF_INET) return GuestIpv4(reinterpret_cast(&reinterpret_cast(address)->sin_addr)); const auto* v6 = reinterpret_cast(address); return GuestIpv6(reinterpret_cast(&v6->sin6_addr), v6->sin6_scope_id); } std::string CheckedName(const char* name) { const std::string checked(name); if (checked.empty() || checked.size() >= guestNameCapacity) throw std::runtime_error("getifaddrs: host interface name does not fit the guest's 16-byte limit: " + checked); return checked; } std::size_t Aligned(std::size_t size) { return (size + 7) & ~static_cast(7); } std::vector BroadcastAddress(const std::vector& address, const std::vector& netmask) { std::vector broadcast = address; for (std::size_t index = 4; index < 8; ++index) broadcast[index] = static_cast(address[index] | static_cast(~netmask[index])); return broadcast; } #ifdef _WIN32 std::vector PrefixMask(int family, unsigned prefix) { const unsigned bits = family == AF_INET ? 32 : 128; if (prefix > bits) prefix = bits; std::uint8_t mask[16] = {}; for (unsigned bit = 0; bit < prefix; ++bit) mask[bit / 8] |= static_cast(0x80 >> (bit % 8)); return family == AF_INET ? GuestIpv4(mask) : GuestIpv6(mask, 0); } std::uint32_t InterfaceFlags(const IP_ADAPTER_ADDRESSES* adapter) { std::uint32_t flags = 0; if (adapter->OperStatus == IfOperStatusUp) flags |= guestUp | guestRunning; if (adapter->IfType == IF_TYPE_SOFTWARE_LOOPBACK) flags |= guestLoopback; else if (adapter->IfType != IF_TYPE_PPP && adapter->IfType != IF_TYPE_TUNNEL) flags |= guestBroadcast; if (!(adapter->Flags & IP_ADAPTER_NO_MULTICAST)) flags |= guestMulticast; return flags; } std::string InterfaceName(const IP_ADAPTER_ADDRESSES* adapter) { char name[NDIS_IF_MAX_STRING_SIZE + 1] = {}; const auto status = ConvertInterfaceLuidToNameA(&adapter->Luid, name, sizeof(name)); if (status != NO_ERROR) throw std::runtime_error("getifaddrs: ConvertInterfaceLuidToNameA failed with " + std::to_string(status)); if (std::strlen(name) < guestNameCapacity) return CheckedName(name); return "if" + std::to_string(adapter->IfIndex ? adapter->IfIndex : adapter->Ipv6IfIndex); } int HostEntries(std::vector& entries) { const ULONG options = GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_DNS_SERVER; ULONG size = 0; std::vector storage; ULONG result = GetAdaptersAddresses(AF_UNSPEC, options, nullptr, nullptr, &size); for (unsigned calls = 1; result == ERROR_BUFFER_OVERFLOW && calls < 4; ++calls) { storage.resize(size); result = GetAdaptersAddresses(AF_UNSPEC, options, nullptr, reinterpret_cast(storage.data()), &size); } if (result == ERROR_NO_DATA) return 0; if (result == ERROR_BUFFER_OVERFLOW) { *__error_nid_postfix() = 55; return -1; } if (result == ERROR_NOT_ENOUGH_MEMORY) { *__error_nid_postfix() = 12; return -1; } if (result != NO_ERROR) throw std::runtime_error("getifaddrs: GetAdaptersAddresses failed with " + std::to_string(result)); for (auto* adapter = reinterpret_cast(storage.data()); adapter; adapter = adapter->Next) { std::string name; for (auto* unicast = adapter->FirstUnicastAddress; unicast; unicast = unicast->Next) { const sockaddr* address = unicast->Address.lpSockaddr; if (!address || (address->sa_family != AF_INET && address->sa_family != AF_INET6)) continue; if (name.empty()) name = InterfaceName(adapter); Entry entry{name, InterfaceFlags(adapter), GuestAddress(address), PrefixMask(address->sa_family, unicast->OnLinkPrefixLength), {}}; if (address->sa_family == AF_INET && (entry.flags & guestBroadcast)) entry.destination = BroadcastAddress(entry.address, entry.netmask); entries.push_back(std::move(entry)); } } return 0; } #else std::uint32_t InterfaceFlags(unsigned int host) { constexpr std::pair flags[] = { {IFF_UP, guestUp}, {IFF_BROADCAST, guestBroadcast}, {IFF_LOOPBACK, guestLoopback}, {IFF_POINTOPOINT, guestPointToPoint}, {IFF_RUNNING, guestRunning}, {IFF_NOARP, 0x80}, {IFF_PROMISC, 0x100}, {IFF_ALLMULTI, 0x200}, {IFF_MULTICAST, guestMulticast}, }; std::uint32_t guest = 0; for (const auto& [hostFlag, guestFlag] : flags) if (host & hostFlag) guest |= guestFlag; return guest; } int HostEntries(std::vector& entries) { ifaddrs* host = nullptr; if (::getifaddrs(&host) != 0) { const int hostError = errno; switch (hostError) { case ENOMEM: *__error_nid_postfix() = 12; return -1; case ENOBUFS: *__error_nid_postfix() = 55; return -1; case EMFILE: *__error_nid_postfix() = 24; return -1; case ENFILE: *__error_nid_postfix() = 23; return -1; default: throw std::runtime_error(std::string("getifaddrs: host enumeration failed: ") + std::strerror(hostError)); } } try { for (auto* item = host; item; item = item->ifa_next) { if (!item->ifa_addr || (item->ifa_addr->sa_family != AF_INET && item->ifa_addr->sa_family != AF_INET6)) continue; if (!item->ifa_netmask) throw std::runtime_error(std::string("getifaddrs: host address without a netmask on ") + item->ifa_name); Entry entry{CheckedName(item->ifa_name), InterfaceFlags(item->ifa_flags), GuestAddress(item->ifa_addr), GuestAddress(item->ifa_netmask), {}}; const int family = item->ifa_addr->sa_family; if (item->ifa_flags & IFF_POINTOPOINT) { if (item->ifa_dstaddr && item->ifa_dstaddr->sa_family == family) entry.destination = GuestAddress(item->ifa_dstaddr); else entry.flags &= ~guestPointToPoint; } else if (family == AF_INET && (item->ifa_flags & IFF_BROADCAST)) { entry.destination = item->ifa_broadaddr && item->ifa_broadaddr->sa_family == AF_INET ? GuestAddress(item->ifa_broadaddr) : BroadcastAddress(entry.address, entry.netmask); } entries.push_back(std::move(entry)); } } catch (...) { ::freeifaddrs(host); throw; } ::freeifaddrs(host); return 0; } #endif } extern "C" { int APS5_VABI getifaddrs_nid_postfix(GuestInterfaceAddress** list) { if (!list) { *__error_nid_postfix() = 14; return -1; } try { std::vector entries; if (HostEntries(entries) != 0) return -1; if (entries.empty()) { *list = nullptr; return 0; } std::size_t size = entries.size() * sizeof(GuestInterfaceAddress); for (const auto& entry : entries) size += Aligned(entry.name.size() + 1) + Aligned(entry.address.size()) + Aligned(entry.netmask.size()) + Aligned(entry.destination.size()); auto* block = static_cast(std::malloc(size)); if (!block) { *__error_nid_postfix() = 12; return -1; } auto* items = reinterpret_cast(block); std::size_t offset = entries.size() * sizeof(GuestInterfaceAddress); const auto place = [&](const void* source, std::size_t length) { void* target = block + offset; std::memcpy(target, source, length); offset += Aligned(length); return target; }; for (std::size_t index = 0; index < entries.size(); ++index) { const Entry& entry = entries[index]; GuestInterfaceAddress& item = items[index]; item.next = index + 1 < entries.size() ? &items[index + 1] : nullptr; item.name = static_cast(place(entry.name.c_str(), entry.name.size() + 1)); item.flags = entry.flags; item.address = place(entry.address.data(), entry.address.size()); item.netmask = place(entry.netmask.data(), entry.netmask.size()); item.destination = entry.destination.empty() ? nullptr : place(entry.destination.data(), entry.destination.size()); item.data = nullptr; } *list = items; return 0; } catch (const std::bad_alloc&) { *__error_nid_postfix() = 12; return -1; } } void APS5_VABI freeifaddrs_nid_postfix(GuestInterfaceAddress* list) { std::free(list); } }