Make IP Binding more global
Some checks failed
Generate Translation Template / Generate Translation Template (push) Has been cancelled
Build RPCS3 / RPCS3 Linux ubuntu-24.04 gcc (push) Has been cancelled
Build RPCS3 / RPCS3 Linux ubuntu-24.04-arm clang (push) Has been cancelled
Build RPCS3 / RPCS3 Linux ubuntu-24.04 clang (push) Has been cancelled
Build RPCS3 / RPCS3 Windows (push) Has been cancelled

This commit is contained in:
RipleyTom 2025-05-10 03:40:21 +02:00 committed by Elad
parent d21358e91f
commit 3894c903bc
17 changed files with 80 additions and 36 deletions

View file

@ -7,6 +7,7 @@
#include "sys_net_helpers.h" #include "sys_net_helpers.h"
#include "Emu/NP/vport0.h" #include "Emu/NP/vport0.h"
#include "Emu/NP/np_handler.h" #include "Emu/NP/np_handler.h"
#include "Emu/NP/np_helpers.h"
LOG_CHANNEL(sys_net); LOG_CHANNEL(sys_net);
@ -59,6 +60,7 @@ nt_p2p_port::nt_p2p_port(u16 port)
int ret_bind = 0; int ret_bind = 0;
const u16 be_port = std::bit_cast<u16, be_t<u16>>(port); const u16 be_port = std::bit_cast<u16, be_t<u16>>(port);
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
if (is_ipv6) if (is_ipv6)
{ {
@ -73,15 +75,23 @@ nt_p2p_port::nt_p2p_port(u16 port)
else else
{ {
::sockaddr_in p2p_ipv4_addr{.sin_family = AF_INET, .sin_port = be_port}; ::sockaddr_in p2p_ipv4_addr{.sin_family = AF_INET, .sin_port = be_port};
ret_bind = ::bind(p2p_socket, reinterpret_cast<sockaddr*>(&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<const sockaddr*>(&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<const sockaddr*>(&p2p_ipv4_addr), sizeof(p2p_ipv4_addr));
}
}
} }
if (ret_bind == -1) if (ret_bind == -1)
fmt::throw_exception("Failed to bind DGRAM socket to %d for P2P: %s!", port, get_last_error(true)); fmt::throw_exception("Failed to bind DGRAM socket to %d for P2P: %s!", port, get_last_error(true));
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
nph.upnp_add_port_mapping(port, "UDP"); nph.upnp_add_port_mapping(port, "UDP");
sys_net.notice("P2P port %d was bound!", port); sys_net.notice("P2P port %d was bound!", port);
} }

View file

@ -1,5 +1,6 @@
#include "stdafx.h" #include "stdafx.h"
#include "Emu/IdManager.h" #include "Emu/IdManager.h"
#include "Emu/system_config.h"
#include "Emu/Cell/PPUThread.h" #include "Emu/Cell/PPUThread.h"
#include "lv2_socket.h" #include "lv2_socket.h"
#include "sys_net_helpers.h" #include "sys_net_helpers.h"
@ -199,6 +200,26 @@ void clear_ppu_to_awake(ppu_thread& ppu)
g_fxo->get<p2p_context>().del_ppu_to_awake(&ppu); g_fxo->get<p2p_context>().del_ppu_to_awake(&ppu);
} }
be_t<u32> 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 #ifdef _WIN32
// Workaround function for WSAPoll not reporting failed connections // Workaround function for WSAPoll not reporting failed connections
// Note that this was fixed in Windows 10 version 2004 (after more than 10 years lol) // Note that this was fixed in Windows 10 version 2004 (after more than 10 years lol)

View file

@ -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); bool is_ip_public_address(const ::sockaddr_in& addr);
u32 network_clear_queue(ppu_thread& ppu); u32 network_clear_queue(ppu_thread& ppu);
void clear_ppu_to_awake(ppu_thread& ppu); void clear_ppu_to_awake(ppu_thread& ppu);
be_t<u32> resolve_binding_ip();
#ifdef _WIN32 #ifdef _WIN32
void windows_poll(std::vector<pollfd>& fds, unsigned long nfds, int timeout, std::vector<bool>& connecting); void windows_poll(std::vector<pollfd>& fds, unsigned long nfds, int timeout, std::vector<bool>& connecting);

View file

