sceNetConfigGetDefaultRoute threw. It now writes the IPv4 gateway of the
host interface that libSceNetCtl's sceNetCtlGetInfo already describes as
DEFAULT_ROUTE, in network byte order, the order sceNetInetPton writes an
AF_INET address in, and returns 0. libSceNetCtl exports
NetCtlHostIpv4Gateway_nid_no_patch for it, next to the DNS helper
sceNetGetDnsInfo uses. Without a gateway, or for a null pointer, it
still throws: the console's answer for a missing route is not measured.
Tested: guest_sce_net_tests, after sceNetCtlInit, prints the gateway
with sceNetInetNtop and compares it with sceNetCtlGetInfo's
DEFAULT_ROUTE text (192.0.2.1 on this host); a null pointer throws.
On a host without a default route the case checks that the call
throws and leaves the output untouched.
sceNetInetNtopWithScopeId and sceNetInetPtonWithScopeId threw. They now
handle AF_INET6 text with an RFC 4007 numeric zone (fe80::1%2): the
address part goes through sceNetInetNtop and sceNetInetPton, so it
follows the FreeBSD inet text rules, and the zone is printed and parsed
as FreeBSD 11.4's getnameinfo (NI_NUMERICSCOPE) and getaddrinfo do. A
zero scope ID prints the address alone and text without a zone parses
to scope ID 0. An empty zone, one above 2^32 - 1, or an invalid address
part returns 0 and leaves both outputs untouched; a buffer too small
for the scoped text gives ENOSPC.
A zone given as an interface name, and AF_INET, still throw: the tree
has no mapping from interface names to the console's interface indexes
and no answer for an IPv4 zone. The debt file says so.
Tested: guest_sce_net_tests round-trips fe80::1%2 (same address bytes
as fe80::1, scope ID 2) and ff02::1%4294967295, checks the exact
buffer size and ENOSPC one byte short, the unscoped forms, four
rejected texts with untouched outputs, EINVAL for null pointers, and
that an interface-name zone and AF_INET throw.
sceNetSysctl threw. Its restored argument list is FreeBSD's sysctl(3)
(name, name length, old value and size, new value and size), so it now
calls libkernel's sysctl. It answers exactly the MIBs libkernel models,
and a failure is returned as the library's other calls return one
(0x80410100 | errno, with the errno set).
Tested: guest_sce_net_tests reads hw.pagesize (6.7) through both
sysctl and sceNetSysctl and gets the same value and size, including a
size-only query. A one-level name gives EINVAL, a null name EFAULT, a
write EPERM and a one-byte buffer ENOMEM, each as 0x804101xx with the
errno set.
sceNetSocketInternal, sceNetGetsockoptEx and sceNetSetsockoptEx threw.
Their argument lists, restored with the rest of the firmware exports in
4d6942f1e, are the same as sceNetSocket, sceNetGetsockopt and
sceNetSetsockopt, so they now call those. Nothing in the tree records
how the console tells the entry points apart (for example by the
calling process); the debt file says so.
Tested: guest_sce_net_tests creates a datagram socket through
sceNetSocketInternal, sets SO_REUSEADDR and SO_RCVTIMEO through
sceNetSetsockoptEx and reads them back through sceNetGetsockoptEx and
sceNetGetsockopt with the same values. On a closed socket and on -1,
and for an unsupported family and type, each Ex/Internal call returns
the same error and errno as its public twin.
Base for the libSceNet Ex/Internal forwards: upstream main 94fbb8cea with fix/libscenet-inet-text-rules (ef49574f5) and fix/libkernel-getsockopt-net-sockets (aea804d9f).
setsockopt reaches a socket created with sceNetSocket through the option setter libSceNet registers, but getsockopt looked the descriptor up only among libkernel's own sockets and failed, so a title could set an option on such a socket through the BSD call and not read it back. libSceNet now registers an option getter with libkernel next to its setter, which answers through sceNetGetsockopt, so SO_RCVTIMEO and SO_SNDTIMEO come back as a struct timeval.
sceNetSetsockopt takes both, but sceNetGetsockopt failed them with EOPNOTSUPP. It now reads them from the host socket. FreeBSD's sogetopt returns so_options & option for the boolean options, so they read back as 0x4 and 0x20 when set.
sceNet translated only 21 host socket errors and reported every other one
as EIO. libkernel already owns the complete FreeBSD table in
GuestSockets::GuestErrorFromHost, so use it for net_error. The new check
makes a stream socket with the UDP protocol fail with EPROTONOSUPPORT (43)
instead of EIO (5).
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Report the host IPv4 DNS servers of the interface sceNetCtlGetInfo
uses, through a helper libSceNetCtl exports, and return their count.
The signature and layout follow the OpenOrbis musl resolver and Net.h.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
sceNetGetSockInfo returned 0 for a known socket and wrote nothing, so
callers read uninitialised bytes as the socket's endpoints. Monster
Hunter Wilds (PPSA24191) calls it after bind, connect and accept and
stores the local and remote address and port it reads back.
It now writes one 0xA0-byte record in the layout SharpProspero and
shadPS4 give: the socket id, the socket type, the local and remote
IPv4 address and port of the host socket in network byte order, the
SELF and NONBLOCK status flags, and the receive and send buffer sizes
sceNetGetsockopt reports for the socket. The other fields are 0. It
returns 1, the number of records written, and EINVAL for a null record
or an n below 1, as shadPS4 does.
An s below 0 lists the sockets as shadPS4 does: with a null record it
returns their number, otherwise it writes up to n records in socket
order and returns how many it wrote. Unlike shadPS4, which lists IPv6
sockets with zero addresses and ignores flags, an IPv6 socket fails
with EAFNOSUPPORT and is left out of the count and the list, and a
flags bit other than 0x40 fails with EINVAL. SharpProspero names 0x40
SockInfoTimeWait; closed sockets leave the table at once, so it lists
the same sockets. docs/dev/debt/libSceNet.md lists the fields left 0
and these differences.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Implement sceNetShowRoute as a silent stub returning 0, matching the (void) signature in shadPS4 and the behavior of sceNetShowIfconfig and sceNetShowNetstat. Adds guest unit tests in GuestSceNet.cpp and updates technical debt documentation.
sceNetRecv, sceNetRecvfrom and sceNetRecvmsg on a datagram socket without a
local address waited in wait_for_local_address until the socket was bound,
connected or written to, because only a non-blocking socket made that wait
fail. FreeBSD 11.4 soreceive_dgram fails a MSG_DONTWAIT receive without data
with EWOULDBLOCK. The flags now reach wait_for_local_address and the test
covers an unbound socket.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
sceNetSend, sceNetSendto, sceNetSendmsg, sceNetRecv, sceNetRecvfrom and
sceNetRecvmsg failed with EOPNOTSUPP for MSG_DONTWAIT on Windows, where
FreeBSD 11.4 sosend_generic and soreceive_generic make the one call
non-blocking and fail with EWOULDBLOCK when it would wait. Winsock has no
per-call flag, so the call now asks WSAPoll with a zero timeout whether the
socket is ready and fails with EAGAIN when it is not. The Windows branch of
the test that expected EOPNOTSUPP now runs the checks of the other hosts.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
On PS4 firmware 10.71 sceNetInetPtonEx reads IPv4 text the way
inet_aton does, not the way sceNetInetPton does: "1.2.3" is 1.2.0.3,
"127.1" is 127.0.0.1, hex and octal parts are accepted, a part value
wraps at 32 bits ("1.4294967296" is 1.0.0.0) and the text ends at the
first whitespace. Two differences from BSD inet_aton were measured: a
part starting with "09" is decimal ("0989" is 989), and an octal part
containing 8 or 9 fails.
The flags argument changed nothing for 0, every single bit up to
0x100, 0x10000, 0x80000000 and -1, so it is ignored instead of
throwing. IPv6 still converts as sceNetInetPton does, which matches
the console.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Implement sceNetGetIfName, sceNetGetRandom, sceNetMemoryAllocate and
sceNetMemoryFree with the signatures shadPS4 uses, and cover them in
guest_sce_net_tests.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
guest_sce_net skips its IPv6 checks when sending to ::1 fails and
HostIpv6LoopbackUnavailable confirms the host cannot reach it. The host
probe accepts EADDRNOTAVAIL, ENETUNREACH and EHOSTUNREACH, but the guest
check accepted only ENETUNREACH (51). On a Linux host without ::1 the
send can fail with EADDRNOTAVAIL instead, which the guest sees as 49,
and the test aborted in CheckUnspecifiedIpv6 instead of skipping.
Accept the guest numbers of the same three errors (49, 51 and 65).
sceNetSetsockopt and sceNetGetsockopt failed with EOPNOTSUPP
(0x8041012d) for every level other than SOL_SOCKET, so TCP_NODELAY
could not be set on a libSceNet stream socket; libkernel setsockopt on
such a socket failed the same way. FreeBSD 11.4's sosetopt and sogetopt
(uipc_socket.c) pass any other level to the protocol: tcp_ctloutput
sets TF_NODELAY and returns 0, a get returns t_flags & TF_NODELAY (4),
an option it does not know fails with ENOPROTOOPT, and on a UDP or raw
socket ip_ctloutput and rip_ctloutput fail the IPPROTO_TCP level with
EINVAL. Set TCP_NODELAY on the host socket of a stream socket, read it
back as TF_NODELAY, fail other IPPROTO_TCP options on a stream socket
with ENOPROTOOPT (0x8041012a), and fail the IPPROTO_TCP level with
EINVAL on other socket types, as libkernel already does for its own
sockets.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
#7924 adds its check between CheckInetPtonEx and CheckUsleep, where
these checks were, so the two pull requests conflicted in
GuestSceNet.cpp. The checks now sit after CheckAddressText.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
PS4 firmware 10.71 converts "1.2.3.4" followed by a space, tab, LF,
VT, FF or CR, and anything after it ("1.2.3.4 x"), to 1.2.3.4 and
returns 1, where host inet_pton returns 0. Leading whitespace, other
trailing bytes (0x01, 0xa0), "1.2.3 " and IPv6 text with whitespace
still fail there.
A failed conversion leaves the destination and sceNetErrnoLoc
unchanged on the console; Winsock inet_pton writes the parsed prefix
before failing, so the conversion now goes through a local buffer.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
sceNetUsleep fails with EINVAL for a negative count and otherwise sleeps
through libkernel's sceKernelUsleep, a cancellation point. sceNetInetPtonEx
converts as sceNetInetPton does when its flags are 0 and throws for any
other flags, whose meaning is unknown. The signatures follow shadPS4 and
are recorded as unverified in the libSceNet technical debt.
sceNetCtlRegisterCallback and sceNetCtlUnregisterCallback share the
callback table logic with the V6 pair, which guest_sce_net checks, but
the plain pair is not called by any test. The new block mirrors the V6
one (null function, null output, eight slots, the ninth refused, invalid
ids, slot reuse) with the guest-ABI IgnoreNetCtlEvent callback, and checks
that the two tables are separate.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
sceNetCtlRegisterCallback and sceNetCtlRegisterCallbackV6 stored only
the function and dropped its argument, and sceNetCtlCheckCallback
returned 0 without calling anything, so a title that polls
sceNetCtlCheckCallback until its callback reports IPOBTAINED never
got the event.
Each callback slot now keeps the function, its argument and the last
event it received; unregistration clears the slot. With no IPv4
callback registered, sceNetCtlCheckCallback returns 0 without querying
the host. Otherwise it takes the state from the same host-interface
predicate as sceNetCtlGetState, IPOBTAINED (3) or DISCONNECTED (1)
(shadPS4 src/core/libraries/network/net_ctl_codes.h L27-29
@15abf68c9b52), marks the IPv4 slots whose last event differs and
calls them on the calling thread with their argument after the lock is
released, so a callback can unregister itself. Delivery once per state
change and slot follows KytyPS5 src/libs/network.cpp @9e41d8320a27
(NetCtlCallbackSlot L3447-3451, NetCtlCheckCallback L3648-3661,
NetCtlRegisterCallback L3687-3707); shadPS4 net_ctl_obj.cpp L47-57
delivers on every check instead, and the debt entry records the
difference and that KytyPS5 calls the callbacks under its lock.
sceNetCtlTerm clears both tables, as KytyPS5 NetCtlTerm (L3625-3633)
does. V6 slots keep their argument but get no event, since no source
says which call delivers them.
NetCtlCallback is now an APS5_VABI pointer, so the Windows build calls
the guest function with the System V ABI.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
MXGP 26 (PPSA30839) opens TCP sockets with libkernel's socket() and
later closes them with sceNetSocketClose. libSceNet only looked in its
own socket table, so every one of those closes failed with
SCE_NET_ERROR_EBADF and the host socket stayed open. On the console both
calls work on the same kernel sockets.
A descriptor libSceNet does not own but libkernel has open as a socket
is now closed through libkernel's close(). Anything else still fails
with EBADF. The fallback runs after g_mutex is released, because
libkernel's close() first asks libSceNet's socket closer, which takes it.
guest_sce_net closes a libkernel TCP socket with sceNetSocketClose,
checks that libkernel no longer has it open, and checks that a second
close fails with EBADF.
Co-Authored-By: Claude Opus 5.5 <[email protected]>