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 <noreply@anthropic.com>
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 <noreply@anthropic.com>
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 <noreply@anthropic.com>
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 <noreply@anthropic.com>
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.
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 <noreply@anthropic.com>
MXGP 26 (PPSA30839) opens TCP sockets with libkernel's socket() and
turns on TCP_NODELAY with sceNetSetsockopt. libSceNet only looked in its
own socket table, so the call failed with SCE_NET_ERROR_EBADF. On the
console both calls work on the same kernel sockets.
A descriptor libSceNet does not own but libkernel has open as a socket
now goes to libkernel's setsockopt(), which answers the options it
models. Anything else still fails with EBADF. The fallback runs after
g_mutex is released.
guest_sce_net sets SO_SNDBUF on a libkernel TCP socket through
sceNetSetsockopt, and checks that the call fails with EBADF once the
socket is closed.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Windows answers a SYN to a closed port with a RST and then retransmits
the SYN, so connect to a closed loopback port takes about 2 s before
ECONNREFUSED; FreeBSD fails at once. PS5VR (PPSA99177) makes about 33
such connects to 127.0.0.1:17600 and reaches its home screen 45 s late.
When no socket listens on the destination port (GetExtendedTcpTable),
a loopback connect now sets SIO_TCP_INITIAL_RTO with no SYN
retransmissions and fails at once. A port with a listener keeps the
retransmissions: Windows also answers a SYN to a full accept backlog
with a RST, and the retransmission connects once the server accepts,
as the console's does after FreeBSD drops the SYN.
guest_refused_connect requires ECONNREFUSED in under a second for a
closed port through libkernel connect and sceNetConnect, and a second
connect to a listener with backlog 1 to succeed once the server accepts
the first, through both.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
When the address came during the wait, the host receive that followed
waited its whole SO_RCVTIMEO again, so a receive could take up to twice
the timeout. FreeBSD's soreceive_dgram sleeps once per timeout. Now the
receive waits for a datagram with WSAPoll only for what is left of the
timeout, and fails with EAGAIN when it runs out, as before. Nothing
changes when the socket already had an address or has no timeout.
A receive on an unbound datagram socket checked the host socket every
10 ms, so it went on waiting up to 10 ms after another thread's bind,
connect or send had named the socket. sceNetBind now wakes the waiting
receives. On Windows a datagram connect or a send marks the socket bound
and wakes them once the host socket has its local address; a raw socket
that a send leaves without one stays unmarked and keeps waiting.
On Windows a receive on a datagram socket with no local address waits
for one. A sceNetShutdown of the receive side now ends that wait, and
later receives return 0 at once; a zero-length receive returns 0 without
waiting. FreeBSD's soreceive_dgram returns 0 in both cases. Before, a
thread blocked in such a receive kept waiting after the socket was shut
down.
With libSceNet sockets on ordinary descriptor numbers (#4391), poll on a
libSceNet socket threw "neither a socket nor a pipe": poll only knew
libkernel sockets and pipes, while select already asks libSceNet through
its poller. libSceNet now lends libkernel the host socket behind a
descriptor, together with an owner that keeps it open, so poll puts it in
the same WSAPoll or poll call as libkernel sockets. ProsperoLight
(PPSA99002) stopped here on Linux and now reaches its PC list.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EvDbpwUPTa78oCAJqe7m9K
With libSceNet sockets on ordinary descriptor numbers (#4391), a title
that makes a libSceNet socket non-blocking with plain fcntl(F_GETFL) and
fcntl(F_SETFL, O_NONBLOCK) reached File::Fcntl, whose classification
throws for a socket. libSceNet now registers a status control with
libkernel, like its option setter: F_GETFL returns O_RDWR with the
socket's non-blocking flag, F_SETFL sets it through SO_NBIO, as fcntl
already does for libkernel sockets. ProsperoLight (PPSA99002) stopped
here on Linux.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017WeVmjnnPPKdxVTpr2Vjtw
sceNetRecv, sceNetRecvfrom and sceNetRecvmsg called Winsock's receive
straight away. Winsock fails a receive on a socket with no local address
with WSAEINVAL, so on Windows the call returned EINVAL at once. On the
console the receive waits: FreeBSD 11.4's soreceive_dgram sleeps on the
receive buffer whether or not the socket has an address, returns
EWOULDBLOCK for a non-blocking socket, and a send from another thread
binds the socket so the reply can arrive.
Steam Remote Play (PPSA99350) relies on that. Its stream library opens
its discovery socket, starts receiving on a worker thread, and binds the
socket only when another thread sends the discovery broadcast. On
Windows the worker got EINVAL, took it as fatal and closed the socket
before the broadcast went out, so the title never found a PC running
Steam.
On Windows a receive on a datagram socket with no local address now
waits until a bind, connect or send gives it one, checking every 10 ms.
A non-blocking socket fails with EAGAIN at once, and the receive timeout
ends the wait with EAGAIN. An abort or close ends the wait, and the host
receive then fails as before. Once the socket has an address it is noted
as bound, so later receives skip the check, and a later sceNetBind fails
with EINVAL as it does on the console.
sceNetSetsockopt took the BSD timeout options, SO_SNDTIMEO (0x1005) and
SO_RCVTIMEO (0x1006), like Sony's 0x1105 and 0x1106: as an int of
microseconds read from the first four bytes. A caller passing a struct
timeval got tv_sec taken as microseconds, so {0, 100000} meant to wait
100 ms became 0 and disabled the timeout, and the receive blocked until
data came.
On the console 0x1006 takes a timeval, as FreeBSD 11.4's sosetopt does.
ProsperoLight (PPSA99002) gives it {0, 100000} through sceNetSetsockopt,
and its log from a console (firmware 12.20) shows Moonlight's receive
timing out every 100 ms before the first packet ("Received first video
packet after 400 ms"). Sony moved only these two timeval options to
0x1105 and 0x1106, as ints. On AnyPS5 the receive never times out, so
ProsperoLight's receive threads never wake when the PC stops sending and
LiStopConnection never returns: the stream screen freezes when a session
ends from the PC side. Steam Remote Play (PPSA99350) sets {0, 10000} the
same way and stays on "THE PC DID NOT ANSWER" when the PC doesn't reply.
0x1005 and 0x1006 now go through the timeval path that setsockopt() on a
libSceNet socket already uses (EINVAL for a value shorter than a timeval,
EDOM for a negative or out-of-range one). sceNetGetsockopt gives them
back as a timeval cut to the caller's length, as FreeBSD's sooptcopyout
does. 0x1105 and 0x1106 keep taking an int of microseconds.
sceNetConnect() on a connected datagram socket rejected an address
whose family is AF_UNSPEC (a null address) with EINVAL before touching
the socket, so the socket stayed connected: sceNetGetpeername() still
reported the old peer and datagrams from any other sender were dropped
by the host.
FreeBSD 11.4 disconnects a connected datagram socket first and only then
looks at the address. sys/kern/uipc_socket.c, soconnectat():
* If protocol is connection-based, can only connect once.
* Otherwise, if connected, try to disconnect first. This allows
* user to disconnect by connecting to, e.g., a null address.
*/
if (so->so_state & (SS_ISCONNECTED|SS_ISCONNECTING) &&
((so->so_proto->pr_flags & PR_CONNREQUIRED) ||
(error = sodisconnect(so)))) {
udp_disconnect() (sys/netinet/udp_usrreq.c) clears the peer and keeps
the local port. connect(2): "Datagram sockets may dissolve the
association by connecting to an invalid address, such as a null
address." libkernel's connect gets the same change in #4498.
A connected datagram socket given an address whose family byte is 0 is
now disconnected on the host first (AF_UNSPEC on Linux, a zeroed
address of the socket's family on Windows), and the call then fails
with EINVAL as before. Linux also drops an ephemeral local port on that
disconnect, so the port is bound again on the wildcard address, which
is what FreeBSD leaves. A host failure during the disconnect is
returned as the call's error. Stream sockets keep the previous path.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Steam Remote Play (PPSA99350) reads SO_RCVBUF on its UDP socket with
sceNetGetsockopt, and main fails it with EOPNOTSUPP (0x8041012d),
although sceNetSetsockopt already accepts the option.
FreeBSD 11.4 answers both options with the buffer's high-water mark
(sys/kern/uipc_socket.c, sogetopt), which setsockopt sets to the
requested size through sbreserve:
case SO_SNDBUF:
optval = so->so_snd.sb_hiwat;
goto integer;
case SO_RCVBUF:
optval = so->so_rcv.sb_hiwat;
goto integer;
sceNetGetsockopt now answers both. A size the guest set is reported as
set, as on FreeBSD; reading the host would give twice the value on
Linux, which doubles it. Before the guest sets one, the host socket's
current size is reported.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Resolve a host name to its first IPv6 address through the host resolver
(getaddrinfo with AF_INET6) and write the 16-byte address. The lookup
shares the IPv4 path's resolver bookkeeping (abort preservation, error
state); a name without an IPv6 address fails with RESOLVER_ENODNS like a
failed lookup. Nonzero flags throw.
Metal Gear Solid 4 Master Collection (PPSA18194) imports it and failed to
load without it (unresolved zl35YNs9jnI from libSceNet.prx).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
#4988 adds a constant after NET_SO_RCVTIMEO and #4989 adds a function before receive_error, so defining the SCE timeout constants and timeout_option there made the pull requests conflict.