network: UDPP2P

This commit is contained in:
nmzik
2026-10-17 10:24:45 +02:00
parent 3ecc6b7d1b
commit 56d03f16e9
2 changed files with 70 additions and 6 deletions
+23 -6
View File
@@ -891,6 +891,7 @@ struct P2pEndpoint {
uint16_t virtual_port = 0; uint16_t virtual_port = 0;
int nonblocking = 0; int nonblocking = 0;
int broadcast = 0; int broadcast = 0;
int use_crypto = 0;
int send_buffer = 9216; int send_buffer = 9216;
int receive_buffer = 40 * 1024; int receive_buffer = 40 * 1024;
}; };
@@ -1095,6 +1096,7 @@ static int* P2pSocketOption(P2pEndpoint& endpoint, int option) {
case 0x1001: return &endpoint.send_buffer; case 0x1001: return &endpoint.send_buffer;
case 0x1002: return &endpoint.receive_buffer; case 0x1002: return &endpoint.receive_buffer;
case 0x1200: return &endpoint.nonblocking; case 0x1200: return &endpoint.nonblocking;
case 0x20000: return &endpoint.use_crypto;
default: return nullptr; 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; return -1;
} }
NativeSocket socket = INVALID_NATIVE_SOCKET; NativeSocket socket = INVALID_NATIVE_SOCKET;
std::shared_ptr<SocketTransport> transport; SocketSlot state;
if (!GetSocketBackend(s, &socket, nullptr, &transport)) { if (!GetSocketBackend(s, &socket, &state)) {
return -1; 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) { if (host_flags < 0) {
return -1; 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<SocketIoLength>( const auto host_len = static_cast<SocketIoLength>(
std::min<uint64_t>(len, std::numeric_limits<SocketIoLength>::max())); std::min<uint64_t>(len, std::numeric_limits<SocketIoLength>::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); SocketLength host_addrlen = sizeof(host_addr);
int64_t result = 0; int64_t result = 0;
#if defined(_WIN32) #if defined(_WIN32)
if (transport->type == SOCK_STREAM) { if (state.transport->type == SOCK_STREAM) {
if (addr != nullptr && if (addr != nullptr &&
::getpeername(socket, reinterpret_cast<sockaddr*>(&host_addr), &host_addrlen) != 0) { ::getpeername(socket, reinterpret_cast<sockaddr*>(&host_addr), &host_addrlen) != 0) {
return SetHostSocketError(); return SetHostSocketError();
} }
result = ReceiveStream(*transport, static_cast<char*>(buf), host_len, host_flags, result = ReceiveStream(*state.transport, static_cast<char*>(buf), host_len, host_flags,
(flags & 0x80) != 0); (flags & 0x80) != 0);
if (result < 0) { if (result < 0) {
return result; return result;
@@ -2489,7 +2506,7 @@ int64_t KYTY_SYSV_ABI Recvfrom(int s, void* buf, uint64_t len, int flags, void*
} else } else
#endif #endif
{ {
transport.reset(); state.transport.reset();
if (addr == nullptr) { if (addr == nullptr) {
result = ::recv(socket, static_cast<char*>(buf), host_len, host_flags); result = ::recv(socket, static_cast<char*>(buf), host_len, host_flags);
} else { } else {
+47
View File
@@ -2173,6 +2173,53 @@ void CheckSocketWakeup() {
"write shutdown drains queued bytes, signals EOF, and preserves reverse traffic"); "write shutdown drains queued bytes, signals EOF, and preserves reverse traffic");
Check(Net::SocketClose(shutdown_reader) == 0 && Net::SocketClose(shutdown_writer) == 0, Check(Net::SocketClose(shutdown_reader) == 0 && Net::SocketClose(shutdown_writer) == 0,
"close half-shutdown sockets"); "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<uint8_t, 16> 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"); Check(Net::SocketClose(writer) == 0, "close wake writer");
*net_errno = Libs::Posix::POSIX_EINVAL; *net_errno = Libs::Posix::POSIX_EINVAL;
Check(net_recv(reader, received.data(), received.size(), 0) == 0 && Check(net_recv(reader, received.data(), received.size(), 0) == 0 &&