Merge pull request #5411 from ps54all/feat/libscenet-resolver-ntoa6

feat(libSceNet): implement sceNetResolverStartNtoa6
This commit is contained in:
Dean 2026-10-14 11:12:27 +13:00 committed by GitHub
commit 703aa8f63d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 44 additions and 3 deletions

View File

@ -14,6 +14,7 @@
#include <unistd.h>
#endif
#include <algorithm>
#include <array>
#include <atomic>
#include <climits>
#include <chrono>
@ -1299,12 +1300,12 @@ int APS5_VABI sceNetResolverDestroy(int rid) {
return g_resolvers.erase(rid) != 0 ? 0 : fail(NET_EBADF);
}
int lookup_ipv4(int rid, const char* hostname, const char* func, std::vector<std::uint32_t>& addresses) {
int lookup_host(int rid, const char* hostname, const char* func, int family, std::vector<std::array<std::uint8_t, 16>>& addresses) {
if (!initialize_sockets()) return fail(5);
std::uint64_t generation = 0;
if (const int error = begin_lookup(rid, &Resolver::abort_ntoa, generation)) return error;
addrinfo hints{};
hints.ai_family = AF_INET;
hints.ai_family = family;
addrinfo* results = nullptr;
const int result = ::getaddrinfo(hostname, nullptr, &hints, &results);
if (result != 0) {
@ -1313,13 +1314,31 @@ int lookup_ipv4(int rid, const char* hostname, const char* func, std::vector<std
return end_lookup(rid, generation, NET_ERROR_RESOLVER_ENODNS);
}
for (const addrinfo* entry = results; entry != nullptr; entry = entry->ai_next) {
const auto address = reinterpret_cast<const sockaddr_in*>(entry->ai_addr)->sin_addr.s_addr;
if (entry->ai_family != family) continue;
std::array<std::uint8_t, 16> address{};
if (family == AF_INET6) std::memcpy(address.data(), &reinterpret_cast<const sockaddr_in6*>(entry->ai_addr)->sin6_addr, 16);
else std::memcpy(address.data(), &reinterpret_cast<const sockaddr_in*>(entry->ai_addr)->sin_addr, 4);
if (std::find(addresses.begin(), addresses.end(), address) == addresses.end()) addresses.push_back(address);
}
::freeaddrinfo(results);
if (addresses.empty()) {
set_errno(NET_ENOENT);
return end_lookup(rid, generation, NET_ERROR_RESOLVER_ENODNS);
}
return end_lookup(rid, generation, 0);
}
int lookup_ipv4(int rid, const char* hostname, const char* func, std::vector<std::uint32_t>& addresses) {
std::vector<std::array<std::uint8_t, 16>> found;
if (const int error = lookup_host(rid, hostname, func, AF_INET, found)) return error;
for (const auto& address : found) {
std::uint32_t value = 0;
std::memcpy(&value, address.data(), sizeof(value));
addresses.push_back(value);
}
return 0;
}
int APS5_VABI sceNetResolverStartNtoa(int rid, const char* hostname, void* addr, int timeout, int retry, int flags) {
(void)timeout;
(void)retry;
@ -1331,6 +1350,17 @@ int APS5_VABI sceNetResolverStartNtoa(int rid, const char* hostname, void* addr,
return 0;
}
int APS5_VABI sceNetResolverStartNtoa6(int rid, const char* hostname, void* addr, int timeout, int retry, int flags) {
(void)timeout;
(void)retry;
if (flags != 0) throw std::runtime_error("sceNetResolverStartNtoa6: flags " + std::to_string(flags) + " are not supported");
if (!hostname || !addr) return fail(NET_EINVAL);
std::vector<std::array<std::uint8_t, 16>> addresses;
if (const int error = lookup_host(rid, hostname, __func__, AF_INET6, addresses)) return error;
std::memcpy(addr, addresses.front().data(), addresses.front().size());
return 0;
}
struct NetResolverRecord {
std::uint32_t address;
std::uint8_t address6[12];

View File

@ -52,6 +52,7 @@ extern const std::uint32_t sce_net_in6addr_any[4];
extern const std::uint8_t sce_net_in6addr_linklocal_allnodes[16];
int APS5_VABI sceNetResolverCreate(const char*, int, int);
int APS5_VABI sceNetResolverStartNtoa(int, const char*, void*, int, int, int);
int APS5_VABI sceNetResolverStartNtoa6(int, const char*, void*, int, int, int);
int APS5_VABI sceNetResolverStartNtoaMultipleRecordsEx(int, const char*, void*, int, int, int);
int APS5_VABI sceNetResolverStartNtoaMultipleRecords(int, const char*, void*, int, int, int);
int APS5_VABI sceNetResolverStartAton(int, const void*, char*, int, int, int, int);
@ -837,6 +838,15 @@ int main() {
resolver_error == static_cast<int>(0x804101E1));
Require(sceNetResolverStartNtoa(resolver, "localhost", ipv4.data(), 5000000, 1, 0) == 0);
Require(ipv4[0] == 127);
std::array<std::uint8_t, 16> resolved6{};
resolved6.fill(0xA5);
Require(sceNetResolverStartNtoa6(resolver, "::1", resolved6.data(), 5000000, 1, 0) == 0);
Require(std::all_of(resolved6.begin(), resolved6.end() - 1, [](std::uint8_t byte) { return byte == 0; }) && resolved6[15] == 1);
Require(sceNetResolverGetError(resolver, &resolver_error) == 0 && resolver_error == 0);
Require(sceNetResolverStartNtoa6(resolver, nullptr, resolved6.data(), 5000000, 1, 0) == static_cast<int>(0x80410116));
Require(sceNetResolverStartNtoa6(resolver, "127.0.0.1", resolved6.data(), 5000000, 1, 0) == static_cast<int>(0x804101E1));
Require(sceNetResolverGetError(resolver, &resolver_error) == 0 && resolver_error == static_cast<int>(0x804101E1));
Require(sceNetResolverStartNtoa(resolver, "localhost", ipv4.data(), 5000000, 1, 0) == 0);
Require(sceNetResolverGetError(resolver, &resolver_error) == 0 && resolver_error == 0);
std::array<std::uint8_t, 512> records{};
records.fill(0xA5);

View File

@ -11,3 +11,4 @@
- [sceNetSetsockopt](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) sets `SO_RCVTIMEO` and `SO_SNDTIMEO` as the host socket's timeouts. On Windows a receive or send on a stream socket that times out fails with `ETIMEDOUT`, where FreeBSD returns `EAGAIN` or the partial count: Winsock leaves the connection in an indeterminate state after a timeout and reports no partial count ([SOL_SOCKET options](https://learn.microsoft.com/en-us/windows/win32/winsock/sol-socket-socket-options)), so FreeBSD's behaviour needs the timeout waited for without the host option. A receive on a datagram socket that times out fails with `EAGAIN`
- [sceNetSendmsg and sceNetRecvmsg](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) copy the scatter/gather list through one host buffer and accept only the flags `sceNetSend`/`sceNetRecv` accept. Sending control data throws; a receive never returns control data (`msg_controllen` 0), since no socket option that queues it is supported
- [sceNetResolverAbort and sceNetResolverStartNtoaMultipleRecords](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) follow shadPS4 ([net.cpp](https://github.com/shadps4-emu/shadPS4/blob/811bfcd7d17ed8ae975c477d9ff28abc744e1ca8/src/core/libraries/net/net.cpp), [net_resolver.cpp](https://github.com/shadps4-emu/shadPS4/blob/811bfcd7d17ed8ae975c477d9ff28abc744e1ca8/src/core/libraries/net/net_resolver.cpp)); the `(int rid, int flags)` signature of the abort also matches [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/5dfdb4b0b166e83dc9ae275001afb695f12c7174/src/SharpProspero/Interop/Net/Socket.cs). Unverified on hardware. Lookups run synchronously over the host resolver, which cannot be interrupted: an abort while a lookup runs on another thread returns at once, and that lookup fails with `EINTR` without writing its output only when the host call returns; the abort flags are then ignored, as shadPS4 does for a running lookup. With nothing running, abort flag 1 (NTOA preservation) or 2 (ATON preservation) makes the next `sceNetResolverStartNtoa*` or `sceNetResolverStartAton` call on that resolver fail with `EINTR`, reported by `sceNetResolverGetError`, after its arguments are checked. Unlike shadPS4, which ignores them, other abort flag bits throw, and `sceNetResolverStartNtoaMultipleRecords` throws for any flag, including async (1) and 0x10000 (IP address literals disabled), as `sceNetResolverStartNtoaMultipleRecordsEx` does
- [sceNetResolverStartNtoa6](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) resolves a host name to its first IPv6 address through the host resolver (`getaddrinfo` with `AF_INET6`) and writes the 16-byte address; the `(rid, hostname, addr, timeout, retry, flags)` signature follows `sceNetResolverStartNtoa`. A name with no IPv6 address fails with `RESOLVER_ENODNS` (`0x804101E1`) like a failed lookup; whether the console returns IPv4-mapped addresses instead is not verified. Timeout and retry are ignored as in `sceNetResolverStartNtoa`, and any nonzero flags throw because their meaning for this call is not known. Unverified on hardware.