Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 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 @@ -4,6 +4,16 @@ target_link_libraries(libSceNet PRIVATE libSceNetCtl)
configure_windows_unwind(libSceNet)

if(BUILD_TESTING)
add_test_executable(guest_net_protocol_tests ${LIBS_INCLUDE_DIR}/tests/GuestNetProtocol.cpp)
target_include_directories(guest_net_protocol_tests PRIVATE ${LIBS_INCLUDE_DIR})
target_link_libraries(guest_net_protocol_tests PRIVATE libSceNet libkernel libc)
configure_windows_unwind(guest_net_protocol_tests)
add_test(NAME guest_net_protocol COMMAND guest_net_protocol_tests)
set_tests_properties(guest_net_protocol PROPERTIES TIMEOUT 20)
if(WIN32)
set_tests_properties(guest_net_protocol PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libSceNet>")
endif()
add_test_executable(net_dontwait_tests ${LIBS_INCLUDE_DIR}/tests/NetDontWait.cpp)
target_include_directories(net_dontwait_tests PRIVATE ${LIBS_INCLUDE_DIR})
target_link_libraries(net_dontwait_tests PRIVATE libSceNet libkernel libc)
Expand Down
12 changes: 12 additions & 0 deletions core/libs/prx/libSceNet/Export.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,7 @@ constexpr int NET_TF_NODELAY = 4;
constexpr int NET_SO_SNDTIMEO = 0x1005;
constexpr int NET_SO_RCVTIMEO = 0x1006;
constexpr int NET_SO_ERROR = 0x1007;
constexpr int NET_SO_PROTOCOL = 0x1016;
constexpr int NET_SO_NBIO = 0x1200;
constexpr int NET_SO_REUSEADDR = 0x4;
constexpr int NET_SO_BROADCAST = 0x20;
Expand Down Expand Up @@ -399,6 +400,7 @@ bool native_to_guest_address(const sockaddr_storage& native, void* address, std:
struct Sock {
int family = 0;
int type = 0;
int protocol = 0;
std::shared_ptr<NativeSocketHandle> native;
bool bound = false;
bool listening = false;
Expand Down Expand Up @@ -1039,6 +1041,9 @@ int APS5_VABI sceNetSocket(const char* name, int family, int type, int protocol)
Sock s;
s.family = family;
s.type = type;
s.protocol = protocol;
if (protocol == 0 && type == NET_SOCK_STREAM) s.protocol = IPPROTO_TCP;
if (protocol == 0 && type == NET_SOCK_DGRAM) s.protocol = IPPROTO_UDP;
s.native = std::move(handle);
g_socks[fd] = s;
return fd;
Expand Down Expand Up @@ -1163,6 +1168,7 @@ int APS5_VABI sceNetAccept(int s, void* addr, uint32_t* addrlen) {
Sock connection;
connection.family = listener.family;
connection.type = NET_SOCK_STREAM;
connection.protocol = listener.protocol;
connection.native = std::move(handle);
#ifdef _WIN32
connection.native->receiveTimeout = listener.native->receiveTimeout.load();
Expand Down Expand Up @@ -1571,6 +1577,12 @@ int APS5_VABI sceNetGetsockopt(int s, int level, int optname, void* optval, uint
socket = it->second;
}
if (optval == nullptr || optlen == nullptr) return fail(NET_EINVAL);
if (level == NET_SOL_SOCKET && optname == NET_SO_PROTOCOL) {
if (socket.type == NET_SOCK_RAW) NotImplemented_nid_no_patch(__func__);
*optlen = std::min(*optlen, static_cast<std::uint32_t>(sizeof(socket.protocol)));
std::memcpy(optval, &socket.protocol, *optlen);
return 0;
}
if (level == NET_SOL_SOCKET && (optname == NET_SO_RCVTIMEO || optname == NET_SO_SNDTIMEO)) {
const int microseconds = optname == NET_SO_RCVTIMEO ? socket.native->receiveTimeout.load() : socket.native->sendTimeout.load();
const KernelTimeval timeout{microseconds / 1000000, microseconds % 1000000};
Expand Down
84 changes: 84 additions & 0 deletions core/libs/tests/GuestNetProtocol.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
#include <algorithm>
#include <array>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <source_location>
#include "prx/libc/include/general/VabiMacros.hpp"

extern "C" {
int APS5_VABI sceNetInit_nid_postfix();
int APS5_VABI sceNetSocket(const char*, int, int, int);
int APS5_VABI sceNetSocketClose(int);
int APS5_VABI sceNetGetsockopt(int, int, int, void*, std::uint32_t*);
int APS5_VABI sceNetSetsockopt(int, int, int, const void*, std::uint32_t);
int APS5_VABI sceNetBind_nid_postfix(int, const void*, std::uint32_t);
int APS5_VABI sceNetGetsockname(int, void*, std::uint32_t*);
int APS5_VABI sceNetListen(int, int);
int APS5_VABI sceNetConnect(int, const void*, std::uint32_t);
int APS5_VABI sceNetAccept(int, void*, std::uint32_t*);
int* APS5_VABI sceNetErrnoLoc();
}

static void Require(bool condition, std::source_location where = std::source_location::current()) {
if (!condition) {
std::fprintf(stderr, "check failed at line %u\n", where.line());
std::abort();
}
}

static void CheckProtocol(int socket, int expected) {
for (const std::uint32_t capacity : {0u, 1u, 3u, 4u, 8u}) {
std::array<std::uint8_t, 8> bytes;
bytes.fill(0xa5);
auto size = capacity;
Require(sceNetGetsockopt(socket, 0xffff, 0x1016, bytes.data(), &size) == 0);
Require(size == std::min(capacity, 4u));
Require(std::memcmp(bytes.data(), &expected, size) == 0);
Require(std::all_of(bytes.begin() + size, bytes.end(), [](auto value) { return value == 0xa5; }));
}
}

int main() {
Require(sceNetInit_nid_postfix() == 0);
for (const int family : {2, 28}) {
for (const int type : {1, 2}) {
const int expected = type == 1 ? 6 : 17;
for (const int protocol : {0, expected}) {
const int socket = sceNetSocket("protocol", family, type, protocol);
Require(socket >= 0);
CheckProtocol(socket, expected);
std::uint32_t size = 4;
int value = 99;
Require(sceNetGetsockopt(socket, 0xffff, 0x1016, nullptr, &size) < 0 && *sceNetErrnoLoc() == 22);
Require(sceNetGetsockopt(socket, 0xffff, 0x1016, &value, nullptr) < 0 && *sceNetErrnoLoc() == 22);
Require(value == 99 && size == 4);
Require(sceNetSetsockopt(socket, 0xffff, 0x1016, &value, sizeof(value)) < 0);
CheckProtocol(socket, expected);
Require(sceNetSocketClose(socket) == 0);
Require(sceNetGetsockopt(socket, 0xffff, 0x1016, &value, &size) < 0 && *sceNetErrnoLoc() == 9);
Require(value == 99 && size == 4);
}
}
const int listener = sceNetSocket("protocol-listener", family, 1, 0);
const int client = sceNetSocket("protocol-client", family, 1, 6);
Require(listener >= 0 && client >= 0);
std::array<std::uint8_t, 28> address{};
std::uint32_t size = family == 2 ? 16 : 28;
address[0] = size;
address[1] = family;
if (family == 2) { address[4] = 127; address[7] = 1; }
else address[23] = 1;
Require(sceNetBind_nid_postfix(listener, address.data(), size) == 0);
Require(sceNetListen(listener, 1) == 0);
CheckProtocol(listener, 6);
Require(sceNetGetsockname(listener, address.data(), &size) == 0);
Require(sceNetConnect(client, address.data(), size) == 0);
const int accepted = sceNetAccept(listener, nullptr, nullptr);
Require(accepted >= 0);
CheckProtocol(accepted, 6);
CheckProtocol(client, 6);
for (const int socket : {accepted, client, listener}) Require(sceNetSocketClose(socket) == 0);
}
}
2 changes: 2 additions & 0 deletions docs/dev/debt/libSceNet.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@

### Unknown function info

- [sceNetGetsockopt](../../../core/libs/prx/libSceNet/Export.cpp) - `SO_PROTOCOL` on raw sockets throws: FreeBSD's raw protocol-switch selection has not been mapped to host raw sockets. For stream and datagram sockets, the effective creation protocol and accepted-socket inheritance follow FreeBSD 11.4's [protocol tables](https://github.com/freebsd/freebsd-src/blob/releng/11.4/sys/netinet/in_proto.c) and [sogetopt](https://github.com/freebsd/freebsd-src/blob/releng/11.4/sys/kern/uipc_socket.c); the `0x1016` option and truncated integer output are not measured on PS5. Existing null-pointer validation still returns `EINVAL`.

- [sceNetConfigAddIfaddr](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - disassembly confirms the same interface/address/broadcast/netmask arguments as SetIfaddr, entering its shared handler with add mode selected. GetIfaddr copies up to two 16-byte interface-address records. GetRouteInfo uses a family, record buffer, signed capacity and byte flags; internal statistics output is 16 bytes. The address-record and internal-statistics fields remain incomplete.

- [Resolver-connect, interface and diagnostic queries, WLAN configuration and DHCP queries](../../../core/libs/prx/libSceNet/Export.cpp) retain unsupported implementations. Their restored argument lists still reference incomplete connection, interface, route, WLAN and statistics types. Kernel ioctl payloads need reconstruction from their handlers or consumers; buffer lengths alone do not establish field types.
Expand Down
Loading