mirror of
https://github.com/RPCSX/rpcsx.git
synced 2026-04-20 22:05:12 +00:00
Move rpcs3/Emu/Cell/lv2 to kernel/cellos
This commit is contained in:
parent
fce4127c2e
commit
dbfa5002e5
282 changed files with 40062 additions and 41342 deletions
157
kernel/cellos/src/sys_net/lv2_socket.cpp
Normal file
157
kernel/cellos/src/sys_net/lv2_socket.cpp
Normal file
|
|
@ -0,0 +1,157 @@
|
|||
#include "stdafx.h"
|
||||
|
||||
#include "sys_net/lv2_socket.h"
|
||||
#include "sys_net/network_context.h"
|
||||
|
||||
LOG_CHANNEL(sys_net);
|
||||
|
||||
lv2_socket::lv2_socket(lv2_socket_family family, lv2_socket_type type,
|
||||
lv2_ip_protocol protocol) {
|
||||
this->family = family;
|
||||
this->type = type;
|
||||
this->protocol = protocol;
|
||||
}
|
||||
|
||||
std::unique_lock<shared_mutex> lv2_socket::lock() {
|
||||
return std::unique_lock(mutex);
|
||||
}
|
||||
|
||||
lv2_socket_family lv2_socket::get_family() const { return family; }
|
||||
|
||||
lv2_socket_type lv2_socket::get_type() const { return type; }
|
||||
lv2_ip_protocol lv2_socket::get_protocol() const { return protocol; }
|
||||
std::size_t lv2_socket::get_queue_size() const { return queue.size(); }
|
||||
socket_type lv2_socket::get_socket() const { return native_socket; }
|
||||
|
||||
#ifdef _WIN32
|
||||
bool lv2_socket::is_connecting() const { return connecting; }
|
||||
void lv2_socket::set_connecting(bool connecting) {
|
||||
this->connecting = connecting;
|
||||
}
|
||||
#endif
|
||||
|
||||
void lv2_socket::set_lv2_id(u32 id) { lv2_id = id; }
|
||||
|
||||
bs_t<lv2_socket::poll_t> lv2_socket::get_events() const {
|
||||
return events.load();
|
||||
}
|
||||
|
||||
void lv2_socket::set_poll_event(bs_t<lv2_socket::poll_t> event) {
|
||||
events += event;
|
||||
}
|
||||
|
||||
void lv2_socket::poll_queue(
|
||||
shared_ptr<ppu_thread> ppu, bs_t<lv2_socket::poll_t> event,
|
||||
std::function<bool(bs_t<lv2_socket::poll_t>)> poll_cb) {
|
||||
set_poll_event(event);
|
||||
queue.emplace_back(std::move(ppu), poll_cb);
|
||||
|
||||
// Makes sure network_context thread is awaken
|
||||
if (type == SYS_NET_SOCK_STREAM || type == SYS_NET_SOCK_DGRAM) {
|
||||
auto &nc = g_fxo->get<network_context>();
|
||||
const u32 prev_value = nc.num_polls.fetch_add(1);
|
||||
if (!prev_value) {
|
||||
nc.num_polls.notify_one();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
u32 lv2_socket::clear_queue(ppu_thread *ppu) {
|
||||
std::lock_guard lock(mutex);
|
||||
|
||||
u32 cleared = 0;
|
||||
|
||||
for (auto it = queue.begin(); it != queue.end();) {
|
||||
if (it->first.get() == ppu) {
|
||||
it = queue.erase(it);
|
||||
cleared++;
|
||||
continue;
|
||||
}
|
||||
|
||||
it++;
|
||||
}
|
||||
|
||||
if (queue.empty()) {
|
||||
events.store({});
|
||||
}
|
||||
|
||||
if (cleared && (type == SYS_NET_SOCK_STREAM || type == SYS_NET_SOCK_DGRAM)) {
|
||||
// Makes sure network_context thread can go back to sleep if there is no
|
||||
// active polling
|
||||
const u32 prev_value =
|
||||
g_fxo->get<network_context>().num_polls.fetch_sub(cleared);
|
||||
ensure(prev_value >= cleared);
|
||||
}
|
||||
|
||||
return cleared;
|
||||
}
|
||||
|
||||
void lv2_socket::handle_events(const pollfd &native_pfd,
|
||||
[[maybe_unused]] bool unset_connecting) {
|
||||
bs_t<lv2_socket::poll_t> events_happening{};
|
||||
|
||||
if (native_pfd.revents & (POLLIN | POLLHUP) &&
|
||||
events.test_and_reset(lv2_socket::poll_t::read))
|
||||
events_happening += lv2_socket::poll_t::read;
|
||||
if (native_pfd.revents & POLLOUT &&
|
||||
events.test_and_reset(lv2_socket::poll_t::write))
|
||||
events_happening += lv2_socket::poll_t::write;
|
||||
if (native_pfd.revents & POLLERR &&
|
||||
events.test_and_reset(lv2_socket::poll_t::error))
|
||||
events_happening += lv2_socket::poll_t::error;
|
||||
|
||||
if (events_happening || (!queue.empty() && (so_rcvtimeo || so_sendtimeo))) {
|
||||
std::lock_guard lock(mutex);
|
||||
#ifdef _WIN32
|
||||
if (unset_connecting)
|
||||
set_connecting(false);
|
||||
#endif
|
||||
u32 handled = 0;
|
||||
|
||||
for (auto it = queue.begin(); it != queue.end();) {
|
||||
if (it->second(events_happening)) {
|
||||
it = queue.erase(it);
|
||||
handled++;
|
||||
continue;
|
||||
}
|
||||
|
||||
it++;
|
||||
}
|
||||
|
||||
if (handled &&
|
||||
(type == SYS_NET_SOCK_STREAM || type == SYS_NET_SOCK_DGRAM)) {
|
||||
const u32 prev_value =
|
||||
g_fxo->get<network_context>().num_polls.fetch_sub(handled);
|
||||
ensure(prev_value >= handled);
|
||||
}
|
||||
|
||||
if (queue.empty()) {
|
||||
events.store({});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void lv2_socket::queue_wake(ppu_thread *ppu) {
|
||||
switch (type) {
|
||||
case SYS_NET_SOCK_STREAM:
|
||||
case SYS_NET_SOCK_DGRAM:
|
||||
g_fxo->get<network_context>().add_ppu_to_awake(ppu);
|
||||
break;
|
||||
case SYS_NET_SOCK_DGRAM_P2P:
|
||||
case SYS_NET_SOCK_STREAM_P2P:
|
||||
g_fxo->get<p2p_context>().add_ppu_to_awake(ppu);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
lv2_socket &lv2_socket::operator=(thread_state s) noexcept {
|
||||
if (s == thread_state::destroying_context) {
|
||||
close();
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
lv2_socket::~lv2_socket() noexcept {}
|
||||
1182
kernel/cellos/src/sys_net/lv2_socket_native.cpp
Normal file
1182
kernel/cellos/src/sys_net/lv2_socket_native.cpp
Normal file
File diff suppressed because it is too large
Load diff
400
kernel/cellos/src/sys_net/lv2_socket_p2p.cpp
Normal file
400
kernel/cellos/src/sys_net/lv2_socket_p2p.cpp
Normal file
|
|
@ -0,0 +1,400 @@
|
|||
#include "stdafx.h"
|
||||
|
||||
#include "Emu/NP/np_helpers.h"
|
||||
#include "sys_net/lv2_socket_p2p.h"
|
||||
#include "sys_net/network_context.h"
|
||||
#include "sys_net/sys_net_helpers.h"
|
||||
|
||||
LOG_CHANNEL(sys_net);
|
||||
|
||||
lv2_socket_p2p::lv2_socket_p2p(lv2_socket_family family, lv2_socket_type type,
|
||||
lv2_ip_protocol protocol)
|
||||
: lv2_socket(family, type, protocol) {
|
||||
sockopt_cache cache_type;
|
||||
cache_type.data._int = SYS_NET_SOCK_DGRAM_P2P;
|
||||
cache_type.len = 4;
|
||||
|
||||
sockopts[(static_cast<u64>(SYS_NET_SOL_SOCKET) << 32ull) | SYS_NET_SO_TYPE] =
|
||||
cache_type;
|
||||
}
|
||||
|
||||
lv2_socket_p2p::lv2_socket_p2p(utils::serial &ar, lv2_socket_type type)
|
||||
: lv2_socket(make_exact(ar), type) {
|
||||
ar(port, vport, bound_addr);
|
||||
|
||||
auto data_dequeue =
|
||||
ar.pop<std::deque<std::pair<sys_net_sockaddr_in_p2p, std::vector<u8>>>>();
|
||||
|
||||
for (; !data_dequeue.empty(); data_dequeue.pop_front()) {
|
||||
data.push(std::move(data_dequeue.front()));
|
||||
}
|
||||
}
|
||||
|
||||
void lv2_socket_p2p::save(utils::serial &ar) {
|
||||
lv2_socket::save(ar, true);
|
||||
ar(port, vport, bound_addr);
|
||||
|
||||
std::deque<std::pair<sys_net_sockaddr_in_p2p, std::vector<u8>>> data_dequeue;
|
||||
|
||||
for (auto save_data = ::as_rvalue(data); !save_data.empty();
|
||||
save_data.pop()) {
|
||||
data_dequeue.push_back(std::move(save_data.front()));
|
||||
}
|
||||
|
||||
ar(data_dequeue);
|
||||
}
|
||||
|
||||
void lv2_socket_p2p::handle_new_data(sys_net_sockaddr_in_p2p p2p_addr,
|
||||
std::vector<u8> p2p_data) {
|
||||
std::lock_guard lock(mutex);
|
||||
|
||||
sys_net.trace("Received a P2P packet for vport %d and saved it",
|
||||
p2p_addr.sin_vport);
|
||||
data.push(std::make_pair(std::move(p2p_addr), std::move(p2p_data)));
|
||||
|
||||
// Check if poll is happening
|
||||
if (events.test_and_reset(lv2_socket::poll_t::read)) {
|
||||
bs_t<lv2_socket::poll_t> read_event = lv2_socket::poll_t::read;
|
||||
for (auto it = queue.begin(); it != queue.end();) {
|
||||
if (it->second(read_event)) {
|
||||
it = queue.erase(it);
|
||||
continue;
|
||||
}
|
||||
it++;
|
||||
}
|
||||
|
||||
if (queue.empty()) {
|
||||
events.store({});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::tuple<bool, s32, shared_ptr<lv2_socket>, sys_net_sockaddr>
|
||||
lv2_socket_p2p::accept([[maybe_unused]] bool is_lock) {
|
||||
sys_net.fatal("[P2P] accept() called on a P2P socket");
|
||||
return {};
|
||||
}
|
||||
|
||||
std::optional<s32>
|
||||
lv2_socket_p2p::connect([[maybe_unused]] const sys_net_sockaddr &addr) {
|
||||
sys_net.fatal("[P2P] connect() called on a P2P socket");
|
||||
return {};
|
||||
}
|
||||
|
||||
s32 lv2_socket_p2p::connect_followup() {
|
||||
sys_net.fatal("[P2P] connect_followup() called on a P2P socket");
|
||||
return {};
|
||||
}
|
||||
|
||||
std::pair<s32, sys_net_sockaddr> lv2_socket_p2p::getpeername() {
|
||||
sys_net.fatal("[P2P] getpeername() called on a P2P socket");
|
||||
return {};
|
||||
}
|
||||
|
||||
s32 lv2_socket_p2p::listen([[maybe_unused]] s32 backlog) {
|
||||
sys_net.fatal("[P2P] listen() called on a P2P socket");
|
||||
return {};
|
||||
}
|
||||
|
||||
s32 lv2_socket_p2p::bind(const sys_net_sockaddr &addr) {
|
||||
const auto *psa_in_p2p =
|
||||
reinterpret_cast<const sys_net_sockaddr_in_p2p *>(&addr);
|
||||
u16 p2p_port = psa_in_p2p->sin_port;
|
||||
u16 p2p_vport = psa_in_p2p->sin_vport;
|
||||
|
||||
sys_net.notice("[P2P] Trying to bind %s:%d:%d",
|
||||
np::ip_to_string(std::bit_cast<u32>(psa_in_p2p->sin_addr)),
|
||||
p2p_port, p2p_vport);
|
||||
|
||||
if (p2p_port != SCE_NP_PORT) {
|
||||
if (p2p_port == 0) {
|
||||
return -SYS_NET_EINVAL;
|
||||
}
|
||||
sys_net.warning("[P2P] Attempting to bind a socket to a port != %d",
|
||||
+SCE_NP_PORT);
|
||||
}
|
||||
|
||||
socket_type real_socket{};
|
||||
|
||||
auto &nc = g_fxo->get<p2p_context>();
|
||||
{
|
||||
std::lock_guard list_lock(nc.list_p2p_ports_mutex);
|
||||
|
||||
nc.create_p2p_port(p2p_port);
|
||||
auto &pport = ::at32(nc.list_p2p_ports, p2p_port);
|
||||
real_socket = pport.p2p_socket;
|
||||
|
||||
{
|
||||
std::lock_guard lock(pport.bound_p2p_vports_mutex);
|
||||
|
||||
if (p2p_vport == 0) {
|
||||
// Find a free vport starting at 30000
|
||||
p2p_vport = 30000;
|
||||
while (pport.bound_p2p_vports.contains(p2p_vport)) {
|
||||
p2p_vport++;
|
||||
}
|
||||
}
|
||||
|
||||
if (pport.bound_p2p_vports.contains(p2p_vport)) {
|
||||
// Check that all other sockets are SO_REUSEADDR or SO_REUSEPORT
|
||||
auto &bound_sockets = ::at32(pport.bound_p2p_vports, p2p_vport);
|
||||
if (!sys_net_helpers::all_reusable(bound_sockets)) {
|
||||
return -SYS_NET_EADDRINUSE;
|
||||
}
|
||||
|
||||
bound_sockets.insert(lv2_id);
|
||||
} else {
|
||||
std::set<s32> bound_ports{lv2_id};
|
||||
pport.bound_p2p_vports.insert(
|
||||
std::make_pair(p2p_vport, std::move(bound_ports)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
std::lock_guard lock(mutex);
|
||||
port = p2p_port;
|
||||
vport = p2p_vport;
|
||||
native_socket = real_socket;
|
||||
bound_addr = psa_in_p2p->sin_addr;
|
||||
}
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
std::pair<s32, sys_net_sockaddr> lv2_socket_p2p::getsockname() {
|
||||
std::lock_guard lock(mutex);
|
||||
|
||||
// Unbound socket
|
||||
if (!native_socket) {
|
||||
return {CELL_OK, {}};
|
||||
}
|
||||
|
||||
sys_net_sockaddr sn_addr{};
|
||||
sys_net_sockaddr_in_p2p *paddr =
|
||||
reinterpret_cast<sys_net_sockaddr_in_p2p *>(&sn_addr);
|
||||
|
||||
paddr->sin_len = sizeof(sys_net_sockaddr_in);
|
||||
paddr->sin_family = SYS_NET_AF_INET;
|
||||
paddr->sin_port = port;
|
||||
paddr->sin_vport = vport;
|
||||
paddr->sin_addr = bound_addr;
|
||||
|
||||
return {CELL_OK, sn_addr};
|
||||
}
|
||||
|
||||
std::tuple<s32, lv2_socket::sockopt_data, u32>
|
||||
lv2_socket_p2p::getsockopt(s32 level, s32 optname, u32 len) {
|
||||
std::lock_guard lock(mutex);
|
||||
|
||||
const u64 key = (static_cast<u64>(level) << 32) | static_cast<u64>(optname);
|
||||
|
||||
if (!sockopts.contains(key)) {
|
||||
sys_net.error("Unhandled getsockopt(level=%d, optname=%d, len=%d)", level,
|
||||
optname, len);
|
||||
return {};
|
||||
}
|
||||
|
||||
const auto &cache = ::at32(sockopts, key);
|
||||
return {CELL_OK, cache.data, cache.len};
|
||||
}
|
||||
|
||||
s32 lv2_socket_p2p::setsockopt(s32 level, s32 optname,
|
||||
const std::vector<u8> &optval) {
|
||||
std::lock_guard lock(mutex);
|
||||
|
||||
int native_int = *reinterpret_cast<const be_t<s32> *>(optval.data());
|
||||
|
||||
if (level == SYS_NET_SOL_SOCKET && optname == SYS_NET_SO_NBIO) {
|
||||
so_nbio = native_int;
|
||||
}
|
||||
|
||||
const u64 key = (static_cast<u64>(level) << 32) | static_cast<u64>(optname);
|
||||
sockopt_cache cache{};
|
||||
memcpy(&cache.data._int, optval.data(), optval.size());
|
||||
cache.len = ::size32(optval);
|
||||
|
||||
sockopts[key] = std::move(cache);
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
std::optional<std::tuple<s32, std::vector<u8>, sys_net_sockaddr>>
|
||||
lv2_socket_p2p::recvfrom(s32 flags, u32 len, bool is_lock) {
|
||||
std::unique_lock<shared_mutex> lock(mutex, std::defer_lock);
|
||||
|
||||
if (is_lock) {
|
||||
lock.lock();
|
||||
}
|
||||
|
||||
if (data.empty()) {
|
||||
if (so_nbio || (flags & SYS_NET_MSG_DONTWAIT))
|
||||
return {{-SYS_NET_EWOULDBLOCK, {}, {}}};
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
sys_net.trace("[P2P] p2p_data for vport %d contains %d elements", vport,
|
||||
data.size());
|
||||
|
||||
std::vector<u8> res_buf(len);
|
||||
|
||||
const auto &p2p_data = data.front();
|
||||
s32 native_result = std::min(len, static_cast<u32>(p2p_data.second.size()));
|
||||
memcpy(res_buf.data(), p2p_data.second.data(), native_result);
|
||||
|
||||
sys_net_sockaddr sn_addr;
|
||||
memcpy(&sn_addr, &p2p_data.first, sizeof(sn_addr));
|
||||
|
||||
data.pop();
|
||||
|
||||
return {{native_result, res_buf, sn_addr}};
|
||||
}
|
||||
|
||||
std::optional<s32>
|
||||
lv2_socket_p2p::sendto(s32 flags, const std::vector<u8> &buf,
|
||||
std::optional<sys_net_sockaddr> opt_sn_addr,
|
||||
bool is_lock) {
|
||||
std::unique_lock<shared_mutex> lock(mutex, std::defer_lock);
|
||||
|
||||
if (is_lock) {
|
||||
lock.lock();
|
||||
}
|
||||
|
||||
ensure(opt_sn_addr);
|
||||
ensure(socket); // ensures it has been bound
|
||||
ensure(
|
||||
buf.size() <=
|
||||
static_cast<usz>(
|
||||
65535 -
|
||||
VPORT_P2P_HEADER_SIZE)); // catch games using full payload for future
|
||||
// fragmentation implementation if necessary
|
||||
const u16 p2p_port =
|
||||
reinterpret_cast<const sys_net_sockaddr_in *>(&*opt_sn_addr)->sin_port;
|
||||
const u16 p2p_vport =
|
||||
reinterpret_cast<const sys_net_sockaddr_in_p2p *>(&*opt_sn_addr)
|
||||
->sin_vport;
|
||||
|
||||
auto native_addr = sys_net_addr_to_native_addr(*opt_sn_addr);
|
||||
|
||||
char ip_str[16];
|
||||
inet_ntop(AF_INET, &native_addr.sin_addr, ip_str, sizeof(ip_str));
|
||||
sys_net.trace("[P2P] Sending a packet to %s:%d:%d", ip_str, p2p_port,
|
||||
p2p_vport);
|
||||
|
||||
std::vector<u8> p2p_data(buf.size() + VPORT_P2P_HEADER_SIZE);
|
||||
const le_t<u16> p2p_vport_le = p2p_vport;
|
||||
const le_t<u16> src_vport_le = vport;
|
||||
const le_t<u16> p2p_flags_le = P2P_FLAG_P2P;
|
||||
memcpy(p2p_data.data(), &p2p_vport_le, sizeof(u16));
|
||||
memcpy(p2p_data.data() + sizeof(u16), &src_vport_le, sizeof(u16));
|
||||
memcpy(p2p_data.data() + sizeof(u16) + sizeof(u16), &p2p_flags_le,
|
||||
sizeof(u16));
|
||||
memcpy(p2p_data.data() + VPORT_P2P_HEADER_SIZE, buf.data(), buf.size());
|
||||
|
||||
int native_flags = 0;
|
||||
if (flags & SYS_NET_MSG_WAITALL) {
|
||||
native_flags |= MSG_WAITALL;
|
||||
}
|
||||
|
||||
auto native_result = np::sendto_possibly_ipv6(
|
||||
native_socket, reinterpret_cast<const char *>(p2p_data.data()),
|
||||
::size32(p2p_data), &native_addr, native_flags);
|
||||
|
||||
if (native_result >= 0) {
|
||||
return {std::max<s32>(native_result - VPORT_P2P_HEADER_SIZE, 0l)};
|
||||
}
|
||||
|
||||
s32 result = get_last_error(!so_nbio && (flags & SYS_NET_MSG_DONTWAIT) == 0);
|
||||
|
||||
if (result) {
|
||||
return {-result};
|
||||
}
|
||||
|
||||
// Note that this can only happen if the send buffer is full
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<s32>
|
||||
lv2_socket_p2p::sendmsg([[maybe_unused]] s32 flags,
|
||||
[[maybe_unused]] const sys_net_msghdr &msg,
|
||||
[[maybe_unused]] bool is_lock) {
|
||||
sys_net.todo("lv2_socket_p2p::sendmsg");
|
||||
return {};
|
||||
}
|
||||
|
||||
void lv2_socket_p2p::close() {
|
||||
if (!port || !vport) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (g_fxo->is_init<p2p_context>()) {
|
||||
auto &nc = g_fxo->get<p2p_context>();
|
||||
std::lock_guard lock(nc.list_p2p_ports_mutex);
|
||||
|
||||
if (!nc.list_p2p_ports.contains(port))
|
||||
return;
|
||||
|
||||
auto &p2p_port = ::at32(nc.list_p2p_ports, port);
|
||||
{
|
||||
std::lock_guard lock(p2p_port.bound_p2p_vports_mutex);
|
||||
if (!p2p_port.bound_p2p_vports.contains(vport)) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto &bound_sockets = ::at32(p2p_port.bound_p2p_vports, vport);
|
||||
bound_sockets.erase(lv2_id);
|
||||
|
||||
if (bound_sockets.empty()) {
|
||||
p2p_port.bound_p2p_vports.erase(vport);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
s32 lv2_socket_p2p::shutdown([[maybe_unused]] s32 how) {
|
||||
sys_net.todo("[P2P] shutdown");
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 lv2_socket_p2p::poll(sys_net_pollfd &sn_pfd,
|
||||
[[maybe_unused]] pollfd &native_pfd) {
|
||||
std::lock_guard lock(mutex);
|
||||
ensure(vport);
|
||||
|
||||
// Check if it's a bound P2P socket
|
||||
if ((sn_pfd.events & SYS_NET_POLLIN) && !data.empty()) {
|
||||
sys_net.trace("[P2P] p2p_data for vport %d contains %d elements", vport,
|
||||
data.size());
|
||||
sn_pfd.revents |= SYS_NET_POLLIN;
|
||||
}
|
||||
|
||||
// Data can always be written on a dgram socket
|
||||
if (sn_pfd.events & SYS_NET_POLLOUT) {
|
||||
sn_pfd.revents |= SYS_NET_POLLOUT;
|
||||
}
|
||||
|
||||
return sn_pfd.revents ? 1 : 0;
|
||||
}
|
||||
|
||||
std::tuple<bool, bool, bool>
|
||||
lv2_socket_p2p::select(bs_t<lv2_socket::poll_t> selected,
|
||||
[[maybe_unused]] pollfd &native_pfd) {
|
||||
std::lock_guard lock(mutex);
|
||||
|
||||
bool read_set = false;
|
||||
bool write_set = false;
|
||||
|
||||
// Check if it's a bound P2P socket
|
||||
if ((selected & lv2_socket::poll_t::read) && vport && !data.empty()) {
|
||||
sys_net.trace("[P2P] p2p_data for vport %d contains %d elements", vport,
|
||||
data.size());
|
||||
read_set = true;
|
||||
}
|
||||
|
||||
if (selected & lv2_socket::poll_t::write) {
|
||||
write_set = true;
|
||||
}
|
||||
|
||||
return {read_set, write_set, false};
|
||||
}
|
||||
1012
kernel/cellos/src/sys_net/lv2_socket_p2ps.cpp
Normal file
1012
kernel/cellos/src/sys_net/lv2_socket_p2ps.cpp
Normal file
File diff suppressed because it is too large
Load diff
137
kernel/cellos/src/sys_net/lv2_socket_raw.cpp
Normal file
137
kernel/cellos/src/sys_net/lv2_socket_raw.cpp
Normal file
|
|
@ -0,0 +1,137 @@
|
|||
#include "stdafx.h"
|
||||
|
||||
#include "Emu/NP/vport0.h"
|
||||
#include "sys_net/lv2_socket_raw.h"
|
||||
|
||||
LOG_CHANNEL(sys_net);
|
||||
|
||||
template <typename T> struct socket_raw_logging {
|
||||
socket_raw_logging() = default;
|
||||
|
||||
socket_raw_logging(const socket_raw_logging &) = delete;
|
||||
socket_raw_logging &operator=(const socket_raw_logging &) = delete;
|
||||
|
||||
atomic_t<bool> logged = false;
|
||||
};
|
||||
|
||||
#define LOG_ONCE(raw_var, message) \
|
||||
if (!g_fxo->get<socket_raw_logging<class raw_var>>().logged.exchange( \
|
||||
true)) { \
|
||||
sys_net.todo(message); \
|
||||
}
|
||||
|
||||
lv2_socket_raw::lv2_socket_raw(lv2_socket_family family, lv2_socket_type type,
|
||||
lv2_ip_protocol protocol)
|
||||
: lv2_socket(family, type, protocol) {}
|
||||
|
||||
lv2_socket_raw::lv2_socket_raw(utils::serial &ar, lv2_socket_type type)
|
||||
: lv2_socket(make_exact(ar), type) {}
|
||||
|
||||
void lv2_socket_raw::save(utils::serial &ar) { lv2_socket::save(ar, true); }
|
||||
|
||||
std::tuple<bool, s32, shared_ptr<lv2_socket>, sys_net_sockaddr>
|
||||
lv2_socket_raw::accept([[maybe_unused]] bool is_lock) {
|
||||
sys_net.fatal("[RAW] accept() called on a RAW socket");
|
||||
return {};
|
||||
}
|
||||
|
||||
std::optional<s32>
|
||||
lv2_socket_raw::connect([[maybe_unused]] const sys_net_sockaddr &addr) {
|
||||
sys_net.fatal("[RAW] connect() called on a RAW socket");
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 lv2_socket_raw::connect_followup() {
|
||||
sys_net.fatal("[RAW] connect_followup() called on a RAW socket");
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
std::pair<s32, sys_net_sockaddr> lv2_socket_raw::getpeername() {
|
||||
LOG_ONCE(raw_getpeername, "[RAW] getpeername() called on a RAW socket");
|
||||
return {};
|
||||
}
|
||||
|
||||
s32 lv2_socket_raw::listen([[maybe_unused]] s32 backlog) {
|
||||
LOG_ONCE(raw_listen, "[RAW] listen() called on a RAW socket");
|
||||
return {};
|
||||
}
|
||||
|
||||
s32 lv2_socket_raw::bind([[maybe_unused]] const sys_net_sockaddr &addr) {
|
||||
LOG_ONCE(raw_bind, "lv2_socket_raw::bind");
|
||||
return {};
|
||||
}
|
||||
|
||||
std::pair<s32, sys_net_sockaddr> lv2_socket_raw::getsockname() {
|
||||
LOG_ONCE(raw_getsockname, "lv2_socket_raw::getsockname");
|
||||
return {};
|
||||
}
|
||||
|
||||
std::tuple<s32, lv2_socket::sockopt_data, u32>
|
||||
lv2_socket_raw::getsockopt([[maybe_unused]] s32 level,
|
||||
[[maybe_unused]] s32 optname,
|
||||
[[maybe_unused]] u32 len) {
|
||||
LOG_ONCE(raw_getsockopt, "lv2_socket_raw::getsockopt");
|
||||
return {};
|
||||
}
|
||||
|
||||
s32 lv2_socket_raw::setsockopt(s32 level, s32 optname,
|
||||
const std::vector<u8> &optval) {
|
||||
LOG_ONCE(raw_setsockopt, "lv2_socket_raw::setsockopt");
|
||||
|
||||
// TODO
|
||||
int native_int = *reinterpret_cast<const be_t<s32> *>(optval.data());
|
||||
|
||||
if (level == SYS_NET_SOL_SOCKET && optname == SYS_NET_SO_NBIO) {
|
||||
so_nbio = native_int;
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
std::optional<std::tuple<s32, std::vector<u8>, sys_net_sockaddr>>
|
||||
lv2_socket_raw::recvfrom(s32 flags, [[maybe_unused]] u32 len,
|
||||
[[maybe_unused]] bool is_lock) {
|
||||
LOG_ONCE(raw_recvfrom, "lv2_socket_raw::recvfrom");
|
||||
|
||||
if (so_nbio || (flags & SYS_NET_MSG_DONTWAIT)) {
|
||||
return {{-SYS_NET_EWOULDBLOCK, {}, {}}};
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
std::optional<s32> lv2_socket_raw::sendto(
|
||||
[[maybe_unused]] s32 flags, [[maybe_unused]] const std::vector<u8> &buf,
|
||||
[[maybe_unused]] std::optional<sys_net_sockaddr> opt_sn_addr,
|
||||
[[maybe_unused]] bool is_lock) {
|
||||
LOG_ONCE(raw_sendto, "lv2_socket_raw::sendto");
|
||||
return ::size32(buf);
|
||||
}
|
||||
|
||||
std::optional<s32>
|
||||
lv2_socket_raw::sendmsg([[maybe_unused]] s32 flags,
|
||||
[[maybe_unused]] const sys_net_msghdr &msg,
|
||||
[[maybe_unused]] bool is_lock) {
|
||||
LOG_ONCE(raw_sendmsg, "lv2_socket_raw::sendmsg");
|
||||
return {};
|
||||
}
|
||||
|
||||
void lv2_socket_raw::close() { LOG_ONCE(raw_close, "lv2_socket_raw::close"); }
|
||||
|
||||
s32 lv2_socket_raw::shutdown([[maybe_unused]] s32 how) {
|
||||
LOG_ONCE(raw_shutdown, "lv2_socket_raw::shutdown");
|
||||
return {};
|
||||
}
|
||||
|
||||
s32 lv2_socket_raw::poll([[maybe_unused]] sys_net_pollfd &sn_pfd,
|
||||
[[maybe_unused]] pollfd &native_pfd) {
|
||||
LOG_ONCE(raw_poll, "lv2_socket_raw::poll");
|
||||
return {};
|
||||
}
|
||||
|
||||
std::tuple<bool, bool, bool>
|
||||
lv2_socket_raw::select([[maybe_unused]] bs_t<lv2_socket::poll_t> selected,
|
||||
[[maybe_unused]] pollfd &native_pfd) {
|
||||
LOG_ONCE(raw_select, "lv2_socket_raw::select");
|
||||
return {};
|
||||
}
|
||||
299
kernel/cellos/src/sys_net/network_context.cpp
Normal file
299
kernel/cellos/src/sys_net/network_context.cpp
Normal file
|
|
@ -0,0 +1,299 @@
|
|||
#include "stdafx.h"
|
||||
|
||||
#include "Emu/NP/ip_address.h"
|
||||
#include "cellos/sys_sync.h"
|
||||
#include "rpcsx/fw/ps3/sceNp.h" // for SCE_NP_PORT
|
||||
|
||||
#include "sys_net/network_context.h"
|
||||
#include "sys_net/sys_net_helpers.h"
|
||||
|
||||
LOG_CHANNEL(sys_net);
|
||||
|
||||
// Used by RPCN to send signaling packets to RPCN server(for UDP hole punching)
|
||||
bool send_packet_from_p2p_port_ipv4(const std::vector<u8> &data,
|
||||
const sockaddr_in &addr) {
|
||||
auto &nc = g_fxo->get<p2p_context>();
|
||||
{
|
||||
std::lock_guard list_lock(nc.list_p2p_ports_mutex);
|
||||
if (nc.list_p2p_ports.contains(SCE_NP_PORT)) {
|
||||
auto &def_port = ::at32(nc.list_p2p_ports, SCE_NP_PORT);
|
||||
|
||||
if (def_port.is_ipv6) {
|
||||
const auto addr6 = np::sockaddr_to_sockaddr6(addr);
|
||||
|
||||
if (::sendto(def_port.p2p_socket,
|
||||
reinterpret_cast<const char *>(data.data()),
|
||||
::size32(data), 0,
|
||||
reinterpret_cast<const sockaddr *>(&addr6),
|
||||
sizeof(sockaddr_in6)) == -1) {
|
||||
sys_net.error(
|
||||
"Failed to send IPv4 signaling packet on IPv6 socket: %s",
|
||||
get_last_error(false, false));
|
||||
return false;
|
||||
}
|
||||
} else if (::sendto(def_port.p2p_socket,
|
||||
reinterpret_cast<const char *>(data.data()),
|
||||
::size32(data), 0,
|
||||
reinterpret_cast<const sockaddr *>(&addr),
|
||||
sizeof(sockaddr_in)) == -1) {
|
||||
sys_net.error("Failed to send signaling packet on IPv4 socket: %s",
|
||||
get_last_error(false, false));
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
sys_net.error("send_packet_from_p2p_port_ipv4: port %d not present",
|
||||
+SCE_NP_PORT);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool send_packet_from_p2p_port_ipv6(const std::vector<u8> &data,
|
||||
const sockaddr_in6 &addr) {
|
||||
auto &nc = g_fxo->get<p2p_context>();
|
||||
{
|
||||
std::lock_guard list_lock(nc.list_p2p_ports_mutex);
|
||||
if (nc.list_p2p_ports.contains(SCE_NP_PORT)) {
|
||||
auto &def_port = ::at32(nc.list_p2p_ports, SCE_NP_PORT);
|
||||
ensure(def_port.is_ipv6);
|
||||
|
||||
if (::sendto(def_port.p2p_socket,
|
||||
reinterpret_cast<const char *>(data.data()), ::size32(data),
|
||||
0, reinterpret_cast<const sockaddr *>(&addr),
|
||||
sizeof(sockaddr_in6)) == -1) {
|
||||
sys_net.error("Failed to send signaling packet on IPv6 socket: %s",
|
||||
get_last_error(false, false));
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
sys_net.error("send_packet_from_p2p_port_ipv6: port %d not present",
|
||||
+SCE_NP_PORT);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::vector<std::vector<u8>> get_rpcn_msgs() {
|
||||
std::vector<std::vector<u8>> msgs;
|
||||
auto &nc = g_fxo->get<p2p_context>();
|
||||
{
|
||||
std::lock_guard list_lock(nc.list_p2p_ports_mutex);
|
||||
if (nc.list_p2p_ports.contains(SCE_NP_PORT)) {
|
||||
auto &def_port = ::at32(nc.list_p2p_ports, SCE_NP_PORT);
|
||||
{
|
||||
std::lock_guard lock(def_port.s_rpcn_mutex);
|
||||
msgs = std::move(def_port.rpcn_msgs);
|
||||
def_port.rpcn_msgs.clear();
|
||||
}
|
||||
} else {
|
||||
sys_net.error("get_rpcn_msgs: port %d not present", +SCE_NP_PORT);
|
||||
}
|
||||
}
|
||||
|
||||
return msgs;
|
||||
}
|
||||
|
||||
std::vector<signaling_message> get_sign_msgs() {
|
||||
std::vector<signaling_message> msgs;
|
||||
auto &nc = g_fxo->get<p2p_context>();
|
||||
{
|
||||
std::lock_guard list_lock(nc.list_p2p_ports_mutex);
|
||||
if (nc.list_p2p_ports.contains(SCE_NP_PORT)) {
|
||||
auto &def_port = ::at32(nc.list_p2p_ports, SCE_NP_PORT);
|
||||
{
|
||||
std::lock_guard lock(def_port.s_sign_mutex);
|
||||
msgs = std::move(def_port.sign_msgs);
|
||||
def_port.sign_msgs.clear();
|
||||
}
|
||||
} else {
|
||||
sys_net.error("get_sign_msgs: port %d not present", +SCE_NP_PORT);
|
||||
}
|
||||
}
|
||||
|
||||
return msgs;
|
||||
}
|
||||
|
||||
namespace np {
|
||||
void init_np_handler_dependencies();
|
||||
}
|
||||
|
||||
void base_network_thread::add_ppu_to_awake(ppu_thread *ppu) {
|
||||
std::lock_guard lock(mutex_ppu_to_awake);
|
||||
ppu_to_awake.emplace_back(ppu);
|
||||
}
|
||||
|
||||
void base_network_thread::del_ppu_to_awake(ppu_thread *ppu) {
|
||||
std::lock_guard lock(mutex_ppu_to_awake);
|
||||
|
||||
for (auto it = ppu_to_awake.begin(); it != ppu_to_awake.end();) {
|
||||
if (*it == ppu) {
|
||||
it = ppu_to_awake.erase(it);
|
||||
continue;
|
||||
}
|
||||
|
||||
it++;
|
||||
}
|
||||
}
|
||||
|
||||
void base_network_thread::wake_threads() {
|
||||
std::lock_guard lock(mutex_ppu_to_awake);
|
||||
|
||||
ppu_to_awake.erase(std::unique(ppu_to_awake.begin(), ppu_to_awake.end()),
|
||||
ppu_to_awake.end());
|
||||
for (ppu_thread *ppu : ppu_to_awake) {
|
||||
network_clear_queue(*ppu);
|
||||
lv2_obj::append(ppu);
|
||||
}
|
||||
|
||||
if (!ppu_to_awake.empty()) {
|
||||
ppu_to_awake.clear();
|
||||
lv2_obj::awake_all();
|
||||
}
|
||||
}
|
||||
|
||||
p2p_thread::p2p_thread() { np::init_np_handler_dependencies(); }
|
||||
|
||||
void p2p_thread::bind_sce_np_port() {
|
||||
std::lock_guard list_lock(list_p2p_ports_mutex);
|
||||
create_p2p_port(SCE_NP_PORT);
|
||||
}
|
||||
|
||||
void network_thread::operator()() {
|
||||
std::vector<shared_ptr<lv2_socket>> socklist;
|
||||
socklist.reserve(lv2_socket::id_count);
|
||||
|
||||
{
|
||||
std::lock_guard lock(mutex_ppu_to_awake);
|
||||
ppu_to_awake.clear();
|
||||
}
|
||||
|
||||
std::vector<::pollfd> fds(lv2_socket::id_count);
|
||||
#ifdef _WIN32
|
||||
std::vector<bool> connecting(lv2_socket::id_count);
|
||||
std::vector<bool> was_connecting(lv2_socket::id_count);
|
||||
#endif
|
||||
|
||||
while (thread_ctrl::state() != thread_state::aborting) {
|
||||
if (!num_polls) {
|
||||
thread_ctrl::wait_on(num_polls, 0);
|
||||
continue;
|
||||
}
|
||||
|
||||
ensure(socklist.size() <= lv2_socket::id_count);
|
||||
|
||||
// Wait with 1ms timeout
|
||||
#ifdef _WIN32
|
||||
windows_poll(fds, ::size32(socklist), 1, connecting);
|
||||
#else
|
||||
::poll(fds.data(), socklist.size(), 1);
|
||||
#endif
|
||||
|
||||
std::lock_guard lock(mutex_thread_loop);
|
||||
|
||||
for (usz i = 0; i < socklist.size(); i++) {
|
||||
#ifdef _WIN32
|
||||
socklist[i]->handle_events(fds[i], was_connecting[i] && !connecting[i]);
|
||||
#else
|
||||
socklist[i]->handle_events(fds[i]);
|
||||
#endif
|
||||
}
|
||||
|
||||
wake_threads();
|
||||
socklist.clear();
|
||||
|
||||
// Obtain all native active sockets
|
||||
idm::select<lv2_socket>([&](u32 id, lv2_socket &s) {
|
||||
if (s.get_type() == SYS_NET_SOCK_DGRAM ||
|
||||
s.get_type() == SYS_NET_SOCK_STREAM) {
|
||||
socklist.emplace_back(idm::get_unlocked<lv2_socket>(id));
|
||||
}
|
||||
});
|
||||
|
||||
for (usz i = 0; i < socklist.size(); i++) {
|
||||
auto events = socklist[i]->get_events();
|
||||
|
||||
fds[i].fd = events ? socklist[i]->get_socket() : -1;
|
||||
fds[i].events = (events & lv2_socket::poll_t::read ? POLLIN : 0) |
|
||||
(events & lv2_socket::poll_t::write ? POLLOUT : 0) | 0;
|
||||
fds[i].revents = 0;
|
||||
#ifdef _WIN32
|
||||
const auto cur_connecting = socklist[i]->is_connecting();
|
||||
was_connecting[i] = cur_connecting;
|
||||
connecting[i] = cur_connecting;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Must be used under list_p2p_ports_mutex lock!
|
||||
void p2p_thread::create_p2p_port(u16 p2p_port) {
|
||||
if (!list_p2p_ports.contains(p2p_port)) {
|
||||
list_p2p_ports.emplace(std::piecewise_construct,
|
||||
std::forward_as_tuple(p2p_port),
|
||||
std::forward_as_tuple(p2p_port));
|
||||
const u32 prev_value = num_p2p_ports.fetch_add(1);
|
||||
if (!prev_value) {
|
||||
num_p2p_ports.notify_one();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void p2p_thread::operator()() {
|
||||
std::vector<::pollfd> p2p_fd(lv2_socket::id_count);
|
||||
|
||||
while (thread_ctrl::state() != thread_state::aborting) {
|
||||
if (!num_p2p_ports) {
|
||||
thread_ctrl::wait_on(num_p2p_ports, 0);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check P2P sockets for incoming packets
|
||||
auto num_p2p_sockets = 0;
|
||||
std::memset(p2p_fd.data(), 0, p2p_fd.size() * sizeof(::pollfd));
|
||||
{
|
||||
auto set_fd = [&](socket_type socket) {
|
||||
p2p_fd[num_p2p_sockets].events = POLLIN;
|
||||
p2p_fd[num_p2p_sockets].revents = 0;
|
||||
p2p_fd[num_p2p_sockets].fd = socket;
|
||||
num_p2p_sockets++;
|
||||
};
|
||||
|
||||
std::lock_guard lock(list_p2p_ports_mutex);
|
||||
for (const auto &[_, p2p_port] : list_p2p_ports) {
|
||||
set_fd(p2p_port.p2p_socket);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
const auto ret_p2p = WSAPoll(p2p_fd.data(), num_p2p_sockets, 1);
|
||||
#else
|
||||
const auto ret_p2p = ::poll(p2p_fd.data(), num_p2p_sockets, 1);
|
||||
#endif
|
||||
if (ret_p2p > 0) {
|
||||
std::lock_guard lock(list_p2p_ports_mutex);
|
||||
auto fd_index = 0;
|
||||
|
||||
auto process_fd = [&](nt_p2p_port &p2p_port) {
|
||||
if ((p2p_fd[fd_index].revents & POLLIN) == POLLIN ||
|
||||
(p2p_fd[fd_index].revents & POLLRDNORM) == POLLRDNORM) {
|
||||
while (p2p_port.recv_data())
|
||||
;
|
||||
}
|
||||
fd_index++;
|
||||
};
|
||||
|
||||
for (auto &[_, p2p_port] : list_p2p_ports) {
|
||||
process_fd(p2p_port);
|
||||
}
|
||||
|
||||
wake_threads();
|
||||
} else if (ret_p2p < 0) {
|
||||
sys_net.error("[P2P] Error poll on master P2P socket: %d",
|
||||
get_last_error(false));
|
||||
}
|
||||
}
|
||||
}
|
||||
377
kernel/cellos/src/sys_net/nt_p2p_port.cpp
Normal file
377
kernel/cellos/src/sys_net/nt_p2p_port.cpp
Normal file
|
|
@ -0,0 +1,377 @@
|
|||
#include "sys_net/nt_p2p_port.h"
|
||||
#include "Emu/NP/ip_address.h"
|
||||
#include "Emu/NP/np_handler.h"
|
||||
#include "Emu/NP/signaling_handler.h"
|
||||
#include "Emu/NP/vport0.h"
|
||||
#include "stdafx.h"
|
||||
#include "sys_net/lv2_socket_p2ps.h"
|
||||
#include "sys_net/sys_net_helpers.h"
|
||||
|
||||
LOG_CHANNEL(sys_net);
|
||||
|
||||
namespace sys_net_helpers {
|
||||
bool all_reusable(const std::set<s32> &sock_ids) {
|
||||
for (const s32 sock_id : sock_ids) {
|
||||
const auto [_, reusable] =
|
||||
idm::check<lv2_socket>(sock_id, [&](lv2_socket &sock) -> bool {
|
||||
auto [res_reuseaddr, optval_reuseaddr, optlen_reuseaddr] =
|
||||
sock.getsockopt(SYS_NET_SOL_SOCKET, SYS_NET_SO_REUSEADDR,
|
||||
sizeof(s32));
|
||||
auto [res_reuseport, optval_reuseport, optlen_reuseport] =
|
||||
sock.getsockopt(SYS_NET_SOL_SOCKET, SYS_NET_SO_REUSEPORT,
|
||||
sizeof(s32));
|
||||
|
||||
const bool reuse_addr =
|
||||
optlen_reuseaddr == 4 && !!optval_reuseaddr._int;
|
||||
const bool reuse_port =
|
||||
optlen_reuseport == 4 && !!optval_reuseport._int;
|
||||
|
||||
return (reuse_addr || reuse_port);
|
||||
});
|
||||
|
||||
if (!reusable) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
} // namespace sys_net_helpers
|
||||
|
||||
nt_p2p_port::nt_p2p_port(u16 port) : port(port) {
|
||||
is_ipv6 = np::is_ipv6_supported();
|
||||
|
||||
// Creates and bind P2P Socket
|
||||
p2p_socket = is_ipv6 ? ::socket(AF_INET6, SOCK_DGRAM, 0)
|
||||
: ::socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
|
||||
#ifdef _WIN32
|
||||
if (p2p_socket == INVALID_SOCKET)
|
||||
#else
|
||||
if (p2p_socket == -1)
|
||||
#endif
|
||||
fmt::throw_exception("Failed to create DGRAM socket for P2P socket: %s!",
|
||||
get_last_error(true));
|
||||
|
||||
np::set_socket_non_blocking(p2p_socket);
|
||||
|
||||
u32 optval = 131072; // value obtained from DECR for a SOCK_DGRAM_P2P
|
||||
// socket(should maybe be bigger for actual socket?)
|
||||
if (setsockopt(p2p_socket, SOL_SOCKET, SO_RCVBUF,
|
||||
reinterpret_cast<const char *>(&optval), sizeof(optval)) != 0)
|
||||
fmt::throw_exception("Error setsockopt SO_RCVBUF on P2P socket: %s",
|
||||
get_last_error(true));
|
||||
|
||||
int ret_bind = 0;
|
||||
const u16 be_port = std::bit_cast<u16, be_t<u16>>(port);
|
||||
|
||||
if (is_ipv6) {
|
||||
// Some OS(Windows, maybe more) will only support IPv6 adressing by default
|
||||
// and we need IPv4 over IPv6
|
||||
optval = 0;
|
||||
if (setsockopt(p2p_socket, IPPROTO_IPV6, IPV6_V6ONLY,
|
||||
reinterpret_cast<const char *>(&optval),
|
||||
sizeof(optval)) != 0)
|
||||
fmt::throw_exception("Error setsockopt IPV6_V6ONLY on P2P socket: %s",
|
||||
get_last_error(true));
|
||||
|
||||
::sockaddr_in6 p2p_ipv6_addr{.sin6_family = AF_INET6, .sin6_port = be_port};
|
||||
ret_bind = ::bind(p2p_socket, reinterpret_cast<sockaddr *>(&p2p_ipv6_addr),
|
||||
sizeof(p2p_ipv6_addr));
|
||||
} else {
|
||||
::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));
|
||||
}
|
||||
|
||||
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<named_thread<np::np_handler>>();
|
||||
nph.upnp_add_port_mapping(port, "UDP");
|
||||
|
||||
sys_net.notice("P2P port %d was bound!", port);
|
||||
}
|
||||
|
||||
nt_p2p_port::~nt_p2p_port() { np::close_socket(p2p_socket); }
|
||||
|
||||
void nt_p2p_port::dump_packet(p2ps_encapsulated_tcp *tcph) {
|
||||
sys_net.trace("PACKET DUMP:\nsrc_port: %d\ndst_port: %d\nflags: %d\nseq: "
|
||||
"%d\nack: %d\nlen: %d",
|
||||
tcph->src_port, tcph->dst_port, tcph->flags, tcph->seq,
|
||||
tcph->ack, tcph->length);
|
||||
}
|
||||
|
||||
// Must be used under bound_p2p_vports_mutex lock
|
||||
u16 nt_p2p_port::get_port() {
|
||||
if (binding_port == 0) {
|
||||
binding_port = 30000;
|
||||
}
|
||||
|
||||
return binding_port++;
|
||||
}
|
||||
|
||||
bool nt_p2p_port::handle_connected(s32 sock_id,
|
||||
p2ps_encapsulated_tcp *tcp_header, u8 *data,
|
||||
::sockaddr_storage *op_addr) {
|
||||
const auto sock =
|
||||
idm::check<lv2_socket>(sock_id, [&](lv2_socket &sock) -> bool {
|
||||
ensure(sock.get_type() == SYS_NET_SOCK_STREAM_P2P);
|
||||
auto &sock_p2ps = reinterpret_cast<lv2_socket_p2ps &>(sock);
|
||||
|
||||
return sock_p2ps.handle_connected(tcp_header, data, op_addr, this);
|
||||
});
|
||||
|
||||
if (!sock) {
|
||||
sys_net.error("[P2PS] Couldn't find the socket!");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!sock.ret) {
|
||||
sys_net.error("[P2PS] handle_connected() failed!");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool nt_p2p_port::handle_listening(s32 sock_id,
|
||||
p2ps_encapsulated_tcp *tcp_header, u8 *data,
|
||||
::sockaddr_storage *op_addr) {
|
||||
auto sock = idm::get_unlocked<lv2_socket>(sock_id);
|
||||
if (!sock)
|
||||
return false;
|
||||
|
||||
auto &sock_p2ps = reinterpret_cast<lv2_socket_p2ps &>(*sock);
|
||||
return sock_p2ps.handle_listening(tcp_header, data, op_addr);
|
||||
}
|
||||
|
||||
bool nt_p2p_port::recv_data() {
|
||||
::sockaddr_storage native_addr{};
|
||||
::socklen_t native_addrlen = sizeof(native_addr);
|
||||
const auto recv_res = ::recvfrom(
|
||||
p2p_socket, reinterpret_cast<char *>(p2p_recv_data.data()),
|
||||
::size32(p2p_recv_data), 0,
|
||||
reinterpret_cast<struct sockaddr *>(&native_addr), &native_addrlen);
|
||||
|
||||
if (recv_res == -1) {
|
||||
auto lerr = get_last_error(false);
|
||||
if (lerr != SYS_NET_EINPROGRESS && lerr != SYS_NET_EWOULDBLOCK)
|
||||
sys_net.error("Error recvfrom on %s P2P socket: %d",
|
||||
is_ipv6 ? "IPv6" : "IPv4", lerr);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (recv_res < static_cast<s32>(sizeof(u16))) {
|
||||
sys_net.error("Received badly formed packet on P2P port(no vport)!");
|
||||
return true;
|
||||
}
|
||||
|
||||
u16 dst_vport = reinterpret_cast<le_t<u16> &>(p2p_recv_data[0]);
|
||||
|
||||
if (is_ipv6) {
|
||||
const auto *addr_ipv6 = reinterpret_cast<sockaddr_in6 *>(&native_addr);
|
||||
const auto addr_ipv4 = np::sockaddr6_to_sockaddr(*addr_ipv6);
|
||||
native_addr = {};
|
||||
std::memcpy(&native_addr, &addr_ipv4, sizeof(addr_ipv4));
|
||||
}
|
||||
|
||||
if (dst_vport == 0) {
|
||||
if (recv_res < VPORT_0_HEADER_SIZE) {
|
||||
sys_net.error("Bad vport 0 packet(no subset)!");
|
||||
return true;
|
||||
}
|
||||
|
||||
const u8 subset = p2p_recv_data[2];
|
||||
const auto data_size = recv_res - VPORT_0_HEADER_SIZE;
|
||||
std::vector<u8> vport_0_data(p2p_recv_data.data() + VPORT_0_HEADER_SIZE,
|
||||
p2p_recv_data.data() + VPORT_0_HEADER_SIZE +
|
||||
data_size);
|
||||
|
||||
switch (subset) {
|
||||
case SUBSET_RPCN: {
|
||||
std::lock_guard lock(s_rpcn_mutex);
|
||||
rpcn_msgs.push_back(std::move(vport_0_data));
|
||||
return true;
|
||||
}
|
||||
case SUBSET_SIGNALING: {
|
||||
signaling_message msg;
|
||||
msg.src_addr =
|
||||
reinterpret_cast<struct sockaddr_in *>(&native_addr)->sin_addr.s_addr;
|
||||
msg.src_port = std::bit_cast<u16, be_t<u16>>(
|
||||
reinterpret_cast<struct sockaddr_in *>(&native_addr)->sin_port);
|
||||
msg.data = std::move(vport_0_data);
|
||||
|
||||
{
|
||||
std::lock_guard lock(s_sign_mutex);
|
||||
sign_msgs.push_back(std::move(msg));
|
||||
}
|
||||
|
||||
auto &sigh = g_fxo->get<named_thread<signaling_handler>>();
|
||||
sigh.wake_up();
|
||||
return true;
|
||||
}
|
||||
default: {
|
||||
sys_net.error("Invalid vport 0 subset!");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (recv_res < VPORT_P2P_HEADER_SIZE) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const u16 src_vport =
|
||||
*reinterpret_cast<le_t<u16> *>(p2p_recv_data.data() + sizeof(u16));
|
||||
const u16 vport_flags = *reinterpret_cast<le_t<u16> *>(
|
||||
p2p_recv_data.data() + sizeof(u16) + sizeof(u16));
|
||||
std::vector<u8> p2p_data(recv_res - VPORT_P2P_HEADER_SIZE);
|
||||
memcpy(p2p_data.data(), p2p_recv_data.data() + VPORT_P2P_HEADER_SIZE,
|
||||
p2p_data.size());
|
||||
|
||||
if (vport_flags & P2P_FLAG_P2P) {
|
||||
std::lock_guard lock(bound_p2p_vports_mutex);
|
||||
if (bound_p2p_vports.contains(dst_vport)) {
|
||||
sys_net_sockaddr_in_p2p p2p_addr{};
|
||||
|
||||
p2p_addr.sin_len = sizeof(sys_net_sockaddr_in);
|
||||
p2p_addr.sin_family = SYS_NET_AF_INET;
|
||||
p2p_addr.sin_addr = std::bit_cast<be_t<u32>, u32>(
|
||||
reinterpret_cast<struct sockaddr_in *>(&native_addr)
|
||||
->sin_addr.s_addr);
|
||||
p2p_addr.sin_vport = src_vport;
|
||||
p2p_addr.sin_port = std::bit_cast<be_t<u16>, u16>(
|
||||
reinterpret_cast<struct sockaddr_in *>(&native_addr)->sin_port);
|
||||
|
||||
auto &bound_sockets = ::at32(bound_p2p_vports, dst_vport);
|
||||
|
||||
for (const auto sock_id : bound_sockets) {
|
||||
const auto sock =
|
||||
idm::check<lv2_socket>(sock_id, [&](lv2_socket &sock) {
|
||||
ensure(sock.get_type() == SYS_NET_SOCK_DGRAM_P2P);
|
||||
auto &sock_p2p = reinterpret_cast<lv2_socket_p2p &>(sock);
|
||||
|
||||
sock_p2p.handle_new_data(p2p_addr, p2p_data);
|
||||
});
|
||||
|
||||
if (!sock) {
|
||||
sys_net.error("Socket %d found in bound_p2p_vports didn't exist!",
|
||||
sock_id);
|
||||
bound_sockets.erase(sock_id);
|
||||
if (bound_sockets.empty()) {
|
||||
bound_p2p_vports.erase(dst_vport);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
} else if (vport_flags & P2P_FLAG_P2PS) {
|
||||
if (p2p_data.size() < sizeof(p2ps_encapsulated_tcp)) {
|
||||
sys_net.notice("Received P2P packet targeted at unbound vport(likely) or "
|
||||
"invalid(vport=%d)",
|
||||
dst_vport);
|
||||
return true;
|
||||
}
|
||||
|
||||
auto *tcp_header =
|
||||
reinterpret_cast<p2ps_encapsulated_tcp *>(p2p_data.data());
|
||||
|
||||
// Validate signature & length
|
||||
if (tcp_header->signature != P2PS_U2S_SIG) {
|
||||
sys_net.notice("Received P2P packet targeted at unbound vport(vport=%d)",
|
||||
dst_vport);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (tcp_header->length !=
|
||||
(p2p_data.size() - sizeof(p2ps_encapsulated_tcp))) {
|
||||
sys_net.error(
|
||||
"Received STREAM-P2P packet tcp length didn't match packet length");
|
||||
return true;
|
||||
}
|
||||
|
||||
// Sanity check
|
||||
if (tcp_header->dst_port != dst_vport) {
|
||||
sys_net.error("Received STREAM-P2P packet with dst_port != vport");
|
||||
return true;
|
||||
}
|
||||
|
||||
// Validate checksum
|
||||
u16 given_checksum = tcp_header->checksum;
|
||||
tcp_header->checksum = 0;
|
||||
if (given_checksum !=
|
||||
u2s_tcp_checksum(reinterpret_cast<const le_t<u16> *>(p2p_data.data()),
|
||||
p2p_data.size())) {
|
||||
sys_net.error("Checksum is invalid, dropping packet!");
|
||||
return true;
|
||||
}
|
||||
|
||||
// The packet is valid
|
||||
dump_packet(tcp_header);
|
||||
|
||||
// Check if it's bound
|
||||
const u64 key_connected =
|
||||
(reinterpret_cast<struct sockaddr_in *>(&native_addr)
|
||||
->sin_addr.s_addr) |
|
||||
(static_cast<u64>(tcp_header->src_port) << 48) |
|
||||
(static_cast<u64>(tcp_header->dst_port) << 32);
|
||||
|
||||
{
|
||||
std::lock_guard lock(bound_p2p_vports_mutex);
|
||||
if (bound_p2p_streams.contains(key_connected)) {
|
||||
const auto sock_id = ::at32(bound_p2p_streams, key_connected);
|
||||
sys_net.trace("Received packet for connected STREAM-P2P socket(s=%d)",
|
||||
sock_id);
|
||||
handle_connected(sock_id, tcp_header,
|
||||
p2p_data.data() + sizeof(p2ps_encapsulated_tcp),
|
||||
&native_addr);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (bound_p2ps_vports.contains(tcp_header->dst_port)) {
|
||||
const auto &bound_sockets =
|
||||
::at32(bound_p2ps_vports, tcp_header->dst_port);
|
||||
|
||||
for (const auto sock_id : bound_sockets) {
|
||||
sys_net.trace("Received packet for listening STREAM-P2P socket(s=%d)",
|
||||
sock_id);
|
||||
handle_listening(sock_id, tcp_header,
|
||||
p2p_data.data() + sizeof(p2ps_encapsulated_tcp),
|
||||
&native_addr);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if (tcp_header->flags == p2ps_tcp_flags::RST) {
|
||||
sys_net.trace("[P2PS] Received RST on unbound P2PS");
|
||||
return true;
|
||||
}
|
||||
|
||||
// The P2PS packet was sent to an unbound vport, send a RST packet
|
||||
p2ps_encapsulated_tcp send_hdr;
|
||||
send_hdr.src_port = tcp_header->dst_port;
|
||||
send_hdr.dst_port = tcp_header->src_port;
|
||||
send_hdr.flags = p2ps_tcp_flags::RST;
|
||||
auto packet = generate_u2s_packet(send_hdr, nullptr, 0);
|
||||
|
||||
if (np::sendto_possibly_ipv6(
|
||||
p2p_socket, reinterpret_cast<char *>(packet.data()),
|
||||
::size32(packet),
|
||||
reinterpret_cast<const sockaddr_in *>(&native_addr), 0) == -1) {
|
||||
sys_net.error("[P2PS] Error sending RST to sender to unbound P2PS: %s",
|
||||
get_last_error(false));
|
||||
return true;
|
||||
}
|
||||
|
||||
sys_net.trace("[P2PS] Sent RST to sender to unbound P2PS");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
sys_net.notice("Received a P2P packet with no bound target(dst_vport = %d)",
|
||||
dst_vport);
|
||||
return true;
|
||||
}
|
||||
243
kernel/cellos/src/sys_net/sys_net_helpers.cpp
Normal file
243
kernel/cellos/src/sys_net/sys_net_helpers.cpp
Normal file
|
|
@ -0,0 +1,243 @@
|
|||
#include "stdafx.h"
|
||||
|
||||
#include "Emu/Cell/PPUThread.h"
|
||||
#include "Emu/IdManager.h"
|
||||
#include "sys_net/lv2_socket.h"
|
||||
#include "sys_net/network_context.h"
|
||||
#include "sys_net/sys_net_helpers.h"
|
||||
|
||||
LOG_CHANNEL(sys_net);
|
||||
|
||||
int get_native_error() {
|
||||
int native_error;
|
||||
#ifdef _WIN32
|
||||
native_error = WSAGetLastError();
|
||||
#else
|
||||
native_error = errno;
|
||||
#endif
|
||||
|
||||
return native_error;
|
||||
}
|
||||
|
||||
sys_net_error convert_error(bool is_blocking, int native_error,
|
||||
[[maybe_unused]] bool is_connecting) {
|
||||
// Convert the error code for socket functions to a one for sys_net
|
||||
sys_net_error result{};
|
||||
const char *name{};
|
||||
|
||||
#ifdef _WIN32
|
||||
#define ERROR_CASE(error) \
|
||||
case WSA##error: \
|
||||
result = SYS_NET_##error; \
|
||||
name = #error; \
|
||||
break;
|
||||
#else
|
||||
#define ERROR_CASE(error) \
|
||||
case error: \
|
||||
result = SYS_NET_##error; \
|
||||
name = #error; \
|
||||
break;
|
||||
#endif
|
||||
switch (native_error) {
|
||||
#ifndef _WIN32
|
||||
ERROR_CASE(ENOENT);
|
||||
ERROR_CASE(ENOMEM);
|
||||
ERROR_CASE(EBUSY);
|
||||
ERROR_CASE(ENOSPC);
|
||||
ERROR_CASE(EPIPE);
|
||||
#endif
|
||||
|
||||
// TODO: We don't currently support EFAULT or EINTR
|
||||
// ERROR_CASE(EFAULT);
|
||||
// ERROR_CASE(EINTR);
|
||||
|
||||
ERROR_CASE(EBADF);
|
||||
ERROR_CASE(EACCES);
|
||||
ERROR_CASE(EINVAL);
|
||||
ERROR_CASE(EMFILE);
|
||||
ERROR_CASE(EWOULDBLOCK);
|
||||
ERROR_CASE(EINPROGRESS);
|
||||
ERROR_CASE(EALREADY);
|
||||
ERROR_CASE(EDESTADDRREQ);
|
||||
ERROR_CASE(EMSGSIZE);
|
||||
ERROR_CASE(EPROTOTYPE);
|
||||
ERROR_CASE(ENOPROTOOPT);
|
||||
ERROR_CASE(EPROTONOSUPPORT);
|
||||
ERROR_CASE(EOPNOTSUPP);
|
||||
ERROR_CASE(EPFNOSUPPORT);
|
||||
ERROR_CASE(EAFNOSUPPORT);
|
||||
ERROR_CASE(EADDRINUSE);
|
||||
ERROR_CASE(EADDRNOTAVAIL);
|
||||
ERROR_CASE(ENETDOWN);
|
||||
ERROR_CASE(ENETUNREACH);
|
||||
ERROR_CASE(ECONNABORTED);
|
||||
ERROR_CASE(ECONNRESET);
|
||||
ERROR_CASE(ENOBUFS);
|
||||
ERROR_CASE(EISCONN);
|
||||
ERROR_CASE(ENOTCONN);
|
||||
ERROR_CASE(ESHUTDOWN);
|
||||
ERROR_CASE(ETOOMANYREFS);
|
||||
ERROR_CASE(ETIMEDOUT);
|
||||
ERROR_CASE(ECONNREFUSED);
|
||||
ERROR_CASE(EHOSTDOWN);
|
||||
ERROR_CASE(EHOSTUNREACH);
|
||||
#ifdef _WIN32
|
||||
// Windows likes to be special with unique errors
|
||||
case WSAENETRESET:
|
||||
result = SYS_NET_ECONNRESET;
|
||||
name = "WSAENETRESET";
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
fmt::throw_exception("sys_net get_last_error(is_blocking=%d, "
|
||||
"native_error=%d): Unknown/illegal socket error",
|
||||
is_blocking, native_error);
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
if (is_connecting) {
|
||||
// Windows will return SYS_NET_ENOTCONN when recvfrom/sendto is called on a
|
||||
// socket that is connecting but not yet connected
|
||||
if (result == SYS_NET_ENOTCONN)
|
||||
return SYS_NET_EAGAIN;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (name && result != SYS_NET_EWOULDBLOCK && result != SYS_NET_EINPROGRESS) {
|
||||
sys_net.error("Socket error %s", name);
|
||||
}
|
||||
|
||||
if (is_blocking && result == SYS_NET_EWOULDBLOCK) {
|
||||
return {};
|
||||
}
|
||||
|
||||
if (is_blocking && result == SYS_NET_EINPROGRESS) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return result;
|
||||
#undef ERROR_CASE
|
||||
}
|
||||
|
||||
sys_net_error get_last_error(bool is_blocking, bool is_connecting) {
|
||||
return convert_error(is_blocking, get_native_error(), is_connecting);
|
||||
}
|
||||
|
||||
sys_net_sockaddr
|
||||
native_addr_to_sys_net_addr(const ::sockaddr_storage &native_addr) {
|
||||
ensure(native_addr.ss_family == AF_INET ||
|
||||
native_addr.ss_family == AF_UNSPEC);
|
||||
|
||||
sys_net_sockaddr sn_addr;
|
||||
|
||||
sys_net_sockaddr_in *paddr =
|
||||
reinterpret_cast<sys_net_sockaddr_in *>(&sn_addr);
|
||||
*paddr = {};
|
||||
|
||||
paddr->sin_len = sizeof(sys_net_sockaddr_in);
|
||||
paddr->sin_family = SYS_NET_AF_INET;
|
||||
paddr->sin_port = std::bit_cast<be_t<u16>, u16>(
|
||||
reinterpret_cast<const sockaddr_in *>(&native_addr)->sin_port);
|
||||
paddr->sin_addr = std::bit_cast<be_t<u32>, u32>(
|
||||
reinterpret_cast<const sockaddr_in *>(&native_addr)->sin_addr.s_addr);
|
||||
|
||||
return sn_addr;
|
||||
}
|
||||
|
||||
::sockaddr_in sys_net_addr_to_native_addr(const sys_net_sockaddr &sn_addr) {
|
||||
ensure(sn_addr.sa_family == SYS_NET_AF_INET);
|
||||
|
||||
const sys_net_sockaddr_in *psa_in =
|
||||
reinterpret_cast<const sys_net_sockaddr_in *>(&sn_addr);
|
||||
|
||||
::sockaddr_in native_addr{};
|
||||
native_addr.sin_family = AF_INET;
|
||||
native_addr.sin_port = std::bit_cast<u16>(psa_in->sin_port);
|
||||
native_addr.sin_addr.s_addr = std::bit_cast<u32>(psa_in->sin_addr);
|
||||
|
||||
#ifdef _WIN32
|
||||
// Windows doesn't support sending packets to 0.0.0.0 but it works on unixes,
|
||||
// send to 127.0.0.1 instead
|
||||
if (native_addr.sin_addr.s_addr == 0x00000000) {
|
||||
sys_net.warning("[Native] Redirected 0.0.0.0 to 127.0.0.1");
|
||||
native_addr.sin_addr.s_addr = std::bit_cast<u32, be_t<u32>>(0x7F000001);
|
||||
}
|
||||
#endif
|
||||
|
||||
return native_addr;
|
||||
}
|
||||
|
||||
bool is_ip_public_address(const ::sockaddr_in &addr) {
|
||||
const u8 *ip = reinterpret_cast<const u8 *>(&addr.sin_addr.s_addr);
|
||||
|
||||
if ((ip[0] == 10) || (ip[0] == 127) ||
|
||||
(ip[0] == 172 && (ip[1] >= 16 && ip[1] <= 31)) ||
|
||||
(ip[0] == 192 && ip[1] == 168)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
u32 network_clear_queue(ppu_thread &ppu) {
|
||||
u32 cleared = 0;
|
||||
|
||||
idm::select<lv2_socket>(
|
||||
[&](u32, lv2_socket &sock) { cleared += sock.clear_queue(&ppu); });
|
||||
|
||||
return cleared;
|
||||
}
|
||||
|
||||
void clear_ppu_to_awake(ppu_thread &ppu) {
|
||||
g_fxo->get<network_context>().del_ppu_to_awake(&ppu);
|
||||
g_fxo->get<p2p_context>().del_ppu_to_awake(&ppu);
|
||||
}
|
||||
|
||||
#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)
|
||||
void windows_poll(std::vector<pollfd> &fds, unsigned long nfds, int timeout,
|
||||
std::vector<bool> &connecting) {
|
||||
ensure(fds.size() >= nfds);
|
||||
ensure(connecting.size() >= nfds);
|
||||
|
||||
// Don't call WSAPoll with zero nfds (errors 10022 or 10038)
|
||||
if (std::none_of(fds.begin(), fds.begin() + nfds,
|
||||
[](pollfd &pfd) { return pfd.fd != INVALID_SOCKET; })) {
|
||||
if (timeout > 0) {
|
||||
Sleep(timeout);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
int r = ::WSAPoll(fds.data(), nfds, timeout);
|
||||
|
||||
if (r == SOCKET_ERROR) {
|
||||
sys_net.error(
|
||||
"WSAPoll failed: %s",
|
||||
fmt::win_error{static_cast<unsigned long>(WSAGetLastError()), nullptr});
|
||||
return;
|
||||
}
|
||||
|
||||
for (unsigned long i = 0; i < nfds; i++) {
|
||||
if (connecting[i]) {
|
||||
if (!fds[i].revents) {
|
||||
int error = 0;
|
||||
socklen_t intlen = sizeof(error);
|
||||
if (getsockopt(fds[i].fd, SOL_SOCKET, SO_ERROR,
|
||||
reinterpret_cast<char *>(&error), &intlen) == -1 ||
|
||||
error != 0) {
|
||||
// Connection silently failed
|
||||
connecting[i] = false;
|
||||
fds[i].revents =
|
||||
POLLERR | POLLHUP | (fds[i].events & (POLLIN | POLLOUT));
|
||||
}
|
||||
} else {
|
||||
connecting[i] = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
Loading…
Add table
Add a link
Reference in a new issue