diff options
| author | cirakg <cirakg@cirakg.xyz> | 2026-11-03 00:17:50 +0100 |
|---|---|---|
| committer | cirakg <cirakg@cirakg.xyz> | 2026-11-03 00:17:50 +0100 |
| commit | fd3e2fe18a101e2bbfd5935263183e25ab26497b (patch) | |
| tree | f15e5f5ebb59b0d5cfd72a50346355e607c31ddd /src/core/hle/service/nifm/nifm.cpp | |
| parent | a931609f31672f782d2817f5764d65c00dea4dad (diff) | |
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)
Diffstat (limited to 'src/core/hle/service/nifm/nifm.cpp')
| -rw-r--r-- | src/core/hle/service/nifm/nifm.cpp | 37 |
1 files changed, 18 insertions, 19 deletions
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<u8> blob(adapters.size() * kEntry, 0); @@ -633,6 +630,12 @@ void IGeneralService::EnumerateNetworkInterfaces(HLERequestContext& ctx) { *reinterpret_cast<in_addr*>(base + 0x1C) = host.subnet_mask; *reinterpret_cast<in_addr*>(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}; |
