From 56d03f16e90b2a3ef340046918d11fdf6b892cff Mon Sep 17 00:00:00 2001 From: nmzik Date: Sat, 17 Oct 2026 10:24:45 +0200 Subject: [PATCH] network: UDPP2P --- src/libs/network.cpp | 29 +++++++++++++++----- tests/KernelFileSystemTests.cpp | 47 +++++++++++++++++++++++++++++++++ 2 files changed, 70 insertions(+), 6 deletions(-) diff --git a/src/libs/network.cpp b/src/libs/network.cpp index 5b330c62e..f9d5cd90c 100644 --- a/src/libs/network.cpp +++ b/src/libs/network.cpp @@ -891,6 +891,7 @@ struct P2pEndpoint { uint16_t virtual_port = 0; int nonblocking = 0; int broadcast = 0; + int use_crypto = 0; int send_buffer = 9216; int receive_buffer = 40 * 1024; }; @@ -1095,6 +1096,7 @@ static int* P2pSocketOption(P2pEndpoint& endpoint, int option) { case 0x1001: return &endpoint.send_buffer; case 0x1002: return &endpoint.receive_buffer; case 0x1200: return &endpoint.nonblocking; + case 0x20000: return &endpoint.use_crypto; default: return nullptr; } } @@ -2459,9 +2461,9 @@ int64_t KYTY_SYSV_ABI Recvfrom(int s, void* buf, uint64_t len, int flags, void* return -1; } - NativeSocket socket = INVALID_NATIVE_SOCKET; - std::shared_ptr transport; - if (!GetSocketBackend(s, &socket, nullptr, &transport)) { + NativeSocket socket = INVALID_NATIVE_SOCKET; + SocketSlot state; + if (!GetSocketBackend(s, &socket, &state)) { return -1; } @@ -2469,6 +2471,21 @@ int64_t KYTY_SYSV_ABI Recvfrom(int s, void* buf, uint64_t len, int flags, void* if (host_flags < 0) { return -1; } + if (state.p2p) { + if (!state.p2p->bound || (!state.p2p->nonblocking && (flags & 0x80) == 0)) { + return SetGuestSocketError(Posix::POSIX_EOPNOTSUPP); + } + char pending; + if (::recv(socket, &pending, sizeof(pending), MSG_PEEK) < 0) { + SetHostSocketError(); + // Winsock reports a peeked datagram's truncation as EMSGSIZE. + if (*Posix::GetErrorAddr() != Posix::POSIX_EMSGSIZE) { + return -1; + } + } + // Pending UDPP2P packets still require framing and optional decryption. + return SetGuestSocketError(Posix::POSIX_EOPNOTSUPP); + } const auto host_len = static_cast( std::min(len, std::numeric_limits::max())); @@ -2476,12 +2493,12 @@ int64_t KYTY_SYSV_ABI Recvfrom(int s, void* buf, uint64_t len, int flags, void* SocketLength host_addrlen = sizeof(host_addr); int64_t result = 0; #if defined(_WIN32) - if (transport->type == SOCK_STREAM) { + if (state.transport->type == SOCK_STREAM) { if (addr != nullptr && ::getpeername(socket, reinterpret_cast(&host_addr), &host_addrlen) != 0) { return SetHostSocketError(); } - result = ReceiveStream(*transport, static_cast(buf), host_len, host_flags, + result = ReceiveStream(*state.transport, static_cast(buf), host_len, host_flags, (flags & 0x80) != 0); if (result < 0) { return result; @@ -2489,7 +2506,7 @@ int64_t KYTY_SYSV_ABI Recvfrom(int s, void* buf, uint64_t len, int flags, void* } else #endif { - transport.reset(); + state.transport.reset(); if (addr == nullptr) { result = ::recv(socket, static_cast(buf), host_len, host_flags); } else { diff --git a/tests/KernelFileSystemTests.cpp b/tests/KernelFileSystemTests.cpp index c1407c676..4a417f6ef 100644 --- a/tests/KernelFileSystemTests.cpp +++ b/tests/KernelFileSystemTests.cpp @@ -2173,6 +2173,53 @@ void CheckSocketWakeup() { "write shutdown drains queued bytes, signals EOF, and preserves reverse traffic"); Check(Net::SocketClose(shutdown_reader) == 0 && Net::SocketClose(shutdown_writer) == 0, "close half-shutdown sockets"); + const int encrypted = Net::Socket(2, 6, 0); + int crypto = -1; + uint32_t crypto_size = sizeof(crypto); + Check(encrypted >= 0 && + Net::Getsockopt(encrypted, 0xffff, 0x20000, &crypto, &crypto_size) == 0 && + crypto == 0, + "UDPP2P encryption defaults to disabled"); + for (const int setting : {7, 0}) { + Check(Net::Setsockopt(encrypted, 0xffff, 0x20000, &setting, sizeof(setting)) == 0 && + Net::Getsockopt(encrypted, 0xffff, 0x20000, &crypto, &crypto_size) == 0 && + crypto == (setting != 0), + "UDPP2P encryption configuration retains normalized enable and disable values"); + Check(Net::Send(encrypted, "x", 1, 0) == -1 && + *Libs::Posix::GetErrorAddr() == Libs::Posix::POSIX_EOPNOTSUPP, + "UDPP2P configuration never exposes unframed plaintext transport"); + } + std::array p2p_address {16, 2, 0x0e, 0x4a, 127, 0, 0, 1, 0x1e, 0x61}; + Check(Net::Bind(encrypted, p2p_address.data(), p2p_address.size()) == 0, + "bind UDPP2P physical and virtual ports"); + char untouched = '!'; + auto source = p2p_address; + uint32_t source_size = source.size(); + Check(Net::Recvfrom(encrypted, &untouched, 1, 0x80, source.data(), &source_size) == -1 && + *Libs::Posix::GetErrorAddr() == Libs::Posix::POSIX_EWOULDBLOCK, + "per-call nonblocking UDPP2P receive reports an actually empty queue"); + Check(Net::Setsockopt(encrypted, 0xffff, 0x1200, &enabled, sizeof(enabled)) == 0 && + Net::Setsockopt(encrypted, 0xffff, 0x20000, &enabled, sizeof(enabled)) == 0 && + Net::Recvfrom(encrypted, &untouched, 1, 0, source.data(), &source_size) == -1 && + *Libs::Posix::GetErrorAddr() == Libs::Posix::POSIX_EWOULDBLOCK && + untouched == '!' && source == p2p_address && source_size == source.size(), + "empty encrypted nonblocking receive preserves all caller outputs"); + const int raw_sender = Net::Socket(2, 2, 0); + Check(raw_sender >= 0 && + Net::Sendto(raw_sender, "pending", 7, 0, p2p_address.data(), p2p_address.size()) == 7, + "queue a datagram requiring unsupported UDPP2P processing"); + const auto pending_deadline = std::chrono::steady_clock::now() + std::chrono::seconds(2); + while (Net::Recv(encrypted, &untouched, 1, 0) == -1 && + *Libs::Posix::GetErrorAddr() == Libs::Posix::POSIX_EWOULDBLOCK && + std::chrono::steady_clock::now() < pending_deadline) { + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + } + Check(*Libs::Posix::GetErrorAddr() == Libs::Posix::POSIX_EOPNOTSUPP && + Net::Recv(encrypted, &untouched, 1, 0) == -1 && + *Libs::Posix::GetErrorAddr() == Libs::Posix::POSIX_EOPNOTSUPP && untouched == '!', + "queued packets remain queued and unsupported instead of masquerading as empty"); + Check(Net::SocketClose(encrypted) == 0 && Net::SocketClose(raw_sender) == 0, + "close UDPP2P readiness fixtures"); Check(Net::SocketClose(writer) == 0, "close wake writer"); *net_errno = Libs::Posix::POSIX_EINVAL; Check(net_recv(reader, received.data(), received.size(), 0) == 0 &&