Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions core/libs/prx/libSceNet/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,16 @@ if(BUILD_TESTING)
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libSceNet>")
endif()
endforeach()
add_test_executable(guest_net_dns_info_tests ${LIBS_INCLUDE_DIR}/tests/GuestNetDnsInfo.cpp)
target_include_directories(guest_net_dns_info_tests PRIVATE ${LIBS_INCLUDE_DIR})
target_link_libraries(guest_net_dns_info_tests PRIVATE libSceNet libkernel libc)
configure_windows_unwind(guest_net_dns_info_tests)
add_test(NAME guest_net_dns_info COMMAND guest_net_dns_info_tests)
set_tests_properties(guest_net_dns_info PROPERTIES TIMEOUT 20)
if(WIN32)
set_tests_properties(guest_net_dns_info PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libSceNet>")
endif()
add_test_executable(guest_refused_connect_tests ${LIBS_INCLUDE_DIR}/tests/GuestRefusedConnect.cpp)
target_include_directories(guest_refused_connect_tests PRIVATE ${LIBS_INCLUDE_DIR})
target_link_libraries(guest_refused_connect_tests PRIVATE libSceNet libkernel libc)
Expand Down
45 changes: 39 additions & 6 deletions core/libs/prx/libSceNet/Export.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -356,6 +356,15 @@ int* errno_slot() {

extern "C" std::size_t NetCtlHostDnsServers_nid_no_patch(std::uint32_t* servers, std::size_t capacity);

std::mutex g_dnsLock;
bool g_dnsOverridden = false;
NetDnsInfo g_dnsOverride{};
NetDns6Info g_dns6Override{};

bool IsZeroAddress(const NetIpv6Address& address) {
return std::all_of(std::begin(address.bytes), std::end(address.bytes), [](std::uint8_t byte) { return byte == 0; });
}

void set_errno(int err) {
*errno_slot() = err;
*__error_nid_postfix() = err;
Expand Down Expand Up @@ -2262,7 +2271,14 @@ int APS5_VABI sceNetGetInterfaceStats(NetInterfaceStats* stats, std::uint32_t fl
}

int APS5_VABI sceNetSetDnsInfo(const NetDnsInfo* info, std::uint32_t flags) {
NotImplemented_nid_no_patch(__func__);
if (!g_net_inited) return fail(NET_ENOTINIT);
if (flags != 0) return fail(NET_EINVAL);
NetDnsInfo servers{};
if (info && !GuestSocketReadGuest_nid_no_patch(&servers, info, sizeof(servers))) return fail(NET_EFAULT);
if (info && servers.primaryAddress == 0) return fail(NET_EINVAL);
std::lock_guard lock(g_dnsLock);
g_dnsOverridden = info != nullptr;
g_dnsOverride = servers;
return 0;
}

Expand Down Expand Up @@ -2311,7 +2327,13 @@ int APS5_VABI sceNetResolverConnectDestroy(NetResolverConnection* connection) {
}

int APS5_VABI sceNetSetDns6Info(const NetDns6Info* info, std::uint32_t flags) {
NotImplemented_nid_no_patch(__func__);
if (!g_net_inited) return fail(NET_ENOTINIT);
if (flags != 0) return fail(NET_EINVAL);
NetDns6Info servers{};
if (info && !GuestSocketReadGuest_nid_no_patch(&servers, info, sizeof(servers))) return fail(NET_EFAULT);
if (info && IsZeroAddress(servers.primaryAddress)) return fail(NET_EINVAL);
std::lock_guard lock(g_dnsLock);
g_dns6Override = servers;
return 0;
}

Expand Down Expand Up @@ -2380,8 +2402,15 @@ int APS5_VABI sceNetGetAppMemInfo(NetAppMemoryInfo* info, std::uint32_t flags) {
}

int APS5_VABI sceNetGetDnsInfo(NetDnsInfo* info, std::uint32_t flags) {
if (flags != 0) throw std::runtime_error("sceNetGetDnsInfo: flags " + std::to_string(flags) + " are not supported");
if (!info) throw std::runtime_error("sceNetGetDnsInfo: a null info is not supported");
if (!g_net_inited) return fail(NET_ENOTINIT);
if (flags != 0 || !info) return fail(NET_EINVAL);
{
std::lock_guard lock(g_dnsLock);
if (g_dnsOverridden) {
*info = g_dnsOverride;
return info->secondaryAddress == 0 ? 1 : 2;
}
}
std::array<std::uint32_t, 2> servers{};
const std::size_t count = std::min(NetCtlHostDnsServers_nid_no_patch(servers.data(), servers.size()), servers.size());
std::memcpy(info, servers.data(), sizeof(servers));
Expand Down Expand Up @@ -2637,8 +2666,12 @@ int APS5_VABI sceNetInetPtonWithScopeId(std::int32_t family, const char* text, v
}

int APS5_VABI sceNetGetDns6Info(NetDns6Info* info, std::uint32_t flags) {
NotImplemented_nid_no_patch(__func__);
return 0;
if (!g_net_inited) return fail(NET_ENOTINIT);
if (flags != 0 || !info) return fail(NET_EINVAL);
std::lock_guard lock(g_dnsLock);
*info = g_dns6Override;
if (IsZeroAddress(info->primaryAddress)) return 0;
return IsZeroAddress(info->secondaryAddress) ? 1 : 2;
}

int APS5_VABI sceNetSetDns6InfoToKernel(const NetDns6Info* info, std::uint32_t flags) {
Expand Down
1 change: 1 addition & 0 deletions core/libs/prx/libkernel/Socket/include/SocketRuntime.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,4 @@ std::int64_t Writev(int descriptor, const Iovec* iov, int iovcnt);
}

extern "C" bool GuestSocketIsOpen_nid_no_patch(int descriptor);
extern "C" bool GuestSocketReadGuest_nid_no_patch(void* destination, const void* source, std::size_t bytes);
4 changes: 4 additions & 0 deletions core/libs/prx/libkernel/Socket/src/Udp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1899,6 +1899,10 @@ int APS5_VABI poll_nid_postfix(GuestPollDescriptor* descriptors, std::uint32_t c
}
}

extern "C" bool GuestSocketReadGuest_nid_no_patch(void* destination, const void* source, std::size_t bytes) {
return ::ReadGuest(destination, source, bytes);
}

std::int64_t GuestSockets::Read(int descriptor, void* buffer, std::size_t length) {
if (length == 0) return IsOpen(descriptor) ? 0 : Fail(9);
return recv_nid_postfix(descriptor, buffer, std::min<std::size_t>(length, INT_MAX), 0);
Expand Down
139 changes: 139 additions & 0 deletions core/libs/tests/GuestNetDnsInfo.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
#include "prx/libSceNet/include/Net.hpp"
#include <cstdint>
#include <cstdlib>
#include <cstring>
#include <initializer_list>
#ifdef _WIN32
#include <windows.h>
#else
#include <sys/mman.h>
#endif

namespace {

void Require(bool value) { if (!value) std::abort(); }

constexpr int kErrorBase = static_cast<int>(0x80410100);
constexpr int kEfault = 14;
constexpr int kEinval = 22;
constexpr int kEnotinit = 200;

bool Failed(int result, int error) { return result == (kErrorBase | error) && *sceNetErrnoLoc() == error; }

void CheckUninitialized() {
NetDnsInfo info{0x08080808u, 0x01010101u};
const NetDnsInfo before = info;
NetDns6Info info6{};
std::memset(&info6, 0x5a, sizeof(info6));
const NetDns6Info before6 = info6;
for (const std::uint32_t flags : {0u, 1u, 0xffffffffu}) {
Require(Failed(sceNetGetDnsInfo(&info, flags), kEnotinit));
Require(Failed(sceNetSetDnsInfo(&info, flags), kEnotinit));
Require(Failed(sceNetGetDns6Info(&info6, flags), kEnotinit));
Require(Failed(sceNetSetDns6Info(&info6, flags), kEnotinit));
Require(Failed(sceNetGetDnsInfo(nullptr, flags), kEnotinit));
Require(Failed(sceNetSetDnsInfo(nullptr, flags), kEnotinit));
Require(Failed(sceNetGetDns6Info(nullptr, flags), kEnotinit));
Require(Failed(sceNetSetDns6Info(nullptr, flags), kEnotinit));
Require(std::memcmp(&info, &before, sizeof(info)) == 0);
Require(std::memcmp(&info6, &before6, sizeof(info6)) == 0);
}
}

void CheckDns() {
NetDnsInfo info{0x08080808u, 0};
Require(Failed(sceNetSetDnsInfo(&info, 1), kEinval));
NetDnsInfo empty{0, 0x01010101u};
Require(Failed(sceNetSetDnsInfo(&empty, 0), kEinval));

Require(sceNetSetDnsInfo(&info, 0) == 0);
NetDnsInfo read{1, 1};
Require(sceNetGetDnsInfo(&read, 0) == 1 && read.primaryAddress == 0x08080808u && read.secondaryAddress == 0);
info.secondaryAddress = 0x04040808u;
Require(sceNetSetDnsInfo(&info, 0) == 0);
Require(sceNetGetDnsInfo(&read, 0) == 2 && read.primaryAddress == 0x08080808u && read.secondaryAddress == 0x04040808u);
Require(sceNetSetDnsInfo(nullptr, 0) == 0);
Require(sceNetGetDnsInfo(&read, 0) >= 0);

NetDns6Info info6{};
Require(Failed(sceNetGetDnsInfo(nullptr, 0), kEinval));
Require(Failed(sceNetGetDns6Info(nullptr, 0), kEinval));
for (const std::uint32_t flags : {1u, 2u, 0xffffffffu}) {
NetDnsInfo unchanged{0x5a5a5a5au, 0x5a5a5a5au};
const NetDnsInfo before = unchanged;
std::memset(&info6, 0x5a, sizeof(info6));
const NetDns6Info before6 = info6;
Require(Failed(sceNetGetDnsInfo(&unchanged, flags), kEinval));
Require(Failed(sceNetGetDns6Info(&info6, flags), kEinval));
Require(Failed(sceNetGetDnsInfo(nullptr, flags), kEinval));
Require(Failed(sceNetGetDns6Info(nullptr, flags), kEinval));
Require(Failed(sceNetSetDnsInfo(nullptr, flags), kEinval));
Require(Failed(sceNetSetDns6Info(nullptr, flags), kEinval));
Require(std::memcmp(&unchanged, &before, sizeof(before)) == 0);
Require(std::memcmp(&info6, &before6, sizeof(before6)) == 0);
}
std::memset(&info6, 0xff, sizeof(info6));
Require(sceNetGetDns6Info(&info6, 0) == 0);
for (const auto byte : info6.primaryAddress.bytes) Require(byte == 0);
for (const auto byte : info6.secondaryAddress.bytes) Require(byte == 0);

NetDns6Info set6{};
Require(Failed(sceNetSetDns6Info(&set6, 0), kEinval));
set6.primaryAddress.bytes[0] = 0x20;
set6.primaryAddress.bytes[15] = 0x01;
Require(Failed(sceNetSetDns6Info(&set6, 2), kEinval));
Require(sceNetSetDns6Info(&set6, 0) == 0);
Require(sceNetGetDns6Info(&info6, 0) == 1 && std::memcmp(&info6, &set6, sizeof(set6)) == 0);
set6.secondaryAddress.bytes[0] = 0x26;
Require(sceNetSetDns6Info(&set6, 0) == 0);
Require(sceNetGetDns6Info(&info6, 0) == 2 && std::memcmp(&info6, &set6, sizeof(set6)) == 0);
Require(sceNetSetDns6Info(nullptr, 0) == 0);
Require(sceNetGetDns6Info(&info6, 0) == 0);
}

}

void* UnreadablePage() {
#ifdef _WIN32
return VirtualAlloc(nullptr, 4096, MEM_RESERVE | MEM_COMMIT, PAGE_NOACCESS);
#else
void* page = mmap(nullptr, 4096, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
return page == MAP_FAILED ? nullptr : page;
#endif
}

void CheckUnreadableInfo() {
void* page = UnreadablePage();
Require(page != nullptr);
NetDnsInfo info{0x08080808u, 0};
Require(sceNetSetDnsInfo(&info, 0) == 0);
Require(Failed(sceNetSetDnsInfo(static_cast<const NetDnsInfo*>(page), 1), kEinval));
Require(Failed(sceNetSetDnsInfo(static_cast<const NetDnsInfo*>(page), 0), kEfault));
NetDnsInfo read{};
Require(sceNetGetDnsInfo(&read, 0) == 1 && read.primaryAddress == 0x08080808u);
Require(sceNetSetDnsInfo(nullptr, 0) == 0);

NetDns6Info info6{};
info6.primaryAddress.bytes[0] = 0x20;
Require(sceNetSetDns6Info(&info6, 0) == 0);
Require(Failed(sceNetSetDns6Info(static_cast<const NetDns6Info*>(page), 1), kEinval));
Require(Failed(sceNetSetDns6Info(static_cast<const NetDns6Info*>(page), 0), kEfault));
NetDns6Info read6{};
Require(sceNetGetDns6Info(&read6, 0) == 1 && std::memcmp(&read6, &info6, sizeof(info6)) == 0);
Require(sceNetSetDns6Info(nullptr, 0) == 0);
#ifdef _WIN32
Require(VirtualFree(page, 0, MEM_RELEASE) != 0);
#else
Require(munmap(page, 4096) == 0);
#endif
}

int main() {
CheckUninitialized();
Require(sceNetInit_nid_postfix() == 0);
CheckDns();
CheckUnreadableInfo();
Require(sceNetTerm() == 0);
CheckUninitialized();
return 0;
}
10 changes: 4 additions & 6 deletions core/libs/tests/GuestSceNet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2329,13 +2329,11 @@ int main() {
Require(std::strcmp(text, reinterpret_cast<const char*>(info.opaque)) == 0);
}
for (const int flags : {0x1, -1}) {
bool threw = false;
try { sceNetGetDnsInfo(reinterpret_cast<NetDnsInfo*>(dns.data()), flags); } catch (const std::runtime_error&) { threw = true; }
Require(threw);
const auto before = dns;
Require(Failed(sceNetGetDnsInfo(reinterpret_cast<NetDnsInfo*>(dns.data()), flags), 22));
Require(dns == before);
}
bool threw = false;
try { sceNetGetDnsInfo(nullptr, 0); } catch (const std::runtime_error&) { threw = true; }
Require(threw);
Require(Failed(sceNetGetDnsInfo(nullptr, 0), 22));
}
NetCtlIfStat first{};
first.device = 7;
Expand Down
3 changes: 2 additions & 1 deletion docs/dev/debt/libSceNet.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
- [sceHttp2GetMemoryPoolStats, sceSslGetMemoryPoolStats, sceNetGetMemoryPoolStats](../../../core/libs/prx/libSceNet/Export.cpp) (libSceHttp2, libSceSsl, libSceNet) - the signatures `(int id, stats*)`, the layouts (pool size, peak and current use, and for Http2 and Ssl a 32-bit reserved field) and the error codes were measured on a PS5 (firmware 13.60). The pool size is the one given at init or pool creation. Http2 and Net allocate nothing from these pools. Ssl counts its certificate backend allocations and returned data against its pool; C++ bookkeeping is outside that budget. All three initially report zero use, where the console already reports 0x15C0 bytes for Ssl, 0x1640 for Http2 and 4 for a Net pool. Ssl's allocation sizes and peak usage do not reproduce the console allocator
- [sceNetClearDnsCache](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - the library has no DNS cache to clear because lookups use the host resolver. The host resolver cache is left unchanged.
- [sceNetEtherStrton](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - strings shorter than 17 bytes return EINVAL without changing the output, rather than reading past the terminating NUL as in the [firmware 13.60 probe](https://gist.github.com/orezraey/789290577123af3c45b340d6db6f395c/71e0a6215c7ee86f49fdb31de989dd2b50720b7a). Non-ASCII digit bytes throw because their decoding is unmeasured; other firmware versions remain unverified. Fixed character pairs, ignored separators, alphabetic values beyond f, non-alphanumeric ASCII values of 0x7f, and null-pointer EINVAL follow those measurements, not the strict [shadPS4 parser](https://github.com/shadps4-emu/shadPS4/blob/d615b3887ba29025a59242e1a26f5b68bb176158/src/core/libraries/net/net.cpp#L1274-L1297)
- [sceNetGetDnsInfo](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - the `(info, int flags)` signature, the two IPv4 server slots in network byte order and the server count as the return value follow the PS4 [OpenOrbis musl resolver](https://github.com/OpenOrbis/musl/blob/2b0bb8ce2fa64ac255cb656785729d159a5f074c/src/network/resolvconf.c) and [OpenOrbis Net.h](https://github.com/OpenOrbis/OpenOrbis-PS4-Toolchain/blob/0a1aaf9dd4a92695538bdeb09fb056d06dd11725/include/orbis/Net.h); unverified on PS5 hardware. It reports the host DNS servers of the interface `sceNetCtlGetInfo` reports, never servers set through `sceNetSetDnsInfo`. Zeroing the unused slots and returning 0 without a host IPv4 interface are unverified. Nonzero flags and a null info throw because their console results are unknown
- [sceNetGetDnsInfo](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - host-interface selection, zeroing unused slots and returning 0 without a host IPv4 interface remain unverified on PS5 hardware. It reports the host interface's DNS servers until a guest override is set. The two network-order IPv4 slots and returned count follow the PS4 [OpenOrbis musl resolver](https://github.com/OpenOrbis/musl/blob/2b0bb8ce2fa64ac255cb656785729d159a5f074c/src/network/resolvconf.c) and [Net.h](https://github.com/OpenOrbis/OpenOrbis-PS4-Toolchain/blob/0a1aaf9dd4a92695538bdeb09fb056d06dd11725/include/orbis/Net.h).
- [sceNetGetRandom](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - generates values with the host random device; the system library maintains its own seeded PRNG state.
- [sceNetInetPtonEx](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - for IPv6 a null `dst` returns `NET_EINVAL` here, where it crashed a payload process on a PS5 (firmware 13.60). The `(int af, const char* src, void* dst, int flags)` signature follows [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/main/src/core/libraries/net/net.cpp). On PS4 firmware 10.71 and on that PS5, IPv4 text is read as `inet_aton` reads it (`a`, `a.b`, `a.b.c`, hex and octal parts, values wrapping at 32 bits, the text ending at a space, tab or VT), except that a part starting with `09` is decimal (`"0989"` is 989) and an octal part with `8` or `9` fails; flags were ignored (PS4: 0, single bits up to 0x100, 0x10000, 0x80000000, -1; PS5: 1, 2, 4, 0x100, 0x10000, -1). On the PS5 only: LF, FF and CR also end the text, a null `src` returns `NET_EINVAL`, and a null `dst` returns 1 or 0 without writing or setting errno. IPv6 converts as `sceNetInetPton` does
- [sceNetMemoryAllocate and sceNetMemoryFree](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - allocations use the guest heap instead of the system network pool. Pool limits, its allocation accounting and free-time fill are not reproduced: a reused block allocated without flag 2 holds 0xdd bytes on the console and its previous contents here. Flag 4 bypasses the system pool reserve threshold; with no pool there is no threshold, so it allocates as other flags do. What the console returns once the pool is exhausted is unmeasured; AnyPS5 returns null without setting an errno. Measured on a PS5 (firmware 13.60): a size of 0 or less returns null and leaves `sceNetErrnoLoc` unchanged, flag 2 zero-fills, flags 1, 4, 8, 0x10, 0x100 and 0x80000000 had no visible effect, and every block of 1 to 65536 bytes was 64-byte aligned
Expand Down Expand Up @@ -55,3 +55,4 @@
- [sceNetInetPton, sceNetInetNtop](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) convert IPv6 text with libkernel's [inet_pton and inet_ntop](../../../core/libs/prx/libkernel/Socket/src/AddressConversion.cpp), so they take the same FreeBSD 11.4 text forms; beyond the PS5 measurements in the entry above, these are unverified on a console. IPv4 text keeps the host conversion. The checks and errors before the conversion are libSceNet's own and unchanged

- [sceNetDhcpStart, sceNetAddrConfig6Start, sceNetDuplicateIpStart and sceNetInfoDumpStart](../../../core/libs/prx/libSceNet/Export.cpp) - argument widths and callback ABI are recovered. Address callbacks receive user data, a 32-bit event and a 32-bit error. DHCP configuration is mutable; the 512-byte IPv6 configuration is read-only. Their complete field layouts remain unknown. The two 32-bit thread parameters are stored but not consumed by this firmware thread wrapper, so their original names and units are unconfirmed. Information-dump options contain flags, an interval in seconds and a 64-byte path. All four exports remain unimplemented.
- [sceNetSetDnsInfo, sceNetSetDns6Info and sceNetGetDns6Info](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - the resolver still uses the host's DNS servers; setters only store servers for the getters. Without an IPv6 override, the getter reports none. Counts follow [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/0918faa0255e9816b795b62c9b09eaea51a1bc5e/src/core/libraries/net/net.cpp), not hardware measurements. All four DNS calls' initialization and argument errors match a reported [firmware 13.42 module probe](https://github.com/boykopovar/AnyPS5/pull/9506#issuecomment-6390574180): ENOTINIT first, then EINVAL for nonzero flags, null getter outputs or zero primary servers; null setters clear the override. Setters use a checked guest read and return EFAULT for unreadable non-null inputs, an AnyPS5 safety measure absent from the probed module.
Loading