AnyPS5/core/libs/prx/libScePosixForWebKit/Resolver.cpp

360 lines
15 KiB
C++

#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
#include <winsock2.h>
#include <ws2tcpip.h>
#else
#include <sys/socket.h>
#include <netdb.h>
#include <netinet/in.h>
#endif
#include "prx/libc/include/general/VabiMacros.hpp"
#include "prx/libc/include/HostThreadLocal.hpp"
#include <cstdint>
#include <array>
#include <cstring>
#include <cstdlib>
#include <cerrno>
#include <new>
#include <string>
#include <vector>
#include "GuestResolver.hpp"
extern "C" int* APS5_VABI sceNetErrnoLoc();
namespace {
struct HostLookup {
GuestResolver::HostEntry entry{};
std::string name;
std::vector<std::string> aliasNames;
std::vector<char*> aliases;
std::vector<std::string> addressBytes;
std::vector<char*> addresses;
};
struct ServiceRecord {
const char* name;
std::uint16_t port;
const char* protocol;
std::array<const char*, 2> aliases;
};
constexpr ServiceRecord Services[]{
{"ftp-data", 20, "tcp", {}}, {"ftp-data", 20, "udp", {}}, {"ftp-data", 20, "sctp", {}},
{"ftp", 21, "tcp", {}}, {"ftp", 21, "udp", {}}, {"ftp", 21, "sctp", {}},
{"ssh", 22, "tcp", {}}, {"ssh", 22, "udp", {}}, {"ssh", 22, "sctp", {}},
{"telnet", 23, "tcp", {}}, {"telnet", 23, "udp", {}},
{"smtp", 25, "tcp", {"mail"}}, {"smtp", 25, "udp", {"mail"}},
{"domain", 53, "tcp", {}}, {"domain", 53, "udp", {}},
{"tftp", 69, "tcp", {}}, {"tftp", 69, "udp", {}},
{"http", 80, "tcp", {"www", "www-http"}}, {"http", 80, "udp", {"www", "www-http"}},
{"http", 80, "sctp", {"www", "www-http"}},
{"pop3", 110, "tcp", {}}, {"pop3", 110, "udp", {}},
{"ntp", 123, "tcp", {}}, {"ntp", 123, "udp", {}},
{"imap", 143, "tcp", {"imap2", "imap4"}}, {"imap", 143, "udp", {"imap2", "imap4"}},
{"https", 443, "tcp", {}}, {"https", 443, "udp", {}}, {"https", 443, "sctp", {}},
{"rtsp", 554, "tcp", {}}, {"rtsp", 554, "udp", {}},
{"submission", 587, "tcp", {}}, {"submission", 587, "udp", {}},
{"imaps", 993, "tcp", {}}, {"imaps", 993, "udp", {}},
{"pop3s", 995, "tcp", {"spop3"}}, {"pop3s", 995, "udp", {"spop3"}},
};
struct ServiceLookup {
GuestResolver::ServiceEntry entry{};
std::string name;
std::string protocol;
std::vector<std::string> aliasNames;
std::vector<char*> aliases;
};
bool NumericAddress(const char* name, std::array<unsigned char, 4>& address) {
std::array<unsigned char, 3> parts{};
std::size_t count = 0;
std::uint64_t value;
bool digit;
for (;;) {
if (*name < '0' || *name > '9') return false;
value = 0;
int base = 10;
digit = false;
if (*name == '0') {
++name;
if (*name == 'x' || *name == 'X') { base = 16; ++name; }
else { base = 8; digit = true; }
}
for (;;) {
const int next = *name >= '0' && *name <= '9' ? *name - '0' :
*name >= 'a' && *name <= 'f' ? *name - 'a' + 10 :
*name >= 'A' && *name <= 'F' ? *name - 'A' + 10 : -1;
if (next < 0 || next >= base) break;
value = value * base + next;
digit = true;
++name;
}
if (*name != '.') break;
if (count == parts.size() || value > 255) return false;
parts[count++] = static_cast<unsigned char>(value);
++name;
}
if (!digit) return false;
if (*name != '\0' && *name != ' ' && *name != '\t' && *name != '\n' &&
*name != '\r' && *name != '\f' && *name != '\v') return false;
if (count && value >= (std::uint64_t{1} << (8 * (4 - count)))) return false;
auto result = static_cast<std::uint32_t>(value);
for (std::size_t i = 0; i < count; ++i)
result |= static_cast<std::uint32_t>(parts[i]) << (24 - 8 * i);
for (std::size_t i = 0; i < address.size(); ++i)
address[i] = static_cast<unsigned char>(result >> (24 - 8 * i));
return true;
}
bool Ready() {
#ifdef _WIN32
static const int status = [] { WSADATA data{}; return WSAStartup(MAKEWORD(2, 2), &data); }();
return status == 0;
#else
return true;
#endif
}
int GuestError(int error) {
if (error == 0) return 0;
if (error == EAI_AGAIN) return 2;
if (error == EAI_BADFLAGS) return 3;
if (error == EAI_FAMILY) return 5;
if (error == EAI_MEMORY) return 6;
if (error == EAI_NONAME) return 8;
if (error == EAI_SERVICE) return 9;
if (error == EAI_SOCKTYPE) return 10;
#ifdef _WIN32
if (error == WSAEFAULT) return 14;
#else
if (error == EAI_OVERFLOW) return 14;
#endif
return 4;
}
}
extern "C" {
GuestResolver::HostEntry* APS5_VABI gethostbyname_nid_postfix(const char* name) {
if (!name || !Ready()) { *sceNetErrnoLoc() = 3; return nullptr; }
try {
std::array<unsigned char, 4> numericAddress{};
char* numericAliases[]{nullptr};
char* numericAddresses[]{reinterpret_cast<char*>(numericAddress.data()), nullptr};
hostent nativeEntry{const_cast<char*>(name), numericAliases, AF_INET, 4, numericAddresses};
hostent* native = &nativeEntry;
const bool numeric = NumericAddress(name, numericAddress);
#ifdef _WIN32
if (!numeric) native = ::gethostbyname(name);
if (!native) {
const auto error = WSAGetLastError();
*sceNetErrnoLoc() = error == WSAHOST_NOT_FOUND ? 1 : error == WSATRY_AGAIN ? 2 : error == WSANO_DATA ? 4 : 3;
return nullptr;
}
#else
std::vector<char> buffer;
int hostError = 0;
int error = 0;
if (!numeric) {
buffer.resize(1024);
while ((error = ::gethostbyname_r(name, &nativeEntry, buffer.data(), buffer.size(), &native, &hostError)) == ERANGE)
buffer.resize(buffer.size() * 2);
}
if (error != 0 || !native) {
*sceNetErrnoLoc() = hostError != 0 ? hostError : 3;
if (hostError == -1) errno = error == EAGAIN ? 35 : error;
return nullptr;
}
#endif
struct LookupTag {};
auto& lookup = HostThreadLocal<HostLookup, LookupTag>();
lookup.name = native->h_name;
lookup.aliasNames.clear();
lookup.aliases.clear();
lookup.addressBytes.clear();
lookup.addresses.clear();
for (auto** alias = native->h_aliases; *alias; ++alias) lookup.aliasNames.emplace_back(*alias);
for (auto& alias : lookup.aliasNames) lookup.aliases.push_back(alias.data());
lookup.aliases.push_back(nullptr);
for (auto** address = native->h_addr_list; *address; ++address)
lookup.addressBytes.emplace_back(*address, native->h_length);
for (auto& address : lookup.addressBytes) lookup.addresses.push_back(address.data());
lookup.addresses.push_back(nullptr);
lookup.entry = {lookup.name.data(), lookup.aliases.data(), native->h_addrtype == AF_INET6 ? 28 : 2,
native->h_length, lookup.addresses.data()};
if (numeric) *sceNetErrnoLoc() = 0;
return &lookup.entry;
} catch (const std::bad_alloc&) {
*sceNetErrnoLoc() = -1;
errno = ENOMEM;
return nullptr;
}
}
GuestResolver::ServiceEntry* APS5_VABI getservbyname_nid_postfix(const char* name, const char* protocol) {
for (const auto& service : Services) {
bool named = std::strcmp(name, service.name) == 0;
for (const char* alias : service.aliases)
named = named || (alias && std::strcmp(name, alias) == 0);
if (!named || (protocol && std::strcmp(protocol, service.protocol) != 0)) continue;
try {
static thread_local ServiceLookup lookup;
lookup.name = service.name;
lookup.protocol = service.protocol;
lookup.aliasNames.clear();
lookup.aliases.clear();
for (const char* alias : service.aliases)
if (alias) lookup.aliasNames.emplace_back(alias);
for (auto& alias : lookup.aliasNames) lookup.aliases.push_back(alias.data());
lookup.aliases.push_back(nullptr);
lookup.entry = {lookup.name.data(), lookup.aliases.data(), htons(service.port), lookup.protocol.data()};
return &lookup.entry;
} catch (const std::bad_alloc&) {
errno = ENOMEM;
return nullptr;
}
}
return nullptr;
}
void APS5_VABI freeaddrinfo_nid_postfix(GuestResolver::AddressInfo* first) {
while (first) {
auto* next = first->next;
std::free(first->canonicalName);
std::free(first->address);
std::free(first);
first = next;
}
}
int APS5_VABI getaddrinfo_nid_postfix(const char* node, const char* service,
const GuestResolver::AddressInfo* hints, GuestResolver::AddressInfo** output) {
if (!output) return 4;
*output = nullptr;
addrinfo nativeHints{};
if (hints) {
if (hints->addressLength || hints->address || hints->canonicalName || hints->next) return 12;
if ((hints->flags & ~0xd0f) != 0) return 3;
if (hints->family != 0 && hints->family != 2 && hints->family != 28) return 5;
if (hints->socketType != 0 && hints->socketType != 1 && hints->socketType != 2) return 10;
nativeHints.ai_family = hints->family == 28 ? AF_INET6 : hints->family == 2 ? AF_INET : AF_UNSPEC;
nativeHints.ai_socktype = hints->socketType == 1 ? SOCK_STREAM : hints->socketType == 2 ? SOCK_DGRAM : 0;
nativeHints.ai_protocol = hints->protocol;
if (hints->flags & 1) nativeHints.ai_flags |= AI_PASSIVE;
if (hints->flags & 2) nativeHints.ai_flags |= AI_CANONNAME;
if (hints->flags & 4) nativeHints.ai_flags |= AI_NUMERICHOST;
if (hints->flags & 8) nativeHints.ai_flags |= AI_NUMERICSERV;
if (hints->flags & 0x100) nativeHints.ai_flags |= AI_ALL;
if (hints->flags & 0x400) nativeHints.ai_flags |= AI_ADDRCONFIG;
if (hints->flags & 0x800) nativeHints.ai_flags |= AI_V4MAPPED;
}
if (!Ready()) return 4;
addrinfo* native = nullptr;
const auto error = ::getaddrinfo(node, service, &nativeHints, &native);
if (error) return GuestError(error);
GuestResolver::AddressInfo* first = nullptr;
auto** tail = &first;
int failure = 0;
for (auto* item = native; item; item = item->ai_next) {
if (item->ai_family != AF_INET && item->ai_family != AF_INET6) { failure = 5; break; }
auto* converted = static_cast<GuestResolver::AddressInfo*>(std::calloc(1, sizeof(GuestResolver::AddressInfo)));
if (!converted) { failure = 6; break; }
*tail = converted;
tail = &converted->next;
converted->flags = hints ? hints->flags : 0;
converted->family = item->ai_family == AF_INET ? 2 : 28;
converted->socketType = item->ai_socktype == SOCK_STREAM ? 1 : item->ai_socktype == SOCK_DGRAM ? 2 : 0;
converted->protocol = item->ai_protocol;
converted->addressLength = item->ai_family == AF_INET ? 16 : 28;
auto* bytes = static_cast<unsigned char*>(std::calloc(1, converted->addressLength));
converted->address = bytes;
if (!bytes) { failure = 6; break; }
bytes[0] = static_cast<unsigned char>(converted->addressLength);
bytes[1] = static_cast<unsigned char>(converted->family);
if (item->ai_family == AF_INET) {
const auto* v4 = reinterpret_cast<const sockaddr_in*>(item->ai_addr);
std::memcpy(bytes + 2, &v4->sin_port, 2);
std::memcpy(bytes + 4, &v4->sin_addr, 4);
} else {
const auto* v6 = reinterpret_cast<const sockaddr_in6*>(item->ai_addr);
std::memcpy(bytes + 2, &v6->sin6_port, 2);
std::memcpy(bytes + 4, &v6->sin6_flowinfo, 4);
std::memcpy(bytes + 8, &v6->sin6_addr, 16);
std::memcpy(bytes + 24, &v6->sin6_scope_id, 4);
}
if (item->ai_canonname) {
const auto size = std::strlen(item->ai_canonname) + 1;
converted->canonicalName = static_cast<char*>(std::malloc(size));
if (!converted->canonicalName) { failure = 6; break; }
std::memcpy(converted->canonicalName, item->ai_canonname, size);
}
}
::freeaddrinfo(native);
if (failure) { freeaddrinfo_nid_postfix(first); return failure; }
*output = first;
return 0;
}
int APS5_VABI getnameinfo_nid_postfix(const void* address, std::uint32_t length,
char* host, std::uint32_t hostLength, char* service, std::uint32_t serviceLength, int flags) {
// PS5 sockaddr begins with byte-sized length and family. The host's does not.
if (!address || length < 2) return 5;
if ((!host && hostLength) || (!service && serviceLength)) return 4;
if ((flags & ~0x1f) != 0) return 3;
const auto* bytes = static_cast<const unsigned char*>(address);
sockaddr_storage native{};
socklen_t nativeLength;
if (bytes[1] == 2) {
if (length < 16 || bytes[0] != 16) return 5;
auto& v4 = reinterpret_cast<sockaddr_in&>(native);
v4.sin_family = AF_INET;
std::memcpy(&v4.sin_port, bytes + 2, 2);
std::memcpy(&v4.sin_addr, bytes + 4, 4);
nativeLength = sizeof(v4);
} else if (bytes[1] == 28) {
if (length < 28 || bytes[0] != 28) return 5;
auto& v6 = reinterpret_cast<sockaddr_in6&>(native);
v6.sin6_family = AF_INET6;
std::memcpy(&v6.sin6_port, bytes + 2, 2);
std::memcpy(&v6.sin6_flowinfo, bytes + 4, 4);
std::memcpy(&v6.sin6_addr, bytes + 8, 16);
std::memcpy(&v6.sin6_scope_id, bytes + 24, 4);
nativeLength = sizeof(v6);
} else return 5;
int nativeFlags = 0;
if (flags & 1) nativeFlags |= NI_NOFQDN;
if (flags & 2) nativeFlags |= NI_NUMERICHOST;
if (flags & 4) nativeFlags |= NI_NAMEREQD;
if (flags & 8) nativeFlags |= NI_NUMERICSERV;
if (flags & 16) nativeFlags |= NI_DGRAM;
if (!Ready()) return 4;
// Stage results so host-specific short-buffer errors do not leak into the
// guest ABI, and neither guest output is partially modified on overflow.
char resolvedHost[NI_MAXHOST]{};
char resolvedService[NI_MAXSERV]{};
const bool wantHost = host && hostLength;
const bool wantService = service && serviceLength;
const int error = ::getnameinfo(reinterpret_cast<const sockaddr*>(&native), nativeLength,
wantHost ? resolvedHost : nullptr, wantHost ? sizeof(resolvedHost) : 0,
wantService ? resolvedService : nullptr, wantService ? sizeof(resolvedService) : 0, nativeFlags);
if (error) return GuestError(error);
if ((wantHost && std::strlen(resolvedHost) >= hostLength) ||
(wantService && std::strlen(resolvedService) >= serviceLength)) return 14;
if (wantHost) std::memcpy(host, resolvedHost, std::strlen(resolvedHost) + 1);
if (wantService) std::memcpy(service, resolvedService, std::strlen(resolvedService) + 1);
return 0;
}
const char* APS5_VABI gai_strerror_nid_postfix(int error) {
static const char* messages[] = {
"Success", "Address family for hostname not supported", "Temporary failure in name resolution",
"Invalid value for ai_flags", "Non-recoverable failure in name resolution", "ai_family not supported",
"Memory allocation failure", "No address associated with hostname", "hostname nor servname provided, or not known",
"servname not supported for ai_socktype", "ai_socktype not supported", "System error returned in errno",
"Invalid value for hints", "Resolved protocol is unknown", "Argument buffer overflow"
};
if (error < 0 || error >= static_cast<int>(sizeof(messages) / sizeof(messages[0])))
return "Unknown error";
return messages[error];
}
}