Commit Graph

54 Commits

Author SHA1 Message Date
babyhuey 0daf1d5ba3 fix(libSceNet): report SO_ERROR from sceNetGetsockopt 2026-10-13 03:15:45 -04:00
screamm e99816e8f3 fix(libkernel): give guest sockets ordinary descriptor numbers below FD_SETSIZE
libkernel numbered guest sockets from GuestSockets::FirstDescriptor
(0x10000000), so a title that puts a socket in a 1024-bit fd_set writes
fd / 8 bytes past it. The Epic Online Services SDK does this on its
RtcNetworkThread and three titles stop there (#1794):

Palworld PPSA23126 (queeeef, main a11c8f7f7):
  FATAL: unhandled exception 0xc0000005 on thread 37164 'RtcNetworkThread'
    rip 0x00007ffcdb1ea2be eossdk-ps5-shipping.prx.guest.prx+0xdea2be
    rcx 0000000010000005 rsi 0000000000400000 r12 0000000010000005
The Mound PPSA26223 (xXPBSXx, main 0a8b1d32b):
  FATAL: unhandled exception 0xc0000005 on thread 'RtcNetworkThread',
  rip eossdk-ps5-shipping.prx.guest.prx+0xefc6f4, rcx = 0x10000005,
  rsi = 0x400000
Dying Light 2 PPSA02262 (tv7):
  FATAL: unhandled exception 0xc0000005 on thread 'RtcNetworkThread'
    rcx 0000000010000005 ... rsi 0000000000400000 ... r12 0000000010000005

On FreeBSD 11.4 (8c639945) FD_SETSIZE is 1024 (sys/sys/select.h:58-59)
and sockets take the lowest free entry of the one process descriptor
table that files use (kern_descrip.c:1643-1660 fdalloc,
uipc_syscalls.c kern_socket through falloc).

Every socket number is now a real host descriptor: a zero-access NUL
handle registered with _open_osfhandle on Windows (as directory
descriptors already are) and an F_DUPFD_CLOEXEC duplicate of the host
socket on Linux. The socket table maps that number to the socket, so
"is this a socket" is a table lookup instead of a range check at the 31
former FirstDescriptor comparisons. close releases both the socket entry
and the number, dup and dup2 duplicate the number through the host table
and keep the socket shared, dup2 of a file onto a socket number drops the
socket entry, and sceKernelClose now closes sockets too. sceNetSocket and
sceNetAccept take their ids from the same table instead of a counter from
32, so libkernel and libSceNet numbers never collide.

select polls guest sockets alongside libSceNet sockets, the random device,
stdio and (on Linux) host descriptors; poll throws only for an open
descriptor that is not a guest socket and reports POLLNVAL otherwise.
File calls that used to fail with EBADF on the socket range return
FreeBSD's answer for a socket: lseek ESPIPE (kern_lseek), ftruncate and
getdirentries EINVAL (invfo_truncate, getvnode), flock EOPNOTSUPP;
fstat on a socket throws. pread on a closed descriptor returns EBADF.

title failing on host descriptor numbers; these three titles are that
case, and this change keeps host numbering for files and only moves
sockets into it.

Tests: guest_socket_descriptors (new: socket below 1024, select on a
socket and a file together, close frees the number for a file, dup of a
socket, sceKernelClose), guest_select (socket and pipe together, Linux),
guest_poll, guest_dup, guest_socket_pair, guest_fstatfs,
guest_posix_filesystem and guest_sce_net updated.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
(cherry picked from commit 20378375b31eb5db922db6e55f45e44a4dae6445)
2026-10-13 03:22:36 +00:00
David 3d64981643 feat(libSceNet): implement sceNetResolverAbort and sceNetResolverStartNtoaMultipleRecords
UFC 6 (PPSA23566) imports sceNetResolverAbort (#992, #1502); Palworld
(PPSA23126) and LEGO Horizon Adventures (PPSA14632) import
sceNetResolverStartNtoaMultipleRecords.

Signatures and behaviour follow shadPS4 811bfcd7d17e
(src/core/libraries/net/net.cpp, net_resolver.cpp); the abort signature
also matches SharpProspero Interop/Net/Socket.cs.

sceNetResolverStartNtoaMultipleRecords shares the lookup of the Ex form
and, like it, throws for any flag. sceNetResolverAbort returns EBADF for
an unknown resolver and throws for flag bits other than NTOA (1) and
ATON (2) preservation. Host lookups cannot be interrupted, so an abort
during a running lookup makes it fail with EINTR when the host call
returns, without writing its output; with nothing running, the
preservation flags make the next lookup of that kind fail with EINTR.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
(cherry picked from commit bcc8be8a58aa6a9e6b7c288d8a90af6ed63837aa)
2026-10-12 22:03:56 +02:00
Dean 31ade9abc2
Merge pull request #4406 from Onny2/test/sce-net-absolute-invalid-name
test(net): look up the unresolvable name as an absolute name
2026-10-13 07:23:34 +13:00
Dean 83e6be8a69
Merge pull request #4394 from AaravMehta-07/codex/sce-net-stream-sender
fix(libSceNet): preserve successful stream recvfrom results
2026-10-13 07:23:13 +13:00
Dean 3377986a60
Merge pull request #4371 from milescoviello/fix/net-socket-timeval-timeouts
fix(libSceNet): read setsockopt timeouts as struct timeval
2026-10-13 07:21:55 +13:00
Aarav 0eec3fd74a test(sce-net): avoid adjacent PR insertion points 2026-10-12 15:30:47 +05:30
Onny2 2bb8b83765 test(net): look up the unresolvable name as an absolute name
guest_sce_net expects sceNetResolverStartNtoa and
sceNetResolverStartNtoaMultipleRecordsEx to fail for
guest-sce-net.invalid. The resolver delegates to the host's
getaddrinfo, which retries a name that is not found with the host's
DNS search domains. On an ordinary home network whose router sets a
search domain that answers any name, the lookup then succeeds as
guest-sce-net.invalid.<search domain> and the test aborts.

Use the absolute name guest-sce-net.invalid. so the host applies no
search domain and the lookup fails on any network.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 11:05:39 +02:00
milescoviello 98f6639e9e fix(net): make new IPv6 sockets IPv6-only as FreeBSD does
On Linux, socket() and sceNetSocket() create AF_INET6 sockets in the
host's default dual-stack mode (net.ipv6.bindv6only is 0), so a wildcard
IPv6 bind also claims the IPv4 port. A title that keeps an IPv4 and an
IPv6 socket on the same port fails: with 0.0.0.0:P bound, binding [::]:P
returns EADDRINUSE (48), and an IPv6 socket bound to [::] also receives
IPv4 traffic from v4-mapped peers.

FreeBSD makes every new IPv6 socket IPv6-only. sys/netinet6/in6_proto.c:

	VNET_DEFINE(int, ip6_v6only) = 1;

sys/netinet/in_pcb.c, in_pcballoc():

	if (INP_SOCKAF(so) == AF_INET6) {
		inp->inp_vflag |= INP_IPV6PROTO;
		if (V_ip6_v6only)
			inp->inp_flags |= IN6P_IPV6_V6ONLY;
	}

and sys/netinet6/in6_pcb.c, in6_pcbbind(), looks for an IPv4 owner of
the port only when the flag is clear:

	if ((inp->inp_flags & IN6P_IPV6_V6ONLY) == 0 &&
	    IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) {

socket() and sceNetSocket() now set IPV6_V6ONLY on every IPv6 socket
they create. Windows already defaults to it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 08:44:27 +00:00
Dean 49b6b6a5c8
Merge pull request #4092 from yunusshaikh7/fix/libscenet-datagram-receive-timeout
fix(libSceNet): report EAGAIN when a datagram receive times out on Windows
2026-10-12 21:39:40 +13:00
milescoviello e9c5ae26be fix(libSceNet): read setsockopt timeouts as struct timeval
libkernel's setsockopt() hands options for a libSceNet socket to
sceNetSetsockopt(), whose SO_RCVTIMEO and SO_SNDTIMEO take an int of
microseconds. The POSIX call takes a struct timeval, so main read the
low half of tv_sec as microseconds: {1, 250000} set a 1 microsecond
receive timeout, {0, 500000} set 0, which disables the timeout, and an
out-of-range tv_usec was accepted.

FreeBSD reads a struct timeval, sys/kern/uipc_socket.c, sosetopt():

	case SO_SNDTIMEO:
	case SO_RCVTIMEO:
	...
		error = sooptcopyin(sopt, &tv, sizeof tv,
		    sizeof tv);
	if (error)
		goto bad;
	if (tv.tv_sec < 0 || tv.tv_usec < 0 ||
	    tv.tv_usec >= 1000000) {
		error = EDOM;
		goto bad;
	}

and sooptcopyin() fails a shorter value with EINVAL:

	if ((valsize = sopt->sopt_valsize) < minlen)
		return EINVAL;

setsockopt() on a libSceNet socket now converts the timeval for these
two options, with EINVAL for a short value and EDOM for a negative or
out-of-range one. sceNetSetsockopt() keeps taking microseconds; both
paths share the code that sets the host timeout.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 07:58:06 +00:00
Dean 98c07d8e77
Merge pull request #4111 from jdmagic/fix/kernel-close-net-sockets
fix(libkernel): close() and setsockopt() reach libSceNet sockets
2026-10-12 20:28:00 +13:00
Dean 2c51fdfba9
Merge pull request #4112 from jdmagic/fix/net-errors-set-kernel-errno
fix(libSceNet): failures also set libkernel's errno
2026-10-12 20:25:23 +13:00
Dean 4303f39b92
Merge pull request #4110 from jdmagic/fix/net-epoll-control-ops
fix(libSceNet): sceNetEpollControl takes 2 as MOD and 3 as DEL
2026-10-12 20:25:02 +13:00
Aarav 74defef675 fix(libSceNet): preserve successful stream recvfrom results 2026-10-12 10:54:08 +05:30
Dean 01e5f3b8a8
Merge pull request #3977 from hansjm10/fix/libscenet-sockaddr-len
fix(libSceNet): accept guest socket addresses whatever their sa_len
2026-10-12 16:08:32 +13:00
jdmagic d5805fb3e4 fix(libkernel): setsockopt sets options of libSceNet sockets
A title creating a socket with sceNetSocket sets its options with the BSD
setsockopt, which looked the descriptor up only among libkernel's own
sockets and returned EBADF. libSceNet now registers an option setter with
libkernel, as it does for close and select, and accepts SO_REUSEADDR,
SO_BROADCAST, SO_SNDBUF and SO_RCVBUF like libkernel's own sockets.

PPSA01289 (Sackboy: A Big Adventure) sets SO_BROADCAST on its UDP P2P
socket this way in UIpNetDriver::InitBase. The failure made every listen
fail, and the engine travelled back to its default map with ?closed,
reloading it about every two seconds behind the loading screen.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 19:23:24 -04:00
jdmagic 5d14cd02ff fix(libSceNet): failures also set libkernel's errno
libSceNet stored its errno only in its own slot (sceNetErrnoLoc). Titles
that use the BSD socket model read errno after a libSceNet call, as the
console's libSceNet calls are kernel calls that set it; shadPS4 mirrors its
net errno into the kernel errno the same way (SetKernelErrnoHook).

PPSA01289 (Sackboy: A Big Adventure) reads errno through Unreal's
GetLastErrorCode after sceNetRecvfrom. It never saw EWOULDBLOCK, so
UIpNetDriver::TickDispatch kept receiving and the game thread never left
its first networked frame.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 19:23:24 -04:00
jdmagic 12b18af550 fix(libkernel): close() closes libSceNet sockets
A libSceNet socket is a kernel descriptor on the console, so titles close it
with close(). libSceNet keeps its sockets in its own table, and libkernel's
close passed their numbers to the CRT, which reported an invalid parameter
and left the socket open. libSceNet now registers a closer with libkernel,
as it registers its poller for select. PPSA01289 (Sackboy: A Big Adventure)
closes its P2P socket this way each time it tears its network session down,
leaking one open socket per attempt.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 19:23:24 -04:00
jdmagic 22827bf6ad fix(libSceNet): sceNetEpollControl takes 2 as MOD and 3 as DEL
sceNetEpollControl used Linux's EPOLL_CTL numbering (1 ADD, 2 DEL, 3 MOD).
The SCE values are 1 ADD, 2 MOD, 3 DEL (shadPS4 net_types.h,
ORBIS_NET_EPOLL_CTL_*). A title removing a socket with op 3 and a null
event got EINVAL, and op 2 deleted the registration it meant to modify.
PPSA01289 (Sackboy: A Big Adventure) removes its UDP socket from its epoll
this way before closing it, every time its network session is torn down.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 19:23:23 -04:00
Yunus Shaikh f7a85491ef fix(libSceNet): report EAGAIN when a datagram receive times out on Windows
With SO_RCVTIMEO set, Winsock fails a receive that waits that long with
WSAETIMEDOUT, which native_error turned into ETIMEDOUT (60). sceNetRecv,
sceNetRecvfrom and sceNetRecvmsg on a datagram socket therefore failed
with 0x8041013C on Windows, where FreeBSD's sbwait returns EWOULDBLOCK
(35) to soreceive, Linux returns EAGAIN, and POSIX (XSH 2.10.16)
requires EAGAIN or EWOULDBLOCK when no data was received.

A datagram socket has no connection that can time out, so WSAETIMEDOUT
there only means that the receive timeout the guest set has expired:
report EAGAIN. Stream sockets keep ETIMEDOUT, since Winsock leaves a
connection in an indeterminate state after a timeout; TechnicalDebt
records that gap and the one for SO_SNDTIMEO.
2026-10-11 16:01:51 -07:00
Jordan Hans 2d3dd337ad fix(libSceNet): accept guest socket addresses whatever their sa_len 2026-10-11 20:17:41 +00:00
overlordxrz-source 4891d625af feat(libSceNet): export sce_net_in6addr_linklocal_allnodes
Co-authored-by: eliasbruno124-dev <230422461+eliasbruno124-dev@users.noreply.github.com>
2026-10-11 11:41:05 -04:00
Abdulrahman Elkordy abed1c4314 fix(libkernel): answer select on the standard descriptors
Dungeons (PPSA16064) calls select on descriptor 0 and stopped with
"select on descriptor 0 (not a libSceNet socket) not implemented".
Descriptor 0 is now never readable and descriptors 1 and 2 are always
writable, as for a console title with no input. The assumption is in
TechnicalDebt next to the termios entry for the same descriptors.

Claude-Session: https://claude.ai/code/session_012SLQbhAiexxW6TtjYToW8a
2026-10-11 13:48:29 +03:00
Abdulrahman Elkordy 1f50b91280 fix(libkernel): report /dev/urandom descriptors ready in select
select passed every descriptor it was given to the libSceNet socket poller
and aborted on any it did not know. A title that selects on its
/dev/urandom descriptor (Dungeons, PPSA16064, aborts right after opening
it) stopped with "select on descriptors that are not libSceNet sockets".
On FreeBSD the random device is always readable and writable, so these
descriptors are now answered without the poller, and a ready one makes
the socket poll return at once.

Claude-Session: https://claude.ai/code/session_012SLQbhAiexxW6TtjYToW8a
2026-10-11 13:48:29 +03:00
MarcZX100 e864e687b3 fix(libkernel): saturate oversized select timeouts 2026-10-11 09:16:59 +02:00
MarcZX100 3d79d42663 test(net): include Windows IPv6 declarations
(cherry picked from commit 811d8c6b155fbfdae5e5277f90b435e68d42ffdc)
2026-10-11 03:47:17 +00:00
MarcZX100 2c012b4fe5 test(net): skip unavailable IPv6 loopback checks
(cherry picked from commit 9f2a0062d580b399424cb417e7f2a17cd093785b)
2026-10-11 03:47:17 +00:00
Dean 9c724baad3
Merge pull request #1786 from Zurisen/fix/windows-socket-shutdown-epipe
fix(net): report EPIPE for a send after shutdown on Windows
2026-10-10 17:41:18 +13:00
Roojool a0f42946e5 fix(net): use native Windows condition waits for empty epoll 2026-10-10 02:05:23 +02:00
Zurisen 2356aba27b fix(net): report EPIPE for a send after shutdown on Windows
Winsock fails send, sendto and sendmsg on a socket shut down for writing
with WSAESHUTDOWN, which neither Windows error map handled, so libkernel
and libSceNet reported EIO. FreeBSD reports EPIPE there, and the Linux
maps already translate the host's EPIPE to 32. Map WSAESHUTDOWN to 32
in both.
2026-10-10 02:02:29 +02:00
Dean 19a0daa786
Merge pull request #2295 from parl0v/test/sce-net-reset-on-unread-close
test(net): reset the closed peer with unread data so the send fails at once
2026-10-10 10:47:13 +13:00
Josip Parlov 61684ce98f test(net): reset the closed peer with unread data so the send fails at once 2026-10-09 16:53:26 +02:00
Claude 4a2769f26d
test(net): skip only the IPv6 socket checks on a host without IPv6
On a host without IPv6, socket(AF_INET6) fails with EAFNOSUPPORT, so
guest_sce_net aborted at its first IPv6 socket and never ran the TCP,
UDP, epoll, sendmsg/recvmsg, resolver and NetCtl checks after it.

When sceNetSocket reports guest EAFNOSUPPORT (47) for the IPv6 socket,
skip those socket checks, run the rest of the test, and exit with 77,
which ctest reports as skipped. Hosts with IPv6 run every check.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ha6Tsy62AFUyp9a5Mf7vjA
2026-10-09 10:30:28 +00:00
Obi Newman 881fb01069
feat(libs): report Http2, Ssl and Net memory pool statistics (#1119)
sceHttp2GetMemoryPoolStats, sceSslGetMemoryPoolStats and
sceNetGetMemoryPoolStats threw. PPSA16064 (Minecraft Dungeons II,
Unreal Engine) queries all three while its HTTP module starts, and
aborted there.

Each library now records the pool size given at sceHttp2Init,
sceSslInit or sceNetPoolCreate and reports it with a peak and current
use of 0: the offline Http2 and Ssl models and the host-socket Net
library allocate nothing from these pools. The layouts follow the PS4
SDK's *MemoryPoolStats structures. Net returns EBADF for an unknown
pool and EINVAL for a null output, like its other pool calls; Http2 and
Ssl throw for both, as their error codes are unknown. Terminating a
context or destroying a pool forgets its size.

sceHttp2SetResolveRetry, which the title calls next, accepts and
ignores its value like the other Http2 settings. The four Http2
WebSocket exports the title imports are added as unimplemented.

guest_http2, guest_ssl and guest_sce_net check the reported sizes;
guest_sce_net also checks the null-output and unknown-pool errors.
2026-10-09 21:03:06 +13:00
LeandroLP23 f054374661 test(libSceNet): pin the resolver info layout and check the buffer on errors
The layout offsets the titles read are static_asserts, and guest_sce_net now
checks a canary past byte 384, distinct records, and an untouched buffer on the
EINVAL, RESOLVER_ENODNS and EBADF paths, as #1845's test did.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 23:58:18 +02:00
LeandroLP23 427fd92591 feat(libSceNet): implement sceNetResolverStartNtoaMultipleRecordsEx
PS5VR (PPSA99177) stops at start-up on the unresolved import sT4nBQKUPqM;
Jelly5, Emby5 and Nuvio import it too. The call fills the 384-byte resolver
info with up to ten IPv4 records (32 bytes each, family at +16) and the record
counts at +320 and +324, from the same host lookup sceNetResolverStartNtoa
uses, now shared. Only flags 0 is implemented; other flags throw.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 23:52:02 +02:00
Adrià f2f7abab71
Merge pull request #1448 from ewraj/fix/net-connect-einprogress
fix(libSceNet): report EINPROGRESS for a non-blocking connect and translate the missing socket errors
2026-10-08 16:02:31 +02:00
Dean f100d1d67a
Merge pull request #1373 from gjonn/feat/kernel-select-net-sockets
feat(libkernel): implement select over libSceNet sockets
2026-10-08 22:36:09 +13:00
Raj d3900b240f fix(libSceNet): translate EHOSTUNREACH and the socket errors libkernel translates
The host's EHOSTUNREACH was reported as ENETUNREACH (51); FreeBSD's
EHOSTUNREACH is 65, which libkernel's sockets already report. EADDRNOTAVAIL,
ENOBUFS, EISCONN, ENOTCONN and EACCES had no translation and became EIO;
they now map to 49, 55, 56, 57 and 13, as in libkernel/Socket/src/Udp.cpp.

guest_sce_net connects the connected client again and expects EISCONN
(0x80410138); before this change it received EIO.
2026-10-08 12:23:36 +05:30
Raj 513479380c fix(libSceNet): report EINPROGRESS for a non-blocking connect
A non-blocking sceNetConnect that cannot complete at once must fail with
EINPROGRESS (36, 0x80410124), as FreeBSD's connect does, and a repeated call
while it is pending with EALREADY (37). On Windows, Winsock reports the
first case as WSAEWOULDBLOCK, which was translated to EAGAIN (0x80410123); on
Linux, EINPROGRESS and EALREADY had no translation and became EIO
(0x80410105). Code that waits for the connection only when connect reports
EINPROGRESS treated both as a failed connection.

guest_sce_net connects a non-blocking socket to the loopback listener and
accepts 0 or EINPROGRESS; before this change it received EAGAIN on Windows.
2026-10-08 12:22:49 +05:30
Dean c8a2f06325
Merge pull request #1248 from eliasbruno124-dev/fix/net-ipv6-any-current
fix(net): provide IPv6 unspecified address object
2026-10-08 18:29:31 +13:00
Gjon Gojcevic a47fe27d28 feat(libkernel): implement select over libSceNet sockets
libSceNet registers a socket poller with libkernel; select polls the requested descriptors through it until the timeout and rewrites the descriptor sets.
2026-10-07 20:47:03 -04:00
eliasbruno124-dev 971099aa15 fix(net): provide IPv6 unspecified address object 2026-10-07 22:11:33 +02:00
kjhq 63af67138f feat(libSceNet): export in6addr_any and in6addr_loopback
libSceNet exports the IPv6 wildcard and loopback addresses as data.
2026-10-07 10:43:24 +00:00
Adrià Franch 92d79996c7 libSceNet: implement sceNetSendmsg and sceNetRecvmsg
Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-06 11:51:42 +02:00
Dean 901ddfd1f8
Merge pull request #783 from adriadam10/feat/net-resolver-get-error
feat(libSceNet): implement sceNetResolverGetError
2026-10-06 14:14:10 +13:00
Dean 9cafa33f94
Merge pull request #658 from NumberGuan/codex/fix-net-inet-ntop-errors
fix(net): report address text buffer errors to the guest
2026-10-06 13:29:03 +13:00
Adrià Franch 9fb83a2435 feat(libSceNet): implement sceNetResolverGetError
Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-06 01:40:36 +02:00
Dean 6eebd23a62
Merge pull request #745 from Blizzeq/fix/net-error-codes
fix(net): return SCE_NET_ERROR codes from failing sceNet calls
2026-10-06 12:24:24 +13:00