@ -1,3 +1,4 @@
#include "Emu/Cell/lv2/sys_net/sys_net_helpers.h"
#include "stdafx.h" #include "stdafx.h"
#include "Emu/system_config.h" #include "Emu/system_config.h"
#include "ip_address.h" #include "ip_address.h"
@ -6,6 +7,7 @@
#include "util/endian.hpp" #include "util/endian.hpp"
#include "util/types.hpp" #include "util/types.hpp"
#include "Emu/NP/rpcn_config.h" #include "Emu/NP/rpcn_config.h"
#include "Emu/Cell/lv2/sys_net/sys_net_helpers.h"
#include <algorithm> #include <algorithm>
#ifndef _WIN32 #ifndef _WIN32
@ -103,6 +105,7 @@ namespace np
// -IPv6 is not disabled in config // -IPv6 is not disabled in config
// -Internet config is Connected // -Internet config is Connected
// -PSN config is RPCN // -PSN config is RPCN
// -Bind IP is not set
// -RPCN host has an IPv6 // -RPCN host has an IPv6
// -Can connect to ipv6.google.com:413 // -Can connect to ipv6.google.com:413
bool is_ipv6_supported(std::optional<IPV6_SUPPORT> force_state) bool is_ipv6_supported(std::optional<IPV6_SUPPORT> 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) 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"); return notice_and_disable("RPCN is disabled");
if (resolve_binding_ip())
return notice_and_disable("Bind IP is used");
addrinfo* addr_info{}; addrinfo* addr_info{};
socket_type socket_ipv6{}; socket_type socket_ipv6{};
const addrinfo hints{.ai_family = AF_INET6}; const addrinfo hints{.ai_family = AF_INET6};

View file

@ -412,7 +412,7 @@ namespace np
nc.bind_sce_np_port(); nc.bind_sce_np_port();
std::lock_guard lock(mutex_rpcn); 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 // Convert bind address
conv = {}; bind_ip = resolve_binding_ip();
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;
if (bind_ip) if (bind_ip)
local_ip_addr = bind_ip; local_ip_addr = bind_ip;
}
if (g_cfg.net.upnp_enabled) if (g_cfg.net.upnp_enabled)
upnp.upnp_enable(); upnp.upnp_enable();
@ -793,7 +784,7 @@ namespace np
if (!rpcn) if (!rpcn)
{ {
rpcn = rpcn::rpcn_client::get_instance(); rpcn = rpcn::rpcn_client::get_instance(bind_ip);
was_already_started = false; was_already_started = false;
} }

View file

@ -11,7 +11,7 @@ namespace np
{ {
std::string ip_to_string(u32 ip_addr) 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)); inet_ntop(AF_INET, &ip_addr, ip_str, sizeof(ip_str));
return std::string(ip_str); return std::string(ip_str);

View file

