Merge pull request #9525 from Queuse/codex/net-socket-state

feat(libSceNet): expose socket type and listen state
This commit is contained in:
Adrià authored and GitHub committed 2026-10-26 12:01:27 +01:00
commit 4b1a9a3b2f
4 files changed
+93

No files matched your search

+10
View File
@@ -4,6 +4,16 @@ target_link_libraries(libSceNet PRIVATE libSceNetCtl)
configure_windows_unwind(libSceNet)
if(BUILD_TESTING)
add_test_executable(guest_net_socket_state_tests ${LIBS_INCLUDE_DIR}/tests/GuestNetSocketState.cpp)
target_include_directories(guest_net_socket_state_tests PRIVATE ${LIBS_INCLUDE_DIR})
target_link_libraries(guest_net_socket_state_tests PRIVATE libSceNet libkernel libc)
configure_windows_unwind(guest_net_socket_state_tests)
add_test(NAME guest_net_socket_state COMMAND guest_net_socket_state_tests)
set_tests_properties(guest_net_socket_state PROPERTIES TIMEOUT 20)
if(WIN32)
set_tests_properties(guest_net_socket_state PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libSceNet>")
endif()
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)
+8
View File
@@ -153,6 +153,8 @@ constexpr int NET_SO_SNDBUF = 0x1001;
constexpr int NET_SO_RCVBUF = 0x1002;
constexpr int NET_SO_SCE_SNDTIMEO = 0x1105;
constexpr int NET_SO_SCE_RCVTIMEO = 0x1106;
constexpr int NET_SO_TYPE = 0x1008;
constexpr int NET_SO_ACCEPTCONN = 0x2;
constexpr int NET_MSG_PEEK = 0x2;
constexpr int NET_MSG_TRUNC = 0x10;
constexpr int NET_MSG_DONTWAIT = 0x80;
@@ -1516,6 +1518,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_TYPE || optname == NET_SO_ACCEPTCONN)) {
const int value = optname == NET_SO_TYPE ? socket.type : socket.listening ? NET_SO_ACCEPTCONN : 0;
*optlen = std::min(*optlen, static_cast<std::uint32_t>(sizeof(value)));
std::memcpy(optval, &value, *optlen);
return 0;
}
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)));
+74
View File
@@ -0,0 +1,74 @@
#include <algorithm>
#include <array>
#include <cstdint>
#include <cstdlib>
#include <cstring>
#include "prx/libSceNet/libSceNet.hpp"
static void Require(bool condition) {
if (!condition) std::abort();
}
static void CheckOption(int socket, int option, int expected) {
for (const std::uint32_t capacity : {0u, 1u, 3u, 4u, 8u}) {
std::array<std::uint8_t, 8> bytes;
bytes.fill(0xa5);
std::uint32_t size = capacity;
Require(sceNetGetsockopt(socket, 0xffff, option, 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 byte) { return byte == 0xa5; }));
}
}
int main() {
Require(sceNetInit_nid_postfix() == 0);
for (const int family : {2, 28}) {
const int datagram = sceNetSocket("state-datagram", family, 2, 17);
const int listener = sceNetSocket("state-listener", family, 1, 6);
const int client = sceNetSocket("state-client", family, 1, 6);
Require(datagram >= 0 && listener >= 0 && client >= 0);
CheckOption(datagram, 0x1008, 2);
CheckOption(datagram, 2, 0);
CheckOption(listener, 0x1008, 1);
CheckOption(listener, 2, 0);
std::array<std::uint8_t, 28> address{};
const std::uint32_t addressSize = family == 2 ? 16 : 28;
address[0] = addressSize;
address[1] = family;
if (family == 2) {
address[4] = 127;
address[7] = 1;
} else {
address[23] = 1;
}
Require(sceNetBind_nid_postfix(listener, address.data(), addressSize) == 0);
Require(sceNetListen(listener, 1) == 0);
CheckOption(listener, 2, 2);
CheckOption(client, 2, 0);
std::uint32_t size = addressSize;
Require(sceNetGetsockname(listener, address.data(), &size) == 0);
Require(sceNetConnect(client, address.data(), size) == 0);
const int accepted = sceNetAccept(listener, nullptr, nullptr);
Require(accepted >= 0);
CheckOption(listener, 2, 2);
CheckOption(client, 0x1008, 1);
CheckOption(client, 2, 0);
CheckOption(accepted, 0x1008, 1);
CheckOption(accepted, 2, 0);
int value = 0;
size = sizeof(value);
for (const int option : {2, 0x1008}) {
Require(sceNetGetsockopt(listener, 0xffff, option, nullptr, &size) == static_cast<int>(0x80410116u));
Require(*sceNetErrnoLoc() == 22);
Require(sceNetGetsockopt(listener, 0xffff, option, &value, nullptr) == static_cast<int>(0x80410116u));
Require(*sceNetErrnoLoc() == 22);
}
for (const int socket : {accepted, client, listener, datagram}) Require(sceNetSocketClose(socket) == 0);
for (const int option : {2, 0x1008}) {
Require(sceNetGetsockopt(listener, 0xffff, option, &value, &size) == static_cast<int>(0x80410109u));
Require(*sceNetErrnoLoc() == 9);
}
}
}
+1
View File
@@ -43,6 +43,7 @@
- [sceNetGetsockopt](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) reads `SO_ERROR` from the host socket. Winsock does not clear a socket's pending error when it is read, so on Windows the library reports a pending error once and shows it again only after a new `sceNetConnect`; a second failure with the same code on the same socket without a `sceNetConnect` in between would be hidden.
- [SO_KEEPALIVE](../../../core/libs/prx/libSceNet/Export.cpp) uses the host TCP keepalive timers and probe count, which can differ from the console. The boolean flag, bit-valued readback and truncated output follow FreeBSD 11.4's [sosetopt and sogetopt](https://github.com/freebsd/freebsd-src/blob/releng/11.4/sys/kern/uipc_socket.c), without PS5 hardware verification. Non-stream sockets retain the flag without sending probes, as FreeBSD does. Null pointers retain the existing libSceNet `EINVAL` result instead of FreeBSD's `EFAULT`.
- [sceNetSetsockopt, sceNetGetsockopt](../../../core/libs/prx/libSceNet/Export.cpp) - `TCP_MAXSEG`, `TCP_NOPUSH`, `TCP_KEEPIDLE` and other TCP options remain unsupported (`ENOPROTOOPT`). `TCP_NODELAY` sets on refused, reset or `TIME_WAIT` sockets can succeed through the host where FreeBSD 11.4's [tcp_ctloutput](https://github.com/freebsd/freebsd-src/blob/releng/11.4/sys/netinet/tcp_usrreq.c#L1498-L1502) returns `ECONNRESET`. A null setter value returns `EINVAL` rather than `kern_setsockopt`'s `EFAULT`. The non-stream `IPPROTO_TCP` error (`EINVAL`) and `TF_NODELAY` flag readback follow FreeBSD, not PS5 measurements.
- [SO_TYPE and SO_ACCEPTCONN](../../../core/libs/prx/libSceNet/Export.cpp) read the guest socket type and listening flag, with bit-valued listening results and truncated output following FreeBSD 11.4's [sogetopt](https://github.com/freebsd/freebsd-src/blob/releng/11.4/sys/kern/uipc_socket.c#L2667-L2693), without PS5 verification. Null pointers retain the existing libSceNet `EINVAL` result instead of FreeBSD's pointer handling.
- [sceNetGetSockInfo](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) reports 0 for the name, pid, policy, priority, queue lengths, virtual ports, state, bandwidth, drops and waits, sets no status flag except `SELF` (0x1) and `NONBLOCK` (0x200000), and reports the host's buffer sizes where none was set. An IPv6 socket fails with `EAFNOSUPPORT` and is not counted or listed; shadPS4 lists it with zero addresses. `flags` bits other than 0x40 fail with `EINVAL`, where shadPS4 ignores `flags`; 0x40 adds sockets in TIME_WAIT ([SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/5dfdb4b0b166e83dc9ae275001afb695f12c7174/src/SharpProspero/Interop/Net/Socket.cs)), but closed sockets are not kept. Otherwise the `s` below 0 count and list, the return value and `EINVAL` for a null `info` or an `n` below 1 follow [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/06e813ff8c8e2a6d81b02cabfcc9543c3debf78e/src/core/libraries/net/net.cpp#L1498-L1522). Not measured on a console.
- [SO_SNDBUF and SO_RCVBUF validation](../../../core/libs/prx/libSceNet/Export.cpp) remains unverified on PS5, and positive sizes remain subject to host buffer limits. Nonpositive sizes fail with `EINVAL` before changing the native socket, following FreeBSD 11.4 [sosetopt](https://github.com/freebsd/freebsd-src/blob/releng/11.4/sys/kern/uipc_socket.c#L2494-L2509).
- [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