summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar FearlessTobi2022-07-30 05:58:23 +0200
committerGravatar FearlessTobi2022-08-15 20:25:42 +0200
commitf80c7c4cd5c090b9a31f89a0eb3d86cbe928c50b (patch)
treead359908ba2d3cd003082b39cc7217b61e5b18f6
parentweb_service: Correct jwt issuer string (diff)
downloadyuzu-f80c7c4cd5c090b9a31f89a0eb3d86cbe928c50b.tar.gz
yuzu-f80c7c4cd5c090b9a31f89a0eb3d86cbe928c50b.tar.xz
yuzu-f80c7c4cd5c090b9a31f89a0eb3d86cbe928c50b.zip
core, network: Add ability to proxy socket packets
-rw-r--r--src/common/CMakeLists.txt1
-rw-r--r--src/common/announce_multiplayer_room.h10
-rw-r--r--src/common/socket_types.h52
-rw-r--r--src/core/CMakeLists.txt2
-rw-r--r--src/core/hle/service/nifm/nifm.cpp341
-rw-r--r--src/core/hle/service/nifm/nifm.h27
-rw-r--r--src/core/hle/service/sockets/bsd.cpp40
-rw-r--r--src/core/hle/service/sockets/bsd.h13
-rw-r--r--src/core/hle/service/sockets/sockets.h6
-rw-r--r--src/core/hle/service/sockets/sockets_translate.cpp2
-rw-r--r--src/core/internal_network/network.cpp61
-rw-r--r--src/core/internal_network/network.h43
-rw-r--r--src/core/internal_network/socket_proxy.cpp282
-rw-r--r--src/core/internal_network/socket_proxy.h102
-rw-r--r--src/core/internal_network/sockets.h136
-rw-r--r--src/network/room.cpp170
-rw-r--r--src/network/room.h14
-rw-r--r--src/network/room_member.cpp123
-rw-r--r--src/network/room_member.h70
-rw-r--r--src/yuzu/multiplayer/chat_room.cpp14
-rw-r--r--src/yuzu/multiplayer/client_room.cpp1
-rw-r--r--src/yuzu/multiplayer/direct_connect.cpp10
-rw-r--r--src/yuzu/multiplayer/host_room.cpp4
-rw-r--r--src/yuzu/multiplayer/lobby.cpp2
-rw-r--r--src/yuzu/multiplayer/message.cpp7
-rw-r--r--src/yuzu/multiplayer/message.h3
-rw-r--r--src/yuzu/multiplayer/state.cpp8
-rw-r--r--src/yuzu_cmd/yuzu.cpp10
28 files changed, 1028 insertions, 526 deletions
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt
index a6dc31b53..635fb85c8 100644
--- a/src/common/CMakeLists.txt
+++ b/src/common/CMakeLists.txt
@@ -124,6 +124,7 @@ add_library(common STATIC
124 settings.h 124 settings.h
125 settings_input.cpp 125 settings_input.cpp
126 settings_input.h 126 settings_input.h
127 socket_types.h
127 spin_lock.cpp 128 spin_lock.cpp
128 spin_lock.h 129 spin_lock.h
129 stream.cpp 130 stream.cpp
diff --git a/src/common/announce_multiplayer_room.h b/src/common/announce_multiplayer_room.h
index 0ad9da2be..cb004e0eb 100644
--- a/src/common/announce_multiplayer_room.h
+++ b/src/common/announce_multiplayer_room.h
@@ -8,12 +8,11 @@
8#include <string> 8#include <string>
9#include <vector> 9#include <vector>
10#include "common/common_types.h" 10#include "common/common_types.h"
11#include "common/socket_types.h"
11#include "web_service/web_result.h" 12#include "web_service/web_result.h"
12 13
13namespace AnnounceMultiplayerRoom { 14namespace AnnounceMultiplayerRoom {
14 15
15using MacAddress = std::array<u8, 6>;
16
17struct GameInfo { 16struct GameInfo {
18 std::string name{""}; 17 std::string name{""};
19 u64 id{0}; 18 u64 id{0};
@@ -24,7 +23,7 @@ struct Member {
24 std::string nickname; 23 std::string nickname;
25 std::string display_name; 24 std::string display_name;
26 std::string avatar_url; 25 std::string avatar_url;
27 MacAddress mac_address; 26 Network::IPv4Address fake_ip;
28 GameInfo game; 27 GameInfo game;
29}; 28};
30 29
@@ -75,10 +74,7 @@ public:
75 const bool has_password, const GameInfo& preferred_game) = 0; 74 const bool has_password, const GameInfo& preferred_game) = 0;
76 /** 75 /**
77 * Adds a player information to the data that gets announced 76 * Adds a player information to the data that gets announced
78 * @param nickname The nickname of the player 77 * @param member The player to add
79 * @param mac_address The MAC Address of the player
80 * @param game_id The title id of the game the player plays
81 * @param game_name The name of the game the player plays
82 */ 78 */
83 virtual void AddPlayer(const Member& member) = 0; 79 virtual void AddPlayer(const Member& member) = 0;
84 80
diff --git a/src/common/socket_types.h b/src/common/socket_types.h
new file mode 100644
index 000000000..5bb309a44
--- /dev/null
+++ b/src/common/socket_types.h
@@ -0,0 +1,52 @@
1// Copyright 2022 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "common/common_types.h"
8
9namespace Network {
10
11/// Address families
12enum class Domain : u8 {
13 INET, ///< Address family for IPv4
14};
15
16/// Socket types
17enum class Type {
18 STREAM,
19 DGRAM,
20 RAW,
21 SEQPACKET,
22};
23
24/// Protocol values for sockets
25enum class Protocol : u8 {
26 ICMP,
27 TCP,
28 UDP,
29};
30
31/// Shutdown mode
32enum class ShutdownHow {
33 RD,
34 WR,
35 RDWR,
36};
37
38/// Array of IPv4 address
39using IPv4Address = std::array<u8, 4>;
40
41/// Cross-platform sockaddr structure
42struct SockAddrIn {
43 Domain family;
44 IPv4Address ip;
45 u16 portno;
46};
47
48constexpr u32 FLAG_MSG_PEEK = 0x2;
49constexpr u32 FLAG_MSG_DONTWAIT = 0x80;
50constexpr u32 FLAG_O_NONBLOCK = 0x800;
51
52} // namespace Network
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 4e39649a8..3230d7199 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -724,6 +724,8 @@ add_library(core STATIC
724 internal_network/network_interface.cpp 724 internal_network/network_interface.cpp
725 internal_network/network_interface.h 725 internal_network/network_interface.h
726 internal_network/sockets.h 726 internal_network/sockets.h
727 internal_network/socket_proxy.cpp
728 internal_network/socket_proxy.h
727 loader/deconstructed_rom_directory.cpp 729 loader/deconstructed_rom_directory.cpp
728 loader/deconstructed_rom_directory.h 730 loader/deconstructed_rom_directory.h
729 loader/kip.cpp 731 loader/kip.cpp
diff --git a/src/core/hle/service/nifm/nifm.cpp b/src/core/hle/service/nifm/nifm.cpp
index 2889973e4..42ed17187 100644
--- a/src/core/hle/service/nifm/nifm.cpp
+++ b/src/core/hle/service/nifm/nifm.cpp
@@ -6,7 +6,6 @@
6#include "core/hle/kernel/k_event.h" 6#include "core/hle/kernel/k_event.h"
7#include "core/hle/service/kernel_helpers.h" 7#include "core/hle/service/kernel_helpers.h"
8#include "core/hle/service/nifm/nifm.h" 8#include "core/hle/service/nifm/nifm.h"
9#include "core/hle/service/service.h"
10 9
11namespace { 10namespace {
12 11
@@ -271,213 +270,227 @@ public:
271 } 270 }
272}; 271};
273 272
274class IGeneralService final : public ServiceFramework<IGeneralService> { 273void IGeneralService::GetClientId(Kernel::HLERequestContext& ctx) {
275public: 274 static constexpr u32 client_id = 1;
276 explicit IGeneralService(Core::System& system_); 275 LOG_WARNING(Service_NIFM, "(STUBBED) called");
277 276
278private: 277 IPC::ResponseBuilder rb{ctx, 4};
279 void GetClientId(Kernel::HLERequestContext& ctx) { 278 rb.Push(ResultSuccess);
280 static constexpr u32 client_id = 1; 279 rb.Push<u64>(client_id); // Client ID needs to be non zero otherwise it's considered invalid
281 LOG_WARNING(Service_NIFM, "(STUBBED) called"); 280}
282 281
283 IPC::ResponseBuilder rb{ctx, 4}; 282void IGeneralService::CreateScanRequest(Kernel::HLERequestContext& ctx) {
284 rb.Push(ResultSuccess); 283 LOG_DEBUG(Service_NIFM, "called");
285 rb.Push<u64>(client_id); // Client ID needs to be non zero otherwise it's considered invalid
286 }
287 284
288 void CreateScanRequest(Kernel::HLERequestContext& ctx) { 285 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
289 LOG_DEBUG(Service_NIFM, "called");
290 286
291 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 287 rb.Push(ResultSuccess);
288 rb.PushIpcInterface<IScanRequest>(system);
289}
292 290
293 rb.Push(ResultSuccess); 291void IGeneralService::CreateRequest(Kernel::HLERequestContext& ctx) {
294 rb.PushIpcInterface<IScanRequest>(system); 292 LOG_DEBUG(Service_NIFM, "called");
295 }
296 293
297 void CreateRequest(Kernel::HLERequestContext& ctx) { 294 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
298 LOG_DEBUG(Service_NIFM, "called");
299 295
300 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 296 rb.Push(ResultSuccess);
297 rb.PushIpcInterface<IRequest>(system);
298}
301 299
302 rb.Push(ResultSuccess); 300void IGeneralService::GetCurrentNetworkProfile(Kernel::HLERequestContext& ctx) {
303 rb.PushIpcInterface<IRequest>(system); 301 LOG_WARNING(Service_NIFM, "(STUBBED) called");
304 }
305 302
306 void GetCurrentNetworkProfile(Kernel::HLERequestContext& ctx) { 303 const auto net_iface = Network::GetSelectedNetworkInterface();
307 LOG_WARNING(Service_NIFM, "(STUBBED) called");
308 304
309 const auto net_iface = Network::GetSelectedNetworkInterface(); 305 SfNetworkProfileData network_profile_data = [&net_iface] {
310 306 if (!net_iface) {
311 const SfNetworkProfileData network_profile_data = [&net_iface] { 307 return SfNetworkProfileData{};
312 if (!net_iface) { 308 }
313 return SfNetworkProfileData{}; 309
314 } 310 return SfNetworkProfileData{
315 311 .ip_setting_data{
316 return SfNetworkProfileData{ 312 .ip_address_setting{
317 .ip_setting_data{ 313 .is_automatic{true},
318 .ip_address_setting{ 314 .current_address{Network::TranslateIPv4(net_iface->ip_address)},
319 .is_automatic{true}, 315 .subnet_mask{Network::TranslateIPv4(net_iface->subnet_mask)},
320 .current_address{Network::TranslateIPv4(net_iface->ip_address)}, 316 .gateway{Network::TranslateIPv4(net_iface->gateway)},
321 .subnet_mask{Network::TranslateIPv4(net_iface->subnet_mask)},
322 .gateway{Network::TranslateIPv4(net_iface->gateway)},
323 },
324 .dns_setting{
325 .is_automatic{true},
326 .primary_dns{1, 1, 1, 1},
327 .secondary_dns{1, 0, 0, 1},
328 },
329 .proxy_setting{
330 .enabled{false},
331 .port{},
332 .proxy_server{},
333 .automatic_auth_enabled{},
334 .user{},
335 .password{},
336 },
337 .mtu{1500},
338 }, 317 },
339 .uuid{0xdeadbeef, 0xdeadbeef}, 318 .dns_setting{
340 .network_name{"yuzu Network"}, 319 .is_automatic{true},
341 .wireless_setting_data{ 320 .primary_dns{1, 1, 1, 1},
342 .ssid_length{12}, 321 .secondary_dns{1, 0, 0, 1},
343 .ssid{"yuzu Network"},
344 .passphrase{"yuzupassword"},
345 }, 322 },
346 }; 323 .proxy_setting{
347 }(); 324 .enabled{false},
348 325 .port{},
349 ctx.WriteBuffer(network_profile_data); 326 .proxy_server{},
327 .automatic_auth_enabled{},
328 .user{},
329 .password{},
330 },
331 .mtu{1500},
332 },
333 .uuid{0xdeadbeef, 0xdeadbeef},
334 .network_name{"yuzu Network"},
335 .wireless_setting_data{
336 .ssid_length{12},
337 .ssid{"yuzu Network"},
338 .passphrase{"yuzupassword"},
339 },
340 };
341 }();
350 342
351 IPC::ResponseBuilder rb{ctx, 2}; 343 // When we're connected to a room, spoof the hosts IP address
352 rb.Push(ResultSuccess); 344 if (auto room_member = network.GetRoomMember().lock()) {
345 if (room_member->IsConnected()) {
346 network_profile_data.ip_setting_data.ip_address_setting.current_address =
347 room_member->GetFakeIpAddress();
348 }
353 } 349 }
354 350
355 void RemoveNetworkProfile(Kernel::HLERequestContext& ctx) { 351 ctx.WriteBuffer(network_profile_data);
356 LOG_WARNING(Service_NIFM, "(STUBBED) called");
357 352
358 IPC::ResponseBuilder rb{ctx, 2}; 353 IPC::ResponseBuilder rb{ctx, 2};
359 rb.Push(ResultSuccess); 354 rb.Push(ResultSuccess);
360 } 355}
361 356
362 void GetCurrentIpAddress(Kernel::HLERequestContext& ctx) { 357void IGeneralService::RemoveNetworkProfile(Kernel::HLERequestContext& ctx) {
363 LOG_WARNING(Service_NIFM, "(STUBBED) called"); 358 LOG_WARNING(Service_NIFM, "(STUBBED) called");
364 359
365 auto ipv4 = Network::GetHostIPv4Address(); 360 IPC::ResponseBuilder rb{ctx, 2};
366 if (!ipv4) { 361 rb.Push(ResultSuccess);
367 LOG_ERROR(Service_NIFM, "Couldn't get host IPv4 address, defaulting to 0.0.0.0"); 362}
368 ipv4.emplace(Network::IPv4Address{0, 0, 0, 0});
369 }
370 363
371 IPC::ResponseBuilder rb{ctx, 3}; 364void IGeneralService::GetCurrentIpAddress(Kernel::HLERequestContext& ctx) {
372 rb.Push(ResultSuccess); 365 LOG_WARNING(Service_NIFM, "(STUBBED) called");
373 rb.PushRaw(*ipv4); 366
367 auto ipv4 = Network::GetHostIPv4Address();
368 if (!ipv4) {
369 LOG_ERROR(Service_NIFM, "Couldn't get host IPv4 address, defaulting to 0.0.0.0");
370 ipv4.emplace(Network::IPv4Address{0, 0, 0, 0});
374 } 371 }
375 372
376 void CreateTemporaryNetworkProfile(Kernel::HLERequestContext& ctx) { 373 // When we're connected to a room, spoof the hosts IP address
377 LOG_DEBUG(Service_NIFM, "called"); 374 if (auto room_member = network.GetRoomMember().lock()) {
375 if (room_member->IsConnected()) {
376 ipv4 = room_member->GetFakeIpAddress();
377 }
378 }
378 379
379 ASSERT_MSG(ctx.GetReadBufferSize() == 0x17c, 380 IPC::ResponseBuilder rb{ctx, 3};
380 "SfNetworkProfileData is not the correct size"); 381 rb.Push(ResultSuccess);
381 u128 uuid{}; 382 rb.PushRaw(*ipv4);
382 auto buffer = ctx.ReadBuffer(); 383}
383 std::memcpy(&uuid, buffer.data() + 8, sizeof(u128)); 384void IGeneralService::CreateTemporaryNetworkProfile(Kernel::HLERequestContext& ctx) {
385 LOG_DEBUG(Service_NIFM, "called");
384 386
385 IPC::ResponseBuilder rb{ctx, 6, 0, 1}; 387 ASSERT_MSG(ctx.GetReadBufferSize() == 0x17c, "SfNetworkProfileData is not the correct size");
388 u128 uuid{};
389 auto buffer = ctx.ReadBuffer();
390 std::memcpy(&uuid, buffer.data() + 8, sizeof(u128));
386 391
387 rb.Push(ResultSuccess); 392 IPC::ResponseBuilder rb{ctx, 6, 0, 1};
388 rb.PushIpcInterface<INetworkProfile>(system);
389 rb.PushRaw<u128>(uuid);
390 }
391 393
392 void GetCurrentIpConfigInfo(Kernel::HLERequestContext& ctx) { 394 rb.Push(ResultSuccess);
393 LOG_WARNING(Service_NIFM, "(STUBBED) called"); 395 rb.PushIpcInterface<INetworkProfile>(system);
396 rb.PushRaw<u128>(uuid);
397}
394 398
395 struct IpConfigInfo { 399void IGeneralService::GetCurrentIpConfigInfo(Kernel::HLERequestContext& ctx) {
396 IpAddressSetting ip_address_setting{}; 400 LOG_WARNING(Service_NIFM, "(STUBBED) called");
397 DnsSetting dns_setting{};
398 };
399 static_assert(sizeof(IpConfigInfo) == sizeof(IpAddressSetting) + sizeof(DnsSetting),
400 "IpConfigInfo has incorrect size.");
401 401
402 const auto net_iface = Network::GetSelectedNetworkInterface(); 402 struct IpConfigInfo {
403 IpAddressSetting ip_address_setting{};
404 DnsSetting dns_setting{};
405 };
406 static_assert(sizeof(IpConfigInfo) == sizeof(IpAddressSetting) + sizeof(DnsSetting),
407 "IpConfigInfo has incorrect size.");
403 408
404 const IpConfigInfo ip_config_info = [&net_iface] { 409 const auto net_iface = Network::GetSelectedNetworkInterface();
405 if (!net_iface) {
406 return IpConfigInfo{};
407 }
408 410
409 return IpConfigInfo{ 411 IpConfigInfo ip_config_info = [&net_iface] {
410 .ip_address_setting{ 412 if (!net_iface) {
411 .is_automatic{true}, 413 return IpConfigInfo{};
412 .current_address{Network::TranslateIPv4(net_iface->ip_address)}, 414 }
413 .subnet_mask{Network::TranslateIPv4(net_iface->subnet_mask)},
414 .gateway{Network::TranslateIPv4(net_iface->gateway)},
415 },
416 .dns_setting{
417 .is_automatic{true},
418 .primary_dns{1, 1, 1, 1},
419 .secondary_dns{1, 0, 0, 1},
420 },
421 };
422 }();
423 415
424 IPC::ResponseBuilder rb{ctx, 2 + (sizeof(IpConfigInfo) + 3) / sizeof(u32)}; 416 return IpConfigInfo{
425 rb.Push(ResultSuccess); 417 .ip_address_setting{
426 rb.PushRaw<IpConfigInfo>(ip_config_info); 418 .is_automatic{true},
419 .current_address{Network::TranslateIPv4(net_iface->ip_address)},
420 .subnet_mask{Network::TranslateIPv4(net_iface->subnet_mask)},
421 .gateway{Network::TranslateIPv4(net_iface->gateway)},
422 },
423 .dns_setting{
424 .is_automatic{true},
425 .primary_dns{1, 1, 1, 1},
426 .secondary_dns{1, 0, 0, 1},
427 },
428 };
429 }();
430
431 // When we're connected to a room, spoof the hosts IP address
432 if (auto room_member = network.GetRoomMember().lock()) {
433 if (room_member->IsConnected()) {
434 ip_config_info.ip_address_setting.current_address = room_member->GetFakeIpAddress();
435 }
427 } 436 }
428 437
429 void IsWirelessCommunicationEnabled(Kernel::HLERequestContext& ctx) { 438 IPC::ResponseBuilder rb{ctx, 2 + (sizeof(IpConfigInfo) + 3) / sizeof(u32)};
430 LOG_WARNING(Service_NIFM, "(STUBBED) called"); 439 rb.Push(ResultSuccess);
440 rb.PushRaw<IpConfigInfo>(ip_config_info);
441}
431 442
432 IPC::ResponseBuilder rb{ctx, 3}; 443void IGeneralService::IsWirelessCommunicationEnabled(Kernel::HLERequestContext& ctx) {
433 rb.Push(ResultSuccess); 444 LOG_WARNING(Service_NIFM, "(STUBBED) called");
434 rb.Push<u8>(0);
435 }
436 445
437 void GetInternetConnectionStatus(Kernel::HLERequestContext& ctx) { 446 IPC::ResponseBuilder rb{ctx, 3};
438 LOG_WARNING(Service_NIFM, "(STUBBED) called"); 447 rb.Push(ResultSuccess);
448 rb.Push<u8>(1);
449}
439 450
440 struct Output { 451void IGeneralService::GetInternetConnectionStatus(Kernel::HLERequestContext& ctx) {
441 InternetConnectionType type{InternetConnectionType::WiFi}; 452 LOG_WARNING(Service_NIFM, "(STUBBED) called");
442 u8 wifi_strength{3};
443 InternetConnectionStatus state{InternetConnectionStatus::Connected};
444 };
445 static_assert(sizeof(Output) == 0x3, "Output has incorrect size.");
446 453
447 constexpr Output out{}; 454 struct Output {
455 InternetConnectionType type{InternetConnectionType::WiFi};
456 u8 wifi_strength{3};
457 InternetConnectionStatus state{InternetConnectionStatus::Connected};
458 };
459 static_assert(sizeof(Output) == 0x3, "Output has incorrect size.");
448 460
449 IPC::ResponseBuilder rb{ctx, 3}; 461 constexpr Output out{};
450 rb.Push(ResultSuccess);
451 rb.PushRaw(out);
452 }
453 462
454 void IsEthernetCommunicationEnabled(Kernel::HLERequestContext& ctx) { 463 IPC::ResponseBuilder rb{ctx, 3};
455 LOG_WARNING(Service_NIFM, "(STUBBED) called"); 464 rb.Push(ResultSuccess);
465 rb.PushRaw(out);
466}
456 467
457 IPC::ResponseBuilder rb{ctx, 3}; 468void IGeneralService::IsEthernetCommunicationEnabled(Kernel::HLERequestContext& ctx) {
458 rb.Push(ResultSuccess); 469 LOG_WARNING(Service_NIFM, "(STUBBED) called");
459 if (Network::GetHostIPv4Address().has_value()) { 470
460 rb.Push<u8>(1); 471 IPC::ResponseBuilder rb{ctx, 3};
461 } else { 472 rb.Push(ResultSuccess);
462 rb.Push<u8>(0); 473 if (Network::GetHostIPv4Address().has_value()) {
463 } 474 rb.Push<u8>(1);
475 } else {
476 rb.Push<u8>(0);
464 } 477 }
478}
465 479
466 void IsAnyInternetRequestAccepted(Kernel::HLERequestContext& ctx) { 480void IGeneralService::IsAnyInternetRequestAccepted(Kernel::HLERequestContext& ctx) {
467 LOG_WARNING(Service_NIFM, "(STUBBED) called"); 481 LOG_ERROR(Service_NIFM, "(STUBBED) called");
468 482
469 IPC::ResponseBuilder rb{ctx, 3}; 483 IPC::ResponseBuilder rb{ctx, 3};
470 rb.Push(ResultSuccess); 484 rb.Push(ResultSuccess);
471 if (Network::GetHostIPv4Address().has_value()) { 485 if (Network::GetHostIPv4Address().has_value()) {
472 rb.Push<u8>(1); 486 rb.Push<u8>(1);
473 } else { 487 } else {
474 rb.Push<u8>(0); 488 rb.Push<u8>(0);
475 }
476 } 489 }
477}; 490}
478 491
479IGeneralService::IGeneralService(Core::System& system_) 492IGeneralService::IGeneralService(Core::System& system_)
480 : ServiceFramework{system_, "IGeneralService"} { 493 : ServiceFramework{system_, "IGeneralService"}, network{system_.GetRoomNetwork()} {
481 // clang-format off 494 // clang-format off
482 static const FunctionInfo functions[] = { 495 static const FunctionInfo functions[] = {
483 {1, &IGeneralService::GetClientId, "GetClientId"}, 496 {1, &IGeneralService::GetClientId, "GetClientId"},
@@ -528,6 +541,8 @@ IGeneralService::IGeneralService(Core::System& system_)
528 RegisterHandlers(functions); 541 RegisterHandlers(functions);
529} 542}
530 543
544IGeneralService::~IGeneralService() = default;
545
531class NetworkInterface final : public ServiceFramework<NetworkInterface> { 546class NetworkInterface final : public ServiceFramework<NetworkInterface> {
532public: 547public:
533 explicit NetworkInterface(const char* name, Core::System& system_) 548 explicit NetworkInterface(const char* name, Core::System& system_)
diff --git a/src/core/hle/service/nifm/nifm.h b/src/core/hle/service/nifm/nifm.h
index 5f62d0014..48161be28 100644
--- a/src/core/hle/service/nifm/nifm.h
+++ b/src/core/hle/service/nifm/nifm.h
@@ -3,6 +3,11 @@
3 3
4#pragma once 4#pragma once
5 5
6#include "core/hle/service/service.h"
7#include "network/network.h"
8#include "network/room.h"
9#include "network/room_member.h"
10
6namespace Core { 11namespace Core {
7class System; 12class System;
8} 13}
@@ -16,4 +21,26 @@ namespace Service::NIFM {
16/// Registers all NIFM services with the specified service manager. 21/// Registers all NIFM services with the specified service manager.
17void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system); 22void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system);
18 23
24class IGeneralService final : public ServiceFramework<IGeneralService> {
25public:
26 explicit IGeneralService(Core::System& system_);
27 ~IGeneralService() override;
28
29private:
30 void GetClientId(Kernel::HLERequestContext& ctx);
31 void CreateScanRequest(Kernel::HLERequestContext& ctx);
32 void CreateRequest(Kernel::HLERequestContext& ctx);
33 void GetCurrentNetworkProfile(Kernel::HLERequestContext& ctx);
34 void RemoveNetworkProfile(Kernel::HLERequestContext& ctx);
35 void GetCurrentIpAddress(Kernel::HLERequestContext& ctx);
36 void CreateTemporaryNetworkProfile(Kernel::HLERequestContext& ctx);
37 void GetCurrentIpConfigInfo(Kernel::HLERequestContext& ctx);
38 void IsWirelessCommunicationEnabled(Kernel::HLERequestContext& ctx);
39 void GetInternetConnectionStatus(Kernel::HLERequestContext& ctx);
40 void IsEthernetCommunicationEnabled(Kernel::HLERequestContext& ctx);
41 void IsAnyInternetRequestAccepted(Kernel::HLERequestContext& ctx);
42
43 Network::RoomNetwork& network;
44};
45
19} // namespace Service::NIFM 46} // namespace Service::NIFM
diff --git a/src/core/hle/service/sockets/bsd.cpp b/src/core/hle/service/sockets/bsd.cpp
index c7194731e..e08c3cb67 100644
--- a/src/core/hle/service/sockets/bsd.cpp
+++ b/src/core/hle/service/sockets/bsd.cpp
@@ -9,12 +9,16 @@
9#include <fmt/format.h> 9#include <fmt/format.h>
10 10
11#include "common/microprofile.h" 11#include "common/microprofile.h"
12#include "common/socket_types.h"
13#include "core/core.h"
12#include "core/hle/ipc_helpers.h" 14#include "core/hle/ipc_helpers.h"
13#include "core/hle/kernel/k_thread.h" 15#include "core/hle/kernel/k_thread.h"
14#include "core/hle/service/sockets/bsd.h" 16#include "core/hle/service/sockets/bsd.h"
15#include "core/hle/service/sockets/sockets_translate.h" 17#include "core/hle/service/sockets/sockets_translate.h"
16#include "core/internal_network/network.h" 18#include "core/internal_network/network.h"
19#include "core/internal_network/socket_proxy.h"
17#include "core/internal_network/sockets.h" 20#include "core/internal_network/sockets.h"
21#include "network/network.h"
18 22
19namespace Service::Sockets { 23namespace Service::Sockets {
20 24
@@ -472,7 +476,13 @@ std::pair<s32, Errno> BSD::SocketImpl(Domain domain, Type type, Protocol protoco
472 476
473 LOG_INFO(Service, "New socket fd={}", fd); 477 LOG_INFO(Service, "New socket fd={}", fd);
474 478
475 descriptor.socket = std::make_unique<Network::Socket>(); 479 auto room_member = room_network.GetRoomMember().lock();
480 if (room_member && room_member->IsConnected()) {
481 descriptor.socket = std::make_unique<Network::ProxySocket>(room_network);
482 } else {
483 descriptor.socket = std::make_unique<Network::Socket>();
484 }
485
476 descriptor.socket->Initialize(Translate(domain), Translate(type), Translate(type, protocol)); 486 descriptor.socket->Initialize(Translate(domain), Translate(type), Translate(type, protocol));
477 descriptor.is_connection_based = IsConnectionBased(type); 487 descriptor.is_connection_based = IsConnectionBased(type);
478 488
@@ -648,7 +658,7 @@ std::pair<s32, Errno> BSD::FcntlImpl(s32 fd, FcntlCmd cmd, s32 arg) {
648 ASSERT(arg == 0); 658 ASSERT(arg == 0);
649 return {descriptor.flags, Errno::SUCCESS}; 659 return {descriptor.flags, Errno::SUCCESS};
650 case FcntlCmd::SETFL: { 660 case FcntlCmd::SETFL: {
651 const bool enable = (arg & FLAG_O_NONBLOCK) != 0; 661 const bool enable = (arg & Network::FLAG_O_NONBLOCK) != 0;
652 const Errno bsd_errno = Translate(descriptor.socket->SetNonBlock(enable)); 662 const Errno bsd_errno = Translate(descriptor.socket->SetNonBlock(enable));
653 if (bsd_errno != Errno::SUCCESS) { 663 if (bsd_errno != Errno::SUCCESS) {
654 return {-1, bsd_errno}; 664 return {-1, bsd_errno};
@@ -669,7 +679,7 @@ Errno BSD::SetSockOptImpl(s32 fd, u32 level, OptName optname, size_t optlen, con
669 return Errno::BADF; 679 return Errno::BADF;
670 } 680 }
671 681
672 Network::Socket* const socket = file_descriptors[fd]->socket.get(); 682 Network::SocketBase* const socket = file_descriptors[fd]->socket.get();
673 683
674 if (optname == OptName::LINGER) { 684 if (optname == OptName::LINGER) {
675 ASSERT(optlen == sizeof(Linger)); 685 ASSERT(optlen == sizeof(Linger));
@@ -724,6 +734,8 @@ std::pair<s32, Errno> BSD::RecvImpl(s32 fd, u32 flags, std::vector<u8>& message)
724 FileDescriptor& descriptor = *file_descriptors[fd]; 734 FileDescriptor& descriptor = *file_descriptors[fd];
725 735
726 // Apply flags 736 // Apply flags
737 using Network::FLAG_MSG_DONTWAIT;
738 using Network::FLAG_O_NONBLOCK;
727 if ((flags & FLAG_MSG_DONTWAIT) != 0) { 739 if ((flags & FLAG_MSG_DONTWAIT) != 0) {
728 flags &= ~FLAG_MSG_DONTWAIT; 740 flags &= ~FLAG_MSG_DONTWAIT;
729 if ((descriptor.flags & FLAG_O_NONBLOCK) == 0) { 741 if ((descriptor.flags & FLAG_O_NONBLOCK) == 0) {
@@ -759,6 +771,8 @@ std::pair<s32, Errno> BSD::RecvFromImpl(s32 fd, u32 flags, std::vector<u8>& mess
759 } 771 }
760 772
761 // Apply flags 773 // Apply flags
774 using Network::FLAG_MSG_DONTWAIT;
775 using Network::FLAG_O_NONBLOCK;
762 if ((flags & FLAG_MSG_DONTWAIT) != 0) { 776 if ((flags & FLAG_MSG_DONTWAIT) != 0) {
763 flags &= ~FLAG_MSG_DONTWAIT; 777 flags &= ~FLAG_MSG_DONTWAIT;
764 if ((descriptor.flags & FLAG_O_NONBLOCK) == 0) { 778 if ((descriptor.flags & FLAG_O_NONBLOCK) == 0) {
@@ -857,8 +871,19 @@ void BSD::BuildErrnoResponse(Kernel::HLERequestContext& ctx, Errno bsd_errno) co
857 rb.PushEnum(bsd_errno); 871 rb.PushEnum(bsd_errno);
858} 872}
859 873
874void BSD::OnProxyPacketReceived(const Network::ProxyPacket& packet) {
875 for (auto& optional_descriptor : file_descriptors) {
876 if (!optional_descriptor.has_value()) {
877 continue;
878 }
879 FileDescriptor& descriptor = *optional_descriptor;
880 descriptor.socket.get()->HandleProxyPacket(packet);
881 }
882}
883
860BSD::BSD(Core::System& system_, const char* name) 884BSD::BSD(Core::System& system_, const char* name)
861 : ServiceFramework{system_, name, ServiceThreadType::CreateNew} { 885 : ServiceFramework{system_, name, ServiceThreadType::CreateNew}, room_network{
886 system_.GetRoomNetwork()} {
862 // clang-format off 887 // clang-format off
863 static const FunctionInfo functions[] = { 888 static const FunctionInfo functions[] = {
864 {0, &BSD::RegisterClient, "RegisterClient"}, 889 {0, &BSD::RegisterClient, "RegisterClient"},
@@ -899,6 +924,13 @@ BSD::BSD(Core::System& system_, const char* name)
899 // clang-format on 924 // clang-format on
900 925
901 RegisterHandlers(functions); 926 RegisterHandlers(functions);
927
928 if (auto room_member = room_network.GetRoomMember().lock()) {
929 proxy_packet_received = room_member->BindOnProxyPacketReceived(
930 [this](const Network::ProxyPacket& packet) { OnProxyPacketReceived(packet); });
931 } else {
932 LOG_ERROR(Service, "Network isn't initalized");
933 }
902} 934}
903 935
904BSD::~BSD() = default; 936BSD::~BSD() = default;
diff --git a/src/core/hle/service/sockets/bsd.h b/src/core/hle/service/sockets/bsd.h
index 9ea36428d..81e855e0f 100644
--- a/src/core/hle/service/sockets/bsd.h
+++ b/src/core/hle/service/sockets/bsd.h
@@ -7,14 +7,17 @@
7#include <vector> 7#include <vector>
8 8
9#include "common/common_types.h" 9#include "common/common_types.h"
10#include "common/socket_types.h"
10#include "core/hle/service/service.h" 11#include "core/hle/service/service.h"
11#include "core/hle/service/sockets/sockets.h" 12#include "core/hle/service/sockets/sockets.h"
13#include "network/network.h"
12 14
13namespace Core { 15namespace Core {
14class System; 16class System;
15} 17}
16 18
17namespace Network { 19namespace Network {
20class SocketBase;
18class Socket; 21class Socket;
19} // namespace Network 22} // namespace Network
20 23
@@ -30,7 +33,7 @@ private:
30 static constexpr size_t MAX_FD = 128; 33 static constexpr size_t MAX_FD = 128;
31 34
32 struct FileDescriptor { 35 struct FileDescriptor {
33 std::unique_ptr<Network::Socket> socket; 36 std::unique_ptr<Network::SocketBase> socket;
34 s32 flags = 0; 37 s32 flags = 0;
35 bool is_connection_based = false; 38 bool is_connection_based = false;
36 }; 39 };
@@ -165,6 +168,14 @@ private:
165 void BuildErrnoResponse(Kernel::HLERequestContext& ctx, Errno bsd_errno) const noexcept; 168 void BuildErrnoResponse(Kernel::HLERequestContext& ctx, Errno bsd_errno) const noexcept;
166 169
167 std::array<std::optional<FileDescriptor>, MAX_FD> file_descriptors; 170 std::array<std::optional<FileDescriptor>, MAX_FD> file_descriptors;
171
172 Network::RoomNetwork& room_network;
173
174 /// Callback to parse and handle a received wifi packet.
175 void OnProxyPacketReceived(const Network::ProxyPacket& packet);
176
177 // Callback identifier for the OnProxyPacketReceived event.
178 Network::RoomMember::CallbackHandle<Network::ProxyPacket> proxy_packet_received;
168}; 179};
169 180
170class BSDCFG final : public ServiceFramework<BSDCFG> { 181class BSDCFG final : public ServiceFramework<BSDCFG> {
diff --git a/src/core/hle/service/sockets/sockets.h b/src/core/hle/service/sockets/sockets.h
index b735b00fc..31b7dad33 100644
--- a/src/core/hle/service/sockets/sockets.h
+++ b/src/core/hle/service/sockets/sockets.h
@@ -22,7 +22,9 @@ enum class Errno : u32 {
22 AGAIN = 11, 22 AGAIN = 11,
23 INVAL = 22, 23 INVAL = 22,
24 MFILE = 24, 24 MFILE = 24,
25 MSGSIZE = 90,
25 NOTCONN = 107, 26 NOTCONN = 107,
27 TIMEDOUT = 110,
26}; 28};
27 29
28enum class Domain : u32 { 30enum class Domain : u32 {
@@ -96,10 +98,6 @@ struct Linger {
96 u32 linger; 98 u32 linger;
97}; 99};
98 100
99constexpr u32 FLAG_MSG_DONTWAIT = 0x80;
100
101constexpr u32 FLAG_O_NONBLOCK = 0x800;
102
103/// Registers all Sockets services with the specified service manager. 101/// Registers all Sockets services with the specified service manager.
104void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system); 102void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system);
105 103
diff --git a/src/core/hle/service/sockets/sockets_translate.cpp b/src/core/hle/service/sockets/sockets_translate.cpp
index 2db10ec81..023aa0486 100644
--- a/src/core/hle/service/sockets/sockets_translate.cpp
+++ b/src/core/hle/service/sockets/sockets_translate.cpp
@@ -25,6 +25,8 @@ Errno Translate(Network::Errno value) {
25 return Errno::MFILE; 25 return Errno::MFILE;
26 case Network::Errno::NOTCONN: 26 case Network::Errno::NOTCONN:
27 return Errno::NOTCONN; 27 return Errno::NOTCONN;
28 case Network::Errno::TIMEDOUT:
29 return Errno::TIMEDOUT;
28 default: 30 default:
29 UNIMPLEMENTED_MSG("Unimplemented errno={}", value); 31 UNIMPLEMENTED_MSG("Unimplemented errno={}", value);
30 return Errno::SUCCESS; 32 return Errno::SUCCESS;
diff --git a/src/core/internal_network/network.cpp b/src/core/internal_network/network.cpp
index 36c43cc8f..160cc83e4 100644
--- a/src/core/internal_network/network.cpp
+++ b/src/core/internal_network/network.cpp
@@ -32,6 +32,7 @@
32#include "core/internal_network/network.h" 32#include "core/internal_network/network.h"
33#include "core/internal_network/network_interface.h" 33#include "core/internal_network/network_interface.h"
34#include "core/internal_network/sockets.h" 34#include "core/internal_network/sockets.h"
35#include "network/network.h"
35 36
36namespace Network { 37namespace Network {
37 38
@@ -114,7 +115,10 @@ Errno TranslateNativeError(int e) {
114 return Errno::NETDOWN; 115 return Errno::NETDOWN;
115 case WSAENETUNREACH: 116 case WSAENETUNREACH:
116 return Errno::NETUNREACH; 117 return Errno::NETUNREACH;
118 case WSAEMSGSIZE:
119 return Errno::MSGSIZE;
117 default: 120 default:
121 UNIMPLEMENTED_MSG("Unimplemented errno={}", e);
118 return Errno::OTHER; 122 return Errno::OTHER;
119 } 123 }
120} 124}
@@ -125,7 +129,6 @@ using SOCKET = int;
125using WSAPOLLFD = pollfd; 129using WSAPOLLFD = pollfd;
126using ULONG = u64; 130using ULONG = u64;
127 131
128constexpr SOCKET INVALID_SOCKET = -1;
129constexpr SOCKET SOCKET_ERROR = -1; 132constexpr SOCKET SOCKET_ERROR = -1;
130 133
131constexpr int SD_RECEIVE = SHUT_RD; 134constexpr int SD_RECEIVE = SHUT_RD;
@@ -206,7 +209,10 @@ Errno TranslateNativeError(int e) {
206 return Errno::NETDOWN; 209 return Errno::NETDOWN;
207 case ENETUNREACH: 210 case ENETUNREACH:
208 return Errno::NETUNREACH; 211 return Errno::NETUNREACH;
212 case EMSGSIZE:
213 return Errno::MSGSIZE;
209 default: 214 default:
215 UNIMPLEMENTED_MSG("Unimplemented errno={}", e);
210 return Errno::OTHER; 216 return Errno::OTHER;
211 } 217 }
212} 218}
@@ -329,16 +335,6 @@ PollEvents TranslatePollRevents(short revents) {
329 return result; 335 return result;
330} 336}
331 337
332template <typename T>
333Errno SetSockOpt(SOCKET fd, int option, T value) {
334 const int result =
335 setsockopt(fd, SOL_SOCKET, option, reinterpret_cast<const char*>(&value), sizeof(value));
336 if (result != SOCKET_ERROR) {
337 return Errno::SUCCESS;
338 }
339 return GetAndLogLastError();
340}
341
342} // Anonymous namespace 338} // Anonymous namespace
343 339
344NetworkInstance::NetworkInstance() { 340NetworkInstance::NetworkInstance() {
@@ -350,26 +346,15 @@ NetworkInstance::~NetworkInstance() {
350} 346}
351 347
352std::optional<IPv4Address> GetHostIPv4Address() { 348std::optional<IPv4Address> GetHostIPv4Address() {
353 const std::string& selected_network_interface = Settings::values.network_interface.GetValue(); 349 const auto interface = Network::GetSelectedNetworkInterface();
354 const auto network_interfaces = Network::GetAvailableNetworkInterfaces(); 350 if (!interface.has_value()) {
355 if (network_interfaces.size() == 0) { 351 LOG_ERROR(Network, "GetSelectedNetworkInterface returned no interface");
356 LOG_ERROR(Network, "GetAvailableNetworkInterfaces returned no interfaces");
357 return {}; 352 return {};
358 } 353 }
359 354
360 const auto res = 355 char ip_addr[16] = {};
361 std::ranges::find_if(network_interfaces, [&selected_network_interface](const auto& iface) { 356 ASSERT(inet_ntop(AF_INET, &interface->ip_address, ip_addr, sizeof(ip_addr)) != nullptr);
362 return iface.name == selected_network_interface; 357 return TranslateIPv4(interface->ip_address);
363 });
364
365 if (res != network_interfaces.end()) {
366 char ip_addr[16] = {};
367 ASSERT(inet_ntop(AF_INET, &res->ip_address, ip_addr, sizeof(ip_addr)) != nullptr);
368 return TranslateIPv4(res->ip_address);
369 } else {
370 LOG_ERROR(Network, "Couldn't find selected interface \"{}\"", selected_network_interface);
371 return {};
372 }
373} 358}
374 359
375std::pair<s32, Errno> Poll(std::vector<PollFD>& pollfds, s32 timeout) { 360std::pair<s32, Errno> Poll(std::vector<PollFD>& pollfds, s32 timeout) {
@@ -412,7 +397,19 @@ Socket::~Socket() {
412 fd = INVALID_SOCKET; 397 fd = INVALID_SOCKET;
413} 398}
414 399
415Socket::Socket(Socket&& rhs) noexcept : fd{std::exchange(rhs.fd, INVALID_SOCKET)} {} 400Socket::Socket(Socket&& rhs) noexcept {
401 fd = std::exchange(rhs.fd, INVALID_SOCKET);
402}
403
404template <typename T>
405Errno Socket::SetSockOpt(SOCKET _fd, int option, T value) {
406 const int result =
407 setsockopt(_fd, SOL_SOCKET, option, reinterpret_cast<const char*>(&value), sizeof(value));
408 if (result != SOCKET_ERROR) {
409 return Errno::SUCCESS;
410 }
411 return GetAndLogLastError();
412}
416 413
417Errno Socket::Initialize(Domain domain, Type type, Protocol protocol) { 414Errno Socket::Initialize(Domain domain, Type type, Protocol protocol) {
418 fd = socket(TranslateDomain(domain), TranslateType(type), TranslateProtocol(protocol)); 415 fd = socket(TranslateDomain(domain), TranslateType(type), TranslateProtocol(protocol));
@@ -423,7 +420,7 @@ Errno Socket::Initialize(Domain domain, Type type, Protocol protocol) {
423 return GetAndLogLastError(); 420 return GetAndLogLastError();
424} 421}
425 422
426std::pair<Socket::AcceptResult, Errno> Socket::Accept() { 423std::pair<SocketBase::AcceptResult, Errno> Socket::Accept() {
427 sockaddr addr; 424 sockaddr addr;
428 socklen_t addrlen = sizeof(addr); 425 socklen_t addrlen = sizeof(addr);
429 const SOCKET new_socket = accept(fd, &addr, &addrlen); 426 const SOCKET new_socket = accept(fd, &addr, &addrlen);
@@ -634,4 +631,8 @@ bool Socket::IsOpened() const {
634 return fd != INVALID_SOCKET; 631 return fd != INVALID_SOCKET;
635} 632}
636 633
634void Socket::HandleProxyPacket(const ProxyPacket& packet) {
635 LOG_WARNING(Network, "ProxyPacket received, but not in Proxy mode!");
636}
637
637} // namespace Network 638} // namespace Network
diff --git a/src/core/internal_network/network.h b/src/core/internal_network/network.h
index 10e5ef10d..36994c22e 100644
--- a/src/core/internal_network/network.h
+++ b/src/core/internal_network/network.h
@@ -8,6 +8,7 @@
8 8
9#include "common/common_funcs.h" 9#include "common/common_funcs.h"
10#include "common/common_types.h" 10#include "common/common_types.h"
11#include "common/socket_types.h"
11 12
12#ifdef _WIN32 13#ifdef _WIN32
13#include <winsock2.h> 14#include <winsock2.h>
@@ -17,6 +18,7 @@
17 18
18namespace Network { 19namespace Network {
19 20
21class SocketBase;
20class Socket; 22class Socket;
21 23
22/// Error code for network functions 24/// Error code for network functions
@@ -31,46 +33,11 @@ enum class Errno {
31 HOSTUNREACH, 33 HOSTUNREACH,
32 NETDOWN, 34 NETDOWN,
33 NETUNREACH, 35 NETUNREACH,
36 TIMEDOUT,
37 MSGSIZE,
34 OTHER, 38 OTHER,
35}; 39};
36 40
37/// Address families
38enum class Domain {
39 INET, ///< Address family for IPv4
40};
41
42/// Socket types
43enum class Type {
44 STREAM,
45 DGRAM,
46 RAW,
47 SEQPACKET,
48};
49
50/// Protocol values for sockets
51enum class Protocol {
52 ICMP,
53 TCP,
54 UDP,
55};
56
57/// Shutdown mode
58enum class ShutdownHow {
59 RD,
60 WR,
61 RDWR,
62};
63
64/// Array of IPv4 address
65using IPv4Address = std::array<u8, 4>;
66
67/// Cross-platform sockaddr structure
68struct SockAddrIn {
69 Domain family;
70 IPv4Address ip;
71 u16 portno;
72};
73
74/// Cross-platform poll fd structure 41/// Cross-platform poll fd structure
75 42
76enum class PollEvents : u16 { 43enum class PollEvents : u16 {
@@ -86,7 +53,7 @@ enum class PollEvents : u16 {
86DECLARE_ENUM_FLAG_OPERATORS(PollEvents); 53DECLARE_ENUM_FLAG_OPERATORS(PollEvents);
87 54
88struct PollFD { 55struct PollFD {
89 Socket* socket; 56 SocketBase* socket;
90 PollEvents events; 57 PollEvents events;
91 PollEvents revents; 58 PollEvents revents;
92}; 59};
diff --git a/src/core/internal_network/socket_proxy.cpp b/src/core/internal_network/socket_proxy.cpp
new file mode 100644
index 000000000..6e8924822
--- /dev/null
+++ b/src/core/internal_network/socket_proxy.cpp
@@ -0,0 +1,282 @@
1// Copyright 2022 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <chrono>
6#include <thread>
7
8#include "common/assert.h"
9#include "common/logging/log.h"
10#include "core/internal_network/network.h"
11#include "core/internal_network/network_interface.h"
12#include "core/internal_network/socket_proxy.h"
13
14namespace Network {
15
16ProxySocket::ProxySocket(RoomNetwork& room_network_) noexcept : room_network{room_network_} {}
17
18ProxySocket::ProxySocket(ProxySocket&& rhs) noexcept : room_network{rhs.room_network} {
19 fd = std::exchange(rhs.fd, INVALID_SOCKET);
20}
21
22ProxySocket::~ProxySocket() {
23 if (fd == INVALID_SOCKET) {
24 return;
25 }
26 fd = INVALID_SOCKET;
27}
28
29void ProxySocket::HandleProxyPacket(const ProxyPacket& packet) {
30 if (protocol != packet.protocol || local_endpoint.portno != packet.remote_endpoint.portno ||
31 closed) {
32 return;
33 }
34 std::lock_guard<std::mutex> guard(packets_mutex);
35 received_packets.push(packet);
36}
37
38template <typename T>
39Errno ProxySocket::SetSockOpt(SOCKET _fd, int option, T value) {
40 socket_options[option] = reinterpret_cast<const char*>(&value);
41 return Errno::SUCCESS;
42}
43
44Errno ProxySocket::Initialize(Domain domain, Type type, Protocol socket_protocol) {
45 protocol = socket_protocol;
46 socket_options[0x1008] = reinterpret_cast<const char*>(&type);
47
48 return Errno::SUCCESS;
49}
50
51std::pair<ProxySocket::AcceptResult, Errno> ProxySocket::Accept() {
52 LOG_WARNING(Network, "(STUBBED) called");
53 return {AcceptResult{}, Errno::SUCCESS};
54}
55
56Errno ProxySocket::Connect(SockAddrIn addr_in) {
57 LOG_WARNING(Network, "(STUBBED) called");
58 return Errno::SUCCESS;
59}
60
61std::pair<SockAddrIn, Errno> ProxySocket::GetPeerName() {
62 LOG_WARNING(Network, "(STUBBED) called");
63 return {SockAddrIn{}, Errno::SUCCESS};
64}
65
66std::pair<SockAddrIn, Errno> ProxySocket::GetSockName() {
67 LOG_WARNING(Network, "(STUBBED) called");
68 return {SockAddrIn{}, Errno::SUCCESS};
69}
70
71Errno ProxySocket::Bind(SockAddrIn addr) {
72 if (is_bound) {
73 LOG_WARNING(Network, "Rebinding Socket is unimplemented!");
74 return Errno::SUCCESS;
75 }
76 local_endpoint = addr;
77 is_bound = true;
78
79 return Errno::SUCCESS;
80}
81
82Errno ProxySocket::Listen(s32 backlog) {
83 LOG_WARNING(Network, "(STUBBED) called");
84 return Errno::SUCCESS;
85}
86
87Errno ProxySocket::Shutdown(ShutdownHow how) {
88 LOG_WARNING(Network, "(STUBBED) called");
89 return Errno::SUCCESS;
90}
91
92std::pair<s32, Errno> ProxySocket::Recv(int flags, std::vector<u8>& message) {
93 LOG_WARNING(Network, "(STUBBED) called");
94 ASSERT(flags == 0);
95 ASSERT(message.size() < static_cast<size_t>(std::numeric_limits<int>::max()));
96
97 return {static_cast<s32>(0), Errno::SUCCESS};
98}
99
100std::pair<s32, Errno> ProxySocket::RecvFrom(int flags, std::vector<u8>& message, SockAddrIn* addr) {
101 ASSERT(flags == 0);
102 ASSERT(message.size() < static_cast<size_t>(std::numeric_limits<int>::max()));
103
104 {
105 std::lock_guard<std::mutex> guard(packets_mutex);
106 if (received_packets.size() > 0) {
107 return ReceivePacket(flags, message, addr, message.size());
108 }
109 }
110
111 if (blocking) {
112 if (receive_timeout > 0) {
113 std::this_thread::sleep_for(std::chrono::milliseconds(receive_timeout));
114 }
115 } else {
116 return {-1, Errno::AGAIN};
117 }
118
119 std::lock_guard<std::mutex> guard(packets_mutex);
120 if (received_packets.size() > 0) {
121 return ReceivePacket(flags, message, addr, message.size());
122 }
123
124 return {-1, Errno::TIMEDOUT};
125}
126
127std::pair<s32, Errno> ProxySocket::ReceivePacket(int flags, std::vector<u8>& message,
128 SockAddrIn* addr, std::size_t max_length) {
129 ProxyPacket& packet = received_packets.front();
130 if (addr) {
131 addr->family = Domain::INET;
132 addr->ip = packet.local_endpoint.ip; // The senders ip address
133 addr->portno = packet.local_endpoint.portno; // The senders port number
134 }
135
136 bool peek = (flags & FLAG_MSG_PEEK) != 0;
137 std::size_t read_bytes;
138 if (packet.data.size() > max_length) {
139 read_bytes = max_length;
140 message.clear();
141 std::copy(packet.data.begin(), packet.data.begin() + read_bytes,
142 std::back_inserter(message));
143 message.resize(max_length);
144
145 if (protocol == Protocol::UDP) {
146 if (!peek) {
147 received_packets.pop();
148 }
149 return {-1, Errno::MSGSIZE};
150 } else if (protocol == Protocol::TCP) {
151 std::vector<u8> numArray(packet.data.size() - max_length);
152 std::copy(packet.data.begin() + max_length, packet.data.end(),
153 std::back_inserter(numArray));
154 packet.data = numArray;
155 }
156 } else {
157 read_bytes = packet.data.size();
158 message.clear();
159 std::copy(packet.data.begin(), packet.data.end(), std::back_inserter(message));
160 message.resize(max_length);
161 if (!peek) {
162 received_packets.pop();
163 }
164 }
165
166 return {static_cast<u32>(read_bytes), Errno::SUCCESS};
167}
168
169std::pair<s32, Errno> ProxySocket::Send(const std::vector<u8>& message, int flags) {
170 LOG_WARNING(Network, "(STUBBED) called");
171 ASSERT(message.size() < static_cast<size_t>(std::numeric_limits<int>::max()));
172 ASSERT(flags == 0);
173
174 return {static_cast<s32>(0), Errno::SUCCESS};
175}
176
177void ProxySocket::SendPacket(ProxyPacket& packet) {
178 if (auto room_member = room_network.GetRoomMember().lock()) {
179 if (room_member->IsConnected()) {
180 room_member->SendProxyPacket(packet);
181 }
182 }
183}
184
185std::pair<s32, Errno> ProxySocket::SendTo(u32 flags, const std::vector<u8>& message,
186 const SockAddrIn* addr) {
187 ASSERT(flags == 0);
188
189 if (!is_bound) {
190 LOG_ERROR(Network, "ProxySocket is not bound!");
191 return {static_cast<s32>(message.size()), Errno::SUCCESS};
192 }
193
194 if (auto room_member = room_network.GetRoomMember().lock()) {
195 if (!room_member->IsConnected()) {
196 return {static_cast<s32>(message.size()), Errno::SUCCESS};
197 }
198 }
199
200 ProxyPacket packet;
201 packet.local_endpoint = local_endpoint;
202 packet.remote_endpoint = *addr;
203 packet.protocol = protocol;
204 packet.broadcast = broadcast;
205
206 auto& ip = local_endpoint.ip;
207 auto ipv4 = Network::GetHostIPv4Address();
208 // If the ip is all zeroes (INADDR_ANY) or if it matches the hosts ip address,
209 // replace it with a "fake" routing address
210 if (std::all_of(ip.begin(), ip.end(), [](u8 i) { return i == 0; }) || (ipv4 && ipv4 == ip)) {
211 if (auto room_member = room_network.GetRoomMember().lock()) {
212 packet.local_endpoint.ip = room_member->GetFakeIpAddress();
213 }
214 }
215
216 packet.data.clear();
217 std::copy(message.begin(), message.end(), std::back_inserter(packet.data));
218
219 SendPacket(packet);
220
221 return {static_cast<s32>(message.size()), Errno::SUCCESS};
222}
223
224Errno ProxySocket::Close() {
225 fd = INVALID_SOCKET;
226 closed = true;
227
228 return Errno::SUCCESS;
229}
230
231Errno ProxySocket::SetLinger(bool enable, u32 linger) {
232 struct Linger {
233 u16 linger_enable;
234 u16 linger_time;
235 } values;
236 values.linger_enable = enable ? 1 : 0;
237 values.linger_time = static_cast<u16>(linger);
238
239 return SetSockOpt(fd, SO_LINGER, values);
240}
241
242Errno ProxySocket::SetReuseAddr(bool enable) {
243 return SetSockOpt<u32>(fd, SO_REUSEADDR, enable ? 1 : 0);
244}
245
246Errno ProxySocket::SetBroadcast(bool enable) {
247 broadcast = enable;
248 return SetSockOpt<u32>(fd, SO_BROADCAST, enable ? 1 : 0);
249}
250
251Errno ProxySocket::SetSndBuf(u32 value) {
252 return SetSockOpt(fd, SO_SNDBUF, value);
253}
254
255Errno ProxySocket::SetKeepAlive(bool enable) {
256 return Errno::SUCCESS;
257}
258
259Errno ProxySocket::SetRcvBuf(u32 value) {
260 return SetSockOpt(fd, SO_RCVBUF, value);
261}
262
263Errno ProxySocket::SetSndTimeo(u32 value) {
264 send_timeout = value;
265 return SetSockOpt(fd, SO_SNDTIMEO, static_cast<int>(value));
266}
267
268Errno ProxySocket::SetRcvTimeo(u32 value) {
269 receive_timeout = value;
270 return SetSockOpt(fd, SO_RCVTIMEO, static_cast<int>(value));
271}
272
273Errno ProxySocket::SetNonBlock(bool enable) {
274 blocking = !enable;
275 return Errno::SUCCESS;
276}
277
278bool ProxySocket::IsOpened() const {
279 return fd != INVALID_SOCKET;
280}
281
282} // namespace Network
diff --git a/src/core/internal_network/socket_proxy.h b/src/core/internal_network/socket_proxy.h
new file mode 100644
index 000000000..ef7d5b554
--- /dev/null
+++ b/src/core/internal_network/socket_proxy.h
@@ -0,0 +1,102 @@
1// Copyright 2022 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <mutex>
8#include <vector>
9#include <queue>
10
11#include "core/internal_network/sockets.h"
12#include "network/network.h"
13
14namespace Network {
15
16class ProxySocket : public SocketBase {
17public:
18 ProxySocket(RoomNetwork& room_network_) noexcept;
19 ~ProxySocket() override;
20
21 ProxySocket(const ProxySocket&) = delete;
22 ProxySocket& operator=(const ProxySocket&) = delete;
23
24 ProxySocket(ProxySocket&& rhs) noexcept;
25
26 // Avoid closing sockets implicitly
27 ProxySocket& operator=(ProxySocket&&) noexcept = delete;
28
29 void HandleProxyPacket(const ProxyPacket& packet);
30
31 Errno Initialize(Domain domain, Type type, Protocol socket_protocol) override;
32
33 Errno Close() override;
34
35 std::pair<AcceptResult, Errno> Accept() override;
36
37 Errno Connect(SockAddrIn addr_in) override;
38
39 std::pair<SockAddrIn, Errno> GetPeerName() override;
40
41 std::pair<SockAddrIn, Errno> GetSockName() override;
42
43 Errno Bind(SockAddrIn addr) override;
44
45 Errno Listen(s32 backlog) override;
46
47 Errno Shutdown(ShutdownHow how) override;
48
49 std::pair<s32, Errno> Recv(int flags, std::vector<u8>& message) override;
50
51 std::pair<s32, Errno> RecvFrom(int flags, std::vector<u8>& message, SockAddrIn* addr) override;
52
53 std::pair<s32, Errno> ReceivePacket(int flags, std::vector<u8>& message, SockAddrIn* addr,
54 std::size_t max_length);
55
56 std::pair<s32, Errno> Send(const std::vector<u8>& message, int flags) override;
57
58 void SendPacket(ProxyPacket& packet);
59
60 std::pair<s32, Errno> SendTo(u32 flags, const std::vector<u8>& message,
61 const SockAddrIn* addr) override;
62
63 Errno SetLinger(bool enable, u32 linger) override;
64
65 Errno SetReuseAddr(bool enable) override;
66
67 Errno SetBroadcast(bool enable) override;
68
69 Errno SetKeepAlive(bool enable) override;
70
71 Errno SetSndBuf(u32 value) override;
72
73 Errno SetRcvBuf(u32 value) override;
74
75 Errno SetSndTimeo(u32 value) override;
76
77 Errno SetRcvTimeo(u32 value) override;
78
79 Errno SetNonBlock(bool enable) override;
80
81 template <typename T>
82 Errno SetSockOpt(SOCKET fd, int option, T value);
83
84 bool IsOpened() const override;
85
86 bool broadcast = false;
87 bool closed = false;
88 u32 send_timeout = 0;
89 u32 receive_timeout = 0;
90 std::map<int, const char*> socket_options;
91 bool is_bound = false;
92 SockAddrIn local_endpoint{};
93 bool blocking = true;
94 std::queue<ProxyPacket> received_packets;
95 Protocol protocol;
96
97 std::mutex packets_mutex;
98
99 RoomNetwork& room_network;
100};
101
102} // namespace Network
diff --git a/src/core/internal_network/sockets.h b/src/core/internal_network/sockets.h
index 77e27e928..92dc49993 100644
--- a/src/core/internal_network/sockets.h
+++ b/src/core/internal_network/sockets.h
@@ -14,20 +14,92 @@
14 14
15#include "common/common_types.h" 15#include "common/common_types.h"
16#include "core/internal_network/network.h" 16#include "core/internal_network/network.h"
17#include "network/network.h"
17 18
18// TODO: C++20 Replace std::vector usages with std::span 19// TODO: C++20 Replace std::vector usages with std::span
19 20
20namespace Network { 21namespace Network {
21 22
22class Socket { 23class SocketBase {
23public: 24public:
25#ifdef YUZU_UNIX
26 using SOCKET = int;
27 static constexpr SOCKET INVALID_SOCKET = -1;
28 static constexpr SOCKET SOCKET_ERROR = -1;
29#endif
30
24 struct AcceptResult { 31 struct AcceptResult {
25 std::unique_ptr<Socket> socket; 32 std::unique_ptr<SocketBase> socket;
26 SockAddrIn sockaddr_in; 33 SockAddrIn sockaddr_in;
27 }; 34 };
35 virtual ~SocketBase() {}
36
37 virtual SocketBase& operator=(const SocketBase&) = delete;
38
39 // Avoid closing sockets implicitly
40 virtual SocketBase& operator=(SocketBase&&) noexcept = delete;
41
42 virtual Errno Initialize(Domain domain, Type type, Protocol protocol) = 0;
43
44 virtual Errno Close() = 0;
45
46 virtual std::pair<AcceptResult, Errno> Accept() = 0;
47
48 virtual Errno Connect(SockAddrIn addr_in) = 0;
49
50 virtual std::pair<SockAddrIn, Errno> GetPeerName() = 0;
51
52 virtual std::pair<SockAddrIn, Errno> GetSockName() = 0;
53
54 virtual Errno Bind(SockAddrIn addr) = 0;
55
56 virtual Errno Listen(s32 backlog) = 0;
57
58 virtual Errno Shutdown(ShutdownHow how) = 0;
59
60 virtual std::pair<s32, Errno> Recv(int flags, std::vector<u8>& message) = 0;
61
62 virtual std::pair<s32, Errno> RecvFrom(int flags, std::vector<u8>& message,
63 SockAddrIn* addr) = 0;
64
65 virtual std::pair<s32, Errno> Send(const std::vector<u8>& message, int flags) = 0;
66
67 virtual std::pair<s32, Errno> SendTo(u32 flags, const std::vector<u8>& message,
68 const SockAddrIn* addr) = 0;
69
70 virtual Errno SetLinger(bool enable, u32 linger) = 0;
71
72 virtual Errno SetReuseAddr(bool enable) = 0;
73
74 virtual Errno SetKeepAlive(bool enable) = 0;
75
76 virtual Errno SetBroadcast(bool enable) = 0;
28 77
29 explicit Socket() = default; 78 virtual Errno SetSndBuf(u32 value) = 0;
30 ~Socket(); 79
80 virtual Errno SetRcvBuf(u32 value) = 0;
81
82 virtual Errno SetSndTimeo(u32 value) = 0;
83
84 virtual Errno SetRcvTimeo(u32 value) = 0;
85
86 virtual Errno SetNonBlock(bool enable) = 0;
87
88 virtual bool IsOpened() const = 0;
89
90 virtual void HandleProxyPacket(const ProxyPacket& packet) = 0;
91
92#if defined(_WIN32)
93 SOCKET fd = INVALID_SOCKET;
94#elif YUZU_UNIX
95 int fd = -1;
96#endif
97};
98
99class Socket : public SocketBase {
100public:
101 Socket() = default;
102 ~Socket() override;
31 103
32 Socket(const Socket&) = delete; 104 Socket(const Socket&) = delete;
33 Socket& operator=(const Socket&) = delete; 105 Socket& operator=(const Socket&) = delete;
@@ -37,57 +109,57 @@ public:
37 // Avoid closing sockets implicitly 109 // Avoid closing sockets implicitly
38 Socket& operator=(Socket&&) noexcept = delete; 110 Socket& operator=(Socket&&) noexcept = delete;
39 111
40 Errno Initialize(Domain domain, Type type, Protocol protocol); 112 Errno Initialize(Domain domain, Type type, Protocol protocol) override;
41 113
42 Errno Close(); 114 Errno Close() override;
43 115
44 std::pair<AcceptResult, Errno> Accept(); 116 std::pair<AcceptResult, Errno> Accept() override;
45 117
46 Errno Connect(SockAddrIn addr_in); 118 Errno Connect(SockAddrIn addr_in) override;
47 119
48 std::pair<SockAddrIn, Errno> GetPeerName(); 120 std::pair<SockAddrIn, Errno> GetPeerName() override;
49 121
50 std::pair<SockAddrIn, Errno> GetSockName(); 122 std::pair<SockAddrIn, Errno> GetSockName() override;
51 123
52 Errno Bind(SockAddrIn addr); 124 Errno Bind(SockAddrIn addr) override;
53 125
54 Errno Listen(s32 backlog); 126 Errno Listen(s32 backlog) override;
55 127
56 Errno Shutdown(ShutdownHow how); 128 Errno Shutdown(ShutdownHow how) override;
57 129
58 std::pair<s32, Errno> Recv(int flags, std::vector<u8>& message); 130 std::pair<s32, Errno> Recv(int flags, std::vector<u8>& message) override;
59 131
60 std::pair<s32, Errno> RecvFrom(int flags, std::vector<u8>& message, SockAddrIn* addr); 132 std::pair<s32, Errno> RecvFrom(int flags, std::vector<u8>& message, SockAddrIn* addr) override;
61 133
62 std::pair<s32, Errno> Send(const std::vector<u8>& message, int flags); 134 std::pair<s32, Errno> Send(const std::vector<u8>& message, int flags) override;
63 135
64 std::pair<s32, Errno> SendTo(u32 flags, const std::vector<u8>& message, const SockAddrIn* addr); 136 std::pair<s32, Errno> SendTo(u32 flags, const std::vector<u8>& message,
137 const SockAddrIn* addr) override;
65 138
66 Errno SetLinger(bool enable, u32 linger); 139 Errno SetLinger(bool enable, u32 linger) override;
67 140
68 Errno SetReuseAddr(bool enable); 141 Errno SetReuseAddr(bool enable) override;
69 142
70 Errno SetKeepAlive(bool enable); 143 Errno SetKeepAlive(bool enable) override;
71 144
72 Errno SetBroadcast(bool enable); 145 Errno SetBroadcast(bool enable) override;
73 146
74 Errno SetSndBuf(u32 value); 147 Errno SetSndBuf(u32 value) override;
75 148
76 Errno SetRcvBuf(u32 value); 149 Errno SetRcvBuf(u32 value) override;
77 150
78 Errno SetSndTimeo(u32 value); 151 Errno SetSndTimeo(u32 value) override;
79 152
80 Errno SetRcvTimeo(u32 value); 153 Errno SetRcvTimeo(u32 value) override;
81 154
82 Errno SetNonBlock(bool enable); 155 Errno SetNonBlock(bool enable) override;
83 156
84 bool IsOpened() const; 157 template <typename T>
158 Errno SetSockOpt(SOCKET fd, int option, T value);
85 159
86#if defined(_WIN32) 160 bool IsOpened() const override;
87 SOCKET fd = INVALID_SOCKET; 161
88#elif YUZU_UNIX 162 void HandleProxyPacket(const ProxyPacket& packet) override;
89 int fd = -1;
90#endif
91}; 163};
92 164
93std::pair<s32, Errno> Poll(std::vector<PollFD>& poll_fds, s32 timeout); 165std::pair<s32, Errno> Poll(std::vector<PollFD>& poll_fds, s32 timeout);
diff --git a/src/network/room.cpp b/src/network/room.cpp
index 3fc3a0383..b06797bf1 100644
--- a/src/network/room.cpp
+++ b/src/network/room.cpp
@@ -20,9 +20,7 @@ namespace Network {
20 20
21class Room::RoomImpl { 21class Room::RoomImpl {
22public: 22public:
23 // This MAC address is used to generate a 'Nintendo' like Mac address. 23 std::mt19937 random_gen; ///< Random number generator. Used for GenerateFakeIPAddress
24 const MacAddress NintendoOUI;
25 std::mt19937 random_gen; ///< Random number generator. Used for GenerateMacAddress
26 24
27 ENetHost* server = nullptr; ///< Network interface. 25 ENetHost* server = nullptr; ///< Network interface.
28 26
@@ -35,10 +33,9 @@ public:
35 std::string password; ///< The password required to connect to this room. 33 std::string password; ///< The password required to connect to this room.
36 34
37 struct Member { 35 struct Member {
38 std::string nickname; ///< The nickname of the member. 36 std::string nickname; ///< The nickname of the member.
39 std::string console_id_hash; ///< A hash of the console ID of the member. 37 GameInfo game_info; ///< The current game of the member
40 GameInfo game_info; ///< The current game of the member 38 IPv4Address fake_ip; ///< The assigned fake ip address of the member.
41 MacAddress mac_address; ///< The assigned mac address of the member.
42 /// Data of the user, often including authenticated forum username. 39 /// Data of the user, often including authenticated forum username.
43 VerifyUser::UserData user_data; 40 VerifyUser::UserData user_data;
44 ENetPeer* peer; ///< The remote peer. 41 ENetPeer* peer; ///< The remote peer.
@@ -51,8 +48,7 @@ public:
51 IPBanList ip_ban_list; ///< List of banned IP addresses 48 IPBanList ip_ban_list; ///< List of banned IP addresses
52 mutable std::mutex ban_list_mutex; ///< Mutex for the ban lists 49 mutable std::mutex ban_list_mutex; ///< Mutex for the ban lists
53 50
54 RoomImpl() 51 RoomImpl() : random_gen(std::random_device()()) {}
55 : NintendoOUI{0x00, 0x1F, 0x32, 0x00, 0x00, 0x00}, random_gen(std::random_device()()) {}
56 52
57 /// Thread that receives and dispatches network packets 53 /// Thread that receives and dispatches network packets
58 std::unique_ptr<std::thread> room_thread; 54 std::unique_ptr<std::thread> room_thread;
@@ -101,16 +97,10 @@ public:
101 bool IsValidNickname(const std::string& nickname) const; 97 bool IsValidNickname(const std::string& nickname) const;
102 98
103 /** 99 /**
104 * Returns whether the MAC address is valid, ie. isn't already taken by someone else in the 100 * Returns whether the fake ip address is valid, ie. isn't already taken by someone else in the
105 * room. 101 * room.
106 */ 102 */
107 bool IsValidMacAddress(const MacAddress& address) const; 103 bool IsValidFakeIPAddress(const IPv4Address& address) const;
108
109 /**
110 * Returns whether the console ID (hash) is valid, ie. isn't already taken by someone else in
111 * the room.
112 */
113 bool IsValidConsoleId(const std::string& console_id_hash) const;
114 104
115 /** 105 /**
116 * Returns whether a user has mod permissions. 106 * Returns whether a user has mod permissions.
@@ -128,15 +118,9 @@ public:
128 void SendNameCollision(ENetPeer* client); 118 void SendNameCollision(ENetPeer* client);
129 119
130 /** 120 /**
131 * Sends a ID_ROOM_MAC_COLLISION message telling the client that the MAC is invalid. 121 * Sends a ID_ROOM_IP_COLLISION message telling the client that the IP is invalid.
132 */ 122 */
133 void SendMacCollision(ENetPeer* client); 123 void SendIPCollision(ENetPeer* client);
134
135 /**
136 * Sends a IdConsoleIdCollison message telling the client that another member with the same
137 * console ID exists.
138 */
139 void SendConsoleIdCollision(ENetPeer* client);
140 124
141 /** 125 /**
142 * Sends a ID_ROOM_VERSION_MISMATCH message telling the client that the version is invalid. 126 * Sends a ID_ROOM_VERSION_MISMATCH message telling the client that the version is invalid.
@@ -152,13 +136,13 @@ public:
152 * Notifies the member that its connection attempt was successful, 136 * Notifies the member that its connection attempt was successful,
153 * and it is now part of the room. 137 * and it is now part of the room.
154 */ 138 */
155 void SendJoinSuccess(ENetPeer* client, MacAddress mac_address); 139 void SendJoinSuccess(ENetPeer* client, IPv4Address fake_ip);
156 140
157 /** 141 /**
158 * Notifies the member that its connection attempt was successful, 142 * Notifies the member that its connection attempt was successful,
159 * and it is now part of the room, and it has been granted mod permissions. 143 * and it is now part of the room, and it has been granted mod permissions.
160 */ 144 */
161 void SendJoinSuccessAsMod(ENetPeer* client, MacAddress mac_address); 145 void SendJoinSuccessAsMod(ENetPeer* client, IPv4Address fake_ip);
162 146
163 /** 147 /**
164 * Sends a IdHostKicked message telling the client that they have been kicked. 148 * Sends a IdHostKicked message telling the client that they have been kicked.
@@ -210,7 +194,7 @@ public:
210 * <u32> num_members: the number of currently joined clients 194 * <u32> num_members: the number of currently joined clients
211 * This is followed by the following three values for each member: 195 * This is followed by the following three values for each member:
212 * <String> nickname of that member 196 * <String> nickname of that member
213 * <MacAddress> mac_address of that member 197 * <IPv4Address> fake_ip of that member
214 * <String> game_name of that member 198 * <String> game_name of that member
215 */ 199 */
216 void BroadcastRoomInformation(); 200 void BroadcastRoomInformation();
@@ -219,13 +203,13 @@ public:
219 * Generates a free MAC address to assign to a new client. 203 * Generates a free MAC address to assign to a new client.
220 * The first 3 bytes are the NintendoOUI 0x00, 0x1F, 0x32 204 * The first 3 bytes are the NintendoOUI 0x00, 0x1F, 0x32
221 */ 205 */
222 MacAddress GenerateMacAddress(); 206 IPv4Address GenerateFakeIPAddress();
223 207
224 /** 208 /**
225 * Broadcasts this packet to all members except the sender. 209 * Broadcasts this packet to all members except the sender.
226 * @param event The ENet event containing the data 210 * @param event The ENet event containing the data
227 */ 211 */
228 void HandleWifiPacket(const ENetEvent* event); 212 void HandleProxyPacket(const ENetEvent* event);
229 213
230 /** 214 /**
231 * Extracts a chat entry from a received ENet packet and adds it to the chat queue. 215 * Extracts a chat entry from a received ENet packet and adds it to the chat queue.
@@ -250,7 +234,7 @@ public:
250void Room::RoomImpl::ServerLoop() { 234void Room::RoomImpl::ServerLoop() {
251 while (state != State::Closed) { 235 while (state != State::Closed) {
252 ENetEvent event; 236 ENetEvent event;
253 if (enet_host_service(server, &event, 16) > 0) { 237 if (enet_host_service(server, &event, 50) > 0) {
254 switch (event.type) { 238 switch (event.type) {
255 case ENET_EVENT_TYPE_RECEIVE: 239 case ENET_EVENT_TYPE_RECEIVE:
256 switch (event.packet->data[0]) { 240 switch (event.packet->data[0]) {
@@ -260,8 +244,8 @@ void Room::RoomImpl::ServerLoop() {
260 case IdSetGameInfo: 244 case IdSetGameInfo:
261 HandleGameNamePacket(&event); 245 HandleGameNamePacket(&event);
262 break; 246 break;
263 case IdWifiPacket: 247 case IdProxyPacket:
264 HandleWifiPacket(&event); 248 HandleProxyPacket(&event);
265 break; 249 break;
266 case IdChatMessage: 250 case IdChatMessage:
267 HandleChatPacket(&event); 251 HandleChatPacket(&event);
@@ -313,11 +297,8 @@ void Room::RoomImpl::HandleJoinRequest(const ENetEvent* event) {
313 std::string nickname; 297 std::string nickname;
314 packet.Read(nickname); 298 packet.Read(nickname);
315 299
316 std::string console_id_hash; 300 IPv4Address preferred_fake_ip;
317 packet.Read(console_id_hash); 301 packet.Read(preferred_fake_ip);
318
319 MacAddress preferred_mac;
320 packet.Read(preferred_mac);
321 302
322 u32 client_version; 303 u32 client_version;
323 packet.Read(client_version); 304 packet.Read(client_version);
@@ -338,20 +319,15 @@ void Room::RoomImpl::HandleJoinRequest(const ENetEvent* event) {
338 return; 319 return;
339 } 320 }
340 321
341 if (preferred_mac != NoPreferredMac) { 322 if (preferred_fake_ip != NoPreferredIP) {
342 // Verify if the preferred mac is available 323 // Verify if the preferred fake ip is available
343 if (!IsValidMacAddress(preferred_mac)) { 324 if (!IsValidFakeIPAddress(preferred_fake_ip)) {
344 SendMacCollision(event->peer); 325 SendIPCollision(event->peer);
345 return; 326 return;
346 } 327 }
347 } else { 328 } else {
348 // Assign a MAC address of this client automatically 329 // Assign a fake ip address of this client automatically
349 preferred_mac = GenerateMacAddress(); 330 preferred_fake_ip = GenerateFakeIPAddress();
350 }
351
352 if (!IsValidConsoleId(console_id_hash)) {
353 SendConsoleIdCollision(event->peer);
354 return;
355 } 331 }
356 332
357 if (client_version != network_version) { 333 if (client_version != network_version) {
@@ -361,8 +337,7 @@ void Room::RoomImpl::HandleJoinRequest(const ENetEvent* event) {
361 337
362 // At this point the client is ready to be added to the room. 338 // At this point the client is ready to be added to the room.
363 Member member{}; 339 Member member{};
364 member.mac_address = preferred_mac; 340 member.fake_ip = preferred_fake_ip;
365 member.console_id_hash = console_id_hash;
366 member.nickname = nickname; 341 member.nickname = nickname;
367 member.peer = event->peer; 342 member.peer = event->peer;
368 343
@@ -408,9 +383,9 @@ void Room::RoomImpl::HandleJoinRequest(const ENetEvent* event) {
408 // Notify everyone that the room information has changed. 383 // Notify everyone that the room information has changed.
409 BroadcastRoomInformation(); 384 BroadcastRoomInformation();
410 if (HasModPermission(event->peer)) { 385 if (HasModPermission(event->peer)) {
411 SendJoinSuccessAsMod(event->peer, preferred_mac); 386 SendJoinSuccessAsMod(event->peer, preferred_fake_ip);
412 } else { 387 } else {
413 SendJoinSuccess(event->peer, preferred_mac); 388 SendJoinSuccess(event->peer, preferred_fake_ip);
414 } 389 }
415} 390}
416 391
@@ -575,19 +550,11 @@ bool Room::RoomImpl::IsValidNickname(const std::string& nickname) const {
575 [&nickname](const auto& member) { return member.nickname != nickname; }); 550 [&nickname](const auto& member) { return member.nickname != nickname; });
576} 551}
577 552
578bool Room::RoomImpl::IsValidMacAddress(const MacAddress& address) const { 553bool Room::RoomImpl::IsValidFakeIPAddress(const IPv4Address& address) const {
579 // A MAC address is valid if it is not already taken by anybody else in the room. 554 // An IP address is valid if it is not already taken by anybody else in the room.
580 std::lock_guard lock(member_mutex); 555 std::lock_guard lock(member_mutex);
581 return std::all_of(members.begin(), members.end(), 556 return std::all_of(members.begin(), members.end(),
582 [&address](const auto& member) { return member.mac_address != address; }); 557 [&address](const auto& member) { return member.fake_ip != address; });
583}
584
585bool Room::RoomImpl::IsValidConsoleId(const std::string& console_id_hash) const {
586 // A Console ID is valid if it is not already taken by anybody else in the room.
587 std::lock_guard lock(member_mutex);
588 return std::all_of(members.begin(), members.end(), [&console_id_hash](const auto& member) {
589 return member.console_id_hash != console_id_hash;
590 });
591} 558}
592 559
593bool Room::RoomImpl::HasModPermission(const ENetPeer* client) const { 560bool Room::RoomImpl::HasModPermission(const ENetPeer* client) const {
@@ -621,19 +588,9 @@ void Room::RoomImpl::SendNameCollision(ENetPeer* client) {
621 enet_host_flush(server); 588 enet_host_flush(server);
622} 589}
623 590
624void Room::RoomImpl::SendMacCollision(ENetPeer* client) { 591void Room::RoomImpl::SendIPCollision(ENetPeer* client) {
625 Packet packet; 592 Packet packet;
626 packet.Write(static_cast<u8>(IdMacCollision)); 593 packet.Write(static_cast<u8>(IdIpCollision));
627
628 ENetPacket* enet_packet =
629 enet_packet_create(packet.GetData(), packet.GetDataSize(), ENET_PACKET_FLAG_RELIABLE);
630 enet_peer_send(client, 0, enet_packet);
631 enet_host_flush(server);
632}
633
634void Room::RoomImpl::SendConsoleIdCollision(ENetPeer* client) {
635 Packet packet;
636 packet.Write(static_cast<u8>(IdConsoleIdCollision));
637 594
638 ENetPacket* enet_packet = 595 ENetPacket* enet_packet =
639 enet_packet_create(packet.GetData(), packet.GetDataSize(), ENET_PACKET_FLAG_RELIABLE); 596 enet_packet_create(packet.GetData(), packet.GetDataSize(), ENET_PACKET_FLAG_RELIABLE);
@@ -672,20 +629,20 @@ void Room::RoomImpl::SendVersionMismatch(ENetPeer* client) {
672 enet_host_flush(server); 629 enet_host_flush(server);
673} 630}
674 631
675void Room::RoomImpl::SendJoinSuccess(ENetPeer* client, MacAddress mac_address) { 632void Room::RoomImpl::SendJoinSuccess(ENetPeer* client, IPv4Address fake_ip) {
676 Packet packet; 633 Packet packet;
677 packet.Write(static_cast<u8>(IdJoinSuccess)); 634 packet.Write(static_cast<u8>(IdJoinSuccess));
678 packet.Write(mac_address); 635 packet.Write(fake_ip);
679 ENetPacket* enet_packet = 636 ENetPacket* enet_packet =
680 enet_packet_create(packet.GetData(), packet.GetDataSize(), ENET_PACKET_FLAG_RELIABLE); 637 enet_packet_create(packet.GetData(), packet.GetDataSize(), ENET_PACKET_FLAG_RELIABLE);
681 enet_peer_send(client, 0, enet_packet); 638 enet_peer_send(client, 0, enet_packet);
682 enet_host_flush(server); 639 enet_host_flush(server);
683} 640}
684 641
685void Room::RoomImpl::SendJoinSuccessAsMod(ENetPeer* client, MacAddress mac_address) { 642void Room::RoomImpl::SendJoinSuccessAsMod(ENetPeer* client, IPv4Address fake_ip) {
686 Packet packet; 643 Packet packet;
687 packet.Write(static_cast<u8>(IdJoinSuccessAsMod)); 644 packet.Write(static_cast<u8>(IdJoinSuccessAsMod));
688 packet.Write(mac_address); 645 packet.Write(fake_ip);
689 ENetPacket* enet_packet = 646 ENetPacket* enet_packet =
690 enet_packet_create(packet.GetData(), packet.GetDataSize(), ENET_PACKET_FLAG_RELIABLE); 647 enet_packet_create(packet.GetData(), packet.GetDataSize(), ENET_PACKET_FLAG_RELIABLE);
691 enet_peer_send(client, 0, enet_packet); 648 enet_peer_send(client, 0, enet_packet);
@@ -818,7 +775,7 @@ void Room::RoomImpl::BroadcastRoomInformation() {
818 std::lock_guard lock(member_mutex); 775 std::lock_guard lock(member_mutex);
819 for (const auto& member : members) { 776 for (const auto& member : members) {
820 packet.Write(member.nickname); 777 packet.Write(member.nickname);
821 packet.Write(member.mac_address); 778 packet.Write(member.fake_ip);
822 packet.Write(member.game_info.name); 779 packet.Write(member.game_info.name);
823 packet.Write(member.game_info.id); 780 packet.Write(member.game_info.id);
824 packet.Write(member.user_data.username); 781 packet.Write(member.user_data.username);
@@ -833,34 +790,43 @@ void Room::RoomImpl::BroadcastRoomInformation() {
833 enet_host_flush(server); 790 enet_host_flush(server);
834} 791}
835 792
836MacAddress Room::RoomImpl::GenerateMacAddress() { 793IPv4Address Room::RoomImpl::GenerateFakeIPAddress() {
837 MacAddress result_mac = 794 IPv4Address result_ip{192, 168, 0, 0};
838 NintendoOUI; // The first three bytes of each MAC address will be the NintendoOUI 795 std::uniform_int_distribution<> dis(0x01, 0xFE); // Random byte between 1 and 0xFE
839 std::uniform_int_distribution<> dis(0x00, 0xFF); // Random byte between 0 and 0xFF
840 do { 796 do {
841 for (std::size_t i = 3; i < result_mac.size(); ++i) { 797 for (std::size_t i = 2; i < result_ip.size(); ++i) {
842 result_mac[i] = dis(random_gen); 798 result_ip[i] = dis(random_gen);
843 } 799 }
844 } while (!IsValidMacAddress(result_mac)); 800 } while (!IsValidFakeIPAddress(result_ip));
845 return result_mac; 801
802 return result_ip;
846} 803}
847 804
848void Room::RoomImpl::HandleWifiPacket(const ENetEvent* event) { 805void Room::RoomImpl::HandleProxyPacket(const ENetEvent* event) {
849 Packet in_packet; 806 Packet in_packet;
850 in_packet.Append(event->packet->data, event->packet->dataLength); 807 in_packet.Append(event->packet->data, event->packet->dataLength);
851 in_packet.IgnoreBytes(sizeof(u8)); // Message type 808 in_packet.IgnoreBytes(sizeof(u8)); // Message type
852 in_packet.IgnoreBytes(sizeof(u8)); // WifiPacket Type 809
853 in_packet.IgnoreBytes(sizeof(u8)); // WifiPacket Channel 810 in_packet.IgnoreBytes(sizeof(u8)); // Domain
854 in_packet.IgnoreBytes(sizeof(MacAddress)); // WifiPacket Transmitter Address 811 in_packet.IgnoreBytes(sizeof(IPv4Address)); // IP
855 MacAddress destination_address; 812 in_packet.IgnoreBytes(sizeof(u16)); // Port
856 in_packet.Read(destination_address); 813
814 in_packet.IgnoreBytes(sizeof(u8)); // Domain
815 IPv4Address remote_ip;
816 in_packet.Read(remote_ip); // IP
817 in_packet.IgnoreBytes(sizeof(u16)); // Port
818
819 in_packet.IgnoreBytes(sizeof(u8)); // Protocol
820 bool broadcast;
821 in_packet.Read(broadcast); // Broadcast
857 822
858 Packet out_packet; 823 Packet out_packet;
859 out_packet.Append(event->packet->data, event->packet->dataLength); 824 out_packet.Append(event->packet->data, event->packet->dataLength);
860 ENetPacket* enet_packet = enet_packet_create(out_packet.GetData(), out_packet.GetDataSize(), 825 ENetPacket* enet_packet = enet_packet_create(out_packet.GetData(), out_packet.GetDataSize(),
861 ENET_PACKET_FLAG_RELIABLE); 826 ENET_PACKET_FLAG_RELIABLE);
862 827
863 if (destination_address == BroadcastMac) { // Send the data to everyone except the sender 828 const auto& destination_address = remote_ip;
829 if (broadcast) { // Send the data to everyone except the sender
864 std::lock_guard lock(member_mutex); 830 std::lock_guard lock(member_mutex);
865 bool sent_packet = false; 831 bool sent_packet = false;
866 for (const auto& member : members) { 832 for (const auto& member : members) {
@@ -877,16 +843,16 @@ void Room::RoomImpl::HandleWifiPacket(const ENetEvent* event) {
877 std::lock_guard lock(member_mutex); 843 std::lock_guard lock(member_mutex);
878 auto member = std::find_if(members.begin(), members.end(), 844 auto member = std::find_if(members.begin(), members.end(),
879 [destination_address](const Member& member_entry) -> bool { 845 [destination_address](const Member& member_entry) -> bool {
880 return member_entry.mac_address == destination_address; 846 return member_entry.fake_ip == destination_address;
881 }); 847 });
882 if (member != members.end()) { 848 if (member != members.end()) {
883 enet_peer_send(member->peer, 0, enet_packet); 849 enet_peer_send(member->peer, 0, enet_packet);
884 } else { 850 } else {
885 LOG_ERROR(Network, 851 LOG_ERROR(Network,
886 "Attempting to send to unknown MAC address: " 852 "Attempting to send to unknown IP address: "
887 "{:02X}:{:02X}:{:02X}:{:02X}:{:02X}:{:02X}", 853 "{}.{}.{}.{}",
888 destination_address[0], destination_address[1], destination_address[2], 854 destination_address[0], destination_address[1], destination_address[2],
889 destination_address[3], destination_address[4], destination_address[5]); 855 destination_address[3]);
890 enet_packet_destroy(enet_packet); 856 enet_packet_destroy(enet_packet);
891 } 857 }
892 } 858 }
@@ -1073,7 +1039,7 @@ std::vector<Member> Room::GetRoomMemberList() const {
1073 member.username = member_impl.user_data.username; 1039 member.username = member_impl.user_data.username;
1074 member.display_name = member_impl.user_data.display_name; 1040 member.display_name = member_impl.user_data.display_name;
1075 member.avatar_url = member_impl.user_data.avatar_url; 1041 member.avatar_url = member_impl.user_data.avatar_url;
1076 member.mac_address = member_impl.mac_address; 1042 member.fake_ip = member_impl.fake_ip;
1077 member.game = member_impl.game_info; 1043 member.game = member_impl.game_info;
1078 member_list.push_back(member); 1044 member_list.push_back(member);
1079 } 1045 }
diff --git a/src/network/room.h b/src/network/room.h
index 6f7e3b5b5..c2a4b1a70 100644
--- a/src/network/room.h
+++ b/src/network/room.h
@@ -9,12 +9,12 @@
9#include <vector> 9#include <vector>
10#include "common/announce_multiplayer_room.h" 10#include "common/announce_multiplayer_room.h"
11#include "common/common_types.h" 11#include "common/common_types.h"
12#include "common/socket_types.h"
12#include "network/verify_user.h" 13#include "network/verify_user.h"
13 14
14namespace Network { 15namespace Network {
15 16
16using AnnounceMultiplayerRoom::GameInfo; 17using AnnounceMultiplayerRoom::GameInfo;
17using AnnounceMultiplayerRoom::MacAddress;
18using AnnounceMultiplayerRoom::Member; 18using AnnounceMultiplayerRoom::Member;
19using AnnounceMultiplayerRoom::RoomInformation; 19using AnnounceMultiplayerRoom::RoomInformation;
20 20
@@ -29,12 +29,9 @@ static constexpr u32 MaxConcurrentConnections = 254;
29 29
30constexpr std::size_t NumChannels = 1; // Number of channels used for the connection 30constexpr std::size_t NumChannels = 1; // Number of channels used for the connection
31 31
32/// A special MAC address that tells the room we're joining to assign us a MAC address 32/// A special IP address that tells the room we're joining to assign us a IP address
33/// automatically. 33/// automatically.
34constexpr MacAddress NoPreferredMac = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; 34constexpr IPv4Address NoPreferredIP = {0xFF, 0xFF, 0xFF, 0xFF};
35
36// 802.11 broadcast MAC address
37constexpr MacAddress BroadcastMac = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
38 35
39// The different types of messages that can be sent. The first byte of each packet defines the type 36// The different types of messages that can be sent. The first byte of each packet defines the type
40enum RoomMessageTypes : u8 { 37enum RoomMessageTypes : u8 {
@@ -42,15 +39,14 @@ enum RoomMessageTypes : u8 {
42 IdJoinSuccess, 39 IdJoinSuccess,
43 IdRoomInformation, 40 IdRoomInformation,
44 IdSetGameInfo, 41 IdSetGameInfo,
45 IdWifiPacket, 42 IdProxyPacket,
46 IdChatMessage, 43 IdChatMessage,
47 IdNameCollision, 44 IdNameCollision,
48 IdMacCollision, 45 IdIpCollision,
49 IdVersionMismatch, 46 IdVersionMismatch,
50 IdWrongPassword, 47 IdWrongPassword,
51 IdCloseRoom, 48 IdCloseRoom,
52 IdRoomIsFull, 49 IdRoomIsFull,
53 IdConsoleIdCollision,
54 IdStatusMessage, 50 IdStatusMessage,
55 IdHostKicked, 51 IdHostKicked,
56 IdHostBanned, 52 IdHostBanned,
diff --git a/src/network/room_member.cpp b/src/network/room_member.cpp
index e4f823e98..9f08bf611 100644
--- a/src/network/room_member.cpp
+++ b/src/network/room_member.cpp
@@ -7,6 +7,7 @@
7#include <set> 7#include <set>
8#include <thread> 8#include <thread>
9#include "common/assert.h" 9#include "common/assert.h"
10#include "common/socket_types.h"
10#include "enet/enet.h" 11#include "enet/enet.h"
11#include "network/packet.h" 12#include "network/packet.h"
12#include "network/room_member.h" 13#include "network/room_member.h"
@@ -38,7 +39,7 @@ public:
38 std::string username; ///< The username of this member. 39 std::string username; ///< The username of this member.
39 mutable std::mutex username_mutex; ///< Mutex for locking username. 40 mutable std::mutex username_mutex; ///< Mutex for locking username.
40 41
41 MacAddress mac_address; ///< The mac_address of this member. 42 IPv4Address fake_ip; ///< The fake ip of this member.
42 43
43 std::mutex network_mutex; ///< Mutex that controls access to the `client` variable. 44 std::mutex network_mutex; ///< Mutex that controls access to the `client` variable.
44 /// Thread that receives and dispatches network packets 45 /// Thread that receives and dispatches network packets
@@ -56,7 +57,7 @@ public:
56 CallbackSet<T>& Get(); 57 CallbackSet<T>& Get();
57 58
58 private: 59 private:
59 CallbackSet<WifiPacket> callback_set_wifi_packet; 60 CallbackSet<ProxyPacket> callback_set_proxy_packet;
60 CallbackSet<ChatEntry> callback_set_chat_messages; 61 CallbackSet<ChatEntry> callback_set_chat_messages;
61 CallbackSet<StatusMessageEntry> callback_set_status_messages; 62 CallbackSet<StatusMessageEntry> callback_set_status_messages;
62 CallbackSet<RoomInformation> callback_set_room_information; 63 CallbackSet<RoomInformation> callback_set_room_information;
@@ -78,15 +79,15 @@ public:
78 79
79 /** 80 /**
80 * Sends a request to the server, asking for permission to join a room with the specified 81 * Sends a request to the server, asking for permission to join a room with the specified
81 * nickname and preferred mac. 82 * nickname and preferred fake ip.
82 * @params nickname The desired nickname. 83 * @params nickname The desired nickname.
83 * @params console_id_hash A hash of the Console ID. 84 * @params preferred_fake_ip The preferred IP address to use in the room, the NoPreferredIP
84 * @params preferred_mac The preferred MAC address to use in the room, the NoPreferredMac tells 85 * tells
85 * @params password The password for the room 86 * @params password The password for the room
86 * the server to assign one for us. 87 * the server to assign one for us.
87 */ 88 */
88 void SendJoinRequest(const std::string& nickname_, const std::string& console_id_hash, 89 void SendJoinRequest(const std::string& nickname_,
89 const MacAddress& preferred_mac = NoPreferredMac, 90 const IPv4Address& preferred_fake_ip = NoPreferredIP,
90 const std::string& password = "", const std::string& token = ""); 91 const std::string& password = "", const std::string& token = "");
91 92
92 /** 93 /**
@@ -101,10 +102,10 @@ public:
101 void HandleRoomInformationPacket(const ENetEvent* event); 102 void HandleRoomInformationPacket(const ENetEvent* event);
102 103
103 /** 104 /**
104 * Extracts a WifiPacket from a received ENet packet. 105 * Extracts a ProxyPacket from a received ENet packet.
105 * @param event The ENet event that was received. 106 * @param event The ENet event that was received.
106 */ 107 */
107 void HandleWifiPackets(const ENetEvent* event); 108 void HandleProxyPackets(const ENetEvent* event);
108 109
109 /** 110 /**
110 * Extracts a chat entry from a received ENet packet and adds it to the chat queue. 111 * Extracts a chat entry from a received ENet packet and adds it to the chat queue.
@@ -158,12 +159,12 @@ void RoomMember::RoomMemberImpl::MemberLoop() {
158 while (IsConnected()) { 159 while (IsConnected()) {
159 std::lock_guard lock(network_mutex); 160 std::lock_guard lock(network_mutex);
160 ENetEvent event; 161 ENetEvent event;
161 if (enet_host_service(client, &event, 16) > 0) { 162 if (enet_host_service(client, &event, 100) > 0) {
162 switch (event.type) { 163 switch (event.type) {
163 case ENET_EVENT_TYPE_RECEIVE: 164 case ENET_EVENT_TYPE_RECEIVE:
164 switch (event.packet->data[0]) { 165 switch (event.packet->data[0]) {
165 case IdWifiPacket: 166 case IdProxyPacket:
166 HandleWifiPackets(&event); 167 HandleProxyPackets(&event);
167 break; 168 break;
168 case IdChatMessage: 169 case IdChatMessage:
169 HandleChatPacket(&event); 170 HandleChatPacket(&event);
@@ -198,13 +199,9 @@ void RoomMember::RoomMemberImpl::MemberLoop() {
198 SetState(State::Idle); 199 SetState(State::Idle);
199 SetError(Error::NameCollision); 200 SetError(Error::NameCollision);
200 break; 201 break;
201 case IdMacCollision: 202 case IdIpCollision:
202 SetState(State::Idle); 203 SetState(State::Idle);
203 SetError(Error::MacCollision); 204 SetError(Error::IpCollision);
204 break;
205 case IdConsoleIdCollision:
206 SetState(State::Idle);
207 SetError(Error::ConsoleIdCollision);
208 break; 205 break;
209 case IdVersionMismatch: 206 case IdVersionMismatch:
210 SetState(State::Idle); 207 SetState(State::Idle);
@@ -275,15 +272,13 @@ void RoomMember::RoomMemberImpl::Send(Packet&& packet) {
275} 272}
276 273
277void RoomMember::RoomMemberImpl::SendJoinRequest(const std::string& nickname_, 274void RoomMember::RoomMemberImpl::SendJoinRequest(const std::string& nickname_,
278 const std::string& console_id_hash, 275 const IPv4Address& preferred_fake_ip,
279 const MacAddress& preferred_mac,
280 const std::string& password, 276 const std::string& password,
281 const std::string& token) { 277 const std::string& token) {
282 Packet packet; 278 Packet packet;
283 packet.Write(static_cast<u8>(IdJoinRequest)); 279 packet.Write(static_cast<u8>(IdJoinRequest));
284 packet.Write(nickname_); 280 packet.Write(nickname_);
285 packet.Write(console_id_hash); 281 packet.Write(preferred_fake_ip);
286 packet.Write(preferred_mac);
287 packet.Write(network_version); 282 packet.Write(network_version);
288 packet.Write(password); 283 packet.Write(password);
289 packet.Write(token); 284 packet.Write(token);
@@ -317,7 +312,7 @@ void RoomMember::RoomMemberImpl::HandleRoomInformationPacket(const ENetEvent* ev
317 312
318 for (auto& member : member_information) { 313 for (auto& member : member_information) {
319 packet.Read(member.nickname); 314 packet.Read(member.nickname);
320 packet.Read(member.mac_address); 315 packet.Read(member.fake_ip);
321 packet.Read(member.game_info.name); 316 packet.Read(member.game_info.name);
322 packet.Read(member.game_info.id); 317 packet.Read(member.game_info.id);
323 packet.Read(member.username); 318 packet.Read(member.username);
@@ -342,29 +337,38 @@ void RoomMember::RoomMemberImpl::HandleJoinPacket(const ENetEvent* event) {
342 packet.IgnoreBytes(sizeof(u8)); // Ignore the message type 337 packet.IgnoreBytes(sizeof(u8)); // Ignore the message type
343 338
344 // Parse the MAC Address from the packet 339 // Parse the MAC Address from the packet
345 packet.Read(mac_address); 340 packet.Read(fake_ip);
346} 341}
347 342
348void RoomMember::RoomMemberImpl::HandleWifiPackets(const ENetEvent* event) { 343void RoomMember::RoomMemberImpl::HandleProxyPackets(const ENetEvent* event) {
349 WifiPacket wifi_packet{}; 344 ProxyPacket proxy_packet{};
350 Packet packet; 345 Packet packet;
351 packet.Append(event->packet->data, event->packet->dataLength); 346 packet.Append(event->packet->data, event->packet->dataLength);
352 347
353 // Ignore the first byte, which is the message id. 348 // Ignore the first byte, which is the message id.
354 packet.IgnoreBytes(sizeof(u8)); // Ignore the message type 349 packet.IgnoreBytes(sizeof(u8)); // Ignore the message type
355 350
356 // Parse the WifiPacket from the packet 351 // Parse the ProxyPacket from the packet
357 u8 frame_type; 352 u8 local_family;
358 packet.Read(frame_type); 353 packet.Read(local_family);
359 WifiPacket::PacketType type = static_cast<WifiPacket::PacketType>(frame_type); 354 proxy_packet.local_endpoint.family = static_cast<Domain>(local_family);
355 packet.Read(proxy_packet.local_endpoint.ip);
356 packet.Read(proxy_packet.local_endpoint.portno);
357
358 u8 remote_family;
359 packet.Read(remote_family);
360 proxy_packet.remote_endpoint.family = static_cast<Domain>(remote_family);
361 packet.Read(proxy_packet.remote_endpoint.ip);
362 packet.Read(proxy_packet.remote_endpoint.portno);
363
364 u8 protocol_type;
365 packet.Read(protocol_type);
366 proxy_packet.protocol = static_cast<Protocol>(protocol_type);
360 367
361 wifi_packet.type = type; 368 packet.Read(proxy_packet.broadcast);
362 packet.Read(wifi_packet.channel); 369 packet.Read(proxy_packet.data);
363 packet.Read(wifi_packet.transmitter_address);
364 packet.Read(wifi_packet.destination_address);
365 packet.Read(wifi_packet.data);
366 370
367 Invoke<WifiPacket>(wifi_packet); 371 Invoke<ProxyPacket>(proxy_packet);
368} 372}
369 373
370void RoomMember::RoomMemberImpl::HandleChatPacket(const ENetEvent* event) { 374void RoomMember::RoomMemberImpl::HandleChatPacket(const ENetEvent* event) {
@@ -440,8 +444,8 @@ void RoomMember::RoomMemberImpl::Disconnect() {
440} 444}
441 445
442template <> 446template <>
443RoomMember::RoomMemberImpl::CallbackSet<WifiPacket>& RoomMember::RoomMemberImpl::Callbacks::Get() { 447RoomMember::RoomMemberImpl::CallbackSet<ProxyPacket>& RoomMember::RoomMemberImpl::Callbacks::Get() {
444 return callback_set_wifi_packet; 448 return callback_set_proxy_packet;
445} 449}
446 450
447template <> 451template <>
@@ -525,19 +529,18 @@ const std::string& RoomMember::GetUsername() const {
525 return room_member_impl->username; 529 return room_member_impl->username;
526} 530}
527 531
528const MacAddress& RoomMember::GetMacAddress() const { 532const IPv4Address& RoomMember::GetFakeIpAddress() const {
529 ASSERT_MSG(IsConnected(), "Tried to get MAC address while not connected"); 533 ASSERT_MSG(IsConnected(), "Tried to get fake ip address while not connected");
530 return room_member_impl->mac_address; 534 return room_member_impl->fake_ip;
531} 535}
532 536
533RoomInformation RoomMember::GetRoomInformation() const { 537RoomInformation RoomMember::GetRoomInformation() const {
534 return room_member_impl->room_information; 538 return room_member_impl->room_information;
535} 539}
536 540
537void RoomMember::Join(const std::string& nick, const std::string& console_id_hash, 541void RoomMember::Join(const std::string& nick, const char* server_addr, u16 server_port,
538 const char* server_addr, u16 server_port, u16 client_port, 542 u16 client_port, const IPv4Address& preferred_fake_ip,
539 const MacAddress& preferred_mac, const std::string& password, 543 const std::string& password, const std::string& token) {
540 const std::string& token) {
541 // If the member is connected, kill the connection first 544 // If the member is connected, kill the connection first
542 if (room_member_impl->loop_thread && room_member_impl->loop_thread->joinable()) { 545 if (room_member_impl->loop_thread && room_member_impl->loop_thread->joinable()) {
543 Leave(); 546 Leave();
@@ -571,7 +574,7 @@ void RoomMember::Join(const std::string& nick, const std::string& console_id_has
571 if (net > 0 && event.type == ENET_EVENT_TYPE_CONNECT) { 574 if (net > 0 && event.type == ENET_EVENT_TYPE_CONNECT) {
572 room_member_impl->nickname = nick; 575 room_member_impl->nickname = nick;
573 room_member_impl->StartLoop(); 576 room_member_impl->StartLoop();
574 room_member_impl->SendJoinRequest(nick, console_id_hash, preferred_mac, password, token); 577 room_member_impl->SendJoinRequest(nick, preferred_fake_ip, password, token);
575 SendGameInfo(room_member_impl->current_game_info); 578 SendGameInfo(room_member_impl->current_game_info);
576 } else { 579 } else {
577 enet_peer_disconnect(room_member_impl->server, 0); 580 enet_peer_disconnect(room_member_impl->server, 0);
@@ -584,14 +587,22 @@ bool RoomMember::IsConnected() const {
584 return room_member_impl->IsConnected(); 587 return room_member_impl->IsConnected();
585} 588}
586 589
587void RoomMember::SendWifiPacket(const WifiPacket& wifi_packet) { 590void RoomMember::SendProxyPacket(const ProxyPacket& proxy_packet) {
588 Packet packet; 591 Packet packet;
589 packet.Write(static_cast<u8>(IdWifiPacket)); 592 packet.Write(static_cast<u8>(IdProxyPacket));
590 packet.Write(static_cast<u8>(wifi_packet.type)); 593
591 packet.Write(wifi_packet.channel); 594 packet.Write(static_cast<u8>(proxy_packet.local_endpoint.family));
592 packet.Write(wifi_packet.transmitter_address); 595 packet.Write(proxy_packet.local_endpoint.ip);
593 packet.Write(wifi_packet.destination_address); 596 packet.Write(proxy_packet.local_endpoint.portno);
594 packet.Write(wifi_packet.data); 597
598 packet.Write(static_cast<u8>(proxy_packet.remote_endpoint.family));
599 packet.Write(proxy_packet.remote_endpoint.ip);
600 packet.Write(proxy_packet.remote_endpoint.portno);
601
602 packet.Write(static_cast<u8>(proxy_packet.protocol));
603 packet.Write(proxy_packet.broadcast);
604 packet.Write(proxy_packet.data);
605
595 room_member_impl->Send(std::move(packet)); 606 room_member_impl->Send(std::move(packet));
596} 607}
597 608
@@ -645,8 +656,8 @@ RoomMember::CallbackHandle<RoomMember::Error> RoomMember::BindOnError(
645 return room_member_impl->Bind(callback); 656 return room_member_impl->Bind(callback);
646} 657}
647 658
648RoomMember::CallbackHandle<WifiPacket> RoomMember::BindOnWifiPacketReceived( 659RoomMember::CallbackHandle<ProxyPacket> RoomMember::BindOnProxyPacketReceived(
649 std::function<void(const WifiPacket&)> callback) { 660 std::function<void(const ProxyPacket&)> callback) {
650 return room_member_impl->Bind(callback); 661 return room_member_impl->Bind(callback);
651} 662}
652 663
@@ -685,7 +696,7 @@ void RoomMember::Leave() {
685 room_member_impl->client = nullptr; 696 room_member_impl->client = nullptr;
686} 697}
687 698
688template void RoomMember::Unbind(CallbackHandle<WifiPacket>); 699template void RoomMember::Unbind(CallbackHandle<ProxyPacket>);
689template void RoomMember::Unbind(CallbackHandle<RoomMember::State>); 700template void RoomMember::Unbind(CallbackHandle<RoomMember::State>);
690template void RoomMember::Unbind(CallbackHandle<RoomMember::Error>); 701template void RoomMember::Unbind(CallbackHandle<RoomMember::Error>);
691template void RoomMember::Unbind(CallbackHandle<RoomInformation>); 702template void RoomMember::Unbind(CallbackHandle<RoomInformation>);
diff --git a/src/network/room_member.h b/src/network/room_member.h
index bbb7d13d4..4252b7146 100644
--- a/src/network/room_member.h
+++ b/src/network/room_member.h
@@ -9,6 +9,7 @@
9#include <vector> 9#include <vector>
10#include "common/announce_multiplayer_room.h" 10#include "common/announce_multiplayer_room.h"
11#include "common/common_types.h" 11#include "common/common_types.h"
12#include "common/socket_types.h"
12#include "network/room.h" 13#include "network/room.h"
13 14
14namespace Network { 15namespace Network {
@@ -17,22 +18,12 @@ using AnnounceMultiplayerRoom::GameInfo;
17using AnnounceMultiplayerRoom::RoomInformation; 18using AnnounceMultiplayerRoom::RoomInformation;
18 19
19/// Information about the received WiFi packets. 20/// Information about the received WiFi packets.
20/// Acts as our own 802.11 header. 21struct ProxyPacket {
21struct WifiPacket { 22 SockAddrIn local_endpoint;
22 enum class PacketType : u8 { 23 SockAddrIn remote_endpoint;
23 Beacon, 24 Protocol protocol;
24 Data, 25 bool broadcast;
25 Authentication, 26 std::vector<u8> data;
26 AssociationResponse,
27 Deauthentication,
28 NodeMap
29 };
30 PacketType type; ///< The type of 802.11 frame.
31 std::vector<u8> data; ///< Raw 802.11 frame data, starting at the management frame header
32 /// for management frames.
33 MacAddress transmitter_address; ///< Mac address of the transmitter.
34 MacAddress destination_address; ///< Mac address of the receiver.
35 u8 channel; ///< WiFi channel where this frame was transmitted.
36}; 27};
37 28
38/// Represents a chat message. 29/// Represents a chat message.
@@ -72,15 +63,14 @@ public:
72 HostKicked, ///< Kicked by the host 63 HostKicked, ///< Kicked by the host
73 64
74 // Reasons why connection was rejected 65 // Reasons why connection was rejected
75 UnknownError, ///< Some error [permissions to network device missing or something] 66 UnknownError, ///< Some error [permissions to network device missing or something]
76 NameCollision, ///< Somebody is already using this name 67 NameCollision, ///< Somebody is already using this name
77 MacCollision, ///< Somebody is already using that mac-address 68 IpCollision, ///< Somebody is already using that fake-ip-address
78 ConsoleIdCollision, ///< Somebody in the room has the same Console ID 69 WrongVersion, ///< The room version is not the same as for this RoomMember
79 WrongVersion, ///< The room version is not the same as for this RoomMember 70 WrongPassword, ///< The password doesn't match the one from the Room
80 WrongPassword, ///< The password doesn't match the one from the Room 71 CouldNotConnect, ///< The room is not responding to a connection attempt
81 CouldNotConnect, ///< The room is not responding to a connection attempt 72 RoomIsFull, ///< Room is already at the maximum number of players
82 RoomIsFull, ///< Room is already at the maximum number of players 73 HostBanned, ///< The user is banned by the host
83 HostBanned, ///< The user is banned by the host
84 74
85 // Reasons why moderation request failed 75 // Reasons why moderation request failed
86 PermissionDenied, ///< The user does not have mod permissions 76 PermissionDenied, ///< The user does not have mod permissions
@@ -92,9 +82,9 @@ public:
92 std::string username; ///< The web services username of the member. Can be empty. 82 std::string username; ///< The web services username of the member. Can be empty.
93 std::string display_name; ///< The web services display name of the member. Can be empty. 83 std::string display_name; ///< The web services display name of the member. Can be empty.
94 std::string avatar_url; ///< Url to the member's avatar. Can be empty. 84 std::string avatar_url; ///< Url to the member's avatar. Can be empty.
95 GameInfo game_info; ///< Name of the game they're currently playing, or empty if they're 85 GameInfo game_info; ///< Name of the game they're currently playing, or empty if they're
96 /// not playing anything. 86 /// not playing anything.
97 MacAddress mac_address; ///< MAC address associated with this member. 87 IPv4Address fake_ip; ///< Fake Ip address associated with this member.
98 }; 88 };
99 using MemberList = std::vector<MemberInformation>; 89 using MemberList = std::vector<MemberInformation>;
100 90
@@ -135,7 +125,7 @@ public:
135 /** 125 /**
136 * Returns the MAC address of the RoomMember. 126 * Returns the MAC address of the RoomMember.
137 */ 127 */
138 const MacAddress& GetMacAddress() const; 128 const IPv4Address& GetFakeIpAddress() const;
139 129
140 /** 130 /**
141 * Returns information about the room we're currently connected to. 131 * Returns information about the room we're currently connected to.
@@ -149,19 +139,17 @@ public:
149 139
150 /** 140 /**
151 * Attempts to join a room at the specified address and port, using the specified nickname. 141 * Attempts to join a room at the specified address and port, using the specified nickname.
152 * A console ID hash is passed in to check console ID conflicts.
153 * This may fail if the username or console ID is already taken.
154 */ 142 */
155 void Join(const std::string& nickname, const std::string& console_id_hash, 143 void Join(const std::string& nickname, const char* server_addr = "127.0.0.1",
156 const char* server_addr = "127.0.0.1", u16 server_port = DefaultRoomPort, 144 u16 server_port = DefaultRoomPort, u16 client_port = 0,
157 u16 client_port = 0, const MacAddress& preferred_mac = NoPreferredMac, 145 const IPv4Address& preferred_fake_ip = NoPreferredIP,
158 const std::string& password = "", const std::string& token = ""); 146 const std::string& password = "", const std::string& token = "");
159 147
160 /** 148 /**
161 * Sends a WiFi packet to the room. 149 * Sends a WiFi packet to the room.
162 * @param packet The WiFi packet to send. 150 * @param packet The WiFi packet to send.
163 */ 151 */
164 void SendWifiPacket(const WifiPacket& packet); 152 void SendProxyPacket(const ProxyPacket& packet);
165 153
166 /** 154 /**
167 * Sends a chat message to the room. 155 * Sends a chat message to the room.
@@ -207,14 +195,14 @@ public:
207 CallbackHandle<Error> BindOnError(std::function<void(const Error&)> callback); 195 CallbackHandle<Error> BindOnError(std::function<void(const Error&)> callback);
208 196
209 /** 197 /**
210 * Binds a function to an event that will be triggered every time a WifiPacket is received. 198 * Binds a function to an event that will be triggered every time a ProxyPacket is received.
211 * The function wil be called everytime the event is triggered. 199 * The function wil be called everytime the event is triggered.
212 * The callback function must not bind or unbind a function. Doing so will cause a deadlock 200 * The callback function must not bind or unbind a function. Doing so will cause a deadlock
213 * @param callback The function to call 201 * @param callback The function to call
214 * @return A handle used for removing the function from the registered list 202 * @return A handle used for removing the function from the registered list
215 */ 203 */
216 CallbackHandle<WifiPacket> BindOnWifiPacketReceived( 204 CallbackHandle<ProxyPacket> BindOnProxyPacketReceived(
217 std::function<void(const WifiPacket&)> callback); 205 std::function<void(const ProxyPacket&)> callback);
218 206
219 /** 207 /**
220 * Binds a function to an event that will be triggered every time the RoomInformation changes. 208 * Binds a function to an event that will be triggered every time the RoomInformation changes.
@@ -292,10 +280,8 @@ inline const char* GetErrorStr(const RoomMember::Error& e) {
292 return "UnknownError"; 280 return "UnknownError";
293 case RoomMember::Error::NameCollision: 281 case RoomMember::Error::NameCollision:
294 return "NameCollision"; 282 return "NameCollision";
295 case RoomMember::Error::MacCollision: 283 case RoomMember::Error::IpCollision:
296 return "MaxCollision"; 284 return "IpCollision";
297 case RoomMember::Error::ConsoleIdCollision:
298 return "ConsoleIdCollision";
299 case RoomMember::Error::WrongVersion: 285 case RoomMember::Error::WrongVersion:
300 return "WrongVersion"; 286 return "WrongVersion";
301 case RoomMember::Error::WrongPassword: 287 case RoomMember::Error::WrongPassword:
diff --git a/src/yuzu/multiplayer/chat_room.cpp b/src/yuzu/multiplayer/chat_room.cpp
index 5837b36ab..1968a3c75 100644
--- a/src/yuzu/multiplayer/chat_room.cpp
+++ b/src/yuzu/multiplayer/chat_room.cpp
@@ -316,21 +316,19 @@ void ChatRoom::OnStatusMessageReceive(const Network::StatusMessageEntry& status_
316} 316}
317 317
318void ChatRoom::OnSendChat() { 318void ChatRoom::OnSendChat() {
319 if (auto room = room_network->GetRoomMember().lock()) { 319 if (auto room_member = room_network->GetRoomMember().lock()) {
320 if (room->GetState() != Network::RoomMember::State::Joined && 320 if (!room_member->IsConnected()) {
321 room->GetState() != Network::RoomMember::State::Moderator) {
322
323 return; 321 return;
324 } 322 }
325 auto message = ui->chat_message->text().toStdString(); 323 auto message = ui->chat_message->text().toStdString();
326 if (!ValidateMessage(message)) { 324 if (!ValidateMessage(message)) {
327 return; 325 return;
328 } 326 }
329 auto nick = room->GetNickname(); 327 auto nick = room_member->GetNickname();
330 auto username = room->GetUsername(); 328 auto username = room_member->GetUsername();
331 Network::ChatEntry chat{nick, username, message}; 329 Network::ChatEntry chat{nick, username, message};
332 330
333 auto members = room->GetMemberInformation(); 331 auto members = room_member->GetMemberInformation();
334 auto it = std::find_if(members.begin(), members.end(), 332 auto it = std::find_if(members.begin(), members.end(),
335 [&chat](const Network::RoomMember::MemberInformation& member) { 333 [&chat](const Network::RoomMember::MemberInformation& member) {
336 return member.nickname == chat.nickname && 334 return member.nickname == chat.nickname &&
@@ -341,7 +339,7 @@ void ChatRoom::OnSendChat() {
341 } 339 }
342 auto player = std::distance(members.begin(), it); 340 auto player = std::distance(members.begin(), it);
343 ChatMessage m(chat, *room_network); 341 ChatMessage m(chat, *room_network);
344 room->SendChatMessage(message); 342 room_member->SendChatMessage(message);
345 AppendChatMessage(m.GetPlayerChatMessage(player)); 343 AppendChatMessage(m.GetPlayerChatMessage(player));
346 ui->chat_message->clear(); 344 ui->chat_message->clear();
347 } 345 }
diff --git a/src/yuzu/multiplayer/client_room.cpp b/src/yuzu/multiplayer/client_room.cpp
index a9859ed70..86baafbf0 100644
--- a/src/yuzu/multiplayer/client_room.cpp
+++ b/src/yuzu/multiplayer/client_room.cpp
@@ -74,7 +74,6 @@ void ClientRoomWindow::OnRoomUpdate(const Network::RoomInformation& info) {
74void ClientRoomWindow::OnStateChange(const Network::RoomMember::State& state) { 74void ClientRoomWindow::OnStateChange(const Network::RoomMember::State& state) {
75 if (state == Network::RoomMember::State::Joined || 75 if (state == Network::RoomMember::State::Joined ||
76 state == Network::RoomMember::State::Moderator) { 76 state == Network::RoomMember::State::Moderator) {
77
78 ui->chat->Clear(); 77 ui->chat->Clear();
79 ui->chat->AppendStatusMessage(tr("Connected")); 78 ui->chat->AppendStatusMessage(tr("Connected"));
80 SetModPerms(state == Network::RoomMember::State::Moderator); 79 SetModPerms(state == Network::RoomMember::State::Moderator);
diff --git a/src/yuzu/multiplayer/direct_connect.cpp b/src/yuzu/multiplayer/direct_connect.cpp
index 9000c4531..4c0ea0a6b 100644
--- a/src/yuzu/multiplayer/direct_connect.cpp
+++ b/src/yuzu/multiplayer/direct_connect.cpp
@@ -97,9 +97,9 @@ void DirectConnectWindow::Connect() {
97 QFuture<void> f = QtConcurrent::run([&] { 97 QFuture<void> f = QtConcurrent::run([&] {
98 if (auto room_member = room_network.GetRoomMember().lock()) { 98 if (auto room_member = room_network.GetRoomMember().lock()) {
99 auto port = UISettings::values.multiplayer_port.GetValue(); 99 auto port = UISettings::values.multiplayer_port.GetValue();
100 room_member->Join(ui->nickname->text().toStdString(), "", 100 room_member->Join(ui->nickname->text().toStdString(),
101 ui->ip->text().toStdString().c_str(), port, 0, 101 ui->ip->text().toStdString().c_str(), port, 0, Network::NoPreferredIP,
102 Network::NoPreferredMac, ui->password->text().toStdString().c_str()); 102 ui->password->text().toStdString().c_str());
103 } 103 }
104 }); 104 });
105 watcher->setFuture(f); 105 watcher->setFuture(f);
@@ -121,9 +121,7 @@ void DirectConnectWindow::OnConnection() {
121 EndConnecting(); 121 EndConnecting();
122 122
123 if (auto room_member = room_network.GetRoomMember().lock()) { 123 if (auto room_member = room_network.GetRoomMember().lock()) {
124 if (room_member->GetState() == Network::RoomMember::State::Joined || 124 if (room_member->IsConnected()) {
125 room_member->GetState() == Network::RoomMember::State::Moderator) {
126
127 close(); 125 close();
128 } 126 }
129 } 127 }
diff --git a/src/yuzu/multiplayer/host_room.cpp b/src/yuzu/multiplayer/host_room.cpp
index cb9464b2b..d70a9a3c8 100644
--- a/src/yuzu/multiplayer/host_room.cpp
+++ b/src/yuzu/multiplayer/host_room.cpp
@@ -201,8 +201,8 @@ void HostRoomWindow::Host() {
201 } 201 }
202#endif 202#endif
203 // TODO: Check what to do with this 203 // TODO: Check what to do with this
204 member->Join(ui->username->text().toStdString(), "", "127.0.0.1", port, 0, 204 member->Join(ui->username->text().toStdString(), "127.0.0.1", port, 0,
205 Network::NoPreferredMac, password, token); 205 Network::NoPreferredIP, password, token);
206 206
207 // Store settings 207 // Store settings
208 UISettings::values.multiplayer_room_nickname = ui->username->text(); 208 UISettings::values.multiplayer_room_nickname = ui->username->text();
diff --git a/src/yuzu/multiplayer/lobby.cpp b/src/yuzu/multiplayer/lobby.cpp
index 23c2f21ab..1cc518279 100644
--- a/src/yuzu/multiplayer/lobby.cpp
+++ b/src/yuzu/multiplayer/lobby.cpp
@@ -169,7 +169,7 @@ void Lobby::OnJoinRoom(const QModelIndex& source) {
169 } 169 }
170#endif 170#endif
171 if (auto room_member = room_network.GetRoomMember().lock()) { 171 if (auto room_member = room_network.GetRoomMember().lock()) {
172 room_member->Join(nickname, "", ip.c_str(), port, 0, Network::NoPreferredMac, password, 172 room_member->Join(nickname, ip.c_str(), port, 0, Network::NoPreferredIP, password,
173 token); 173 token);
174 } 174 }
175 }); 175 });
diff --git a/src/yuzu/multiplayer/message.cpp b/src/yuzu/multiplayer/message.cpp
index 76ec276ad..94d7a38b8 100644
--- a/src/yuzu/multiplayer/message.cpp
+++ b/src/yuzu/multiplayer/message.cpp
@@ -43,11 +43,8 @@ const ConnectionError ErrorManager::LOST_CONNECTION(
43 QT_TR_NOOP("Connection to room lost. Try to reconnect.")); 43 QT_TR_NOOP("Connection to room lost. Try to reconnect."));
44const ConnectionError ErrorManager::HOST_KICKED( 44const ConnectionError ErrorManager::HOST_KICKED(
45 QT_TR_NOOP("You have been kicked by the room host.")); 45 QT_TR_NOOP("You have been kicked by the room host."));
46const ConnectionError ErrorManager::MAC_COLLISION( 46const ConnectionError ErrorManager::IP_COLLISION(
47 QT_TR_NOOP("MAC address is already in use. Please choose another.")); 47 QT_TR_NOOP("IP address is already in use. Please choose another."));
48const ConnectionError ErrorManager::CONSOLE_ID_COLLISION(QT_TR_NOOP(
49 "Your Console ID conflicted with someone else's in the room.\n\nPlease go to Emulation "
50 "> Configure > System to regenerate your Console ID."));
51const ConnectionError ErrorManager::PERMISSION_DENIED( 48const ConnectionError ErrorManager::PERMISSION_DENIED(
52 QT_TR_NOOP("You do not have enough permission to perform this action.")); 49 QT_TR_NOOP("You do not have enough permission to perform this action."));
53const ConnectionError ErrorManager::NO_SUCH_USER(QT_TR_NOOP( 50const ConnectionError ErrorManager::NO_SUCH_USER(QT_TR_NOOP(
diff --git a/src/yuzu/multiplayer/message.h b/src/yuzu/multiplayer/message.h
index eb5c8d1be..812495c72 100644
--- a/src/yuzu/multiplayer/message.h
+++ b/src/yuzu/multiplayer/message.h
@@ -40,8 +40,7 @@ public:
40 static const ConnectionError GENERIC_ERROR; 40 static const ConnectionError GENERIC_ERROR;
41 static const ConnectionError LOST_CONNECTION; 41 static const ConnectionError LOST_CONNECTION;
42 static const ConnectionError HOST_KICKED; 42 static const ConnectionError HOST_KICKED;
43 static const ConnectionError MAC_COLLISION; 43 static const ConnectionError IP_COLLISION;
44 static const ConnectionError CONSOLE_ID_COLLISION;
45 static const ConnectionError PERMISSION_DENIED; 44 static const ConnectionError PERMISSION_DENIED;
46 static const ConnectionError NO_SUCH_USER; 45 static const ConnectionError NO_SUCH_USER;
47 /** 46 /**
diff --git a/src/yuzu/multiplayer/state.cpp b/src/yuzu/multiplayer/state.cpp
index 4149b5232..84ea1aa4c 100644
--- a/src/yuzu/multiplayer/state.cpp
+++ b/src/yuzu/multiplayer/state.cpp
@@ -95,7 +95,6 @@ void MultiplayerState::retranslateUi() {
95 status_text->setText(tr("Not Connected. Click here to find a room!")); 95 status_text->setText(tr("Not Connected. Click here to find a room!"));
96 } else if (current_state == Network::RoomMember::State::Joined || 96 } else if (current_state == Network::RoomMember::State::Joined ||
97 current_state == Network::RoomMember::State::Moderator) { 97 current_state == Network::RoomMember::State::Moderator) {
98
99 status_text->setText(tr("Connected")); 98 status_text->setText(tr("Connected"));
100 } else { 99 } else {
101 status_text->setText(tr("Not Connected")); 100 status_text->setText(tr("Not Connected"));
@@ -151,11 +150,8 @@ void MultiplayerState::OnNetworkError(const Network::RoomMember::Error& error) {
151 NetworkMessage::ErrorManager::ShowError( 150 NetworkMessage::ErrorManager::ShowError(
152 NetworkMessage::ErrorManager::USERNAME_NOT_VALID_SERVER); 151 NetworkMessage::ErrorManager::USERNAME_NOT_VALID_SERVER);
153 break; 152 break;
154 case Network::RoomMember::Error::MacCollision: 153 case Network::RoomMember::Error::IpCollision:
155 NetworkMessage::ErrorManager::ShowError(NetworkMessage::ErrorManager::MAC_COLLISION); 154 NetworkMessage::ErrorManager::ShowError(NetworkMessage::ErrorManager::IP_COLLISION);
156 break;
157 case Network::RoomMember::Error::ConsoleIdCollision:
158 NetworkMessage::ErrorManager::ShowError(NetworkMessage::ErrorManager::CONSOLE_ID_COLLISION);
159 break; 155 break;
160 case Network::RoomMember::Error::RoomIsFull: 156 case Network::RoomMember::Error::RoomIsFull:
161 NetworkMessage::ErrorManager::ShowError(NetworkMessage::ErrorManager::ROOM_IS_FULL); 157 NetworkMessage::ErrorManager::ShowError(NetworkMessage::ErrorManager::ROOM_IS_FULL);
diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp
index 003890c07..3a0f33cba 100644
--- a/src/yuzu_cmd/yuzu.cpp
+++ b/src/yuzu_cmd/yuzu.cpp
@@ -108,15 +108,11 @@ static void OnNetworkError(const Network::RoomMember::Error& error) {
108 "You tried to use the same nickname as another user that is connected to the Room"); 108 "You tried to use the same nickname as another user that is connected to the Room");
109 exit(1); 109 exit(1);
110 break; 110 break;
111 case Network::RoomMember::Error::MacCollision: 111 case Network::RoomMember::Error::IpCollision:
112 LOG_ERROR(Network, "You tried to use the same MAC-Address as another user that is " 112 LOG_ERROR(Network, "You tried to use the same fake IP-Address as another user that is "
113 "connected to the Room"); 113 "connected to the Room");
114 exit(1); 114 exit(1);
115 break; 115 break;
116 case Network::RoomMember::Error::ConsoleIdCollision:
117 LOG_ERROR(Network, "Your Console ID conflicted with someone else in the Room");
118 exit(1);
119 break;
120 case Network::RoomMember::Error::WrongPassword: 116 case Network::RoomMember::Error::WrongPassword:
121 LOG_ERROR(Network, "Room replied with: Wrong password"); 117 LOG_ERROR(Network, "Room replied with: Wrong password");
122 exit(1); 118 exit(1);
@@ -365,7 +361,7 @@ int main(int argc, char** argv) {
365 member->BindOnError(OnNetworkError); 361 member->BindOnError(OnNetworkError);
366 LOG_DEBUG(Network, "Start connection to {}:{} with nickname {}", address, port, 362 LOG_DEBUG(Network, "Start connection to {}:{} with nickname {}", address, port,
367 nickname); 363 nickname);
368 member->Join(nickname, "", address.c_str(), port, 0, Network::NoPreferredMac, password); 364 member->Join(nickname, address.c_str(), port, 0, Network::NoPreferredIP, password);
369 } else { 365 } else {
370 LOG_ERROR(Network, "Could not access RoomMember"); 366 LOG_ERROR(Network, "Could not access RoomMember");
371 return 0; 367 return 0;