#ifdef _WIN32 #define WIN32_LEAN_AND_MEAN #include #include #else #include #include #include #endif #include "prx/libc/include/general/VabiMacros.hpp" #include #include #include #include #include #include #include #include #include "GuestResolver.hpp" extern "C" int* APS5_VABI sceNetErrnoLoc(); namespace { struct HostLookup { GuestResolver::HostEntry entry{}; std::string name; std::vector aliasNames; std::vector aliases; std::vector addressBytes; std::vector addresses; }; bool NumericAddress(const char* name, std::array& address) { std::array 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(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(value); for (std::size_t i = 0; i < count; ++i) result |= static_cast(parts[i]) << (24 - 8 * i); for (std::size_t i = 0; i < address.size(); ++i) address[i] = static_cast(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 numericAddress{}; char* numericAliases[]{nullptr}; char* numericAddresses[]{reinterpret_cast(numericAddress.data()), nullptr}; hostent nativeEntry{const_cast(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 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 static thread_local HostLookup lookup; 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; } } 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(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(std::calloc(1, converted->addressLength)); converted->address = bytes; if (!bytes) { failure = 6; break; } bytes[0] = static_cast(converted->addressLength); bytes[1] = static_cast(converted->family); if (item->ai_family == AF_INET) { const auto* v4 = reinterpret_cast(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(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(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(address); sockaddr_storage native{}; socklen_t nativeLength; if (bytes[1] == 2) { if (length < 16 || bytes[0] != 16) return 5; auto& v4 = reinterpret_cast(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(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(&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 flags", "Name resolution failed", "Address family not supported", "Memory allocation failed", "No address for hostname", "Name or service not known", "Service not supported for socket type", "Socket type not supported", "System error", "Invalid hints", "Unknown protocol", "Buffer too small" }; if (error < 0 || error >= static_cast(sizeof(messages) / sizeof(messages[0]))) return "Unknown address resolution error"; return messages[error]; } }