mirror of
https://github.com/boykopovar/AnyPS5.git
synced 2026-10-27 00:28:14 +00:00
Merge pull request #11037 from Bram910/fix/net-send-nosignal
fix(libSceNet): accept MSG_NOSIGNAL in send, sendto and sendmsg
This commit is contained in:
3 files changed
+41
-2
No files matched your search
@@ -168,6 +168,7 @@ constexpr int NET_SO_ACCEPTCONN = 0x2;
|
||||
constexpr int NET_MSG_PEEK = 0x2;
|
||||
constexpr int NET_MSG_TRUNC = 0x10;
|
||||
constexpr int NET_MSG_DONTWAIT = 0x80;
|
||||
constexpr int NET_MSG_NOSIGNAL = 0x20000;
|
||||
constexpr int NET_UIO_MAXIOV = 1024;
|
||||
|
||||
#ifdef _WIN32
|
||||
@@ -1264,7 +1265,7 @@ int64_t APS5_VABI sceNetRecvfrom(int s, void* buf, size_t len, int flags, void*
|
||||
}
|
||||
|
||||
int64_t APS5_VABI sceNetSend(int s, const void* buf, size_t len, int flags) {
|
||||
if (!message_flags_supported(flags, NET_MSG_DONTWAIT)) return fail(NET_EOPNOTSUPP);
|
||||
if (!message_flags_supported(flags, NET_MSG_DONTWAIT | NET_MSG_NOSIGNAL)) return fail(NET_EOPNOTSUPP);
|
||||
if (!buf && len) return fail(NET_EINVAL);
|
||||
if (len > INT_MAX) return fail(NET_EMSGSIZE);
|
||||
Sock socket;
|
||||
@@ -1291,7 +1292,7 @@ int64_t APS5_VABI GuestNetSend_nid_no_patch(int s, const void* buf, size_t len,
|
||||
}
|
||||
|
||||
int64_t APS5_VABI sceNetSendto(int s, const void* buf, size_t len, int flags, const void* to, uint32_t tolen) {
|
||||
if (!message_flags_supported(flags, NET_MSG_DONTWAIT)) return fail(NET_EOPNOTSUPP);
|
||||
if (!message_flags_supported(flags, NET_MSG_DONTWAIT | NET_MSG_NOSIGNAL)) return fail(NET_EOPNOTSUPP);
|
||||
if (!buf && len) return fail(NET_EINVAL);
|
||||
if (len > INT_MAX) return fail(NET_EMSGSIZE);
|
||||
Sock socket;
|
||||
|
||||
@@ -615,6 +615,42 @@ static void CheckDontWait() {
|
||||
Require(sceNetSocketClose(receiver) == 0 && sceNetSocketClose(sender) == 0);
|
||||
}
|
||||
|
||||
static void CheckNoSignal() {
|
||||
constexpr int NoSignal = 0x20000;
|
||||
constexpr int DontWait = 0x80;
|
||||
const int listener = sceNetSocket("nosignal-listener", 2, 1, 6);
|
||||
Require(listener >= 0);
|
||||
std::array<std::uint8_t, 16> address{16, 2, 0, 0, 127, 0, 0, 1};
|
||||
Require(sceNetBind_nid_postfix(listener, address.data(), address.size()) == 0);
|
||||
Require(sceNetListen(listener, 1) == 0);
|
||||
std::uint32_t size = address.size();
|
||||
Require(sceNetGetsockname(listener, address.data(), &size) == 0);
|
||||
const int client = sceNetSocket("nosignal-client", 2, 1, 6);
|
||||
Require(client >= 0);
|
||||
Require(sceNetConnect(client, address.data(), address.size()) == 0);
|
||||
const int accepted = sceNetAccept(listener, nullptr, nullptr);
|
||||
Require(accepted >= 0);
|
||||
char reply[] = "nosignal reply";
|
||||
Require(sceNetSend(accepted, reply, 4, NoSignal) == 4);
|
||||
Require(sceNetSendto(accepted, reply + 4, 4, NoSignal | DontWait, nullptr, 0) == 4);
|
||||
NetIovec out[] = {{reply + 8, sizeof(reply) - 8}};
|
||||
NetMsghdr message{nullptr, 0, out, 1, nullptr, 0, 0};
|
||||
Require(sceNetSendmsg(accepted, &message, NoSignal) == static_cast<std::int64_t>(sizeof(reply) - 8));
|
||||
char received[sizeof(reply)]{};
|
||||
std::size_t total = 0;
|
||||
while (total < sizeof(reply)) {
|
||||
const auto count = sceNetRecv(client, received + total, sizeof(received) - total, 0);
|
||||
Require(count > 0);
|
||||
total += static_cast<std::size_t>(count);
|
||||
}
|
||||
Require(std::memcmp(received, reply, sizeof(reply)) == 0);
|
||||
Require(Failed(sceNetSend(accepted, reply, 1, NoSignal | 0x2), 45));
|
||||
Require(sceNetShutdown(accepted, 1) == 0);
|
||||
Require(Failed(sceNetSend(accepted, reply, 1, NoSignal), 32));
|
||||
Require(Failed(sceNetSendto(accepted, reply, 1, NoSignal, nullptr, 0), 32));
|
||||
Require(sceNetSocketClose(client) == 0 && sceNetSocketClose(accepted) == 0 && sceNetSocketClose(listener) == 0);
|
||||
}
|
||||
|
||||
static void CheckReceiveTimeout(int receiver, int sender, const std::array<std::uint8_t, 16>& address) {
|
||||
const KernelTimeval timeout{0, 50000};
|
||||
Require(sceNetSetsockopt(receiver, 0xffff, 0x1006, &timeout, sizeof(timeout)) == 0);
|
||||
@@ -1933,6 +1969,7 @@ int main() {
|
||||
CheckIfFlags();
|
||||
CheckRejectedArguments();
|
||||
CheckDontWait();
|
||||
CheckNoSignal();
|
||||
CheckAddressText(2, "127.0.0.1");
|
||||
CheckAddressText(2, "255.255.255.255");
|
||||
CheckAddressText(28, "::1");
|
||||
|
||||
@@ -56,6 +56,7 @@
|
||||
- [sceNetGetSockInfo6](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) is not measured on a console; shadPS4 stubs it. It lists only IPv6 sockets and `flags` 0x1000 adds IPv4 sockets, reported with the IPv4 addresses, all-zero IPv6 addresses and no `F_IPV6` (0x400000), which every IPv6 socket sets ([SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/5dfdb4b0b166e83dc9ae275001afb695f12c7174/src/SharpProspero/Interop/Net/Socket.cs#L251-L343), [flags](https://github.com/SvenGDK/SharpProspero/blob/5dfdb4b0b166e83dc9ae275001afb695f12c7174/src/SharpProspero/Interop/Net/Socket.cs#L698-L705), [docs](https://github.com/SvenGDK/SharpProspero/blob/5dfdb4b0b166e83dc9ae275001afb695f12c7174/docs/net.md#L301-L304)). The default of leaving IPv4 sockets out, the zero IPv6 addresses of an IPv4 socket and accepting 0x40 are inferred from those names, not documented. An IPv4 socket without 0x1000 fails with `EAFNOSUPPORT`. The other gaps are those of `sceNetGetSockInfo`.
|
||||
- [sceNetSocketClose, sceNetEpollControl and sceNetEpollWait](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - close and concurrent registration ordering are unmeasured on a console. Closed descriptors retire their registrations, and a wait discards snapshots invalidated by close, DEL or MOD; descriptor identity follows [Linux epoll(7)](https://man7.org/linux/man-pages/man7/epoll.7.html). ADD, MOD and DEL reject closed or invalid socket descriptors with `EBADF`, as [Linux epoll_ctl(2)](https://man7.org/linux/man-pages/man2/epoll_ctl.2.html) does; this error ordering is unmeasured on a console. Duplicate-descriptor alias lifetime is not modeled by this epoll backend.
|
||||
- [MSG_DONTWAIT](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - console ordering between competing readers and writers is unmeasured. The per-call behavior follows FreeBSD 11.4's `soreceive_generic` and `sosend_generic` (`sys/kern/uipc_socket.c`); `0x80` is FreeBSD's flag value (`sys/sys/socket.h`). On Windows native sockets stay non-blocking. `MSG_DONTWAIT` and guest `SO_NBIO` return one host transfer's result. Blocking stream sends queue the whole buffer with one deadline and return progress after a timeout or host error. SSL uses this path through the native owner, retaining the socket, its mode and timeouts. Readiness waits poll at most every 10 ms for local close, so idle blocked operations wake periodically.
|
||||
- [MSG_NOSIGNAL](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - `sceNetSend`, `sceNetSendto` and `sceNetSendmsg` accept `0x20000`, FreeBSD's `MSG_NOSIGNAL` (`sys/sys/socket.h`), which ProsperoTV (PPSA99003) passes on every phone-remote reply. Firmware 13.42's `libSceNet.sprx` adds `0x40020000` to the flags of every send and refuses no flag itself ([measured in an emulator](https://github.com/boykopovar/AnyPS5/pull/11037#issuecomment-6400759240)), so passing the flag changes nothing on the console. What `0x40000000` means and which flags the kernel refuses are unknown; other flags still fail with `EOPNOTSUPP` here. Host sends outside Windows already pass `MSG_NOSIGNAL` and Windows raises no `SIGPIPE`, so a send on a stream whose write side is shut down fails with `EPIPE`
|
||||
- [Stream sends on macOS](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - a `MSG_DONTWAIT` transfer also passes XNU's `MSG_NBIO` (`0x20000`, absent from the SDK headers), because XNU's `MSG_DONTWAIT` does not keep a stream send from waiting for buffer space. A blocking stream send goes to the host in sends of 64 KiB, so an error after some progress, such as an abort, reports the bytes already queued, which one host send would drop; the bytes that the failing 64 KiB send had queued are not counted
|
||||
- [sceNetRecv, sceNetRecvfrom and sceNetRecvmsg](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) on a datagram socket with no local address wait on Windows and macOS until a bind, connect or send gives it one, checking the host socket every 10 ms. Winsock fails such a receive with `WSAEINVAL`, where FreeBSD 11.4's `soreceive_dgram` waits for a datagram (`sys/kern/uipc_socket.c`). On macOS the host receive waits, but a receive-side `shutdown` does not wake it. A non-blocking socket fails with `EAGAIN` at once, and the receive timeout ends the wait with `EAGAIN`; when the address comes first, the call waits for a datagram only for what is left of the timeout. A `sceNetShutdown` of the receive side ends the wait and the receive returns 0, and a zero-length receive returns 0 without waiting, as `soreceive_dgram` does when the socket can't receive more or nothing was asked for. An abort or close ends the wait, and the host receive then fails as it did before
|
||||
- [sceNetResolverAbort and sceNetResolverStartNtoaMultipleRecords](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) follow shadPS4 ([net.cpp](https://github.com/shadps4-emu/shadPS4/blob/811bfcd7d17ed8ae975c477d9ff28abc744e1ca8/src/core/libraries/net/net.cpp), [net_resolver.cpp](https://github.com/shadps4-emu/shadPS4/blob/811bfcd7d17ed8ae975c477d9ff28abc744e1ca8/src/core/libraries/net/net_resolver.cpp)); the `(int rid, int flags)` signature of the abort also matches [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/5dfdb4b0b166e83dc9ae275001afb695f12c7174/src/SharpProspero/Interop/Net/Socket.cs). Unverified on hardware. Lookups run synchronously over the host resolver, which cannot be interrupted: an abort while a lookup runs on another thread returns at once, and that lookup fails with `EINTR` without writing its output only when the host call returns; the abort flags are then ignored, as shadPS4 does for a running lookup. With nothing running, abort flag 1 (NTOA preservation) or 2 (ATON preservation) makes the next `sceNetResolverStartNtoa*` or `sceNetResolverStartAton` call on that resolver fail with `EINTR`, reported by `sceNetResolverGetError`, after its arguments are checked. Unlike shadPS4, which ignores them, other abort flag bits throw, and `sceNetResolverStartNtoaMultipleRecords` throws for any flag, including async (1) and 0x10000 (IP address literals disabled), as `sceNetResolverStartNtoaMultipleRecordsEx` does
|
||||
|
||||
Reference in new issue
Block a user