diff --git a/rpcs3/Emu/Cell/lv2/sys_net/nt_p2p_port.cpp b/rpcs3/Emu/Cell/lv2/sys_net/nt_p2p_port.cpp index 0c472043c1..cf7441db35 100644 --- a/rpcs3/Emu/Cell/lv2/sys_net/nt_p2p_port.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_net/nt_p2p_port.cpp @@ -7,6 +7,7 @@ #include "sys_net_helpers.h" #include "Emu/NP/vport0.h" #include "Emu/NP/np_handler.h" +#include "Emu/NP/np_helpers.h" LOG_CHANNEL(sys_net); @@ -59,6 +60,7 @@ nt_p2p_port::nt_p2p_port(u16 port) int ret_bind = 0; const u16 be_port = std::bit_cast>(port); + auto& nph = g_fxo->get>(); if (is_ipv6) { @@ -73,15 +75,23 @@ nt_p2p_port::nt_p2p_port(u16 port) else { ::sockaddr_in p2p_ipv4_addr{.sin_family = AF_INET, .sin_port = be_port}; - ret_bind = ::bind(p2p_socket, reinterpret_cast(&p2p_ipv4_addr), sizeof(p2p_ipv4_addr)); + p2p_ipv4_addr.sin_addr.s_addr = nph.get_bind_ip(); + + if (ret_bind = ::bind(p2p_socket, reinterpret_cast(&p2p_ipv4_addr), sizeof(p2p_ipv4_addr)); ret_bind == -1) + { + if (nph.get_bind_ip()) + { + sys_net.error("Failed to bind to %s:%d, falling back to 0.0.0.0:%d", np::ip_to_string(nph.get_bind_ip()), port, port); + p2p_ipv4_addr.sin_addr.s_addr = 0; + ret_bind = ::bind(p2p_socket, reinterpret_cast(&p2p_ipv4_addr), sizeof(p2p_ipv4_addr)); + } + } } if (ret_bind == -1) fmt::throw_exception("Failed to bind DGRAM socket to %d for P2P: %s!", port, get_last_error(true)); - auto& nph = g_fxo->get>(); nph.upnp_add_port_mapping(port, "UDP"); - sys_net.notice("P2P port %d was bound!", port); } diff --git a/rpcs3/Emu/Cell/lv2/sys_net/sys_net_helpers.cpp b/rpcs3/Emu/Cell/lv2/sys_net/sys_net_helpers.cpp index 4650f6bcec..96a0f62362 100644 --- a/rpcs3/Emu/Cell/lv2/sys_net/sys_net_helpers.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_net/sys_net_helpers.cpp @@ -1,5 +1,6 @@ #include "stdafx.h" #include "Emu/IdManager.h" +#include "Emu/system_config.h" #include "Emu/Cell/PPUThread.h" #include "lv2_socket.h" #include "sys_net_helpers.h" @@ -199,6 +200,26 @@ void clear_ppu_to_awake(ppu_thread& ppu) g_fxo->get().del_ppu_to_awake(&ppu); } +be_t resolve_binding_ip() +{ + in_addr conv{}; + const std::string cfg_bind_addr = g_cfg.net.bind_address.to_string(); + + if (cfg_bind_addr == "0.0.0.0" || cfg_bind_addr == "") + { + return 0; + } + + if (!inet_pton(AF_INET, cfg_bind_addr.c_str(), &conv)) + { + // Do not set to disconnected on invalid IP just error and continue using default (0.0.0.0) + sys_net.error("Provided IP(%s) address for bind is invalid!", g_cfg.net.bind_address.to_string()); + return 0; + } + + return conv.s_addr; +} + #ifdef _WIN32 // Workaround function for WSAPoll not reporting failed connections // Note that this was fixed in Windows 10 version 2004 (after more than 10 years lol) diff --git a/rpcs3/Emu/Cell/lv2/sys_net/sys_net_helpers.h b/rpcs3/Emu/Cell/lv2/sys_net/sys_net_helpers.h index 1cd9e136ca..b93d00bdd4 100644 --- a/rpcs3/Emu/Cell/lv2/sys_net/sys_net_helpers.h +++ b/rpcs3/Emu/Cell/lv2/sys_net/sys_net_helpers.h @@ -25,6 +25,7 @@ sys_net_sockaddr native_addr_to_sys_net_addr(const ::sockaddr_storage& native_ad bool is_ip_public_address(const ::sockaddr_in& addr); u32 network_clear_queue(ppu_thread& ppu); void clear_ppu_to_awake(ppu_thread& ppu); +be_t resolve_binding_ip(); #ifdef _WIN32 void windows_poll(std::vector& fds, unsigned long nfds, int timeout, std::vector& connecting); diff --git a/rpcs3/Emu/NP/ip_address.cpp b/rpcs3/Emu/NP/ip_address.cpp index 312212973f..7adb38f113 100644 --- a/rpcs3/Emu/NP/ip_address.cpp +++ b/rpcs3/Emu/NP/ip_address.cpp @@ -1,3 +1,4 @@ +#include "Emu/Cell/lv2/sys_net/sys_net_helpers.h" #include "stdafx.h" #include "Emu/system_config.h" #include "ip_address.h" @@ -6,6 +7,7 @@ #include "util/endian.hpp" #include "util/types.hpp" #include "Emu/NP/rpcn_config.h" +#include "Emu/Cell/lv2/sys_net/sys_net_helpers.h" #include #ifndef _WIN32 @@ -103,6 +105,7 @@ namespace np // -IPv6 is not disabled in config // -Internet config is Connected // -PSN config is RPCN + // -Bind IP is not set // -RPCN host has an IPv6 // -Can connect to ipv6.google.com:413 bool is_ipv6_supported(std::optional force_state) @@ -135,6 +138,9 @@ namespace np if (g_cfg.net.net_active != np_internet_status::enabled || g_cfg.net.psn_status != np_psn_status::psn_rpcn) return notice_and_disable("RPCN is disabled"); + if (resolve_binding_ip()) + return notice_and_disable("Bind IP is used"); + addrinfo* addr_info{}; socket_type socket_ipv6{}; const addrinfo hints{.ai_family = AF_INET6}; diff --git a/rpcs3/Emu/NP/np_handler.cpp b/rpcs3/Emu/NP/np_handler.cpp index 4597d27b19..0b779f955a 100644 --- a/rpcs3/Emu/NP/np_handler.cpp +++ b/rpcs3/Emu/NP/np_handler.cpp @@ -412,7 +412,7 @@ namespace np nc.bind_sce_np_port(); std::lock_guard lock(mutex_rpcn); - rpcn = rpcn::rpcn_client::get_instance(); + rpcn = rpcn::rpcn_client::get_instance(bind_ip); } } @@ -455,19 +455,10 @@ namespace np } // Convert bind address - conv = {}; - if (!inet_pton(AF_INET, g_cfg.net.bind_address.to_string().c_str(), &conv)) - { - // Do not set to disconnected on invalid IP just error and continue using default (0.0.0.0) - nph_log.error("Provided IP(%s) address for bind is invalid!", g_cfg.net.bind_address.to_string()); - } - else - { - bind_ip = conv.s_addr; + bind_ip = resolve_binding_ip(); - if (bind_ip) - local_ip_addr = bind_ip; - } + if (bind_ip) + local_ip_addr = bind_ip; if (g_cfg.net.upnp_enabled) upnp.upnp_enable(); @@ -793,7 +784,7 @@ namespace np if (!rpcn) { - rpcn = rpcn::rpcn_client::get_instance(); + rpcn = rpcn::rpcn_client::get_instance(bind_ip); was_already_started = false; } diff --git a/rpcs3/Emu/NP/np_helpers.cpp b/rpcs3/Emu/NP/np_helpers.cpp index be2618b5cf..641a15dde3 100644 --- a/rpcs3/Emu/NP/np_helpers.cpp +++ b/rpcs3/Emu/NP/np_helpers.cpp @@ -11,7 +11,7 @@ namespace np { std::string ip_to_string(u32 ip_addr) { - char ip_str[16]; + char ip_str[16]{}; inet_ntop(AF_INET, &ip_addr, ip_str, sizeof(ip_str)); return std::string(ip_str); diff --git a/rpcs3/Emu/NP/rpcn_client.cpp b/rpcs3/Emu/NP/rpcn_client.cpp index c963e85ab7..4d6ce2e2a1 100644 --- a/rpcs3/Emu/NP/rpcn_client.cpp +++ b/rpcs3/Emu/NP/rpcn_client.cpp @@ -212,6 +212,7 @@ namespace rpcn case rpcn::rpcn_state::failure_input: return localized_string_id::RPCN_ERROR_INVALID_INPUT; case rpcn::rpcn_state::failure_wolfssl: return localized_string_id::RPCN_ERROR_WOLFSSL; case rpcn::rpcn_state::failure_resolve: return localized_string_id::RPCN_ERROR_RESOLVE; + case rpcn::rpcn_state::failure_binding: return localized_string_id::RPCN_ERROR_BINDING; case rpcn::rpcn_state::failure_connect: return localized_string_id::RPCN_ERROR_CONNECT; case rpcn::rpcn_state::failure_id: return localized_string_id::RPCN_ERROR_LOGIN_ERROR; case rpcn::rpcn_state::failure_id_already_logged_in: return localized_string_id::RPCN_ERROR_ALREADY_LOGGED; @@ -316,8 +317,8 @@ namespace rpcn // Constructor, destructor & singleton manager - rpcn_client::rpcn_client() - : sem_connected(0), sem_authentified(0), sem_reader(0), sem_writer(0), sem_rpcn(0), + rpcn_client::rpcn_client(u32 binding_address) + : binding_address(binding_address), sem_connected(0), sem_authentified(0), sem_reader(0), sem_writer(0), sem_rpcn(0), thread_rpcn(std::thread(&rpcn_client::rpcn_thread, this)), thread_rpcn_reader(std::thread(&rpcn_client::rpcn_reader_thread, this)), thread_rpcn_writer(std::thread(&rpcn_client::rpcn_writer_thread, this)) @@ -349,7 +350,7 @@ namespace rpcn sem_authentified.release(); } - std::shared_ptr rpcn_client::get_instance(bool check_config) + std::shared_ptr rpcn_client::get_instance(u32 binding_address, bool check_config) { if (check_config && g_cfg.net.psn_status != np_psn_status::psn_rpcn) { @@ -362,7 +363,7 @@ namespace rpcn sptr = instance.lock(); if (!sptr) { - sptr = std::shared_ptr(new rpcn_client()); + sptr = std::shared_ptr(new rpcn_client(binding_address)); sptr->register_friend_cb(overlay_friend_callback, nullptr); instance = sptr; } @@ -1074,6 +1075,16 @@ namespace rpcn return false; } + sockaddr_in sock_addr = {.sin_family = AF_INET}; + sock_addr.sin_addr.s_addr = binding_address; + + if (::bind(sockfd, reinterpret_cast(&sock_addr), sizeof(sock_addr)) == -1) + { + rpcn_log.error("bind: Failed to bind RPCN client socket to binding address!"); + state = rpcn_state::failure_binding; + return false; + } + if (::connect(sockfd, reinterpret_cast(&addr_rpcn), sizeof(addr_rpcn)) != 0) { rpcn_log.error("connect: Failed to connect to RPCN server!"); diff --git a/rpcs3/Emu/NP/rpcn_client.h b/rpcs3/Emu/NP/rpcn_client.h index 2ada34b581..e3d2fb0299 100644 --- a/rpcs3/Emu/NP/rpcn_client.h +++ b/rpcs3/Emu/NP/rpcn_client.h @@ -227,6 +227,7 @@ namespace rpcn atomic_t authentified = false; atomic_t want_conn = false; atomic_t want_auth = false; + u32 binding_address = 0; std::binary_semaphore sem_connected, sem_authentified; std::mutex mutex_connected, mutex_authentified; @@ -253,7 +254,7 @@ namespace rpcn void handle_message(std::vector data); private: - rpcn_client(); + rpcn_client(u32 binding_address); void rpcn_reader_thread(); void rpcn_writer_thread(); @@ -287,7 +288,7 @@ namespace rpcn ~rpcn_client(); rpcn_client(rpcn_client& other) = delete; void operator=(const rpcn_client&) = delete; - static std::shared_ptr get_instance(bool check_config = false); + static std::shared_ptr get_instance(u32 binding_address, bool check_config = false); rpcn_state wait_for_connection(); rpcn_state wait_for_authentified(); bool terminate_connection(); diff --git a/rpcs3/Emu/NP/rpcn_types.h b/rpcs3/Emu/NP/rpcn_types.h index 1e9fb9acf4..684f0e65e9 100644 --- a/rpcs3/Emu/NP/rpcn_types.h +++ b/rpcs3/Emu/NP/rpcn_types.h @@ -98,6 +98,7 @@ namespace rpcn failure_input, failure_wolfssl, failure_resolve, + failure_binding, failure_connect, failure_id, failure_id_already_logged_in, diff --git a/rpcs3/Emu/RSX/Overlays/FriendsList/overlay_friends_list_dialog.cpp b/rpcs3/Emu/RSX/Overlays/FriendsList/overlay_friends_list_dialog.cpp index 1d13791059..2df72329bf 100644 --- a/rpcs3/Emu/RSX/Overlays/FriendsList/overlay_friends_list_dialog.cpp +++ b/rpcs3/Emu/RSX/Overlays/FriendsList/overlay_friends_list_dialog.cpp @@ -655,7 +655,7 @@ namespace rsx g_cfg_rpcn.load(); // Ensures config is loaded even if rpcn is not running for simulated - m_rpcn = rpcn::rpcn_client::get_instance(); + m_rpcn = rpcn::rpcn_client::get_instance(0); m_rpcn->register_friend_cb(friend_callback, this); diff --git a/rpcs3/Emu/RSX/Overlays/Network/overlay_recvmessage_dialog.cpp b/rpcs3/Emu/RSX/Overlays/Network/overlay_recvmessage_dialog.cpp index 69c3c41678..cb1837b78d 100644 --- a/rpcs3/Emu/RSX/Overlays/Network/overlay_recvmessage_dialog.cpp +++ b/rpcs3/Emu/RSX/Overlays/Network/overlay_recvmessage_dialog.cpp @@ -213,7 +213,7 @@ namespace rsx const bool preserve = options & SCE_NP_BASIC_RECV_MESSAGE_OPTIONS_PRESERVE; const bool include_bootable = options & SCE_NP_BASIC_RECV_MESSAGE_OPTIONS_INCLUDE_BOOTABLE; - m_rpcn = rpcn::rpcn_client::get_instance(true); + m_rpcn = rpcn::rpcn_client::get_instance(0, true); // Get list of messages const auto messages = m_rpcn->get_messages_and_register_cb(type, include_bootable, recvmessage_callback, this); diff --git a/rpcs3/Emu/RSX/Overlays/Network/overlay_sendmessage_dialog.cpp b/rpcs3/Emu/RSX/Overlays/Network/overlay_sendmessage_dialog.cpp index aed778e6da..5369afb097 100644 --- a/rpcs3/Emu/RSX/Overlays/Network/overlay_sendmessage_dialog.cpp +++ b/rpcs3/Emu/RSX/Overlays/Network/overlay_sendmessage_dialog.cpp @@ -183,7 +183,7 @@ namespace rsx break; // Title already set in constructor } - m_rpcn = rpcn::rpcn_client::get_instance(true); + m_rpcn = rpcn::rpcn_client::get_instance(0, true); // Get list of messages rpcn::friend_data data; diff --git a/rpcs3/Emu/localized_string_id.h b/rpcs3/Emu/localized_string_id.h index 5f19c9d6d4..408403fd1f 100644 --- a/rpcs3/Emu/localized_string_id.h +++ b/rpcs3/Emu/localized_string_id.h @@ -163,6 +163,7 @@ enum class localized_string_id RPCN_ERROR_INVALID_INPUT, RPCN_ERROR_WOLFSSL, RPCN_ERROR_RESOLVE, + RPCN_ERROR_BINDING, RPCN_ERROR_CONNECT, RPCN_ERROR_LOGIN_ERROR, RPCN_ERROR_ALREADY_LOGGED, diff --git a/rpcs3/rpcs3qt/localized_emu.h b/rpcs3/rpcs3qt/localized_emu.h index 293ad42bde..1beab0f082 100644 --- a/rpcs3/rpcs3qt/localized_emu.h +++ b/rpcs3/rpcs3qt/localized_emu.h @@ -185,6 +185,7 @@ private: case localized_string_id::RPCN_ERROR_INVALID_INPUT: return tr("RPCN: Invalid Input (Wrong Host/Port)"); case localized_string_id::RPCN_ERROR_WOLFSSL: return tr("RPCN Connection Error: WolfSSL Error"); case localized_string_id::RPCN_ERROR_RESOLVE: return tr("RPCN Connection Error: Resolve Error"); + case localized_string_id::RPCN_ERROR_BINDING: return tr("RPCN Connection Error: Failed to bind to given binding IP"); case localized_string_id::RPCN_ERROR_CONNECT: return tr("RPCN Connection Error"); case localized_string_id::RPCN_ERROR_LOGIN_ERROR: return tr("RPCN Login Error: Identification Error"); case localized_string_id::RPCN_ERROR_ALREADY_LOGGED: return tr("RPCN Login Error: User Already Logged In"); diff --git a/rpcs3/rpcs3qt/recvmessage_dialog_frame.cpp b/rpcs3/rpcs3qt/recvmessage_dialog_frame.cpp index 7a805a5723..eb61516ea0 100644 --- a/rpcs3/rpcs3qt/recvmessage_dialog_frame.cpp +++ b/rpcs3/rpcs3qt/recvmessage_dialog_frame.cpp @@ -42,7 +42,7 @@ error_code recvmessage_dialog_frame::Exec(SceNpBasicMessageMainType type, SceNpB m_dialog->setWindowTitle(tr("Choose message:")); - m_rpcn = rpcn::rpcn_client::get_instance(true); + m_rpcn = rpcn::rpcn_client::get_instance(0, true); QVBoxLayout* vbox_global = new QVBoxLayout(); diff --git a/rpcs3/rpcs3qt/rpcn_settings_dialog.cpp b/rpcs3/rpcs3qt/rpcn_settings_dialog.cpp index 5046435008..bdbb09ce17 100644 --- a/rpcs3/rpcs3qt/rpcn_settings_dialog.cpp +++ b/rpcs3/rpcs3qt/rpcn_settings_dialog.cpp @@ -270,7 +270,7 @@ rpcn_account_dialog::rpcn_account_dialog(QWidget* parent) return; { - const auto rpcn = rpcn::rpcn_client::get_instance(); + const auto rpcn = rpcn::rpcn_client::get_instance(0); const auto avatar_url = "https://rpcs3.net/cdn/netplay/DefaultAvatar.png"; if (auto result = rpcn->wait_for_connection(); result != rpcn::rpcn_state::failure_no_failure) @@ -325,7 +325,7 @@ rpcn_account_dialog::rpcn_account_dialog(QWidget* parent) connect(btn_test, &QAbstractButton::clicked, this, [this]() { - auto rpcn = rpcn::rpcn_client::get_instance(); + auto rpcn = rpcn::rpcn_client::get_instance(0); if (auto res = rpcn->wait_for_connection(); res != rpcn::rpcn_state::failure_no_failure) { @@ -342,7 +342,7 @@ rpcn_account_dialog::rpcn_account_dialog(QWidget* parent) QMessageBox::information(this, tr("RPCN Account Valid!"), tr("Your account is valid!"), QMessageBox::Ok); }); - + connect(checkbox_disable_ipv6, &QCheckBox::checkStateChanged, this, [this](Qt::CheckState state) { g_cfg_rpcn.set_ipv6_support(state == Qt::Unchecked); @@ -761,7 +761,7 @@ void rpcn_account_edit_dialog::resend_token() if (!save_config()) return; - const auto rpcn = rpcn::rpcn_client::get_instance(); + const auto rpcn = rpcn::rpcn_client::get_instance(0); const std::string npid = g_cfg_rpcn.get_npid(); const std::string password = g_cfg_rpcn.get_password(); @@ -814,7 +814,7 @@ void rpcn_account_edit_dialog::change_password() return; { - const auto rpcn = rpcn::rpcn_client::get_instance(); + const auto rpcn = rpcn::rpcn_client::get_instance(0); if (auto result = rpcn->wait_for_connection(); result != rpcn::rpcn_state::failure_no_failure) { const QString error_message = tr("Failed to connect to RPCN server:\n%0").arg(QString::fromStdString(rpcn::rpcn_state_to_string(result))); @@ -859,7 +859,7 @@ void rpcn_account_edit_dialog::change_password() return; { - const auto rpcn = rpcn::rpcn_client::get_instance(); + const auto rpcn = rpcn::rpcn_client::get_instance(0); if (auto result = rpcn->wait_for_connection(); result != rpcn::rpcn_state::failure_no_failure) { const QString error_message = tr("Failed to connect to RPCN server:\n%0").arg(QString::fromStdString(rpcn::rpcn_state_to_string(result))); @@ -1048,7 +1048,7 @@ rpcn_friends_dialog::rpcn_friends_dialog(QWidget* parent) setLayout(vbox_global); // Tries to connect to RPCN - m_rpcn = rpcn::rpcn_client::get_instance(); + m_rpcn = rpcn::rpcn_client::get_instance(0); if (auto res = m_rpcn->wait_for_connection(); res != rpcn::rpcn_state::failure_no_failure) { diff --git a/rpcs3/rpcs3qt/sendmessage_dialog_frame.cpp b/rpcs3/rpcs3qt/sendmessage_dialog_frame.cpp index 7f794b67f2..75477b3fbb 100644 --- a/rpcs3/rpcs3qt/sendmessage_dialog_frame.cpp +++ b/rpcs3/rpcs3qt/sendmessage_dialog_frame.cpp @@ -40,7 +40,7 @@ error_code sendmessage_dialog_frame::Exec(message_data& msg_data, std::setsetWindowTitle(tr("Choose friend to message:")); - m_rpcn = rpcn::rpcn_client::get_instance(true); + m_rpcn = rpcn::rpcn_client::get_instance(0, true); QVBoxLayout* vbox_global = new QVBoxLayout();