From d60c411ebcde5639704df3cebc3f3f50b3757e2e Mon Sep 17 00:00:00 2001 From: cirakg Date: Mon, 2 Nov 2026 21:14:42 +0100 Subject: network: deliver room datagrams through ProxySocket::Recv When connected to an emulator room every guest socket is a ProxySocket. Its Recv was a stub that always returned zero bytes, so games that read UDP replies with recv() never received them. Mario Kart World LAN Play reads its discovery replies this way, so guests never saw the host's room in the LAN browser. Delegate Recv to RecvFrom without an address, so queued room packets, non-blocking, timeout, peek and truncation behaviour match recvfrom(). Assisted-by: Claude Opus 5.5 (investigation and implementation) --- src/core/internal_network/socket_proxy.cpp | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) (limited to 'src/core/internal_network/socket_proxy.cpp') diff --git a/src/core/internal_network/socket_proxy.cpp b/src/core/internal_network/socket_proxy.cpp index 744765bfcc..d3ae33d065 100644 --- a/src/core/internal_network/socket_proxy.cpp +++ b/src/core/internal_network/socket_proxy.cpp @@ -102,11 +102,7 @@ Errno ProxySocket::Shutdown(ShutdownHow how) { } std::pair ProxySocket::Recv(int flags, std::span message) { - LOG_WARNING(Network, "(STUBBED) called"); - ASSERT(flags == 0); - ASSERT(message.size() < static_cast((std::numeric_limits::max)())); - - return {static_cast(0), Errno::SUCCESS}; + return RecvFrom(flags, message, nullptr); } std::pair ProxySocket::RecvFrom(int flags, std::span message, SockAddrIn* addr) { -- cgit v1.2.3 From ba3e708dbdc6f0e3124500b79bc28aa35763580b Mon Sep 17 00:00:00 2001 From: cirakg Date: Mon, 2 Nov 2026 21:36:20 +0100 Subject: network: fix ProxySocket receive edge cases Routing recv() through RecvFrom made several old RecvFrom problems reachable from the normal read path: - A partial TCP read kept zero bytes in front of the unread data. - A blocking read busy-waited with yield() and reported ETIMEDOUT. - MSG_PEEK hit the flags == 0 assert. - A UDP datagram larger than the buffer was dropped with EMSGSIZE. Wait on a condition variable signalled when a packet arrives, and report an expired wait as EAGAIN like an SO_RCVTIMEO timeout. Keep the existing 5s cap for sockets without a receive timeout. Allow MSG_PEEK. Truncate oversized datagrams as BSD recv() does, and keep the unread tail of a TCP packet for the next read. Assisted-by: Claude Opus 5.5 (investigation and implementation) --- src/core/internal_network/socket_proxy.cpp | 83 ++++++++++++------------------ src/core/internal_network/socket_proxy.h | 4 +- 2 files changed, 36 insertions(+), 51 deletions(-) (limited to 'src/core/internal_network/socket_proxy.cpp') diff --git a/src/core/internal_network/socket_proxy.cpp b/src/core/internal_network/socket_proxy.cpp index d3ae33d065..f42ed61e21 100644 --- a/src/core/internal_network/socket_proxy.cpp +++ b/src/core/internal_network/socket_proxy.cpp @@ -3,8 +3,9 @@ // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include #include -#include +#include #include "common/assert.h" #include "common/logging.h" @@ -43,8 +44,11 @@ void ProxySocket::HandleProxyPacket(const ProxyPacket& packet) { auto decompressed = packet; decompressed.data = Common::Compression::DecompressDataZSTD(packet.data); - std::lock_guard guard(packets_mutex); - received_packets.push(decompressed); + { + std::lock_guard guard(packets_mutex); + received_packets.push(std::move(decompressed)); + } + packets_cv.notify_one(); } template @@ -106,37 +110,26 @@ std::pair ProxySocket::Recv(int flags, std::span message) { } std::pair ProxySocket::RecvFrom(int flags, std::span message, SockAddrIn* addr) { - ASSERT(flags == 0); + ASSERT((flags & ~FLAG_MSG_PEEK) == 0); ASSERT(message.size() < static_cast((std::numeric_limits::max)())); - // TODO (flTobi): Verify the timeout behavior and break when connection is lost - const auto timestamp = std::chrono::steady_clock::now(); - // When receive_timeout is set to zero, the socket is supposed to wait indefinitely until a - // packet arrives. In order to prevent lost packets from hanging the emulation thread, we set - // the timeout to 5s instead - const auto timeout = receive_timeout == 0 ? 5000 : receive_timeout; - while (true) { - { - std::lock_guard guard(packets_mutex); - if (received_packets.size() > 0) { - return ReceivePacket(flags, message, addr, message.size()); - } - } - + std::unique_lock lock(packets_mutex); + if (received_packets.empty()) { if (!blocking) { return {-1, Errno::AGAIN}; } - - std::this_thread::yield(); - - const auto time_diff = std::chrono::steady_clock::now() - timestamp; - const auto time_diff_ms = - std::chrono::duration_cast(time_diff).count(); - - if (time_diff_ms > timeout) { - return {-1, Errno::TIMEDOUT}; + // TODO (flTobi): Verify the timeout behavior and break when connection is lost + // When receive_timeout is set to zero, the socket is supposed to wait indefinitely until a + // packet arrives. In order to prevent lost packets from hanging the emulation thread, we set + // the timeout to 5s instead. An expired wait is reported like an SO_RCVTIMEO timeout. + const u32 timeout = receive_timeout == 0 ? 5000 : receive_timeout; + if (!packets_cv.wait_for(lock, std::chrono::milliseconds{timeout}, + [this] { return !received_packets.empty(); })) { + return {-1, Errno::AGAIN}; } } + + return ReceivePacket(flags, message, addr, message.size()); } std::pair ProxySocket::ReceivePacket(int flags, std::span message, SockAddrIn* addr, @@ -148,32 +141,22 @@ std::pair ProxySocket::ReceivePacket(int flags, std::span messag addr->portno = packet.local_endpoint.portno; // The senders port number } - bool peek = (flags & FLAG_MSG_PEEK) != 0; - std::size_t read_bytes; - if (packet.data.size() > max_length) { - read_bytes = max_length; - memcpy(message.data(), packet.data.data(), max_length); - - if (protocol == Protocol::UDP) { - if (!peek) { - received_packets.pop(); - } - return {-1, Errno::MSGSIZE}; - } else if (protocol == Protocol::TCP) { - std::vector numArray(packet.data.size() - max_length); - std::copy(packet.data.begin() + max_length, packet.data.end(), - std::back_inserter(numArray)); - packet.data = numArray; - } + const std::size_t read_bytes = (std::min)(packet.data.size(), max_length); + std::memcpy(message.data(), packet.data.data(), read_bytes); + + if ((flags & FLAG_MSG_PEEK) != 0) { + return {static_cast(read_bytes), Errno::SUCCESS}; + } + + if (protocol == Protocol::TCP && read_bytes < packet.data.size()) { + // Keep the unread part of the stream for the next call + packet.data.erase(packet.data.begin(), packet.data.begin() + read_bytes); } else { - read_bytes = packet.data.size(); - memcpy(message.data(), packet.data.data(), read_bytes); - if (!peek) { - received_packets.pop(); - } + // Like BSD recv(), the rest of a datagram that does not fit is discarded + received_packets.pop(); } - return {static_cast(read_bytes), Errno::SUCCESS}; + return {static_cast(read_bytes), Errno::SUCCESS}; } std::pair ProxySocket::Send(std::span message, int flags) { diff --git a/src/core/internal_network/socket_proxy.h b/src/core/internal_network/socket_proxy.h index caf5d1ee23..8343fcbae1 100644 --- a/src/core/internal_network/socket_proxy.h +++ b/src/core/internal_network/socket_proxy.h @@ -1,10 +1,11 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #pragma once +#include #include #include #include @@ -92,6 +93,7 @@ private: Protocol protocol; std::mutex packets_mutex; + std::condition_variable packets_cv; }; } // namespace Network -- cgit v1.2.3 From 694bc8df5c8d5b614bdbdbfa43c57a08316f88c2 Mon Sep 17 00:00:00 2001 From: cirakg Date: Mon, 2 Nov 2026 23:40:30 +0100 Subject: network: fix room host LAN Play and room socket review issues The room host and room moderators are in the Moderator state, not Joined, so the airplane mode override for joined rooms did not apply to them and LAN Play still returned them to the menu. Accept both states. Share the room address override between the NIFM profile and IP config calls so they cannot drift apart. recv() on a room TCP socket now waited for the receive timeout, because Recv goes through RecvFrom. Room TCP sockets have no peer, so keep the previous immediate end-of-stream result when nothing is queued. Close() now drops queued packets and wakes a blocked recv, which then fails with EBADF. The socket state that the room thread reads when it queues a packet is now guarded by the packet mutex. Assisted-by: Claude Opus 5.5 (investigation and implementation) --- src/core/hle/service/nifm/nifm.cpp | 41 ++++++++++---------- src/core/internal_network/socket_proxy.cpp | 62 +++++++++++++++++++++--------- 2 files changed, 65 insertions(+), 38 deletions(-) (limited to 'src/core/internal_network/socket_proxy.cpp') diff --git a/src/core/hle/service/nifm/nifm.cpp b/src/core/hle/service/nifm/nifm.cpp index 153b0f2193..e0a23f7a63 100644 --- a/src/core/hle/service/nifm/nifm.cpp +++ b/src/core/hle/service/nifm/nifm.cpp @@ -73,17 +73,17 @@ static u128 MakeUuidFromName(std::string_view name) { // A joined room is the emulated network, even in airplane mode. Its sockets only reach room // members, and LAN games (e.g. Mario Kart World LAN Play) need their connection request accepted. static bool IsNetworkAvailable() { - if (const auto room_member = Network::GetRoomMember().lock(); - room_member && room_member->GetState() == Network::RoomMember::State::Joined) { - return true; + if (const auto room_member = Network::GetRoomMember().lock()) { + // The room host and moderators are in the Moderator state instead of Joined + const auto state = room_member->GetState(); + if (state == Network::RoomMember::State::Joined || + state == Network::RoomMember::State::Moderator) { + return true; + } } return Network::GetHostIPv4Address().has_value() && !Settings::values.airplane_mode.GetValue(); } -// Room fake IPs are spread over 192.168.0.0/16, and some room servers give members different /24s. -// A host /24 mask would then put room members on different subnets, so LAN games ignore each other. -constexpr Network::IPv4Address RoomSubnetMask{255, 255, 0, 0}; - // This is nn::nifm::RequestState enum class RequestState : u32 { NotSubmitted = 1, @@ -124,6 +124,17 @@ struct IpAddressSetting { }; static_assert(sizeof(IpAddressSetting) == 0xD, "IpAddressSetting has incorrect size."); +// Room fake IPs are spread over 192.168.0.0/16, and some room servers give members different /24s. +// A host /24 mask would then put room members on different subnets, so LAN games ignore each other. +constexpr Network::IPv4Address RoomSubnetMask{255, 255, 0, 0}; + +static void ApplyRoomAddress(const Network::RoomMember& room_member, IpAddressSetting& setting) { + if (room_member.IsConnected()) { + setting.ip_address = room_member.GetFakeIpAddress(); + setting.subnet_mask = RoomSubnetMask; + } +} + // This is nn::nifm::DnsSetting struct DnsSetting { bool is_automatic{}; @@ -585,11 +596,7 @@ void IGeneralService::GetCurrentNetworkProfile(HLERequestContext& ctx) { std::strncpy(profile.network_name.data(), st.ssid, sizeof(profile.network_name) - 1); if (auto room_member = Network::GetRoomMember().lock()) { - if (room_member->IsConnected()) { - profile.ip_setting_data.ip_address_setting.ip_address = - room_member->GetFakeIpAddress(); - profile.ip_setting_data.ip_address_setting.subnet_mask = RoomSubnetMask; - } + ApplyRoomAddress(*room_member, profile.ip_setting_data.ip_address_setting); profile.ip_setting_data.dns_setting.secondary_dns = {8, 8, 8, 8}; } @@ -726,10 +733,7 @@ void IGeneralService::GetNetworkProfile(HLERequestContext& ctx) { }(); if (auto room_member = Network::GetRoomMember().lock()) { - if (room_member->IsConnected()) { - profile.ip_setting_data.ip_address_setting.ip_address = room_member->GetFakeIpAddress(); - profile.ip_setting_data.ip_address_setting.subnet_mask = RoomSubnetMask; - } + ApplyRoomAddress(*room_member, profile.ip_setting_data.ip_address_setting); } ctx.WriteBuffer(profile); @@ -917,10 +921,7 @@ void IGeneralService::GetCurrentIpConfigInfo(HLERequestContext& ctx) { } if (auto room_member = Network::GetRoomMember().lock()) { - if (room_member->IsConnected()) { - info.ip.ip_address = room_member->GetFakeIpAddress(); - info.ip.subnet_mask = RoomSubnetMask; - } + ApplyRoomAddress(*room_member, info.ip); info.dns.is_automatic = true; info.dns.primary_dns = {1, 1, 1, 1}; info.dns.secondary_dns = {8, 8, 8, 8}; diff --git a/src/core/internal_network/socket_proxy.cpp b/src/core/internal_network/socket_proxy.cpp index f42ed61e21..83eb2ce9d0 100644 --- a/src/core/internal_network/socket_proxy.cpp +++ b/src/core/internal_network/socket_proxy.cpp @@ -31,21 +31,21 @@ ProxySocket::~ProxySocket() { } void ProxySocket::HandleProxyPacket(const ProxyPacket& packet) { - if (protocol != packet.protocol || local_endpoint.portno != packet.remote_endpoint.portno || - closed) { - return; - } - - if (!broadcast && packet.broadcast) { - LOG_INFO(Network, "Received broadcast packet, but not configured for broadcast mode"); - return; - } - - auto decompressed = packet; - decompressed.data = Common::Compression::DecompressDataZSTD(packet.data); - { + // Runs on the room thread, while the game thread may bind, close or reconfigure the socket std::lock_guard guard(packets_mutex); + if (protocol != packet.protocol || local_endpoint.portno != packet.remote_endpoint.portno || + closed) { + return; + } + + if (!broadcast && packet.broadcast) { + LOG_INFO(Network, "Received broadcast packet, but not configured for broadcast mode"); + return; + } + + auto decompressed = packet; + decompressed.data = Common::Compression::DecompressDataZSTD(packet.data); received_packets.push(std::move(decompressed)); } packets_cv.notify_one(); @@ -58,7 +58,10 @@ Errno ProxySocket::SetSockOpt(SOCKET fd_, int option, T value) { } Errno ProxySocket::Initialize(Domain domain, Type type, Protocol socket_protocol) { - protocol = socket_protocol; + { + std::lock_guard guard(packets_mutex); + protocol = socket_protocol; + } SetSockOpt(fd, SO_TYPE, type); return Errno::SUCCESS; @@ -85,6 +88,7 @@ std::pair ProxySocket::GetSockName() { } Errno ProxySocket::Bind(SockAddrIn addr) { + std::lock_guard guard(packets_mutex); if (is_bound) { LOG_WARNING(Network, "Rebinding Socket is unimplemented!"); return Errno::SUCCESS; @@ -106,6 +110,14 @@ Errno ProxySocket::Shutdown(ShutdownHow how) { } std::pair ProxySocket::Recv(int flags, std::span message) { + if (protocol == Protocol::TCP) { + // Room TCP sockets have no peer (Connect and Send are stubs). Report an empty queue as the + // end of the stream, as before, instead of blocking the bsd service until the timeout. + std::lock_guard guard(packets_mutex); + if (received_packets.empty()) { + return {0, Errno::SUCCESS}; + } + } return RecvFrom(flags, message, nullptr); } @@ -114,6 +126,9 @@ std::pair ProxySocket::RecvFrom(int flags, std::span message, So ASSERT(message.size() < static_cast((std::numeric_limits::max)())); std::unique_lock lock(packets_mutex); + if (closed) { + return {-1, Errno::BADF}; + } if (received_packets.empty()) { if (!blocking) { return {-1, Errno::AGAIN}; @@ -124,9 +139,12 @@ std::pair ProxySocket::RecvFrom(int flags, std::span message, So // the timeout to 5s instead. An expired wait is reported like an SO_RCVTIMEO timeout. const u32 timeout = receive_timeout == 0 ? 5000 : receive_timeout; if (!packets_cv.wait_for(lock, std::chrono::milliseconds{timeout}, - [this] { return !received_packets.empty(); })) { + [this] { return closed || !received_packets.empty(); })) { return {-1, Errno::AGAIN}; } + if (closed) { + return {-1, Errno::BADF}; + } } return ReceivePacket(flags, message, addr, message.size()); @@ -217,8 +235,13 @@ std::pair ProxySocket::SendTo(u32 flags, std::span message } Errno ProxySocket::Close() { - fd = INVALID_SOCKET; - closed = true; + { + std::lock_guard guard(packets_mutex); + fd = INVALID_SOCKET; + closed = true; + received_packets = {}; + } + packets_cv.notify_all(); return Errno::SUCCESS; } @@ -239,7 +262,10 @@ Errno ProxySocket::SetReuseAddr(bool enable) { } Errno ProxySocket::SetBroadcast(bool enable) { - broadcast = enable; + { + std::lock_guard guard(packets_mutex); + broadcast = enable; + } return SetSockOpt(fd, SO_BROADCAST, enable ? 1 : 0); } -- cgit v1.2.3 From 78e0a12180a71adf8dffc2c2f6697a0a0735d5ea Mon Sep 17 00:00:00 2001 From: cirakg Date: Tue, 3 Nov 2026 00:17:34 +0100 Subject: network: fix room socket races and share the room network check bsd:s, bsd:u and bsd:a share one file descriptor table, but each service bound its own proxy packet callback, so every room packet was queued three times on each socket. Bind the callback once. The room thread walked the descriptor table without a lock while bsd created and closed sockets, so it could reach a destroyed or not yet created socket. Guard the table with a mutex, and publish a socket only after it is created and initialized. Check airplane mode before an fd is taken, so a refused socket no longer leaks its slot. ProxySocket now initializes protocol, decompresses packets outside the packet lock without copying the compressed data first, and keeps a read offset for partial TCP reads instead of erasing from the front. Add Network::GetRoomIPv4Address(), set only in the Joined and Moderator states. NIFM uses it for network availability and for every reported address, including EnumerateNetworkInterfaces and GetCurrentIpAddress, so no call reports the room address while another rejects the network. Assisted-by: Claude Opus 5.5 (investigation and implementation) --- src/core/hle/service/nifm/nifm.cpp | 37 ++++++++-------- src/core/hle/service/sockets/bsd.cpp | 71 +++++++++++++++++++----------- src/core/hle/service/sockets/bsd.h | 12 +++-- src/core/internal_network/network.cpp | 11 +++++ src/core/internal_network/network.h | 4 ++ src/core/internal_network/socket_proxy.cpp | 29 +++++++++--- src/core/internal_network/socket_proxy.h | 3 +- 7 files changed, 111 insertions(+), 56 deletions(-) (limited to 'src/core/internal_network/socket_proxy.cpp') diff --git a/src/core/hle/service/nifm/nifm.cpp b/src/core/hle/service/nifm/nifm.cpp index e0a23f7a63..8714597f89 100644 --- a/src/core/hle/service/nifm/nifm.cpp +++ b/src/core/hle/service/nifm/nifm.cpp @@ -73,13 +73,8 @@ static u128 MakeUuidFromName(std::string_view name) { // A joined room is the emulated network, even in airplane mode. Its sockets only reach room // members, and LAN games (e.g. Mario Kart World LAN Play) need their connection request accepted. static bool IsNetworkAvailable() { - if (const auto room_member = Network::GetRoomMember().lock()) { - // The room host and moderators are in the Moderator state instead of Joined - const auto state = room_member->GetState(); - if (state == Network::RoomMember::State::Joined || - state == Network::RoomMember::State::Moderator) { - return true; - } + if (Network::GetRoomIPv4Address()) { + return true; } return Network::GetHostIPv4Address().has_value() && !Settings::values.airplane_mode.GetValue(); } @@ -128,9 +123,9 @@ static_assert(sizeof(IpAddressSetting) == 0xD, "IpAddressSetting has incorrect s // A host /24 mask would then put room members on different subnets, so LAN games ignore each other. constexpr Network::IPv4Address RoomSubnetMask{255, 255, 0, 0}; -static void ApplyRoomAddress(const Network::RoomMember& room_member, IpAddressSetting& setting) { - if (room_member.IsConnected()) { - setting.ip_address = room_member.GetFakeIpAddress(); +static void ApplyRoomAddress(IpAddressSetting& setting) { + if (const auto room_ip = Network::GetRoomIPv4Address()) { + setting.ip_address = *room_ip; setting.subnet_mask = RoomSubnetMask; } } @@ -596,7 +591,7 @@ void IGeneralService::GetCurrentNetworkProfile(HLERequestContext& ctx) { std::strncpy(profile.network_name.data(), st.ssid, sizeof(profile.network_name) - 1); if (auto room_member = Network::GetRoomMember().lock()) { - ApplyRoomAddress(*room_member, profile.ip_setting_data.ip_address_setting); + ApplyRoomAddress(profile.ip_setting_data.ip_address_setting); profile.ip_setting_data.dns_setting.secondary_dns = {8, 8, 8, 8}; } @@ -618,6 +613,8 @@ void IGeneralService::EnumerateNetworkInterfaces(HLERequestContext& ctx) { using Network::HostAdapterKind; const auto adapters = Network::GetAvailableNetworkInterfaces(); + const auto selected = Network::GetSelectedNetworkInterface(); + const auto room_ip = Network::GetRoomIPv4Address(); constexpr size_t kEntry = 0x3F0; std::vector blob(adapters.size() * kEntry, 0); @@ -633,6 +630,12 @@ void IGeneralService::EnumerateNetworkInterfaces(HLERequestContext& ctx) { *reinterpret_cast(base + 0x1C) = host.subnet_mask; *reinterpret_cast(base + 0x20) = host.gateway; + // The room traffic goes through the selected adapter, so report it like the other calls + if (room_ip && selected && host.name == selected->name) { + std::memcpy(base + 0x18, room_ip->data(), room_ip->size()); + std::memcpy(base + 0x1C, RoomSubnetMask.data(), RoomSubnetMask.size()); + } + std::string name_utf8 = host.name; name_utf8.resize(0x110, '\0'); std::memcpy(base + 0x2E0, name_utf8.data(), 0x110); @@ -732,9 +735,7 @@ void IGeneralService::GetNetworkProfile(HLERequestContext& ctx) { .wireless_setting_data{wifi}}; }(); - if (auto room_member = Network::GetRoomMember().lock()) { - ApplyRoomAddress(*room_member, profile.ip_setting_data.ip_address_setting); - } + ApplyRoomAddress(profile.ip_setting_data.ip_address_setting); ctx.WriteBuffer(profile); @@ -871,10 +872,8 @@ void IGeneralService::GetCurrentIpAddress(HLERequestContext& ctx) { ipv4.emplace(Network::IPv4Address{0, 0, 0, 0}); } - if (auto room_member = Network::GetRoomMember().lock()) { - if (room_member->IsConnected()) { - ipv4 = room_member->GetFakeIpAddress(); - } + if (const auto room_ip = Network::GetRoomIPv4Address()) { + ipv4 = room_ip; } IPC::ResponseBuilder rb{ctx, 3}; @@ -921,7 +920,7 @@ void IGeneralService::GetCurrentIpConfigInfo(HLERequestContext& ctx) { } if (auto room_member = Network::GetRoomMember().lock()) { - ApplyRoomAddress(*room_member, info.ip); + ApplyRoomAddress(info.ip); info.dns.is_automatic = true; info.dns.primary_dns = {1, 1, 1, 1}; info.dns.secondary_dns = {8, 8, 8, 8}; diff --git a/src/core/hle/service/sockets/bsd.cpp b/src/core/hle/service/sockets/bsd.cpp index 02c6bb7762..1b6b132769 100644 --- a/src/core/hle/service/sockets/bsd.cpp +++ b/src/core/hle/service/sockets/bsd.cpp @@ -535,33 +535,36 @@ std::pair BSD_USA::SocketImpl(Domain domain, Type type, Protocol pro UNIMPLEMENTED_IF_MSG(unk_flag, "Unknown flag in type"); type = static_cast(static_cast(type) & ~0x20000000); - const s32 fd = FindFreeFileDescriptorHandle(); - if (fd < 0) { - LOG_ERROR(Service, "No more file descriptors available"); - return {-1, Errno::MFILE}; + // Checked before taking an fd. A joined room does not lift it: room TCP is only stubbed. + if (Settings::values.airplane_mode.GetValue() && IsConnectionBased(type)) { + LOG_ERROR(Service, "Airplane mode is enabled, cannot create socket"); + return {-1, Errno::NOTCONN}; } - file_descriptors[fd] = FileDescriptor{}; - FileDescriptor& descriptor = *file_descriptors[fd]; - // ENONMEM might be thrown here - - LOG_INFO(Service, "New socket fd={}", fd); - + std::shared_ptr socket; auto room_member = Network::GetRoomMember().lock(); if (room_member && room_member->IsConnected()) { - descriptor.socket = std::make_shared(); + socket = std::make_shared(); } else { - descriptor.socket = std::make_shared(); + socket = std::make_shared(); } + socket->Initialize(Translate(domain), Translate(type), Translate(protocol)); - descriptor.socket->Initialize(Translate(domain), Translate(type), Translate(protocol)); - descriptor.is_connection_based = IsConnectionBased(type); - - if (Settings::values.airplane_mode.GetValue() && descriptor.is_connection_based) { - LOG_ERROR(Service, "Airplane mode is enabled, cannot create socket"); - return {-1, Errno::NOTCONN}; + // Publish the socket only once it is set up, since the room thread delivers packets to it + std::scoped_lock lock{file_descriptors_mutex}; + const s32 fd = FindFreeFileDescriptorHandle(); + if (fd < 0) { + LOG_ERROR(Service, "No more file descriptors available"); + return {-1, Errno::MFILE}; } + file_descriptors[fd] = FileDescriptor{ + .socket = std::move(socket), + .is_connection_based = IsConnectionBased(type), + }; + + LOG_INFO(Service, "New socket fd={}", fd); + return {fd, Errno::SUCCESS}; } @@ -649,10 +652,13 @@ std::pair BSD_USA::AcceptImpl(s32 fd, std::vector& write_buffer) return {-1, Translate(bsd_errno)}; } - file_descriptors[new_fd] = FileDescriptor{}; - FileDescriptor& new_descriptor = *file_descriptors[new_fd]; - new_descriptor.socket = std::move(result.socket); - new_descriptor.is_connection_based = descriptor.is_connection_based; + { + std::scoped_lock lock{file_descriptors_mutex}; + file_descriptors[new_fd] = FileDescriptor{ + .socket = std::move(result.socket), + .is_connection_based = descriptor.is_connection_based, + }; + } const SockAddrIn guest_addr_in = Translate(result.sockaddr_in); PutValue(write_buffer, guest_addr_in); @@ -1007,6 +1013,7 @@ Errno BSD_USA::CloseImpl(s32 fd) { LOG_INFO(Service, "Close socket fd={}", fd); + std::scoped_lock lock{file_descriptors_mutex}; file_descriptors[fd].reset(); return bsd_errno; } @@ -1016,6 +1023,7 @@ std::variant BSD_USA::DuplicateSocketImpl(s32 fd) { return Errno::BADF; } + std::scoped_lock lock{file_descriptors_mutex}; const s32 new_fd = FindFreeFileDescriptorHandle(); if (new_fd < 0) { LOG_ERROR(Service, "No more file descriptors available"); @@ -1071,12 +1079,14 @@ void BSD_USA::BuildErrnoResponse(HLERequestContext& ctx, Errno bsd_errno) const } void BSD_USA::OnProxyPacketReceived(const Network::ProxyPacket& packet) { + // Runs on the room thread. The lock keeps a socket alive while a bsd thread closes it. + std::scoped_lock lock{file_descriptors_mutex}; for (auto& optional_descriptor : file_descriptors) { - if (!optional_descriptor.has_value()) { + if (!optional_descriptor.has_value() || !optional_descriptor->socket) { continue; } FileDescriptor& descriptor = *optional_descriptor; - descriptor.socket.get()->HandleProxyPacket(packet); + descriptor.socket->HandleProxyPacket(packet); } } @@ -1140,18 +1150,27 @@ BSD_USA::BSD_USA(Core::System& system_, const char* name, bool is_user_) RegisterHandlers(functions); + std::scoped_lock lock{proxy_binding_mutex}; + if (proxy_binding_count++ != 0) { + return; + } if (auto room_member = Network::GetRoomMember().lock()) { - proxy_packet_received = room_member->BindOnProxyPacketReceived( - [this](const Network::ProxyPacket& packet) { OnProxyPacketReceived(packet); }); + proxy_packet_received = + room_member->BindOnProxyPacketReceived(&BSD_USA::OnProxyPacketReceived); } else { LOG_ERROR(Service, "Network isn't initialized"); } } BSD_USA::~BSD_USA() { + std::scoped_lock lock{proxy_binding_mutex}; + if (--proxy_binding_count != 0) { + return; + } if (auto room_member = Network::GetRoomMember().lock()) { room_member->Unbind(proxy_packet_received); } + proxy_packet_received.reset(); } std::unique_lock BSD_USA::LockService() noexcept { diff --git a/src/core/hle/service/sockets/bsd.h b/src/core/hle/service/sockets/bsd.h index 5cc3eb5b81..0df5c58945 100644 --- a/src/core/hle/service/sockets/bsd.h +++ b/src/core/hle/service/sockets/bsd.h @@ -7,6 +7,7 @@ #pragma once #include +#include #include #include #include @@ -181,12 +182,17 @@ private: void BuildErrnoResponse(HLERequestContext& ctx, Errno bsd_errno) const noexcept; static inline std::array, MAX_FD> file_descriptors{}; + /// Guards file_descriptors against the room thread, which delivers proxy packets to them + static inline std::mutex file_descriptors_mutex; /// Callback to parse and handle a received wifi packet. - void OnProxyPacketReceived(const Network::ProxyPacket& packet); + static void OnProxyPacketReceived(const Network::ProxyPacket& packet); - // Callback identifier for the OnProxyPacketReceived event. - Network::RoomMember::CallbackHandle proxy_packet_received; + // Callback identifier for the OnProxyPacketReceived event. bsd:s, bsd:u and bsd:a share + // file_descriptors, so they bind once; otherwise each packet is queued once per service. + static inline Network::RoomMember::CallbackHandle proxy_packet_received; + static inline std::mutex proxy_binding_mutex; + static inline size_t proxy_binding_count = 0; protected: std::unique_lock LockService() noexcept override; diff --git a/src/core/internal_network/network.cpp b/src/core/internal_network/network.cpp index 01d37fb35b..d1c436aa08 100644 --- a/src/core/internal_network/network.cpp +++ b/src/core/internal_network/network.cpp @@ -722,6 +722,17 @@ std::optional GetHostIPv4Address() { return TranslateIPv4(network_interface->ip_address); } +std::optional GetRoomIPv4Address() { + if (const auto room_member = GetRoomMember().lock()) { + // The room host and moderators are in the Moderator state instead of Joined + const auto state = room_member->GetState(); + if (state == RoomMember::State::Joined || state == RoomMember::State::Moderator) { + return room_member->GetFakeIpAddress(); + } + } + return std::nullopt; +} + std::string IPv4AddressToString(IPv4Address ip_addr) { std::array buf = {}; ASSERT(inet_ntop(AF_INET, &ip_addr, buf.data(), sizeof(buf)) == buf.data()); diff --git a/src/core/internal_network/network.h b/src/core/internal_network/network.h index 2c9ae523c5..a598f75348 100644 --- a/src/core/internal_network/network.h +++ b/src/core/internal_network/network.h @@ -121,6 +121,10 @@ constexpr IPv4Address TranslateIPv4(in_addr addr) { /// @return human ordered IPv4 address (e.g. 192.168.0.1) as an array std::optional GetHostIPv4Address(); +/// @brief Returns the fake IPv4 address of the joined room, the emulated network while in a room +/// @return nullopt outside a room and while still joining it +std::optional GetRoomIPv4Address(); + std::string IPv4AddressToString(IPv4Address ip_addr); u32 IPv4AddressToInteger(IPv4Address ip_addr); diff --git a/src/core/internal_network/socket_proxy.cpp b/src/core/internal_network/socket_proxy.cpp index 83eb2ce9d0..6c2f79da6e 100644 --- a/src/core/internal_network/socket_proxy.cpp +++ b/src/core/internal_network/socket_proxy.cpp @@ -43,9 +43,21 @@ void ProxySocket::HandleProxyPacket(const ProxyPacket& packet) { LOG_INFO(Network, "Received broadcast packet, but not configured for broadcast mode"); return; } + } - auto decompressed = packet; - decompressed.data = Common::Compression::DecompressDataZSTD(packet.data); + // Decompress without the lock, so the game thread does not wait for it + ProxyPacket decompressed{ + .local_endpoint = packet.local_endpoint, + .remote_endpoint = packet.remote_endpoint, + .protocol = packet.protocol, + .broadcast = packet.broadcast, + .data = Common::Compression::DecompressDataZSTD(packet.data), + }; + { + std::lock_guard guard(packets_mutex); + if (closed) { + return; + } received_packets.push(std::move(decompressed)); } packets_cv.notify_one(); @@ -152,26 +164,28 @@ std::pair ProxySocket::RecvFrom(int flags, std::span message, So std::pair ProxySocket::ReceivePacket(int flags, std::span message, SockAddrIn* addr, std::size_t max_length) { - ProxyPacket& packet = received_packets.front(); + const ProxyPacket& packet = received_packets.front(); if (addr) { addr->family = Domain::INET; addr->ip = packet.local_endpoint.ip; // The senders ip address addr->portno = packet.local_endpoint.portno; // The senders port number } - const std::size_t read_bytes = (std::min)(packet.data.size(), max_length); - std::memcpy(message.data(), packet.data.data(), read_bytes); + const std::span unread = std::span{packet.data}.subspan(front_read_offset); + const std::size_t read_bytes = (std::min)(unread.size(), max_length); + std::memcpy(message.data(), unread.data(), read_bytes); if ((flags & FLAG_MSG_PEEK) != 0) { return {static_cast(read_bytes), Errno::SUCCESS}; } - if (protocol == Protocol::TCP && read_bytes < packet.data.size()) { + if (protocol == Protocol::TCP && read_bytes < unread.size()) { // Keep the unread part of the stream for the next call - packet.data.erase(packet.data.begin(), packet.data.begin() + read_bytes); + front_read_offset += read_bytes; } else { // Like BSD recv(), the rest of a datagram that does not fit is discarded received_packets.pop(); + front_read_offset = 0; } return {static_cast(read_bytes), Errno::SUCCESS}; @@ -240,6 +254,7 @@ Errno ProxySocket::Close() { fd = INVALID_SOCKET; closed = true; received_packets = {}; + front_read_offset = 0; } packets_cv.notify_all(); diff --git a/src/core/internal_network/socket_proxy.h b/src/core/internal_network/socket_proxy.h index 8343fcbae1..d2bd7aae77 100644 --- a/src/core/internal_network/socket_proxy.h +++ b/src/core/internal_network/socket_proxy.h @@ -90,7 +90,8 @@ private: SockAddrIn local_endpoint{}; bool blocking = true; std::queue received_packets; - Protocol protocol; + std::size_t front_read_offset = 0; ///< Bytes of the front TCP packet that were already read + Protocol protocol{}; std::mutex packets_mutex; std::condition_variable packets_cv; -- cgit v1.2.3 From 3e29ad38277195a2930b6ae89dba6d3e698c17e2 Mon Sep 17 00:00:00 2001 From: cirakg Date: Tue, 3 Nov 2026 00:28:31 +0100 Subject: network: use the socket type for room stream reads and wake all readers socket(AF_INET, SOCK_STREAM, 0) leaves the protocol unspecified, so the TCP checks in ProxySocket::Recv and ReceivePacket missed such sockets. recv() then waited for the timeout and returned EAGAIN instead of end of stream, and partial reads dropped the rest of the packet. Keep the socket type and use it for these checks. The protocol stays as it is, because it is sent to other room members. A MSG_PEEK reader leaves the packet queued, so notify every waiter when a packet arrives instead of only one. Assisted-by: Claude Opus 5.5 (investigation and implementation) --- src/core/internal_network/socket_proxy.cpp | 8 +++++--- src/core/internal_network/socket_proxy.h | 1 + 2 files changed, 6 insertions(+), 3 deletions(-) (limited to 'src/core/internal_network/socket_proxy.cpp') diff --git a/src/core/internal_network/socket_proxy.cpp b/src/core/internal_network/socket_proxy.cpp index 6c2f79da6e..1b93ce93e5 100644 --- a/src/core/internal_network/socket_proxy.cpp +++ b/src/core/internal_network/socket_proxy.cpp @@ -60,7 +60,8 @@ void ProxySocket::HandleProxyPacket(const ProxyPacket& packet) { } received_packets.push(std::move(decompressed)); } - packets_cv.notify_one(); + // A MSG_PEEK reader leaves the packet queued, so every waiter must see it + packets_cv.notify_all(); } template @@ -72,6 +73,7 @@ Errno ProxySocket::SetSockOpt(SOCKET fd_, int option, T value) { Errno ProxySocket::Initialize(Domain domain, Type type, Protocol socket_protocol) { { std::lock_guard guard(packets_mutex); + socket_type = type; protocol = socket_protocol; } SetSockOpt(fd, SO_TYPE, type); @@ -122,7 +124,7 @@ Errno ProxySocket::Shutdown(ShutdownHow how) { } std::pair ProxySocket::Recv(int flags, std::span message) { - if (protocol == Protocol::TCP) { + if (socket_type == Type::STREAM) { // Room TCP sockets have no peer (Connect and Send are stubs). Report an empty queue as the // end of the stream, as before, instead of blocking the bsd service until the timeout. std::lock_guard guard(packets_mutex); @@ -179,7 +181,7 @@ std::pair ProxySocket::ReceivePacket(int flags, std::span messag return {static_cast(read_bytes), Errno::SUCCESS}; } - if (protocol == Protocol::TCP && read_bytes < unread.size()) { + if (socket_type == Type::STREAM && read_bytes < unread.size()) { // Keep the unread part of the stream for the next call front_read_offset += read_bytes; } else { diff --git a/src/core/internal_network/socket_proxy.h b/src/core/internal_network/socket_proxy.h index d2bd7aae77..5d6e35693c 100644 --- a/src/core/internal_network/socket_proxy.h +++ b/src/core/internal_network/socket_proxy.h @@ -92,6 +92,7 @@ private: std::queue received_packets; std::size_t front_read_offset = 0; ///< Bytes of the front TCP packet that were already read Protocol protocol{}; + Type socket_type{}; ///< Stream checks use the type, since protocol 0 means the default protocol std::mutex packets_mutex; std::condition_variable packets_cv; -- cgit v1.2.3 From d6b32aac3c75a6a0c4190a8b6b2b1fbc5f3e8801 Mon Sep 17 00:00:00 2001 From: cirakg Date: Tue, 3 Nov 2026 01:15:29 +0100 Subject: network: keep fcntl modes across MSG_DONTWAIT and decompress room packets once A receive with MSG_DONTWAIT restored the blocking mode from the descriptor copy taken before the call. When fcntl(SETFL, O_NONBLOCK) ran during the receive, the fd kept reporting O_NONBLOCK while its socket blocked again. Receives now switch the mode only for MSG_DONTWAIT and read the current flags under file_descriptors_mutex at both ends, and SETFL changes the mode and the stored flags in the same critical section. OnProxyPacketReceived decompressed each room packet once per matching socket while holding file_descriptors_mutex, so every bsd call waited for it. It now copies the distinct sockets under the lock, decompresses once without it and delivers the result. Also reject fd == MAX_FD in IsFileDescriptorValid and PollImpl, which read past the table, and guard the ProxySocket blocking and receive timeout setters with packets_mutex, matching their reader. Assisted-by: Claude Opus 5.5 (investigation and implementation) --- src/core/hle/service/sockets/bsd.cpp | 92 ++++++++++++++++++------------ src/core/hle/service/sockets/bsd.h | 2 + src/core/internal_network/socket_proxy.cpp | 23 ++------ src/core/internal_network/sockets.h | 1 + 4 files changed, 65 insertions(+), 53 deletions(-) (limited to 'src/core/internal_network/socket_proxy.cpp') diff --git a/src/core/hle/service/sockets/bsd.cpp b/src/core/hle/service/sockets/bsd.cpp index 477f32626d..e6d23b7bf6 100644 --- a/src/core/hle/service/sockets/bsd.cpp +++ b/src/core/hle/service/sockets/bsd.cpp @@ -14,6 +14,7 @@ #include "common/logging.h" #include "common/socket_types.h" +#include "common/zstd_compression.h" #include "core/core.h" #include "core/hle/kernel/k_thread.h" #include "core/hle/service/ipc_helpers.h" @@ -608,7 +609,7 @@ std::pair BSD_USA::PollImpl(std::vector& write_buffer, std::span PollFD& pollfd = fds[i]; ASSERT(False(pollfd.revents)); - if (pollfd.fd > static_cast(MAX_FD) || pollfd.fd < 0) { + if (pollfd.fd >= static_cast(MAX_FD) || pollfd.fd < 0) { LOG_ERROR(Service, "File descriptor handle={} is invalid", pollfd.fd); pollfd.revents = PollEvents{}; return {0, Errno::SUCCESS}; @@ -764,11 +765,12 @@ std::pair BSD_USA::FcntlImpl(s32 fd, FcntlCmd cmd, s32 arg) { return {descriptor->flags, Errno::SUCCESS}; case FcntlCmd::SETFL: { const bool enable = (arg & Network::FLAG_O_NONBLOCK) != 0; + // Set the mode and the flags together, so SetDontWait never sees one without the other + std::scoped_lock lock{file_descriptors_mutex}; const Errno bsd_errno = Translate(descriptor->socket->SetNonBlock(enable)); if (bsd_errno != Errno::SUCCESS) { return {-1, bsd_errno}; } - std::scoped_lock lock{file_descriptors_mutex}; if (auto& entry = file_descriptors[fd]; entry && entry->socket == descriptor->socket) { entry->flags = arg; } @@ -880,19 +882,14 @@ std::pair BSD_USA::RecvImpl(s32 fd, u32 flags, std::vector& mess // Apply flags using Network::FLAG_MSG_DONTWAIT; - using Network::FLAG_O_NONBLOCK; - if ((flags & FLAG_MSG_DONTWAIT) != 0) { - flags &= ~FLAG_MSG_DONTWAIT; - if ((descriptor->flags & FLAG_O_NONBLOCK) == 0) { - descriptor->socket->SetNonBlock(true); - } - } + const bool dont_wait = (flags & FLAG_MSG_DONTWAIT) != 0 && SetDontWait(fd, *descriptor, true); + flags &= ~FLAG_MSG_DONTWAIT; const auto [ret, bsd_errno] = Translate(descriptor->socket->Recv(flags, message)); // Restore original state - if ((descriptor->flags & FLAG_O_NONBLOCK) == 0) { - descriptor->socket->SetNonBlock(false); + if (dont_wait) { + SetDontWait(fd, *descriptor, false); } return {ret, bsd_errno}; @@ -916,20 +913,15 @@ std::pair BSD_USA::RecvFromImpl(s32 fd, u32 flags, std::vector& // Apply flags using Network::FLAG_MSG_DONTWAIT; - using Network::FLAG_O_NONBLOCK; - if ((flags & FLAG_MSG_DONTWAIT) != 0) { - flags &= ~FLAG_MSG_DONTWAIT; - if ((descriptor->flags & FLAG_O_NONBLOCK) == 0) { - descriptor->socket->SetNonBlock(true); - } - } + const bool dont_wait = (flags & FLAG_MSG_DONTWAIT) != 0 && SetDontWait(fd, *descriptor, true); + flags &= ~FLAG_MSG_DONTWAIT; const auto [ret, bsd_errno] = Translate(descriptor->socket->RecvFrom(flags, message, p_addr_in)); // Restore original state - if ((descriptor->flags & FLAG_O_NONBLOCK) == 0) { - descriptor->socket->SetNonBlock(false); + if (dont_wait) { + SetDontWait(fd, *descriptor, false); } if (p_addr_in) { @@ -1043,8 +1035,21 @@ s32 BSD_USA::FindFreeFileDescriptorHandle() noexcept { return -1; } +bool BSD_USA::SetDontWait(s32 fd, const FileDescriptor& descriptor, bool enable) { + // Read the current flags, not the copy: fcntl may have changed them during the receive + std::scoped_lock lock{file_descriptors_mutex}; + const auto& entry = file_descriptors[fd]; + const s32 current_flags = + entry && entry->socket == descriptor.socket ? entry->flags : descriptor.flags; + if ((current_flags & Network::FLAG_O_NONBLOCK) != 0) { + return false; + } + descriptor.socket->SetNonBlock(enable); + return true; +} + bool BSD_USA::IsFileDescriptorValid(s32 fd) const noexcept { - if (fd > static_cast(MAX_FD) || fd < 0) { + if (fd >= static_cast(MAX_FD) || fd < 0) { LOG_ERROR(Service, "Invalid file descriptor handle={}", fd); return false; } @@ -1064,22 +1069,37 @@ void BSD_USA::BuildErrnoResponse(HLERequestContext& ctx, Errno bsd_errno) const } void BSD_USA::OnProxyPacketReceived(const Network::ProxyPacket& packet) { - // Runs on the room thread. The lock keeps a socket alive while a bsd thread closes it. - std::scoped_lock lock{file_descriptors_mutex}; - // A duplicated fd shares its socket, which must still get each packet only once - std::array delivered{}; - size_t num_delivered = 0; - for (auto& optional_descriptor : file_descriptors) { - if (!optional_descriptor.has_value() || !optional_descriptor->socket) { - continue; - } - Network::SocketBase* const socket = optional_descriptor->socket.get(); - const auto delivered_end = delivered.begin() + num_delivered; - if (std::find(delivered.begin(), delivered_end, socket) != delivered_end) { - continue; + // Runs on the room thread. The copies keep each socket alive if a bsd thread closes it, so + // the bsd threads do not wait for the decompression below. + std::array, MAX_FD> sockets{}; + size_t num_sockets = 0; + { + std::scoped_lock lock{file_descriptors_mutex}; + for (const auto& descriptor : file_descriptors) { + if (!descriptor.has_value() || !descriptor->socket) { + continue; + } + // A duplicated fd shares its socket, which must still get each packet only once + const auto sockets_end = sockets.begin() + num_sockets; + if (std::find(sockets.begin(), sockets_end, descriptor->socket) == sockets_end) { + sockets[num_sockets++] = descriptor->socket; + } } - delivered[num_delivered++] = socket; - socket->HandleProxyPacket(packet); + } + if (num_sockets == 0) { + return; + } + + // Decompress once for all sockets that share the port + const Network::ProxyPacket decompressed{ + .local_endpoint = packet.local_endpoint, + .remote_endpoint = packet.remote_endpoint, + .protocol = packet.protocol, + .broadcast = packet.broadcast, + .data = Common::Compression::DecompressDataZSTD(packet.data), + }; + for (size_t i = 0; i < num_sockets; ++i) { + sockets[i]->HandleProxyPacket(decompressed); } } diff --git a/src/core/hle/service/sockets/bsd.h b/src/core/hle/service/sockets/bsd.h index f908edc7d2..559830d7a1 100644 --- a/src/core/hle/service/sockets/bsd.h +++ b/src/core/hle/service/sockets/bsd.h @@ -180,6 +180,8 @@ private: bool IsFileDescriptorValid(s32 fd) const noexcept; /// Copies a descriptor under file_descriptors_mutex; nullopt if fd has no socket std::optional CopyDescriptor(s32 fd); + /// Switches a blocking socket for one MSG_DONTWAIT call; false if fd is already nonblocking + bool SetDontWait(s32 fd, const FileDescriptor& descriptor, bool enable); void BuildErrnoResponse(HLERequestContext& ctx, Errno bsd_errno) const noexcept; diff --git a/src/core/internal_network/socket_proxy.cpp b/src/core/internal_network/socket_proxy.cpp index 1b93ce93e5..95f64b9bd0 100644 --- a/src/core/internal_network/socket_proxy.cpp +++ b/src/core/internal_network/socket_proxy.cpp @@ -43,22 +43,7 @@ void ProxySocket::HandleProxyPacket(const ProxyPacket& packet) { LOG_INFO(Network, "Received broadcast packet, but not configured for broadcast mode"); return; } - } - - // Decompress without the lock, so the game thread does not wait for it - ProxyPacket decompressed{ - .local_endpoint = packet.local_endpoint, - .remote_endpoint = packet.remote_endpoint, - .protocol = packet.protocol, - .broadcast = packet.broadcast, - .data = Common::Compression::DecompressDataZSTD(packet.data), - }; - { - std::lock_guard guard(packets_mutex); - if (closed) { - return; - } - received_packets.push(std::move(decompressed)); + received_packets.push(packet); } // A MSG_PEEK reader leaves the packet queued, so every waiter must see it packets_cv.notify_all(); @@ -304,11 +289,15 @@ Errno ProxySocket::SetSndTimeo(u32 value) { } Errno ProxySocket::SetRcvTimeo(u32 value) { - receive_timeout = value; + { + std::lock_guard guard(packets_mutex); + receive_timeout = value; + } return SetSockOpt(fd, SO_RCVTIMEO, static_cast(value)); } Errno ProxySocket::SetNonBlock(bool enable) { + std::lock_guard guard(packets_mutex); blocking = !enable; return Errno::SUCCESS; } diff --git a/src/core/internal_network/sockets.h b/src/core/internal_network/sockets.h index 0b751b6622..37cffd4651 100644 --- a/src/core/internal_network/sockets.h +++ b/src/core/internal_network/sockets.h @@ -89,6 +89,7 @@ public: virtual bool IsOpened() const = 0; + /// Queues a room packet whose data is already decompressed virtual void HandleProxyPacket(const ProxyPacket& packet) = 0; [[nodiscard]] SOCKET GetFD() const { -- cgit v1.2.3