@ -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_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_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_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_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: 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; 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 // Constructor, destructor & singleton manager
rpcn_client::rpcn_client() rpcn_client::rpcn_client(u32 binding_address)
: sem_connected(0), sem_authentified(0), sem_reader(0), sem_writer(0), sem_rpcn(0), : 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(std::thread(&rpcn_client::rpcn_thread, this)),
thread_rpcn_reader(std::thread(&rpcn_client::rpcn_reader_thread, this)), thread_rpcn_reader(std::thread(&rpcn_client::rpcn_reader_thread, this)),
thread_rpcn_writer(std::thread(&rpcn_client::rpcn_writer_thread, this)) thread_rpcn_writer(std::thread(&rpcn_client::rpcn_writer_thread, this))
@ -349,7 +350,7 @@ namespace rpcn
sem_authentified.release(); sem_authentified.release();
} }
std::shared_ptr<rpcn_client> rpcn_client::get_instance(bool check_config) std::shared_ptr<rpcn_client> rpcn_client::get_instance(u32 binding_address, bool check_config)
{ {
if (check_config && g_cfg.net.psn_status != np_psn_status::psn_rpcn) if (check_config && g_cfg.net.psn_status != np_psn_status::psn_rpcn)
{ {
@ -362,7 +363,7 @@ namespace rpcn
sptr = instance.lock(); sptr = instance.lock();
if (!sptr) if (!sptr)
{ {
sptr = std::shared_ptr<rpcn_client>(new rpcn_client()); sptr = std::shared_ptr<rpcn_client>(new rpcn_client(binding_address));
sptr->register_friend_cb(overlay_friend_callback, nullptr); sptr->register_friend_cb(overlay_friend_callback, nullptr);
instance = sptr; instance = sptr;
} }
@ -1074,6 +1075,16 @@ namespace rpcn
return false; return false;
} }
sockaddr_in sock_addr = {.sin_family = AF_INET};
sock_addr.sin_addr.s_addr = binding_address;
if (::bind(sockfd, reinterpret_cast<const sockaddr*>(&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<struct sockaddr*>(&addr_rpcn), sizeof(addr_rpcn)) != 0) if (::connect(sockfd, reinterpret_cast<struct sockaddr*>(&addr_rpcn), sizeof(addr_rpcn)) != 0)
{ {
rpcn_log.error("connect: Failed to connect to RPCN server!"); rpcn_log.error("connect: Failed to connect to RPCN server!");

View file

@ -227,6 +227,7 @@ namespace rpcn
atomic_t<bool> authentified = false; atomic_t<bool> authentified = false;
atomic_t<bool> want_conn = false; atomic_t<bool> want_conn = false;
atomic_t<bool> want_auth = false; atomic_t<bool> want_auth = false;
u32 binding_address = 0;
std::binary_semaphore sem_connected, sem_authentified; std::binary_semaphore sem_connected, sem_authentified;
std::mutex mutex_connected, mutex_authentified; std::mutex mutex_connected, mutex_authentified;
@ -253,7 +254,7 @@ namespace rpcn
void handle_message(std::vector<u8> data); void handle_message(std::vector<u8> data);
private: private:
rpcn_client(); rpcn_client(u32 binding_address);
void rpcn_reader_thread(); void rpcn_reader_thread();
void rpcn_writer_thread(); void rpcn_writer_thread();
@ -287,7 +288,7 @@ namespace rpcn
~rpcn_client(); ~rpcn_client();
rpcn_client(rpcn_client& other) = delete; rpcn_client(rpcn_client& other) = delete;
void operator=(const rpcn_client&) = delete; void operator=(const rpcn_client&) = delete;
static std::shared_ptr<rpcn_client> get_instance(bool check_config = false); static std::shared_ptr<rpcn_client> get_instance(u32 binding_address, bool check_config = false);
rpcn_state wait_for_connection(); rpcn_state wait_for_connection();
rpcn_state wait_for_authentified(); rpcn_state wait_for_authentified();
bool terminate_connection(); bool terminate_connection();

View file

@ -98,6 +98,7 @@ namespace rpcn
failure_input, failure_input,
failure_wolfssl, failure_wolfssl,
failure_resolve, failure_resolve,
failure_binding,
failure_connect, failure_connect,
failure_id, failure_id,
failure_id_already_logged_in, failure_id_already_logged_in,

View file

@ -655,7 +655,7 @@ namespace rsx
g_cfg_rpcn.load(); // Ensures config is loaded even if rpcn is not running for simulated 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); m_rpcn->register_friend_cb(friend_callback, this);

View file

@ -213,7 +213,7 @@ namespace rsx
const bool preserve = options & SCE_NP_BASIC_RECV_MESSAGE_OPTIONS_PRESERVE; const bool preserve = options & SCE_NP_BASIC_RECV_MESSAGE_OPTIONS_PRESERVE;
const bool include_bootable = options & SCE_NP_BASIC_RECV_MESSAGE_OPTIONS_INCLUDE_BOOTABLE; 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 // Get list of messages
const auto messages = m_rpcn->get_messages_and_register_cb(type, include_bootable, recvmessage_callback, this); const auto messages = m_rpcn->get_messages_and_register_cb(type, include_bootable, recvmessage_callback, this);

View file

@ -183,7 +183,7 @@ namespace rsx
break; // Title already set in constructor 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 // Get list of messages
rpcn::friend_data data; rpcn::friend_data data;

View file

@ -163,6 +163,7 @@ enum class localized_string_id
RPCN_ERROR_INVALID_INPUT, RPCN_ERROR_INVALID_INPUT,
RPCN_ERROR_WOLFSSL, RPCN_ERROR_WOLFSSL,
RPCN_ERROR_RESOLVE, RPCN_ERROR_RESOLVE,
RPCN_ERROR_BINDING,
RPCN_ERROR_CONNECT, RPCN_ERROR_CONNECT,
RPCN_ERROR_LOGIN_ERROR, RPCN_ERROR_LOGIN_ERROR,
RPCN_ERROR_ALREADY_LOGGED, RPCN_ERROR_ALREADY_LOGGED,

View file

@ -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_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_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_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_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_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"); case localized_string_id::RPCN_ERROR_ALREADY_LOGGED: return tr("RPCN Login Error: User Already Logged In");

View file

@ -42,7 +42,7 @@ error_code recvmessage_dialog_frame::Exec(SceNpBasicMessageMainType type, SceNpB
m_dialog->setWindowTitle(tr("Choose message:")); 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(); QVBoxLayout* vbox_global = new QVBoxLayout();

View file

@ -270,7 +270,7 @@ rpcn_account_dialog::rpcn_account_dialog(QWidget* parent)
return; 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"; 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) 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]() 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) if (auto res = rpcn->wait_for_connection(); res != rpcn::rpcn_state::failure_no_failure)
{ {
@ -761,7 +761,7 @@ void rpcn_account_edit_dialog::resend_token()
if (!save_config()) if (!save_config())
return; 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 npid = g_cfg_rpcn.get_npid();
const std::string password = g_cfg_rpcn.get_password(); const std::string password = g_cfg_rpcn.get_password();
@ -814,7 +814,7 @@ void rpcn_account_edit_dialog::change_password()
return; 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) 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))); 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; 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) 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))); 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); setLayout(vbox_global);
// Tries to connect to RPCN // 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) if (auto res = m_rpcn->wait_for_connection(); res != rpcn::rpcn_state::failure_no_failure)
{ {

View file

@ -40,7 +40,7 @@ error_code sendmessage_dialog_frame::Exec(message_data& msg_data, std::set<std::
m_dialog->setWindowTitle(tr("Choose friend to message:")); m_dialog->setWindowTitle(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(); QVBoxLayout* vbox_global = new QVBoxLayout();