mirror of
https://github.com/RPCSX/rpcsx.git
synced 2025-12-06 07:12:14 +01:00
Simplify signaling by making Matching2 a layer over normal signaling. Implements UPNP port forwarding Implement sceNpMatching2AbortRequest Fix reported bw in sceNpUtil Hack for Fat Princess binding udp on 3658 Reenable CB for sceNpBasicAddPlayersHistoryAsync Misc fixes
1083 lines
28 KiB
C++
1083 lines
28 KiB
C++
#include "stdafx.h"
|
|
#include "Emu/system_config.h"
|
|
#include "Emu/NP/np_handler.h"
|
|
#include "Emu/Cell/PPUModule.h"
|
|
#include "Emu/Cell/Modules/sceNp.h"
|
|
#include "Emu/Cell/Modules/sceNp2.h"
|
|
#include "Emu/Cell/Modules/cellNetCtl.h"
|
|
#include "Utilities/StrUtil.h"
|
|
#include "Emu/IdManager.h"
|
|
#include "Emu/NP/np_structs_extra.h"
|
|
#include "Emu/System.h"
|
|
#include "Emu/NP/rpcn_config.h"
|
|
#include "Emu/NP/np_contexts.h"
|
|
#include "Emu/NP/np_helpers.h"
|
|
#include "Emu/RSX/Overlays/overlay_message.h"
|
|
#include "Emu/Cell/lv2/sys_net/network_context.h"
|
|
#include "Emu/Cell/lv2/sys_net/sys_net_helpers.h"
|
|
|
|
#ifdef _WIN32
|
|
#include <winsock2.h>
|
|
#include <WS2tcpip.h>
|
|
#include <iphlpapi.h>
|
|
#else
|
|
#ifdef __clang__
|
|
#pragma GCC diagnostic push
|
|
#pragma GCC diagnostic ignored "-Wold-style-cast"
|
|
#endif
|
|
#include <sys/socket.h>
|
|
#include <netinet/in.h>
|
|
#include <net/if.h>
|
|
#include <arpa/inet.h>
|
|
#include <netdb.h>
|
|
#include <unistd.h>
|
|
#ifdef __clang__
|
|
#pragma GCC diagnostic pop
|
|
#endif
|
|
#endif
|
|
|
|
#if defined(__FreeBSD__) || defined(__APPLE__)
|
|
#include <ifaddrs.h>
|
|
#include <net/if_dl.h>
|
|
#endif
|
|
|
|
#include "util/asm.hpp"
|
|
|
|
#include <span>
|
|
|
|
LOG_CHANNEL(sys_net);
|
|
LOG_CHANNEL(sceNp2);
|
|
LOG_CHANNEL(sceNp);
|
|
|
|
LOG_CHANNEL(rpcn_log, "rpcn");
|
|
LOG_CHANNEL(nph_log, "NPHandler");
|
|
LOG_CHANNEL(ticket_log, "Ticket");
|
|
|
|
namespace np
|
|
{
|
|
ticket::ticket(std::vector<u8>&& raw_data)
|
|
: raw_data(raw_data)
|
|
{
|
|
parse();
|
|
}
|
|
|
|
std::size_t ticket::size() const
|
|
{
|
|
return raw_data.size();
|
|
}
|
|
|
|
const u8* ticket::data() const
|
|
{
|
|
return raw_data.data();
|
|
}
|
|
|
|
bool ticket::empty() const
|
|
{
|
|
return raw_data.empty();
|
|
}
|
|
|
|
bool ticket::get_value(s32 param_id, vm::ptr<SceNpTicketParam> param) const
|
|
{
|
|
if (!parse_success)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
switch (param_id)
|
|
{
|
|
case SCE_NP_TICKET_PARAM_SERIAL_ID:
|
|
{
|
|
const auto& node = nodes[0].data.data_nodes[0];
|
|
if (node.len != SCE_NP_TICKET_SERIAL_ID_SIZE)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
memcpy(param->data, node.data.data_vec.data(), SCE_NP_TICKET_SERIAL_ID_SIZE);
|
|
break;
|
|
}
|
|
case SCE_NP_TICKET_PARAM_ISSUER_ID:
|
|
{
|
|
const auto& node = nodes[0].data.data_nodes[1];
|
|
param->ui32 = node.data.data_u32;
|
|
break;
|
|
}
|
|
case SCE_NP_TICKET_PARAM_ISSUED_DATE:
|
|
{
|
|
const auto& node = nodes[0].data.data_nodes[2];
|
|
param->ui64 = node.data.data_u64;
|
|
break;
|
|
}
|
|
case SCE_NP_TICKET_PARAM_EXPIRE_DATE:
|
|
{
|
|
const auto& node = nodes[0].data.data_nodes[3];
|
|
param->ui64 = node.data.data_u64;
|
|
break;
|
|
}
|
|
case SCE_NP_TICKET_PARAM_SUBJECT_ACCOUNT_ID:
|
|
{
|
|
const auto& node = nodes[0].data.data_nodes[4];
|
|
param->ui64 = node.data.data_u64;
|
|
break;
|
|
}
|
|
case SCE_NP_TICKET_PARAM_SUBJECT_ONLINE_ID:
|
|
{
|
|
const auto& node = nodes[0].data.data_nodes[5];
|
|
if (node.len != 0x20)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
memcpy(param->data, node.data.data_vec.data(), 0x20);
|
|
break;
|
|
}
|
|
case SCE_NP_TICKET_PARAM_SUBJECT_REGION:
|
|
{
|
|
const auto& node = nodes[0].data.data_nodes[6];
|
|
if (node.len != SCE_NP_SUBJECT_REGION_SIZE)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
memcpy(param->data, node.data.data_vec.data(), SCE_NP_SUBJECT_REGION_SIZE);
|
|
break;
|
|
}
|
|
case SCE_NP_TICKET_PARAM_SUBJECT_DOMAIN:
|
|
{
|
|
const auto& node = nodes[0].data.data_nodes[7];
|
|
if (node.len != SCE_NP_SUBJECT_DOMAIN_SIZE)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
memcpy(param->data, node.data.data_vec.data(), SCE_NP_SUBJECT_DOMAIN_SIZE);
|
|
break;
|
|
}
|
|
case SCE_NP_TICKET_PARAM_SERVICE_ID:
|
|
{
|
|
const auto& node = nodes[0].data.data_nodes[8];
|
|
if (node.len != SCE_NP_SERVICE_ID_SIZE)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
memcpy(param->data, node.data.data_vec.data(), SCE_NP_SERVICE_ID_SIZE);
|
|
break;
|
|
}
|
|
case SCE_NP_TICKET_PARAM_SUBJECT_STATUS:
|
|
{
|
|
const auto& node = nodes[0].data.data_nodes[9];
|
|
param->ui32 = node.data.data_u32;
|
|
break;
|
|
}
|
|
case SCE_NP_TICKET_PARAM_STATUS_DURATION:
|
|
case SCE_NP_TICKET_PARAM_SUBJECT_DOB:
|
|
{
|
|
param->ui64 = 0;
|
|
break;
|
|
}
|
|
default:
|
|
sceNp.fatal("Invalid ticket param id requested!");
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
std::optional<ticket_data> ticket::parse_node(std::size_t index) const
|
|
{
|
|
if ((index + MIN_TICKET_DATA_SIZE) > size())
|
|
{
|
|
ticket_log.error("node didn't meet minimum size requirements");
|
|
return std::nullopt;
|
|
}
|
|
|
|
ticket_data tdata{};
|
|
const auto* ptr = data() + index;
|
|
tdata.id = read_from_ptr<be_t<u16>>(ptr);
|
|
tdata.len = read_from_ptr<be_t<u16>>(ptr + 2);
|
|
const auto* data_ptr = data() + index + 4;
|
|
|
|
auto check_size = [&](std::size_t expected) -> bool
|
|
{
|
|
if ((index + MIN_TICKET_DATA_SIZE + expected) > size())
|
|
{
|
|
return false;
|
|
}
|
|
return true;
|
|
};
|
|
|
|
switch (tdata.id)
|
|
{
|
|
case 0:
|
|
if (tdata.len != 0)
|
|
{
|
|
return std::nullopt;
|
|
}
|
|
break;
|
|
case 1:
|
|
if (tdata.len != 4 || !check_size(4))
|
|
{
|
|
return std::nullopt;
|
|
}
|
|
tdata.data.data_u32 = read_from_ptr<be_t<u32>>(data_ptr);
|
|
break;
|
|
case 2:
|
|
case 7:
|
|
if (tdata.len != 8 || !check_size(8))
|
|
{
|
|
return std::nullopt;
|
|
}
|
|
tdata.data.data_u64 = read_from_ptr<be_t<u64>>(data_ptr);
|
|
break;
|
|
case 4:
|
|
case 8:
|
|
if (!check_size(tdata.len))
|
|
{
|
|
return std::nullopt;
|
|
}
|
|
tdata.data.data_vec = std::vector<u8>(tdata.len);
|
|
memcpy(tdata.data.data_vec.data(), data_ptr, tdata.len);
|
|
break;
|
|
default:
|
|
if ((tdata.id & 0x3000) == 0x3000)
|
|
{
|
|
if (!check_size(tdata.len))
|
|
{
|
|
return std::nullopt;
|
|
}
|
|
|
|
std::size_t sub_index = 0;
|
|
tdata.data.data_nodes = {};
|
|
while (sub_index < tdata.len)
|
|
{
|
|
auto sub_node = parse_node(sub_index + index + 4);
|
|
if (!sub_node)
|
|
{
|
|
ticket_log.error("Failed to parse subnode at %d", sub_index + index + 4);
|
|
return std::nullopt;
|
|
}
|
|
sub_index += sub_node->len + MIN_TICKET_DATA_SIZE;
|
|
tdata.data.data_nodes.push_back(std::move(*sub_node));
|
|
}
|
|
break;
|
|
}
|
|
return std::nullopt;
|
|
}
|
|
|
|
return tdata;
|
|
}
|
|
|
|
void ticket::parse()
|
|
{
|
|
nodes.clear();
|
|
parse_success = false;
|
|
|
|
if (size() < (sizeof(u32) * 2))
|
|
{
|
|
return;
|
|
}
|
|
|
|
version = read_from_ptr<be_t<u32>>(data());
|
|
if (version != 0x21010000)
|
|
{
|
|
ticket_log.error("Invalid version: 0x%08x", version);
|
|
return;
|
|
}
|
|
|
|
u32 given_size = read_from_ptr<be_t<u32>>(data() + 4);
|
|
if ((given_size + 8) != size())
|
|
{
|
|
ticket_log.error("Size mismatch (gs: %d vs s: %d)", given_size, size());
|
|
return;
|
|
}
|
|
|
|
std::size_t index = 8;
|
|
while (index < size())
|
|
{
|
|
auto node = parse_node(index);
|
|
if (!node)
|
|
{
|
|
ticket_log.error("Failed to parse node at index %d", index);
|
|
return;
|
|
}
|
|
|
|
index += (node->len + MIN_TICKET_DATA_SIZE);
|
|
nodes.push_back(std::move(*node));
|
|
}
|
|
|
|
// Check that everything expected is there
|
|
if (nodes.size() != 2)
|
|
{
|
|
ticket_log.error("Expected 2 blobs, found %d", nodes.size());
|
|
return;
|
|
}
|
|
|
|
if (nodes[0].id != 0x3000 && nodes[1].id != 0x3002)
|
|
{
|
|
ticket_log.error("The 2 blobs ids are incorrect");
|
|
return;
|
|
}
|
|
|
|
if (nodes[0].data.data_nodes.size() < 12)
|
|
{
|
|
ticket_log.error("Expected at least 12 sub-nodes, found %d", nodes[0].data.data_nodes.size());
|
|
return;
|
|
}
|
|
|
|
const auto& subnodes = nodes[0].data.data_nodes;
|
|
|
|
if (subnodes[0].id != 8 || subnodes[1].id != 1 || subnodes[2].id != 7 || subnodes[3].id != 7 ||
|
|
subnodes[4].id != 2 || subnodes[5].id != 4 || subnodes[6].id != 8 || subnodes[7].id != 4 ||
|
|
subnodes[8].id != 8 || subnodes[9].id != 1)
|
|
{
|
|
ticket_log.error("Mismatched node");
|
|
return;
|
|
}
|
|
|
|
parse_success = true;
|
|
return;
|
|
}
|
|
|
|
np_handler::np_handler()
|
|
{
|
|
g_fxo->need<named_thread<signaling_handler>>();
|
|
|
|
is_connected = (g_cfg.net.net_active == np_internet_status::enabled);
|
|
is_psn_active = (g_cfg.net.psn_status >= np_psn_status::psn_fake) && is_connected;
|
|
|
|
if (get_net_status() == CELL_NET_CTL_STATE_IPObtained)
|
|
{
|
|
|
|
if (!discover_ether_address() || !discover_ip_address())
|
|
{
|
|
nph_log.error("Failed to discover ethernet or ip address!");
|
|
is_connected = false;
|
|
is_psn_active = false;
|
|
return;
|
|
}
|
|
|
|
// Convert dns address
|
|
// TODO: recover actual user dns through OS specific API
|
|
in_addr conv{};
|
|
if (!inet_pton(AF_INET, g_cfg.net.dns.to_string().c_str(), &conv))
|
|
{
|
|
// Do not set to disconnected on invalid IP just error and continue using default(google's 8.8.8.8)
|
|
nph_log.error("Provided IP(%s) address for DNS is invalid!", g_cfg.net.dns.to_string());
|
|
}
|
|
else
|
|
{
|
|
dns_ip = conv.s_addr;
|
|
}
|
|
|
|
// 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;
|
|
}
|
|
|
|
if (g_cfg.net.upnp_enabled)
|
|
upnp.upnp_enable();
|
|
}
|
|
|
|
if (is_psn_active && g_cfg.net.psn_status == np_psn_status::psn_rpcn)
|
|
{
|
|
g_fxo->need<network_context>();
|
|
auto& nc = g_fxo->get<network_context>();
|
|
nc.bind_sce_np_port();
|
|
|
|
std::lock_guard lock(mutex_rpcn);
|
|
rpcn = rpcn::rpcn_client::get_instance();
|
|
}
|
|
}
|
|
|
|
np_handler::np_handler(utils::serial& ar)
|
|
: np_handler()
|
|
{
|
|
ar(is_netctl_init, is_NP_init);
|
|
|
|
if (!is_NP_init)
|
|
{
|
|
return;
|
|
}
|
|
|
|
ar(is_NP_Lookup_init, is_NP_Score_init, is_NP2_init, is_NP2_Match2_init, is_NP_Auth_init, manager_cb, manager_cb_arg, std::as_bytes(std::span(&basic_handler, 1)), is_connected, is_psn_active, hostname, ether_address, local_ip_addr, public_ip_addr, dns_ip);
|
|
|
|
// Call init func if needed (np_memory is unaffected when an empty pool is provided)
|
|
init_NP(0, vm::null);
|
|
|
|
np_memory.save(ar);
|
|
|
|
// TODO: IDM-tied objects are not yet saved
|
|
}
|
|
|
|
np_handler::~np_handler()
|
|
{
|
|
std::unordered_map<u32, std::shared_ptr<score_transaction_ctx>> moved_trans;
|
|
{
|
|
std::lock_guard lock(mutex_score_transactions);
|
|
moved_trans = std::move(score_transactions);
|
|
score_transactions.clear();
|
|
}
|
|
|
|
for (auto& [trans_id, trans] : moved_trans)
|
|
{
|
|
trans->abort_score_transaction();
|
|
}
|
|
|
|
for (auto& [trans_id, trans] : moved_trans)
|
|
{
|
|
if (trans->thread.joinable())
|
|
trans->thread.join();
|
|
}
|
|
}
|
|
|
|
void np_handler::save(utils::serial& ar)
|
|
{
|
|
// TODO: See ctor
|
|
ar(is_netctl_init, is_NP_init);
|
|
|
|
if (!is_NP_init)
|
|
{
|
|
return;
|
|
}
|
|
|
|
USING_SERIALIZATION_VERSION(sceNp);
|
|
|
|
ar(is_NP_Lookup_init, is_NP_Score_init, is_NP2_init, is_NP2_Match2_init, is_NP_Auth_init, manager_cb, manager_cb_arg, std::as_bytes(std::span(&basic_handler, 1)), is_connected, is_psn_active, hostname, ether_address, local_ip_addr, public_ip_addr, dns_ip);
|
|
|
|
np_memory.save(ar);
|
|
}
|
|
|
|
void memory_allocator::save(utils::serial& ar)
|
|
{
|
|
ar(m_pool, m_size, m_allocs, m_avail);
|
|
}
|
|
|
|
bool np_handler::discover_ip_address()
|
|
{
|
|
auto sockfd = socket(AF_INET, SOCK_DGRAM, 0);
|
|
|
|
auto close_socket = [&]()
|
|
{
|
|
#ifdef _WIN32
|
|
closesocket(sockfd);
|
|
#else
|
|
close(sockfd);
|
|
#endif
|
|
};
|
|
|
|
::sockaddr_in addr;
|
|
std::memset(&addr, 0, sizeof(addr));
|
|
addr.sin_family = AF_INET;
|
|
addr.sin_port = 53;
|
|
addr.sin_addr.s_addr = 0x08080808;
|
|
if (connect(sockfd, reinterpret_cast<const sockaddr *>(&addr), sizeof(addr)) != 0)
|
|
{
|
|
// If connect fails a route to the internet is not available
|
|
nph_log.error("connect to discover local ip failed: %d", get_native_error());
|
|
close_socket();
|
|
return false; // offline
|
|
}
|
|
|
|
sockaddr_in client_addr;
|
|
socklen_t client_addr_size = sizeof(client_addr);
|
|
if (getsockname(sockfd, reinterpret_cast<struct sockaddr*>(&client_addr), &client_addr_size) != 0)
|
|
{
|
|
rpcn_log.error("getsockname to discover local ip failed: %d", get_native_error());
|
|
close_socket();
|
|
return true; // still assume online
|
|
}
|
|
|
|
local_ip_addr = client_addr.sin_addr.s_addr;
|
|
nph_log.trace("discover_ip_address: IP was determined to be %s", ip_to_string(local_ip_addr));
|
|
close_socket();
|
|
return true;
|
|
}
|
|
|
|
bool np_handler::discover_ether_address()
|
|
{
|
|
#if defined(__FreeBSD__) || defined(__APPLE__)
|
|
ifaddrs* ifap;
|
|
|
|
if (getifaddrs(&ifap) == 0)
|
|
{
|
|
ifaddrs* p;
|
|
for (p = ifap; p; p = p->ifa_next)
|
|
{
|
|
if (p->ifa_addr->sa_family == AF_LINK)
|
|
{
|
|
sockaddr_dl* sdp = reinterpret_cast<sockaddr_dl*>(p->ifa_addr);
|
|
memcpy(ether_address.data(), sdp->sdl_data + sdp->sdl_nlen, 6);
|
|
freeifaddrs(ifap);
|
|
// nph_log.notice("Determined Ethernet address to be %s", ether_to_string(ether_address));
|
|
return true;
|
|
}
|
|
}
|
|
freeifaddrs(ifap);
|
|
}
|
|
#elif defined(_WIN32)
|
|
std::vector<u8> adapter_infos(sizeof(IP_ADAPTER_INFO));
|
|
ULONG size_infos = sizeof(IP_ADAPTER_INFO);
|
|
|
|
if (GetAdaptersInfo(reinterpret_cast<PIP_ADAPTER_INFO>(adapter_infos.data()), &size_infos) == ERROR_BUFFER_OVERFLOW)
|
|
adapter_infos.resize(size_infos);
|
|
|
|
if (GetAdaptersInfo(reinterpret_cast<PIP_ADAPTER_INFO>(adapter_infos.data()), &size_infos) == NO_ERROR && size_infos)
|
|
{
|
|
PIP_ADAPTER_INFO info = reinterpret_cast<PIP_ADAPTER_INFO>(adapter_infos.data());
|
|
memcpy(ether_address.data(), info[0].Address, 6);
|
|
// nph_log.notice("Determined Ethernet address to be %s", ether_to_string(ether_address));
|
|
return true;
|
|
}
|
|
#else
|
|
ifreq ifr;
|
|
ifconf ifc;
|
|
char buf[1024];
|
|
int success = 0;
|
|
|
|
int sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
|
|
if (sock == -1)
|
|
return false;
|
|
|
|
ifc.ifc_len = sizeof(buf);
|
|
ifc.ifc_buf = buf;
|
|
if (ioctl(sock, SIOCGIFCONF, &ifc) == -1)
|
|
return false;
|
|
|
|
ifreq* it = ifc.ifc_req;
|
|
const ifreq* const end = it + (ifc.ifc_len / sizeof(ifreq));
|
|
|
|
for (; it != end; ++it)
|
|
{
|
|
strcpy(ifr.ifr_name, it->ifr_name);
|
|
if (ioctl(sock, SIOCGIFFLAGS, &ifr) == 0)
|
|
{
|
|
if (!(ifr.ifr_flags & IFF_LOOPBACK))
|
|
{
|
|
if (ioctl(sock, SIOCGIFHWADDR, &ifr) == 0)
|
|
{
|
|
success = 1;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (success)
|
|
{
|
|
memcpy(ether_address.data(), ifr.ifr_hwaddr.sa_data, 6);
|
|
// nph_log.notice("Determined Ethernet address to be %s", ether_to_string(ether_address));
|
|
|
|
return true;
|
|
}
|
|
#endif
|
|
|
|
return false;
|
|
}
|
|
|
|
const std::array<u8, 6>& np_handler::get_ether_addr() const
|
|
{
|
|
return ether_address;
|
|
}
|
|
|
|
const std::string& np_handler::get_hostname() const
|
|
{
|
|
return hostname;
|
|
}
|
|
|
|
u32 np_handler::get_local_ip_addr() const
|
|
{
|
|
return local_ip_addr;
|
|
}
|
|
|
|
u32 np_handler::get_public_ip_addr() const
|
|
{
|
|
return public_ip_addr;
|
|
}
|
|
|
|
u32 np_handler::get_dns_ip() const
|
|
{
|
|
return dns_ip;
|
|
}
|
|
|
|
u32 np_handler::get_bind_ip() const
|
|
{
|
|
return bind_ip;
|
|
}
|
|
|
|
s32 np_handler::get_net_status() const
|
|
{
|
|
return is_connected ? CELL_NET_CTL_STATE_IPObtained : CELL_NET_CTL_STATE_Disconnected;
|
|
}
|
|
|
|
s32 np_handler::get_psn_status() const
|
|
{
|
|
return is_psn_active ? SCE_NP_MANAGER_STATUS_ONLINE : SCE_NP_MANAGER_STATUS_OFFLINE;
|
|
}
|
|
|
|
s32 np_handler::get_upnp_status() const
|
|
{
|
|
if (upnp.is_active())
|
|
return SCE_NP_SIGNALING_NETINFO_UPNP_STATUS_VALID;
|
|
|
|
return SCE_NP_SIGNALING_NETINFO_UPNP_STATUS_INVALID;
|
|
}
|
|
|
|
const SceNpId& np_handler::get_npid() const
|
|
{
|
|
return npid;
|
|
}
|
|
|
|
const SceNpOnlineId& np_handler::get_online_id() const
|
|
{
|
|
return npid.handle;
|
|
}
|
|
|
|
const SceNpOnlineName& np_handler::get_online_name() const
|
|
{
|
|
return online_name;
|
|
}
|
|
|
|
const SceNpAvatarUrl& np_handler::get_avatar_url() const
|
|
{
|
|
return avatar_url;
|
|
}
|
|
|
|
void np_handler::init_NP(u32 poolsize, vm::ptr<void> poolptr)
|
|
{
|
|
if (poolsize)
|
|
{
|
|
// Init memory pool (zero arg is reserved for savestate's use)
|
|
np_memory.setup(poolptr, poolsize);
|
|
}
|
|
|
|
memset(&npid, 0, sizeof(npid));
|
|
memset(&online_name, 0, sizeof(online_name));
|
|
memset(&avatar_url, 0, sizeof(avatar_url));
|
|
|
|
if (g_cfg.net.psn_status >= np_psn_status::psn_fake)
|
|
{
|
|
std::string s_npid = g_cfg_rpcn.get_npid();
|
|
ensure(!s_npid.empty()); // It should have been generated before this
|
|
|
|
string_to_npid(s_npid, npid);
|
|
auto& sigh = g_fxo->get<named_thread<signaling_handler>>();
|
|
sigh.set_self_sig_info(npid);
|
|
}
|
|
|
|
switch (g_cfg.net.psn_status)
|
|
{
|
|
case np_psn_status::disabled:
|
|
break;
|
|
case np_psn_status::psn_fake:
|
|
{
|
|
string_to_online_name("RPCS3's user", online_name);
|
|
string_to_avatar_url("https://rpcs3.net/cdn/netplay/DefaultAvatar.png", avatar_url);
|
|
break;
|
|
}
|
|
case np_psn_status::psn_rpcn:
|
|
{
|
|
if (!is_psn_active)
|
|
break;
|
|
|
|
std::lock_guard lock(mutex_rpcn);
|
|
rpcn = rpcn::rpcn_client::get_instance();
|
|
|
|
// Make sure we're connected
|
|
|
|
if (auto state = rpcn->wait_for_connection(); state != rpcn::rpcn_state::failure_no_failure)
|
|
{
|
|
rsx::overlays::queue_message(rpcn::rpcn_state_to_localized_string_id(state));
|
|
rpcn_log.error("Connection to RPCN Failed!");
|
|
is_psn_active = false;
|
|
return;
|
|
}
|
|
|
|
if (auto state = rpcn->wait_for_authentified(); state != rpcn::rpcn_state::failure_no_failure)
|
|
{
|
|
rsx::overlays::queue_message(rpcn::rpcn_state_to_localized_string_id(state));
|
|
rpcn_log.error("RPCN login attempt failed!");
|
|
is_psn_active = false;
|
|
return;
|
|
}
|
|
|
|
rsx::overlays::queue_message(localized_string_id::RPCN_SUCCESS_LOGGED_ON);
|
|
|
|
string_to_online_name(rpcn->get_online_name(), online_name);
|
|
string_to_avatar_url(rpcn->get_avatar_url(), avatar_url);
|
|
public_ip_addr = rpcn->get_addr_sig();
|
|
local_ip_addr = std::bit_cast<u32, be_t<u32>>(rpcn->get_addr_local());
|
|
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void np_handler::terminate_NP()
|
|
{
|
|
np_memory.release();
|
|
|
|
if (g_cfg.net.psn_status == np_psn_status::psn_rpcn)
|
|
{
|
|
rpcn_log.notice("Disconnecting from RPCN!");
|
|
std::lock_guard lock(mutex_rpcn);
|
|
rpcn.reset();
|
|
}
|
|
}
|
|
|
|
u32 np_handler::get_match2_event(SceNpMatching2EventKey event_key, u32 dest_addr, u32 size)
|
|
{
|
|
std::lock_guard lock(mutex_match2_req_results);
|
|
|
|
if (!match2_req_results.contains(event_key))
|
|
return 0;
|
|
|
|
auto& data = ::at32(match2_req_results, event_key);
|
|
data.apply_relocations(dest_addr);
|
|
|
|
vm::ptr<void> dest = vm::cast(dest_addr);
|
|
|
|
u32 size_copied = std::min(size, data.size());
|
|
memcpy(dest.get_ptr(), data.data(), size_copied);
|
|
|
|
np_memory.free(data.addr());
|
|
match2_req_results.erase(event_key);
|
|
|
|
return size_copied;
|
|
}
|
|
|
|
bool np_handler::send_basic_event(s32 event, s32 retCode, u32 reqId)
|
|
{
|
|
if (basic_handler.registered)
|
|
{
|
|
sysutil_register_cb([handler_func = this->basic_handler.handler_func, handler_arg = this->basic_handler.handler_arg, event, retCode, reqId](ppu_thread& cb_ppu) -> s32
|
|
{
|
|
handler_func(cb_ppu, event, retCode, reqId, handler_arg);
|
|
return 0;
|
|
});
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void np_handler::queue_basic_event(basic_event to_queue)
|
|
{
|
|
std::lock_guard lock(mutex_queue_basic_events);
|
|
queue_basic_events.push(std::move(to_queue));
|
|
}
|
|
|
|
error_code np_handler::get_basic_event(vm::ptr<s32> event, vm::ptr<SceNpUserInfo> from, vm::ptr<s32> data, vm::ptr<u32> size)
|
|
{
|
|
basic_event cur_event;
|
|
{
|
|
std::lock_guard lock(mutex_queue_basic_events);
|
|
if (queue_basic_events.empty())
|
|
{
|
|
return not_an_error(SCE_NP_BASIC_ERROR_NO_EVENT);
|
|
}
|
|
|
|
cur_event = std::move(queue_basic_events.front());
|
|
queue_basic_events.pop();
|
|
}
|
|
|
|
const u32 size_avail = *size;
|
|
u32 res_size = std::min(static_cast<u32>(cur_event.data.size()), size_avail);
|
|
|
|
*event = cur_event.event;
|
|
memcpy(from.get_ptr(), &cur_event.from, sizeof(cur_event.from));
|
|
memcpy(data.get_ptr(), cur_event.data.data(), res_size);
|
|
*size = res_size;
|
|
|
|
if (res_size < cur_event.data.size())
|
|
{
|
|
return SCE_NP_BASIC_ERROR_DATA_LOST;
|
|
}
|
|
|
|
return CELL_OK;
|
|
}
|
|
|
|
std::optional<std::shared_ptr<std::pair<std::string, message_data>>> np_handler::get_message(u64 id)
|
|
{
|
|
return rpcn->get_message(id);
|
|
}
|
|
|
|
void np_handler::operator()()
|
|
{
|
|
if (g_cfg.net.psn_status != np_psn_status::psn_rpcn)
|
|
return;
|
|
|
|
while (thread_ctrl::state() != thread_state::aborting && !Emu.IsStopped())
|
|
{
|
|
bool sleep = true;
|
|
if (rpcn)
|
|
{
|
|
std::lock_guard lock(mutex_rpcn);
|
|
if (!rpcn)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
auto replies = rpcn->get_replies();
|
|
for (auto& reply : replies)
|
|
{
|
|
const u16 command = reply.second.first;
|
|
const u32 req_id = reply.first;
|
|
std::vector<u8>& data = reply.second.second;
|
|
|
|
// Every reply should at least contain a return value/error code
|
|
ensure(data.size() >= 1);
|
|
|
|
switch (command)
|
|
{
|
|
case rpcn::CommandType::GetWorldList: reply_get_world_list(req_id, data); break;
|
|
case rpcn::CommandType::CreateRoom: reply_create_join_room(req_id, data); break;
|
|
case rpcn::CommandType::JoinRoom: reply_join_room(req_id, data); break;
|
|
case rpcn::CommandType::LeaveRoom: reply_leave_room(req_id, data); break;
|
|
case rpcn::CommandType::SearchRoom: reply_search_room(req_id, data); break;
|
|
case rpcn::CommandType::GetRoomDataExternalList: reply_get_roomdata_external_list(req_id, data); break;
|
|
case rpcn::CommandType::SetRoomDataExternal: reply_set_roomdata_external(req_id, data); break;
|
|
case rpcn::CommandType::GetRoomDataInternal: reply_get_roomdata_internal(req_id, data); break;
|
|
case rpcn::CommandType::SetRoomDataInternal: reply_set_roomdata_internal(req_id, data); break;
|
|
case rpcn::CommandType::SetRoomMemberDataInternal: reply_set_roommemberdata_internal(req_id, data); break;
|
|
case rpcn::CommandType::PingRoomOwner: reply_get_ping_info(req_id, data); break;
|
|
case rpcn::CommandType::SendRoomMessage: reply_send_room_message(req_id, data); break;
|
|
case rpcn::CommandType::RequestSignalingInfos: reply_req_sign_infos(req_id, data); break;
|
|
case rpcn::CommandType::RequestTicket: reply_req_ticket(req_id, data); break;
|
|
case rpcn::CommandType::GetBoardInfos: reply_get_board_infos(req_id, data); break;
|
|
case rpcn::CommandType::RecordScore: reply_record_score(req_id, data); break;
|
|
case rpcn::CommandType::RecordScoreData: reply_record_score_data(req_id, data); break;
|
|
case rpcn::CommandType::GetScoreData: reply_get_score_data(req_id, data); break;
|
|
case rpcn::CommandType::GetScoreRange: reply_get_score_range(req_id, data); break;
|
|
case rpcn::CommandType::GetScoreFriends: reply_get_score_friends(req_id, data); break;
|
|
case rpcn::CommandType::GetScoreNpid: reply_get_score_npid(req_id, data); break;
|
|
default: rpcn_log.error("Unknown reply(%d) received!", command); break;
|
|
}
|
|
}
|
|
|
|
auto notifications = rpcn->get_notifications();
|
|
for (auto& notif : notifications)
|
|
{
|
|
switch (notif.first)
|
|
{
|
|
case rpcn::NotificationType::UserJoinedRoom: notif_user_joined_room(notif.second); break;
|
|
case rpcn::NotificationType::UserLeftRoom: notif_user_left_room(notif.second); break;
|
|
case rpcn::NotificationType::RoomDestroyed: notif_room_destroyed(notif.second); break;
|
|
case rpcn::NotificationType::UpdatedRoomDataInternal: notif_updated_room_data_internal(notif.second); break;
|
|
case rpcn::NotificationType::UpdatedRoomMemberDataInternal: notif_updated_room_member_data_internal(notif.second); break;
|
|
case rpcn::NotificationType::SignalP2PConnect: notif_p2p_connect(notif.second); break;
|
|
case rpcn::NotificationType::RoomMessageReceived: notif_room_message_received(notif.second); break;
|
|
default: rpcn_log.error("Unknown notification(%d) received!", notif.first); break;
|
|
}
|
|
}
|
|
|
|
auto messages = rpcn->get_new_messages();
|
|
if (basic_handler.registered)
|
|
{
|
|
for (const auto msg_id : messages)
|
|
{
|
|
const auto opt_msg = rpcn->get_message(msg_id);
|
|
if (!opt_msg)
|
|
{
|
|
continue;
|
|
}
|
|
const auto& msg = opt_msg.value();
|
|
if (strncmp(msg->second.commId.data, basic_handler.context.data, sizeof(basic_handler.context.data) - 1) == 0)
|
|
{
|
|
u32 event;
|
|
switch (msg->second.mainType)
|
|
{
|
|
case SCE_NP_BASIC_MESSAGE_MAIN_TYPE_DATA_ATTACHMENT:
|
|
event = SCE_NP_BASIC_EVENT_INCOMING_ATTACHMENT;
|
|
break;
|
|
case SCE_NP_BASIC_MESSAGE_MAIN_TYPE_INVITE:
|
|
event = (msg->second.msgFeatures & SCE_NP_BASIC_MESSAGE_FEATURES_BOOTABLE) ? SCE_NP_BASIC_EVENT_INCOMING_BOOTABLE_INVITATION : SCE_NP_BASIC_EVENT_INCOMING_INVITATION;
|
|
break;
|
|
case SCE_NP_BASIC_MESSAGE_MAIN_TYPE_CUSTOM_DATA:
|
|
event = (msg->second.msgFeatures & SCE_NP_BASIC_MESSAGE_FEATURES_BOOTABLE) ? SCE_NP_BASIC_EVENT_INCOMING_BOOTABLE_CUSTOM_DATA_MESSAGE : SCE_NP_BASIC_EVENT_INCOMING_CUSTOM_DATA_MESSAGE;
|
|
break;
|
|
case SCE_NP_BASIC_MESSAGE_MAIN_TYPE_GENERAL:
|
|
case SCE_NP_BASIC_MESSAGE_MAIN_TYPE_ADD_FRIEND:
|
|
case SCE_NP_BASIC_MESSAGE_MAIN_TYPE_URL_ATTACHMENT:
|
|
event = SCE_NP_BASIC_EVENT_MESSAGE;
|
|
default:
|
|
continue;
|
|
}
|
|
|
|
basic_event to_add{};
|
|
to_add.event = event;
|
|
strcpy_trunc(to_add.from.userId.handle.data, msg->first);
|
|
strcpy_trunc(to_add.from.name.data, msg->first);
|
|
|
|
queue_basic_event(std::move(to_add));
|
|
send_basic_event(event, 0, 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!replies.empty() || !notifications.empty())
|
|
{
|
|
sleep = false;
|
|
}
|
|
}
|
|
|
|
// TODO: replace with an appropriate semaphore
|
|
if (sleep)
|
|
{
|
|
thread_ctrl::wait_for(200'000);
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool np_handler::error_and_disconnect(const std::string& error_msg)
|
|
{
|
|
rpcn_log.error("%s", error_msg);
|
|
rpcn.reset();
|
|
|
|
return false;
|
|
}
|
|
|
|
u32 np_handler::generate_callback_info(SceNpMatching2ContextId ctx_id, vm::cptr<SceNpMatching2RequestOptParam> optParam, SceNpMatching2Event event_type)
|
|
{
|
|
callback_info ret;
|
|
|
|
const auto ctx = get_match2_context(ctx_id);
|
|
ensure(ctx);
|
|
|
|
const u32 req_id = get_req_id(optParam ? optParam->appReqId : ctx->default_match2_optparam.appReqId);
|
|
|
|
ret.ctx_id = ctx_id;
|
|
ret.cb_arg = (optParam && optParam->cbFuncArg) ? optParam->cbFuncArg : ctx->default_match2_optparam.cbFuncArg;
|
|
ret.cb = (optParam && optParam->cbFunc) ? optParam->cbFunc : ctx->default_match2_optparam.cbFunc;
|
|
ret.event_type = event_type;
|
|
|
|
nph_log.warning("Callback used is 0x%x", ret.cb);
|
|
|
|
{
|
|
std::lock_guard lock(mutex_pending_requests);
|
|
pending_requests[req_id] = std::move(ret);
|
|
}
|
|
|
|
return req_id;
|
|
}
|
|
|
|
std::optional<np_handler::callback_info> np_handler::take_pending_request(u32 req_id)
|
|
{
|
|
std::lock_guard lock(mutex_pending_requests);
|
|
|
|
if (!pending_requests.contains(req_id))
|
|
return std::nullopt;
|
|
|
|
const auto cb_info = std::move(::at32(pending_requests, req_id));
|
|
pending_requests.erase(req_id);
|
|
|
|
return cb_info;
|
|
}
|
|
|
|
bool np_handler::abort_request(u32 req_id)
|
|
{
|
|
auto cb_info_opt = take_pending_request(req_id);
|
|
|
|
if (!cb_info_opt)
|
|
return false;
|
|
|
|
cb_info_opt->queue_callback(req_id, 0, SCE_NP_MATCHING2_ERROR_ABORTED, 0);
|
|
|
|
return true;
|
|
}
|
|
|
|
event_data& np_handler::allocate_req_result(u32 event_key, u32 max_size, u32 initial_size)
|
|
{
|
|
std::lock_guard lock(mutex_match2_req_results);
|
|
match2_req_results.emplace(std::piecewise_construct, std::forward_as_tuple(event_key), std::forward_as_tuple(np_memory.allocate(max_size), initial_size, max_size));
|
|
return ::at32(match2_req_results, event_key);
|
|
}
|
|
|
|
u32 np_handler::add_players_to_history(vm::cptr<SceNpId> /*npids*/, u32 /*count*/)
|
|
{
|
|
const u32 req_id = get_req_id(0);
|
|
|
|
if (basic_handler.handler_func)
|
|
{
|
|
sysutil_register_cb([req_id, cb = basic_handler.handler_func, cb_arg = basic_handler.handler_arg](ppu_thread& cb_ppu) -> s32
|
|
{
|
|
cb(cb_ppu, SCE_NP_BASIC_EVENT_ADD_PLAYERS_HISTORY_RESULT, 0, req_id, cb_arg);
|
|
return 0;
|
|
});
|
|
}
|
|
|
|
return req_id;
|
|
}
|
|
|
|
u32 np_handler::get_num_friends()
|
|
{
|
|
return rpcn->get_num_friends();
|
|
}
|
|
|
|
u32 np_handler::get_num_blocks()
|
|
{
|
|
return rpcn->get_num_blocks();
|
|
}
|
|
|
|
std::pair<error_code, std::optional<SceNpId>> np_handler::get_friend_by_index(u32 index)
|
|
{
|
|
auto str_friend = rpcn->get_friend_by_index(index);
|
|
|
|
if (!str_friend)
|
|
{
|
|
return {SCE_NP_ERROR_ID_NOT_FOUND, {}};
|
|
}
|
|
|
|
SceNpId npid_friend;
|
|
string_to_npid(str_friend.value(), npid_friend);
|
|
|
|
return {CELL_OK, npid_friend};
|
|
}
|
|
|
|
std::pair<error_code, std::optional<SceNpId>> np_handler::local_get_npid(u64 room_id, u16 member_id)
|
|
{
|
|
return np_cache.get_npid(room_id, member_id);
|
|
}
|
|
|
|
std::pair<error_code, std::optional<SceNpMatching2SessionPassword>> np_handler::local_get_room_password(SceNpMatching2RoomId room_id)
|
|
{
|
|
return np_cache.get_password(room_id);
|
|
}
|
|
|
|
std::pair<error_code, std::optional<SceNpMatching2RoomSlotInfo>> np_handler::local_get_room_slots(SceNpMatching2RoomId room_id)
|
|
{
|
|
return np_cache.get_slots(room_id);
|
|
}
|
|
|
|
std::pair<error_code, std::vector<SceNpMatching2RoomMemberId>> np_handler::local_get_room_memberids(SceNpMatching2RoomId room_id, s32 sort_method)
|
|
{
|
|
return np_cache.get_memberids(room_id, sort_method);
|
|
}
|
|
|
|
error_code np_handler::local_get_room_member_data(SceNpMatching2RoomId room_id, SceNpMatching2RoomMemberId member_id, const std::vector<SceNpMatching2AttributeId>& binattrs_list, SceNpMatching2RoomMemberDataInternal* ptr_member, u32 addr_data, u32 size_data)
|
|
{
|
|
return np_cache.get_member_and_attrs(room_id, member_id, binattrs_list, ptr_member, addr_data, size_data);
|
|
}
|
|
|
|
void np_handler::upnp_add_port_mapping(u16 internal_port, std::string_view protocol)
|
|
{
|
|
upnp.add_port_redir(np::ip_to_string(get_local_ip_addr()), internal_port, protocol);
|
|
}
|
|
|
|
void np_handler::upnp_remove_port_mapping(u16 internal_port, std::string_view protocol)
|
|
{
|
|
upnp.remove_port_redir(internal_port, protocol);
|
|
}
|
|
} // namespace np
|