diff --git a/.clang-format b/.clang-format index 5ac13feb4b..5019fbeba8 100644 --- a/.clang-format +++ b/.clang-format @@ -16,7 +16,7 @@ AllowShortBlocksOnASingleLine: Never AllowShortCaseLabelsOnASingleLine: true AllowShortFunctionsOnASingleLine: Empty AllowShortLoopsOnASingleLine: false -AllowShortLambdasOnASingleLine: Inline +AllowShortLambdasOnASingleLine: Empty Cpp11BracedListStyle: true IndentCaseLabels: false SortIncludes: false diff --git a/rpcs3/Emu/CMakeLists.txt b/rpcs3/Emu/CMakeLists.txt index c4a99bfb04..551c80486c 100644 --- a/rpcs3/Emu/CMakeLists.txt +++ b/rpcs3/Emu/CMakeLists.txt @@ -189,6 +189,14 @@ target_sources(rpcs3_emu PRIVATE Cell/lv2/sys_mmapper.cpp Cell/lv2/sys_mutex.cpp Cell/lv2/sys_net.cpp + Cell/lv2/sys_net/lv2_socket.cpp + Cell/lv2/sys_net/lv2_socket_native.cpp + Cell/lv2/sys_net/lv2_socket_raw.cpp + Cell/lv2/sys_net/lv2_socket_p2p.cpp + Cell/lv2/sys_net/lv2_socket_p2ps.cpp + Cell/lv2/sys_net/network_context.cpp + Cell/lv2/sys_net/nt_p2p_port.cpp + Cell/lv2/sys_net/sys_net_helpers.cpp Cell/lv2/sys_overlay.cpp Cell/lv2/sys_ppu_thread.cpp Cell/lv2/sys_process.cpp diff --git a/rpcs3/Emu/Cell/lv2/sys_net.cpp b/rpcs3/Emu/Cell/lv2/sys_net.cpp index bfa3c40037..e261eefd26 100644 --- a/rpcs3/Emu/Cell/lv2/sys_net.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_net.cpp @@ -38,1313 +38,226 @@ #include #include +#include "sys_net/network_context.h" +#include "sys_net/lv2_socket.h" +#include "sys_net/lv2_socket_native.h" +#include "sys_net/lv2_socket_raw.h" +#include "sys_net/lv2_socket_p2p.h" +#include "sys_net/lv2_socket_p2ps.h" +#include "sys_net/sys_net_helpers.h" + LOG_CHANNEL(sys_net); LOG_CHANNEL(sys_net_dump); -template<> +template <> void fmt_class_string::format(std::string& out, u64 arg) { format_enum(out, arg, [](auto error) - { - switch (s32 _error = error) { -#define SYS_NET_ERROR_CASE(x) case -x: return "-" #x; case x: return #x - SYS_NET_ERROR_CASE(SYS_NET_ENOENT); - SYS_NET_ERROR_CASE(SYS_NET_EINTR); - SYS_NET_ERROR_CASE(SYS_NET_EBADF); - SYS_NET_ERROR_CASE(SYS_NET_ENOMEM); - SYS_NET_ERROR_CASE(SYS_NET_EACCES); - SYS_NET_ERROR_CASE(SYS_NET_EFAULT); - SYS_NET_ERROR_CASE(SYS_NET_EBUSY); - SYS_NET_ERROR_CASE(SYS_NET_EINVAL); - SYS_NET_ERROR_CASE(SYS_NET_EMFILE); - SYS_NET_ERROR_CASE(SYS_NET_ENOSPC); - SYS_NET_ERROR_CASE(SYS_NET_EPIPE); - SYS_NET_ERROR_CASE(SYS_NET_EAGAIN); - static_assert(SYS_NET_EWOULDBLOCK == SYS_NET_EAGAIN); - SYS_NET_ERROR_CASE(SYS_NET_EINPROGRESS); - SYS_NET_ERROR_CASE(SYS_NET_EALREADY); - SYS_NET_ERROR_CASE(SYS_NET_EDESTADDRREQ); - SYS_NET_ERROR_CASE(SYS_NET_EMSGSIZE); - SYS_NET_ERROR_CASE(SYS_NET_EPROTOTYPE); - SYS_NET_ERROR_CASE(SYS_NET_ENOPROTOOPT); - SYS_NET_ERROR_CASE(SYS_NET_EPROTONOSUPPORT); - SYS_NET_ERROR_CASE(SYS_NET_EOPNOTSUPP); - SYS_NET_ERROR_CASE(SYS_NET_EPFNOSUPPORT); - SYS_NET_ERROR_CASE(SYS_NET_EAFNOSUPPORT); - SYS_NET_ERROR_CASE(SYS_NET_EADDRINUSE); - SYS_NET_ERROR_CASE(SYS_NET_EADDRNOTAVAIL); - SYS_NET_ERROR_CASE(SYS_NET_ENETDOWN); - SYS_NET_ERROR_CASE(SYS_NET_ENETUNREACH); - SYS_NET_ERROR_CASE(SYS_NET_ECONNABORTED); - SYS_NET_ERROR_CASE(SYS_NET_ECONNRESET); - SYS_NET_ERROR_CASE(SYS_NET_ENOBUFS); - SYS_NET_ERROR_CASE(SYS_NET_EISCONN); - SYS_NET_ERROR_CASE(SYS_NET_ENOTCONN); - SYS_NET_ERROR_CASE(SYS_NET_ESHUTDOWN); - SYS_NET_ERROR_CASE(SYS_NET_ETOOMANYREFS); - SYS_NET_ERROR_CASE(SYS_NET_ETIMEDOUT); - SYS_NET_ERROR_CASE(SYS_NET_ECONNREFUSED); - SYS_NET_ERROR_CASE(SYS_NET_EHOSTDOWN); - SYS_NET_ERROR_CASE(SYS_NET_EHOSTUNREACH); + switch (s32 _error = error) + { +#define SYS_NET_ERROR_CASE(x) \ + case -x: return "-" #x; \ + case x: \ + return #x + SYS_NET_ERROR_CASE(SYS_NET_ENOENT); + SYS_NET_ERROR_CASE(SYS_NET_EINTR); + SYS_NET_ERROR_CASE(SYS_NET_EBADF); + SYS_NET_ERROR_CASE(SYS_NET_ENOMEM); + SYS_NET_ERROR_CASE(SYS_NET_EACCES); + SYS_NET_ERROR_CASE(SYS_NET_EFAULT); + SYS_NET_ERROR_CASE(SYS_NET_EBUSY); + SYS_NET_ERROR_CASE(SYS_NET_EINVAL); + SYS_NET_ERROR_CASE(SYS_NET_EMFILE); + SYS_NET_ERROR_CASE(SYS_NET_ENOSPC); + SYS_NET_ERROR_CASE(SYS_NET_EPIPE); + SYS_NET_ERROR_CASE(SYS_NET_EAGAIN); + static_assert(SYS_NET_EWOULDBLOCK == SYS_NET_EAGAIN); + SYS_NET_ERROR_CASE(SYS_NET_EINPROGRESS); + SYS_NET_ERROR_CASE(SYS_NET_EALREADY); + SYS_NET_ERROR_CASE(SYS_NET_EDESTADDRREQ); + SYS_NET_ERROR_CASE(SYS_NET_EMSGSIZE); + SYS_NET_ERROR_CASE(SYS_NET_EPROTOTYPE); + SYS_NET_ERROR_CASE(SYS_NET_ENOPROTOOPT); + SYS_NET_ERROR_CASE(SYS_NET_EPROTONOSUPPORT); + SYS_NET_ERROR_CASE(SYS_NET_EOPNOTSUPP); + SYS_NET_ERROR_CASE(SYS_NET_EPFNOSUPPORT); + SYS_NET_ERROR_CASE(SYS_NET_EAFNOSUPPORT); + SYS_NET_ERROR_CASE(SYS_NET_EADDRINUSE); + SYS_NET_ERROR_CASE(SYS_NET_EADDRNOTAVAIL); + SYS_NET_ERROR_CASE(SYS_NET_ENETDOWN); + SYS_NET_ERROR_CASE(SYS_NET_ENETUNREACH); + SYS_NET_ERROR_CASE(SYS_NET_ECONNABORTED); + SYS_NET_ERROR_CASE(SYS_NET_ECONNRESET); + SYS_NET_ERROR_CASE(SYS_NET_ENOBUFS); + SYS_NET_ERROR_CASE(SYS_NET_EISCONN); + SYS_NET_ERROR_CASE(SYS_NET_ENOTCONN); + SYS_NET_ERROR_CASE(SYS_NET_ESHUTDOWN); + SYS_NET_ERROR_CASE(SYS_NET_ETOOMANYREFS); + SYS_NET_ERROR_CASE(SYS_NET_ETIMEDOUT); + SYS_NET_ERROR_CASE(SYS_NET_ECONNREFUSED); + SYS_NET_ERROR_CASE(SYS_NET_EHOSTDOWN); + SYS_NET_ERROR_CASE(SYS_NET_EHOSTUNREACH); #undef SYS_NET_ERROR_CASE - } + } - return unknown; - }); + return unknown; + }); } template <> void fmt_class_string::format(std::string& out, u64 arg) { format_enum(out, arg, [](auto value) - { - switch (value) { - case SYS_NET_SOCK_STREAM: return "STREAM"; - case SYS_NET_SOCK_DGRAM: return "DGRAM"; - case SYS_NET_SOCK_RAW: return "RAW"; - case SYS_NET_SOCK_DGRAM_P2P: return "DGRAM-P2P"; - case SYS_NET_SOCK_STREAM_P2P: return "STREAM-P2P"; - } + switch (value) + { + case SYS_NET_SOCK_STREAM: return "STREAM"; + case SYS_NET_SOCK_DGRAM: return "DGRAM"; + case SYS_NET_SOCK_RAW: return "RAW"; + case SYS_NET_SOCK_DGRAM_P2P: return "DGRAM-P2P"; + case SYS_NET_SOCK_STREAM_P2P: return "STREAM-P2P"; + } - return unknown; - }); + return unknown; + }); } template <> void fmt_class_string::format(std::string& out, u64 arg) { format_enum(out, arg, [](auto value) - { - switch (value) { - case SYS_NET_AF_UNSPEC: return "UNSPEC"; - case SYS_NET_AF_LOCAL: return "LOCAL"; - case SYS_NET_AF_INET: return "INET"; - case SYS_NET_AF_INET6: return "INET6"; - } + switch (value) + { + case SYS_NET_AF_UNSPEC: return "UNSPEC"; + case SYS_NET_AF_LOCAL: return "LOCAL"; + case SYS_NET_AF_INET: return "INET"; + case SYS_NET_AF_INET6: return "INET6"; + } - return unknown; - }); + return unknown; + }); } template <> void fmt_class_string::format(std::string& out, u64 arg) { format_enum(out, arg, [](auto value) - { - switch (value) { - case SYS_NET_IPPROTO_IP: return "IPPROTO_IP"; - case SYS_NET_IPPROTO_ICMP: return "IPPROTO_ICMP"; - case SYS_NET_IPPROTO_IGMP: return "IPPROTO_IGMP"; - case SYS_NET_IPPROTO_TCP: return "IPPROTO_TCP"; - case SYS_NET_IPPROTO_UDP: return "IPPROTO_UDP"; - case SYS_NET_IPPROTO_ICMPV6: return "IPPROTO_ICMPV6"; - } + switch (value) + { + case SYS_NET_IPPROTO_IP: return "IPPROTO_IP"; + case SYS_NET_IPPROTO_ICMP: return "IPPROTO_ICMP"; + case SYS_NET_IPPROTO_IGMP: return "IPPROTO_IGMP"; + case SYS_NET_IPPROTO_TCP: return "IPPROTO_TCP"; + case SYS_NET_IPPROTO_UDP: return "IPPROTO_UDP"; + case SYS_NET_IPPROTO_ICMPV6: return "IPPROTO_ICMPV6"; + } - return unknown; - }); + return unknown; + }); } template <> void fmt_class_string::format(std::string& out, u64 arg) { format_enum(out, arg, [](auto value) - { - switch (value) { - case SYS_NET_TCP_NODELAY: return "TCP_NODELAY"; - case SYS_NET_TCP_MAXSEG: return "TCP_MAXSEG"; - case SYS_NET_TCP_MSS_TO_ADVERTISE: return "TCP_MSS_TO_ADVERTISE"; - } + switch (value) + { + case SYS_NET_TCP_NODELAY: return "TCP_NODELAY"; + case SYS_NET_TCP_MAXSEG: return "TCP_MAXSEG"; + case SYS_NET_TCP_MSS_TO_ADVERTISE: return "TCP_MSS_TO_ADVERTISE"; + } - return unknown; - }); + return unknown; + }); } template <> void fmt_class_string::format(std::string& out, u64 arg) { format_enum(out, arg, [](auto value) - { - switch (value) { - case SYS_NET_SO_SNDBUF: return "SO_SNDBUF"; - case SYS_NET_SO_RCVBUF: return "SO_RCVBUF"; - case SYS_NET_SO_SNDLOWAT: return "SO_SNDLOWAT"; - case SYS_NET_SO_RCVLOWAT: return "SO_RCVLOWAT"; - case SYS_NET_SO_SNDTIMEO: return "SO_SNDTIMEO"; - case SYS_NET_SO_RCVTIMEO: return "SO_RCVTIMEO"; - case SYS_NET_SO_ERROR: return "SO_ERROR"; - case SYS_NET_SO_TYPE: return "SO_TYPE"; - case SYS_NET_SO_NBIO: return "SO_NBIO"; - case SYS_NET_SO_TPPOLICY: return "SO_TPPOLICY"; - case SYS_NET_SO_REUSEADDR: return "SO_REUSEADDR"; - case SYS_NET_SO_KEEPALIVE: return "SO_KEEPALIVE"; - case SYS_NET_SO_BROADCAST: return "SO_BROADCAST"; - case SYS_NET_SO_LINGER: return "SO_LINGER"; - case SYS_NET_SO_OOBINLINE: return "SO_OOBINLINE"; - case SYS_NET_SO_REUSEPORT: return "SO_REUSEPORT"; - case SYS_NET_SO_ONESBCAST: return "SO_ONESBCAST"; - case SYS_NET_SO_USECRYPTO: return "SO_USECRYPTO"; - case SYS_NET_SO_USESIGNATURE: return "SO_USESIGNATURE"; - case SYS_NET_SOL_SOCKET: return "SOL_SOCKET"; - } + switch (value) + { + case SYS_NET_SO_SNDBUF: return "SO_SNDBUF"; + case SYS_NET_SO_RCVBUF: return "SO_RCVBUF"; + case SYS_NET_SO_SNDLOWAT: return "SO_SNDLOWAT"; + case SYS_NET_SO_RCVLOWAT: return "SO_RCVLOWAT"; + case SYS_NET_SO_SNDTIMEO: return "SO_SNDTIMEO"; + case SYS_NET_SO_RCVTIMEO: return "SO_RCVTIMEO"; + case SYS_NET_SO_ERROR: return "SO_ERROR"; + case SYS_NET_SO_TYPE: return "SO_TYPE"; + case SYS_NET_SO_NBIO: return "SO_NBIO"; + case SYS_NET_SO_TPPOLICY: return "SO_TPPOLICY"; + case SYS_NET_SO_REUSEADDR: return "SO_REUSEADDR"; + case SYS_NET_SO_KEEPALIVE: return "SO_KEEPALIVE"; + case SYS_NET_SO_BROADCAST: return "SO_BROADCAST"; + case SYS_NET_SO_LINGER: return "SO_LINGER"; + case SYS_NET_SO_OOBINLINE: return "SO_OOBINLINE"; + case SYS_NET_SO_REUSEPORT: return "SO_REUSEPORT"; + case SYS_NET_SO_ONESBCAST: return "SO_ONESBCAST"; + case SYS_NET_SO_USECRYPTO: return "SO_USECRYPTO"; + case SYS_NET_SO_USESIGNATURE: return "SO_USESIGNATURE"; + case SYS_NET_SOL_SOCKET: return "SOL_SOCKET"; + } - return unknown; - }); + return unknown; + }); } template <> void fmt_class_string::format(std::string& out, u64 arg) { format_enum(out, arg, [](auto value) - { - switch (value) { - case SYS_NET_IP_HDRINCL: return "IP_HDRINCL"; - case SYS_NET_IP_TOS: return "IP_TOS"; - case SYS_NET_IP_TTL: return "IP_TTL"; - case SYS_NET_IP_MULTICAST_IF: return "IP_MULTICAST_IF"; - case SYS_NET_IP_MULTICAST_TTL: return "IP_MULTICAST_TTL"; - case SYS_NET_IP_MULTICAST_LOOP: return "IP_MULTICAST_LOOP"; - case SYS_NET_IP_ADD_MEMBERSHIP: return "IP_ADD_MEMBERSHIP"; - case SYS_NET_IP_DROP_MEMBERSHIP: return "IP_DROP_MEMBERSHIP"; - case SYS_NET_IP_TTLCHK: return "IP_TTLCHK"; - case SYS_NET_IP_MAXTTL: return "IP_MAXTTL"; - case SYS_NET_IP_DONTFRAG: return "IP_DONTFRAG"; - } + switch (value) + { + case SYS_NET_IP_HDRINCL: return "IP_HDRINCL"; + case SYS_NET_IP_TOS: return "IP_TOS"; + case SYS_NET_IP_TTL: return "IP_TTL"; + case SYS_NET_IP_MULTICAST_IF: return "IP_MULTICAST_IF"; + case SYS_NET_IP_MULTICAST_TTL: return "IP_MULTICAST_TTL"; + case SYS_NET_IP_MULTICAST_LOOP: return "IP_MULTICAST_LOOP"; + case SYS_NET_IP_ADD_MEMBERSHIP: return "IP_ADD_MEMBERSHIP"; + case SYS_NET_IP_DROP_MEMBERSHIP: return "IP_DROP_MEMBERSHIP"; + case SYS_NET_IP_TTLCHK: return "IP_TTLCHK"; + case SYS_NET_IP_MAXTTL: return "IP_MAXTTL"; + case SYS_NET_IP_DONTFRAG: return "IP_DONTFRAG"; + } - return unknown; - }); + return unknown; + }); } - template <> void fmt_class_string::format(std::string& out, u64 arg) { - const uchar* data = reinterpret_cast(&get_object(arg)); + const u8* data = reinterpret_cast(&get_object(arg)); fmt::append(out, "%u.%u.%u.%u", data[0], data[1], data[2], data[3]); } -#ifdef _WIN32 -// Workaround function for WSAPoll not reporting failed connections -void windows_poll(pollfd* fds, unsigned long nfds, int timeout, bool* connecting) +void sys_net_dump_data(std::string_view desc, const u8* data, s32 len) { - // Don't call WSAPoll with zero nfds (errors 10022 or 10038) - if (std::none_of(fds, fds + nfds, [](pollfd& pfd) { return pfd.fd != INVALID_SOCKET; })) + if (sys_net_dump.enabled == logs::level::trace) { - if (timeout > 0) - { - Sleep(timeout); - } + auto data_dump = fmt::format("%s:\n", desc); + const char hex[16] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}; - return; + for (s32 index = 0; index < len; index++) + { + if ((index % 16) == 0) + data_dump += '\n'; + + data_dump += hex[(data[index] >> 4) & 15]; + data_dump += hex[(data[index]) & 15]; + data_dump += ' '; + } + sys_net.trace("%s", data_dump); } - - int r = ::WSAPoll(fds, nfds, timeout); - - if (r == SOCKET_ERROR) - { - sys_net.error("WSAPoll failed: %u", WSAGetLastError()); - 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(&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 - -// Error helper functions -static int get_native_error() -{ - int native_error; -#ifdef _WIN32 - native_error = WSAGetLastError(); -#else - native_error = errno; -#endif - - return native_error; -} - -static sys_net_error get_last_error(bool is_blocking, int native_error = 0) -{ - // Convert the error code for socket functions to a one for sys_net - sys_net_error result{}; - const char* name{}; - - if (!native_error) - { - native_error = get_native_error(); - } - -#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); -#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(EPIPE); - 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); - default: - fmt::throw_exception("sys_net get_last_error(is_blocking=%d, native_error=%d): Unknown/illegal socket error", is_blocking, native_error); - } - - 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 -} - -static void network_clear_queue(ppu_thread& ppu) -{ - idm::select([&](u32, lv2_socket& sock) - { - std::lock_guard lock(sock.mutex); - - for (auto it = sock.queue.begin(); it != sock.queue.end();) - { - if (it->first == ppu.id) - { - it = sock.queue.erase(it); - continue; - } - - it++; - } - - if (sock.queue.empty()) - { - sock.events.store({}); - } - }); -} - -// Object in charge of retransmiting packets for STREAM_P2P sockets -class tcp_timeout_monitor -{ -public: - void add_message(s32 sock_id, const sockaddr_in *dst, std::vector data, u64 seq) - { - { - std::lock_guard lock(data_mutex); - - const auto now = steady_clock::now(); - - message msg; - msg.dst_addr = *dst; - msg.sock_id = sock_id; - msg.data = std::move(data); - msg.seq = seq; - msg.initial_sendtime = now; - - rtt_info rtt = rtts[sock_id]; - - const auto expected_time = now + rtt.rtt_time; - - msgs.insert(std::make_pair(expected_time, std::move(msg))); - } - wakey.notify_one(); // TODO: Should be improved to only wake if new timeout < old timeout - } - - void confirm_data_received(s32 sock_id, u64 ack) - { - std::lock_guard lock(data_mutex); - rtts[sock_id].num_retries = 0; - - const auto now = steady_clock::now(); - - for (auto it = msgs.begin(); it != msgs.end();) - { - auto& msg = it->second; - if (msg.sock_id == sock_id && msg.seq < ack) - { - // Decreases RTT if msg is early - if (now < it->first) - { - const auto actual_rtt = std::chrono::duration_cast(now - it->second.initial_sendtime); - const auto cur_rtt = rtts[sock_id].rtt_time; - if (cur_rtt > actual_rtt) - { - rtts[sock_id].rtt_time = (actual_rtt + cur_rtt) / 2; - } - - } - it = msgs.erase(it); - continue; - } - it++; - } - } - - void operator()() - { - while (thread_ctrl::state() != thread_state::aborting) - { - std::unique_lock lock(data_mutex); - if (msgs.size()) - wakey.wait_until(lock, msgs.begin()->first); - else - wakey.wait(lock); - - if (thread_ctrl::state() == thread_state::aborting) - return; - - const auto now = steady_clock::now(); - // Check for messages that haven't been acked - std::set rtt_increased; - for (auto it = msgs.begin(); it != msgs.end();) - { - if (it->first > now) - break; - - // reply is late, increases rtt - auto& msg = it->second; - const auto addr = msg.dst_addr.sin_addr.s_addr; - rtt_info rtt = rtts[msg.sock_id]; - // Only increases rtt once per loop(in case a big number of packets are sent at once) - if (!rtt_increased.count(msg.sock_id)) - { - rtt.num_retries += 1; - // Increases current rtt by 10% - rtt.rtt_time += (rtt.rtt_time / 10); - rtts[addr] = rtt; - - rtt_increased.emplace(msg.sock_id); - } - - if (rtt.num_retries >= 10) - { - // Too many retries, need to notify the socket that the connection is dead - idm::check(msg.sock_id, [&](lv2_socket& sock) - { - sock.p2ps.status = lv2_socket::p2ps_i::stream_status::stream_closed; - }); - it = msgs.erase(it); - continue; - } - - // resend the message - const auto res = idm::check(msg.sock_id, [&](lv2_socket& sock) -> bool - { - if (sendto(sock.socket, reinterpret_cast(msg.data.data()), msg.data.size(), 0, reinterpret_cast(&msg.dst_addr), sizeof(msg.dst_addr)) == -1) - { - sock.p2ps.status = lv2_socket::p2ps_i::stream_status::stream_closed; - return false; - } - return true; - }); - - if (!res || !res.ret) - { - it = msgs.erase(it); - continue; - } - - // Update key timeout - msgs.insert(std::make_pair(now + rtt.rtt_time, std::move(msg))); - it = msgs.erase(it); - } - } - } - - tcp_timeout_monitor& operator=(thread_state) - { - wakey.notify_one(); - return *this; - } - - public: - static constexpr auto thread_name = "Tcp Over Udp Timeout Manager Thread"sv; - - private: - std::condition_variable wakey; - std::mutex data_mutex; - // List of outgoing messages - struct message - { - s32 sock_id; - ::sockaddr_in dst_addr; - std::vector data; - u64 seq; - steady_clock::time_point initial_sendtime; - }; - std::map msgs; // (wakeup time, msg) - // List of rtts - struct rtt_info - { - unsigned long num_retries = 0; - std::chrono::milliseconds rtt_time = 50ms; - }; - std::unordered_map rtts; // (sock_id, rtt) - }; - -struct nt_p2p_port -{ - // Real socket where P2P packets are received/sent - lv2_socket::socket_type p2p_socket = 0; - u16 port; - - shared_mutex bound_p2p_vports_mutex; - // For DGRAM_P2P sockets(vport, sock_id) - std::map bound_p2p_vports{}; - // For STREAM_P2P sockets(key, sock_id) - // key is ( (src_vport) << 48 | (dst_vport) << 32 | addr ) with src_vport and addr being 0 for listening sockets - std::map bound_p2p_streams{}; - - // Queued messages from RPCN - shared_mutex s_rpcn_mutex; - std::vector> rpcn_msgs{}; - // Queued signaling messages - shared_mutex s_sign_mutex; - std::vector, std::vector>> sign_msgs{}; - - std::array p2p_recv_data{}; - - nt_p2p_port(u16 port) : port(port) - { - // Creates and bind P2P Socket - p2p_socket = ::socket(AF_INET, SOCK_DGRAM, IPPROTO_IP); - - if (p2p_socket == -1) - sys_net.fatal("Failed to create DGRAM socket for P2P socket!"); - -#ifdef _WIN32 - u_long _true = 1; - ::ioctlsocket(p2p_socket, FIONBIO, &_true); -#else - ::fcntl(p2p_socket, F_SETFL, ::fcntl(p2p_socket, F_GETFL, 0) | O_NONBLOCK); -#endif - - u32 optval = 131072; - if (setsockopt(p2p_socket, SOL_SOCKET, SO_RCVBUF, reinterpret_cast(&optval), sizeof(optval)) != 0) - sys_net.fatal("Error setsockopt SO_RCVBUF on P2P socket"); - - ::sockaddr_in p2p_saddr{}; - p2p_saddr.sin_family = AF_INET; - p2p_saddr.sin_port = std::bit_cast>(port); // htons(port); - p2p_saddr.sin_addr.s_addr = 0; // binds to 0.0.0.0 - const auto ret_bind = ::bind(p2p_socket, reinterpret_cast(&p2p_saddr), sizeof(p2p_saddr)); - - if (ret_bind == -1) - sys_net.fatal("Failed to bind DGRAM socket to %d for P2P!", port); - - sys_net.notice("P2P port %d was bound!", port); - } - - ~nt_p2p_port() - { - if (p2p_socket) - { -#ifdef _WIN32 - ::closesocket(p2p_socket); -#else - ::close(p2p_socket); -#endif - } - } - - static u16 tcp_checksum(const u16* buffer, usz size) - { - u32 cksum = 0; - while (size > 1) - { - cksum += *buffer++; - size -= sizeof(u16); - } - if (size) - cksum += *reinterpret_cast(buffer); - - cksum = (cksum >> 16) + (cksum & 0xffff); - cksum += (cksum >> 16); - return static_cast(~cksum); - } - - static std::vector generate_u2s_packet(const lv2_socket::p2ps_i::encapsulated_tcp& header, const u8 *data, const u32 datasize) - { - const u32 packet_size = (sizeof(u16) + sizeof(lv2_socket::p2ps_i::encapsulated_tcp) + datasize); - ensure(packet_size < 65535); // packet size shouldn't be bigger than possible UDP payload - std::vector packet(packet_size); - u8 *packet_data = packet.data(); - - *reinterpret_cast *>(packet_data) = header.dst_port; - memcpy(packet_data+sizeof(u16), &header, sizeof(lv2_socket::p2ps_i::encapsulated_tcp)); - if(datasize) - memcpy(packet_data+sizeof(u16)+sizeof(lv2_socket::p2ps_i::encapsulated_tcp), data, datasize); - - auto* hdr_ptr = reinterpret_cast(packet_data+sizeof(u16)); - hdr_ptr->checksum = 0; - hdr_ptr->checksum = tcp_checksum(utils::bless(hdr_ptr), sizeof(lv2_socket::p2ps_i::encapsulated_tcp) + datasize); - - return packet; - } - - static void send_u2s_packet(lv2_socket &sock, s32 sock_id, std::vector data, const ::sockaddr_in* dst, u32 seq = 0, bool require_ack = true) - { - char ip_str[16]; - inet_ntop(AF_INET, &dst->sin_addr, ip_str, sizeof(ip_str)); - sys_net.trace("Sending U2S packet on socket %d(id:%d): data(%d, seq %d, require_ack %d) to %s:%d", sock.socket, sock_id, data.size(), seq, require_ack, ip_str, std::bit_cast>(dst->sin_port)); - if (sendto(sock.socket, reinterpret_cast(data.data()), data.size(), 0, reinterpret_cast(dst), sizeof(sockaddr_in)) == -1) - { - sys_net.error("Attempting to send a u2s packet failed(%s), closing socket!", get_last_error(false)); - sock.p2ps.status = lv2_socket::p2ps_i::stream_status::stream_closed; - return; - } - - // Adds to tcp timeout monitor to resend the message until an ack is received - if (require_ack) - { - auto& tcpm = g_fxo->get>(); - tcpm.add_message(sock_id, dst, std::move(data), seq); - } - } - - static void dump_packet(lv2_socket::p2ps_i::encapsulated_tcp* tcph) - { - const std::string result = fmt::format("src_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); - sys_net.trace("PACKET DUMP:\n%s", result); - } - - bool handle_connected(s32 sock_id, lv2_socket::p2ps_i::encapsulated_tcp* tcp_header, u8* data, ::sockaddr_storage* op_addr) - { - const auto sock = idm::check(sock_id, [&](lv2_socket& sock) -> bool - { - std::lock_guard lock(sock.mutex); - - if (sock.p2ps.status != lv2_socket::p2ps_i::stream_status::stream_connected && sock.p2ps.status != lv2_socket::p2ps_i::stream_status::stream_handshaking) - return false; - - dump_packet(tcp_header); - - if (tcp_header->flags == lv2_socket::p2ps_i::ACK) - { - auto& tcpm = g_fxo->get>(); - tcpm.confirm_data_received(sock_id, tcp_header->ack); - } - - auto send_ack = [&]() - { - auto final_ack = sock.p2ps.data_beg_seq; - while (sock.p2ps.received_data.contains(final_ack)) - { - final_ack += sock.p2ps.received_data.at(final_ack).size(); - } - sock.p2ps.data_available = final_ack - sock.p2ps.data_beg_seq; - - lv2_socket::p2ps_i::encapsulated_tcp send_hdr; - send_hdr.src_port = tcp_header->dst_port; - send_hdr.dst_port = tcp_header->src_port; - send_hdr.flags = lv2_socket::p2ps_i::ACK; - send_hdr.ack = final_ack; - auto packet = generate_u2s_packet(send_hdr, nullptr, 0); - sys_net.trace("Sent ack %d", final_ack); - send_u2s_packet(sock, sock_id, std::move(packet), reinterpret_cast<::sockaddr_in*>(op_addr), 0, false); - - // check if polling is happening - if (sock.p2ps.data_available && sock.events.test_and_reset(lv2_socket::poll::read)) - { - bs_t events = lv2_socket::poll::read; - for (auto it = sock.queue.begin(); events && it != sock.queue.end();) - { - if (it->second(events)) - { - it = sock.queue.erase(it); - continue; - } - it++; - } - - if (sock.queue.empty()) - { - sock.events.store({}); - } - } - - }; - - if (sock.p2ps.status == lv2_socket::p2ps_i::stream_status::stream_handshaking) - { - // Only expect SYN|ACK - if (tcp_header->flags == (lv2_socket::p2ps_i::SYN | lv2_socket::p2ps_i::ACK)) - { - sys_net.trace("Received SYN|ACK, status is now connected"); - sock.p2ps.data_beg_seq = tcp_header->seq + 1; - sock.p2ps.status = lv2_socket::p2ps_i::stream_status::stream_connected; - send_ack(); - } - - return true; - } - else if (sock.p2ps.status == lv2_socket::p2ps_i::stream_status::stream_connected) - { - if (tcp_header->seq < sock.p2ps.data_beg_seq) - { - // Data has already been processed - sys_net.trace("Data has already been processed"); - if (tcp_header->flags != lv2_socket::p2ps_i::ACK && tcp_header->flags != lv2_socket::p2ps_i::RST) - send_ack(); - return true; - } - - switch (tcp_header->flags) - { - case lv2_socket::p2ps_i::PSH: - case 0: - { - if (!sock.p2ps.received_data.count(tcp_header->seq)) - { - // New data - sock.p2ps.received_data.emplace(tcp_header->seq, std::vector(data, data + tcp_header->length)); - } - else - { - sys_net.trace("Data was not new!"); - } - - send_ack(); - return true; - } - case lv2_socket::p2ps_i::RST: - case lv2_socket::p2ps_i::FIN: - { - sock.p2ps.status = lv2_socket::p2ps_i::stream_status::stream_closed; - return false; - } - default: - { - sys_net.error("Unknown U2S TCP flag received"); - return true; - } - } - } - - return true; - }); - - if (!sock || !sock.ret) - return false; - - return true; - } - - bool handle_listening(s32 sock_id, lv2_socket::p2ps_i::encapsulated_tcp* tcp_header, u8* /*data*/, ::sockaddr_storage* op_addr) - { - auto sock = idm::get(sock_id); - if (!sock) - return false; - - std::lock_guard lock(sock->mutex); - - if (sock->p2ps.status != lv2_socket::p2ps_i::stream_status::stream_listening) - return false; - - // Only valid packet - if (tcp_header->flags == lv2_socket::p2ps_i::SYN && sock->p2ps.backlog.size() < sock->p2ps.max_backlog) - { - // Yes, new connection and a backlog is available, create a new lv2_socket for it and send SYN|ACK - // Prepare reply packet - sys_net.notice("Received connection on listening STREAM-P2P socket!"); - lv2_socket::p2ps_i::encapsulated_tcp send_hdr; - send_hdr.src_port = tcp_header->dst_port; - send_hdr.dst_port = tcp_header->src_port; - send_hdr.flags = lv2_socket::p2ps_i::SYN | lv2_socket::p2ps_i::ACK; - send_hdr.ack = tcp_header->seq + 1; - // Generates random starting SEQ - send_hdr.seq = rand(); - - // Create new socket - auto sock_lv2 = std::make_shared(0, SYS_NET_SOCK_STREAM_P2P, SYS_NET_AF_INET); - sock_lv2->socket = sock->socket; - sock_lv2->p2p.port = sock->p2p.port; - sock_lv2->p2p.vport = sock->p2p.vport; - sock_lv2->p2ps.op_addr = reinterpret_cast(op_addr)->sin_addr.s_addr; - sock_lv2->p2ps.op_port = std::bit_cast>((reinterpret_cast(op_addr)->sin_port)); - sock_lv2->p2ps.op_vport = tcp_header->src_port; - sock_lv2->p2ps.cur_seq = send_hdr.seq + 1; - sock_lv2->p2ps.data_beg_seq = send_hdr.ack; - sock_lv2->p2ps.status = lv2_socket::p2ps_i::stream_status::stream_connected; - const s32 new_sock_id = idm::import_existing(sock_lv2); - const u64 key_connected = (reinterpret_cast(op_addr)->sin_addr.s_addr) | (static_cast(tcp_header->src_port) << 48) | (static_cast(tcp_header->dst_port) << 32); - bound_p2p_streams.emplace(key_connected, new_sock_id); - - auto packet = generate_u2s_packet(send_hdr, nullptr, 0); - { - std::lock_guard lock(sock_lv2->mutex); - send_u2s_packet(*sock_lv2, new_sock_id, std::move(packet), reinterpret_cast<::sockaddr_in*>(op_addr), send_hdr.seq); - } - - sock->p2ps.backlog.push(new_sock_id); - if (sock->events.test_and_reset(lv2_socket::poll::read)) - { - bs_t events = lv2_socket::poll::read; - for (auto it = sock->queue.begin(); events && it != sock->queue.end();) - { - if (it->second(events)) - { - it = sock->queue.erase(it); - continue; - } - it++; - } - - if (sock->queue.empty()) - { - sock->events.store({}); - } - } - - } - else if (tcp_header->flags == lv2_socket::p2ps_i::SYN) - { - // Send a RST packet on backlog full - sys_net.trace("Backlog was full, sent a RST packet"); - lv2_socket::p2ps_i::encapsulated_tcp send_hdr; - send_hdr.src_port = tcp_header->dst_port; - send_hdr.dst_port = tcp_header->src_port; - send_hdr.flags = lv2_socket::p2ps_i::RST; - auto packet = generate_u2s_packet(send_hdr, nullptr, 0); - send_u2s_packet(*sock, sock_id, std::move(packet), reinterpret_cast<::sockaddr_in*>(op_addr), 0, false); - } - - // Ignore other packets? - - return true; - } - - bool recv_data() - { - ::sockaddr_storage native_addr; - ::socklen_t native_addrlen = sizeof(native_addr); - const auto recv_res = ::recvfrom(p2p_socket, reinterpret_cast(p2p_recv_data.data()), p2p_recv_data.size(), 0, reinterpret_cast(&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 P2P socket: %d", lerr); - - return false; - } - - if (recv_res < static_cast(sizeof(u16))) - { - sys_net.error("Received badly formed packet on P2P port(no vport)!"); - return true; - } - - u16 dst_vport = reinterpret_cast&>(p2p_recv_data[0]); - - if (dst_vport == 0) // Reserved for messages from RPCN server - { - std::vector rpcn_msg(recv_res - sizeof(u16)); - memcpy(rpcn_msg.data(), p2p_recv_data.data() + sizeof(u16), recv_res - sizeof(u16)); - - std::lock_guard lock(s_rpcn_mutex); - rpcn_msgs.push_back(std::move(rpcn_msg)); - return true; - } - - if (dst_vport == 65535) // Reserved for signaling - { - std::vector sign_msg(recv_res - sizeof(u16)); - memcpy(sign_msg.data(), p2p_recv_data.data() + sizeof(u16), recv_res - sizeof(u16)); - - std::pair, std::vector> msg; - msg.first.first = reinterpret_cast(&native_addr)->sin_addr.s_addr; - msg.first.second = std::bit_cast>(reinterpret_cast(&native_addr)->sin_port); - msg.second = std::move(sign_msg); - - { - std::lock_guard lock(s_sign_mutex); - sign_msgs.push_back(std::move(msg)); - } - - auto& sigh = g_fxo->get>(); - sigh.wake_up(); - - return true; - } - - { - 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, u32>(reinterpret_cast(&native_addr)->sin_addr.s_addr); - p2p_addr.sin_vport = dst_vport; - p2p_addr.sin_port = std::bit_cast, u16>(reinterpret_cast(&native_addr)->sin_port); - - std::vector p2p_data(recv_res - sizeof(u16)); - memcpy(p2p_data.data(), p2p_recv_data.data() + sizeof(u16), recv_res - sizeof(u16)); - - const auto sock = idm::check(bound_p2p_vports.at(dst_vport), [&](lv2_socket& sock) - { - std::lock_guard lock(sock.mutex); - ensure(sock.type == SYS_NET_SOCK_DGRAM_P2P); - - sock.p2p.data.push(std::make_pair(std::move(p2p_addr), std::move(p2p_data))); - sys_net.trace("Received a P2P packet for vport %d and saved it", dst_vport); - - // Check if poll is happening - if (sock.events.test_and_reset(lv2_socket::poll::read)) - { - bs_t events = lv2_socket::poll::read; - for (auto it = sock.queue.begin(); events && it != sock.queue.end();) - { - if (it->second(events)) - { - it = sock.queue.erase(it); - continue; - } - it++; - } - - if (sock.queue.empty()) - { - sock.events.store({}); - } - } - }); - - // Should not happen in theory - if (!sock) - bound_p2p_vports.erase(dst_vport); - - return true; - } - } - - // Not directed at a bound DGRAM_P2P vport so check if the packet is a STREAM-P2P packet - - const auto sp_size = recv_res - sizeof(u16); - u8 *sp_data = p2p_recv_data.data() + sizeof(u16); - - if (sp_size < sizeof(lv2_socket::p2ps_i::encapsulated_tcp)) - { - sys_net.trace("Received P2P packet targeted at unbound vport(likely) or invalid(vport=%d)", dst_vport); - return true; - } - - auto* tcp_header = reinterpret_cast(sp_data); - - // Validate signature & length - if (tcp_header->signature != lv2_socket::p2ps_i::U2S_sig) - { - sys_net.trace("Received P2P packet targeted at unbound vport(vport=%d)", dst_vport); - return true; - } - - if (tcp_header->length != (sp_size - sizeof(lv2_socket::p2ps_i::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 != nt_p2p_port::tcp_checksum(reinterpret_cast(sp_data), sp_size)) - { - sys_net.error("Checksum is invalid, dropping packet!"); - return true; - } - - // The packet is valid, check if it's bound - const u64 key_connected = (reinterpret_cast(&native_addr)->sin_addr.s_addr) | (static_cast(tcp_header->src_port) << 48) | (static_cast(tcp_header->dst_port) << 32); - const u64 key_listening = (static_cast(tcp_header->dst_port) << 32); - - { - std::lock_guard lock(bound_p2p_vports_mutex); - if (bound_p2p_streams.contains(key_connected)) - { - const auto sock_id = bound_p2p_streams.at(key_connected); - sys_net.trace("Received packet for connected STREAM-P2P socket(s=%d)", sock_id); - handle_connected(sock_id, tcp_header, sp_data + sizeof(lv2_socket::p2ps_i::encapsulated_tcp), &native_addr); - return true; - } - - if (bound_p2p_streams.contains(key_listening)) - { - const auto sock_id = bound_p2p_streams.at(key_listening); - sys_net.trace("Received packet for listening STREAM-P2P socket(s=%d)", sock_id); - handle_listening(sock_id, tcp_header, sp_data + sizeof(lv2_socket::p2ps_i::encapsulated_tcp), &native_addr); - return true; - } - } - - sys_net.trace("Received a STREAM-P2P packet with no bound target"); - return true; - } -}; - -struct network_thread -{ - std::vector s_to_awake; - shared_mutex s_nw_mutex; - - shared_mutex list_p2p_ports_mutex; - std::map list_p2p_ports{}; - - static constexpr auto thread_name = "Network Thread"; - - network_thread() noexcept - { -#ifdef _WIN32 - WSADATA wsa_data; - WSAStartup(MAKEWORD(2, 2), &wsa_data); -#endif - if (g_cfg.net.psn_status == np_psn_status::psn_rpcn) - list_p2p_ports.emplace(std::piecewise_construct, std::forward_as_tuple(3658), std::forward_as_tuple(3658)); - } - - ~network_thread() - { -#ifdef _WIN32 - WSACleanup(); -#endif - } - - void operator()() - { - std::vector> socklist; - socklist.reserve(lv2_socket::id_count); - - s_to_awake.clear(); - - ::pollfd fds[lv2_socket::id_count]{}; -#ifdef _WIN32 - bool connecting[lv2_socket::id_count]{}; - bool was_connecting[lv2_socket::id_count]{}; -#endif - - ::pollfd p2p_fd[lv2_socket::id_count]{}; - - while (thread_ctrl::state() != thread_state::aborting) - { - // Wait with 1ms timeout -#ifdef _WIN32 - windows_poll(fds, ::size32(socklist), 1, connecting); -#else - ::poll(fds, socklist.size(), 1); -#endif - - // Check P2P sockets for incoming packets(timeout could probably be set at 0) - { - std::lock_guard lock(list_p2p_ports_mutex); - std::memset(p2p_fd, 0, sizeof(p2p_fd)); - auto num_p2p_sockets = 0; - for (const auto& p2p_port : list_p2p_ports) - { - p2p_fd[num_p2p_sockets].events = POLLIN; - p2p_fd[num_p2p_sockets].revents = 0; - p2p_fd[num_p2p_sockets].fd = p2p_port.second.p2p_socket; - num_p2p_sockets++; - } - - if (num_p2p_sockets) - { -#ifdef _WIN32 - const auto ret_p2p = WSAPoll(p2p_fd, num_p2p_sockets, 1); -#else - const auto ret_p2p = ::poll(p2p_fd, num_p2p_sockets, 1); -#endif - if (ret_p2p > 0) - { - auto fd_index = 0; - for (auto& p2p_port : list_p2p_ports) - { - if ((p2p_fd[fd_index].revents & POLLIN) == POLLIN || (p2p_fd[fd_index].revents & POLLRDNORM) == POLLRDNORM) - { - while(p2p_port.second.recv_data()); - } - fd_index++; - } - } - else if (ret_p2p < 0) - { - sys_net.error("[P2P] Error poll on master P2P socket: %d", get_last_error(false)); - } - } - } - - std::lock_guard lock(s_nw_mutex); - - for (usz i = 0; i < socklist.size(); i++) - { - bs_t events{}; - - [[maybe_unused]] lv2_socket& sock = *socklist[i]; - - if (fds[i].revents & (POLLIN | POLLHUP) && socklist[i]->events.test_and_reset(lv2_socket::poll::read)) - events += lv2_socket::poll::read; - if (fds[i].revents & POLLOUT && socklist[i]->events.test_and_reset(lv2_socket::poll::write)) - events += lv2_socket::poll::write; - if (fds[i].revents & POLLERR && socklist[i]->events.test_and_reset(lv2_socket::poll::error)) - events += lv2_socket::poll::error; - - if (events) - { - std::lock_guard lock(socklist[i]->mutex); - -#ifdef _WIN32 - if (was_connecting[i] && !connecting[i]) - sock.is_connecting = false; -#endif - - for (auto it = socklist[i]->queue.begin(); events && it != socklist[i]->queue.end();) - { - if (it->second(events)) - { - it = socklist[i]->queue.erase(it); - continue; - } - - it++; - } - - if (socklist[i]->queue.empty()) - { - socklist[i]->events.store({}); - } - } - } - - s_to_awake.erase(std::unique(s_to_awake.begin(), s_to_awake.end()), s_to_awake.end()); - - for (ppu_thread* ppu : s_to_awake) - { - network_clear_queue(*ppu); - lv2_obj::append(ppu); - } - - if (!s_to_awake.empty()) - { - lv2_obj::awake_all(); - } - - s_to_awake.clear(); - socklist.clear(); - - // Obtain all non P2P active sockets - idm::select([&](u32 id, lv2_socket& s) - { - if(s.type != SYS_NET_SOCK_DGRAM_P2P && s.type != SYS_NET_SOCK_STREAM_P2P) - socklist.emplace_back(idm::get_unlocked(id)); - }); - - for (usz i = 0; i < socklist.size(); i++) - { -#ifdef _WIN32 - std::lock_guard lock(socklist[i]->mutex); -#endif - - auto events = socklist[i]->events.load(); - - fds[i].fd = events ? socklist[i]->socket : -1; - fds[i].events = - (events & lv2_socket::poll::read ? POLLIN : 0) | - (events & lv2_socket::poll::write ? POLLOUT : 0) | - 0; - fds[i].revents = 0; -#ifdef _WIN32 - was_connecting[i] = socklist[i]->is_connecting; - connecting[i] = socklist[i]->is_connecting; -#endif - } - } - } -}; - -using network_context = named_thread; - -// Used by RPCN to send signaling packets to RPCN server(for UDP hole punching) -s32 send_packet_from_p2p_port(const std::vector& data, const sockaddr_in& addr) -{ - s32 res{}; - auto& nc = g_fxo->get(); - { - std::lock_guard list_lock(nc.list_p2p_ports_mutex); - if (nc.list_p2p_ports.contains(3658)) - { - auto& def_port = nc.list_p2p_ports.at(3658); - res = ::sendto(def_port.p2p_socket, reinterpret_cast(data.data()), data.size(), 0, reinterpret_cast(&addr), sizeof(sockaddr_in)); - } - else - { - sys_net.error("send_packet_from_p2p_port: port %d not present", 3658); - } - } - - return res; -} - -std::vector> get_rpcn_msgs() -{ - std::vector> msgs; - auto& nc = g_fxo->get(); - { - std::lock_guard list_lock(nc.list_p2p_ports_mutex); - if (nc.list_p2p_ports.contains(3658)) - { - auto& def_port = nc.list_p2p_ports.at(3658); - { - 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", 3658); - } - } - - return msgs; -} - -std::vector, std::vector>> get_sign_msgs() -{ - std::vector, std::vector>> msgs; - auto& nc = g_fxo->get(); - { - std::lock_guard list_lock(nc.list_p2p_ports_mutex); - if (nc.list_p2p_ports.contains(3658)) - { - auto& def_port = nc.list_p2p_ports.at(3658); - { - 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", 3658); - } - } - - return msgs; -} - - -lv2_socket::lv2_socket(lv2_socket::socket_type s, s32 s_type, s32 family) - : socket(s), type{s_type}, family{family} -{ - if (socket) - { - // Set non-blocking -#ifdef _WIN32 - u_long _true = 1; - ::ioctlsocket(socket, FIONBIO, &_true); -#else - ::fcntl(socket, F_SETFL, ::fcntl(socket, F_GETFL, 0) | O_NONBLOCK); -#endif - } -} - -lv2_socket::~lv2_socket() -{ - if (type != SYS_NET_SOCK_DGRAM_P2P && type != SYS_NET_SOCK_STREAM_P2P && socket) - { -#ifdef _WIN32 - ::closesocket(socket); -#else - ::close(socket); -#endif - } -} - -extern void need_network() -{ - g_fxo->need(); - g_fxo->need>(); } error_code sys_net_bnet_accept(ppu_thread& ppu, s32 s, vm::ptr addr, vm::ptr paddrlen) @@ -1358,126 +271,47 @@ error_code sys_net_bnet_accept(ppu_thread& ppu, s32 s, vm::ptr return -SYS_NET_EINVAL; } - lv2_socket::socket_type native_socket = -1; - ::sockaddr_storage native_addr; - ::socklen_t native_addrlen = sizeof(native_addr); s32 result = 0; - bool p2ps = false; - s32 p2ps_result = 0; + sys_net_sockaddr sn_addr{}; const auto sock = idm::check(s, [&](lv2_socket& sock) - { - std::lock_guard lock(sock.mutex); - - if (sock.type == SYS_NET_SOCK_STREAM_P2P) { - const auto accept_success = [&]() - { - p2ps_result = sock.p2ps.backlog.front(); - sock.p2ps.backlog.pop(); - - if (addr) - { - auto* sock_client = idm::check_unlocked(p2ps_result); - std::lock_guard slock(sock_client->mutex); - sys_net_sockaddr_in_p2p* addr_p2p = reinterpret_cast(addr.get_ptr()); - addr_p2p->sin_family = AF_INET; - addr_p2p->sin_addr = std::bit_cast, u32>(sock_client->p2ps.op_addr); - addr_p2p->sin_port = sock_client->p2ps.op_vport; - addr_p2p->sin_vport = sock_client->p2ps.op_port; - addr_p2p->sin_len = sizeof(sys_net_sockaddr_in_p2p); - } - }; - - p2ps = true; - if (sock.p2ps.backlog.size() == 0) - { - if (sock.so_nbio) - { - result = SYS_NET_EWOULDBLOCK; - return false; - } - - sock.events += lv2_socket::poll::read; - sock.queue.emplace_back(ppu.id, [&](bs_t events) -> bool - { - if ((events & lv2_socket::poll::read) && sock.p2ps.backlog.size()) - { - sys_net.trace("Now found a socket in backlog!"); - accept_success(); - lv2_obj::awake(&ppu); - return true; - } - - sock.events += lv2_socket::poll::read; - return false; - }); - - lv2_obj::sleep(ppu); - return false; - } - - sys_net.trace("Found a socket in backlog!"); - accept_success(); - - return true; - } - - //if (!(sock.events & lv2_socket::poll::read)) - { - native_socket = ::accept(sock.socket, reinterpret_cast(&native_addr), &native_addrlen); - - if (native_socket != -1) + const auto [success, res, res_addr] = sock.accept(); + + if (success) { + result = res; + sn_addr = res_addr; return true; } - result = get_last_error(!sock.so_nbio); - - if (result) - { - return false; - } - } - - // Enable read event - sock.events += lv2_socket::poll::read; - sock.queue.emplace_back(ppu.id, [&](bs_t events) -> bool - { - if (events & lv2_socket::poll::read) - { - native_socket = ::accept(sock.socket, reinterpret_cast(&native_addr), &native_addrlen); - - if (native_socket != -1 || (result = get_last_error(!sock.so_nbio))) + sock.poll_queue(ppu.id, lv2_socket::poll_t::read, [&](bs_t events) -> bool { - lv2_obj::awake(&ppu); - return true; - } - } + if (events & lv2_socket::poll_t::read) + { + auto [success, res, res_addr] = sock.accept(false); + if (success) + { + result = res; + sn_addr = res_addr; + lv2_obj::awake(&ppu); + return success; + } + } - sock.events += lv2_socket::poll::read; + sock.set_poll_event(lv2_socket::poll_t::read); + return false; + }); + + lv2_obj::sleep(ppu); return false; }); - lv2_obj::sleep(ppu); - return false; - }); - if (!sock) { return -SYS_NET_EBADF; } - if (!sock.ret && result) - { - if (result == SYS_NET_EWOULDBLOCK) - { - return not_an_error(-result); - } - - return -sys_net_error{result}; - } - if (!sock.ret) { while (auto state = ppu.state.fetch_sub(cpu_flag::signal)) @@ -1495,20 +329,20 @@ error_code sys_net_bnet_accept(ppu_thread& ppu, s32 s, vm::ptr thread_ctrl::wait_on(ppu.state, state); } - if (result) - { - return -sys_net_error{result}; - } - if (ppu.gpr[3] == static_cast(-SYS_NET_EINTR)) { - return -SYS_NET_EINTR; + return -sys_net_error{SYS_NET_EINTR}; + } + + if (result < 0) + { + return sys_net_error{result}; } } - if (p2ps) + if (result < 0) { - return not_an_error(p2ps_result); + return sys_net_error{result}; } if (ppu.is_stopped()) @@ -1516,31 +350,13 @@ error_code sys_net_bnet_accept(ppu_thread& ppu, s32 s, vm::ptr return {}; } - auto newsock = std::make_shared(native_socket, 0, 0); - - result = idm::import_existing(newsock); - - if (result == id_manager::id_traits::invalid) - { - return -SYS_NET_EMFILE; - } - if (addr) { - ensure(native_addr.ss_family == AF_INET); - - vm::ptr paddr = vm::cast(addr.addr()); - - *paddrlen = sizeof(sys_net_sockaddr_in); - - paddr->sin_len = sizeof(sys_net_sockaddr_in); - paddr->sin_family = SYS_NET_AF_INET; - paddr->sin_port = std::bit_cast, u16>(reinterpret_cast(&native_addr)->sin_port); - paddr->sin_addr = std::bit_cast, u32>(reinterpret_cast(&native_addr)->sin_addr.s_addr); - paddr->sin_zero = 0; + *paddrlen = sizeof(sys_net_sockaddr_in); + *addr = sn_addr; } - // Socket id + // Socket ID return not_an_error(result); } @@ -1555,114 +371,24 @@ error_code sys_net_bnet_bind(ppu_thread& ppu, s32 s, vm::cptr return -SYS_NET_EINVAL; } - alignas(16) char addr_buf[sizeof(sys_net_sockaddr)]; - - if (idm::check(s)) - { - std::memcpy(addr_buf, addr.get_ptr(), addr.size()); - } - else + if (!idm::check(s)) { return -SYS_NET_EBADF; } - const auto psa_in = reinterpret_cast(addr_buf); - const auto _addr = reinterpret_cast(addr_buf); + const sys_net_sockaddr sn_addr = *addr; - ::sockaddr_in name{}; - name.sin_family = AF_INET; - name.sin_port = std::bit_cast(psa_in->sin_port); - name.sin_addr.s_addr = std::bit_cast(psa_in->sin_addr); - ::socklen_t namelen = sizeof(name); - - const auto sock = idm::check(s, [&](lv2_socket& sock) -> sys_net_error + // 0 presumably defaults to AF_INET(to check?) + if (sn_addr.sa_family != SYS_NET_AF_INET && sn_addr.sa_family != SYS_NET_AF_UNSPEC) { - // 0 presumably defaults to AF_INET(to check?) - if (_addr->sa_family != SYS_NET_AF_INET && _addr->sa_family != 0) + sys_net.error("sys_net_bnet_bind: unsupported sa_family (%d)", sn_addr.sa_family); + return -SYS_NET_EAFNOSUPPORT; + } + + const auto sock = idm::check(s, [&](lv2_socket& sock) -> s32 { - sys_net.error("sys_net_bnet_bind(s=%d): unsupported sa_family (%d)", s, addr->sa_family); - return SYS_NET_EAFNOSUPPORT; - } - - if (sock.type == SYS_NET_SOCK_DGRAM_P2P || sock.type == SYS_NET_SOCK_STREAM_P2P) - { - auto psa_in_p2p = reinterpret_cast(psa_in); - u16 p2p_port, p2p_vport; - if (sock.type == SYS_NET_SOCK_DGRAM_P2P) - { - p2p_port = psa_in_p2p->sin_port; - p2p_vport = psa_in_p2p->sin_vport; - } - else - { - // For SYS_NET_SOCK_STREAM_P2P sockets, the port is the "fake" tcp port and the vport is the udp port it's bound to - p2p_port = psa_in_p2p->sin_vport; - p2p_vport = psa_in_p2p->sin_port; - } - - char ip_str[16]; - inet_ntop(AF_INET, &name.sin_addr, ip_str, sizeof(ip_str)); - sys_net.notice("[P2P] %s, Socket bind to %s:%d:%d", sock.type, ip_str, p2p_port, p2p_vport); - - if (p2p_port != 3658) - { - sys_net.warning("[P2P] Attempting to bind a socket to a port != 3658"); - } - ensure(p2p_vport != 0); - - lv2_socket::socket_type real_socket{}; - - auto& nc = g_fxo->get(); - { - std::lock_guard list_lock(nc.list_p2p_ports_mutex); - if (!nc.list_p2p_ports.contains(p2p_port)) - { - nc.list_p2p_ports.emplace(std::piecewise_construct, std::forward_as_tuple(p2p_port), std::forward_as_tuple(p2p_port)); - } - - auto& pport = nc.list_p2p_ports.at(p2p_port); - real_socket = pport.p2p_socket; - { - std::lock_guard lock(pport.bound_p2p_vports_mutex); - if (sock.type == SYS_NET_SOCK_DGRAM_P2P) - { - if (pport.bound_p2p_vports.count(p2p_vport) != 0) - { - return sys_net_error::SYS_NET_EADDRINUSE; - } - pport.bound_p2p_vports.insert(std::make_pair(p2p_vport, s)); - } - else - { - const u64 key = (static_cast(p2p_vport) << 32); - pport.bound_p2p_streams.emplace(key, s); - } - } - } - - { - std::lock_guard lock(sock.mutex); - sock.p2p.port = p2p_port; - sock.p2p.vport = p2p_vport; - sock.socket = real_socket; - } - - return {}; - } - else - { - sys_net.warning("Trying to bind %s:%d", name.sin_addr, std::bit_cast, u16>(name.sin_port)); // ntohs(name.sin_port) - } - - std::lock_guard lock(sock.mutex); - - if (::bind(sock.socket, reinterpret_cast(&name), namelen) == 0) - { - return {}; - } - - return get_last_error(false); - }); + return sock.bind(sn_addr, s); + }); if (!sock) { @@ -1671,7 +397,7 @@ error_code sys_net_bnet_bind(ppu_thread& ppu, s32 s, vm::cptr if (sock.ret) { - return -sock.ret; + return sys_net_error{sock.ret}; } return CELL_OK; @@ -1688,225 +414,50 @@ error_code sys_net_bnet_connect(ppu_thread& ppu, s32 s, vm::ptr(addr_buf.buf); - const auto _addr = reinterpret_cast(addr_buf.buf); - - s32 result = 0; - ::sockaddr_in name{}; - name.sin_family = AF_INET; - - if (idm::check(s)) - { - std::memcpy(addr_buf.buf, addr.get_ptr(), 16); - name.sin_port = std::bit_cast(psa_in->sin_port); - name.sin_addr.s_addr = std::bit_cast(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 (name.sin_addr.s_addr == 0x00000000) - { - name.sin_addr.s_addr = 0x0100007F; - } -#endif - } - else + if (!idm::check(s)) { return -SYS_NET_EBADF; } - ::socklen_t namelen = sizeof(name); - - sys_net.notice("Attempting to connect on %s:%d", name.sin_addr, std::bit_cast, u16>(name.sin_port)); // ntohs(name.sin_port) + s32 result = 0; + sys_net_sockaddr sn_addr = *addr; const auto sock = idm::check(s, [&](lv2_socket& sock) - { - if (sock.type == SYS_NET_SOCK_STREAM_P2P) { - lv2_socket::p2ps_i::encapsulated_tcp send_hdr; - const auto psa_in_p2p = reinterpret_cast(addr_buf.buf); + const auto success = sock.connect(sn_addr); + + if (success) { - std::lock_guard lock(sock.mutex); - // This is purposefully inverted, not a bug - const u16 dst_vport = psa_in_p2p->sin_port; - const u16 dst_port = psa_in_p2p->sin_vport; - - lv2_socket::socket_type real_socket{}; - - auto& nc = g_fxo->get(); - { - std::lock_guard list_lock(nc.list_p2p_ports_mutex); - if (!nc.list_p2p_ports.contains(sock.p2p.port)) - nc.list_p2p_ports.emplace(std::piecewise_construct, std::forward_as_tuple(sock.p2p.port), std::forward_as_tuple(sock.p2p.port)); - - auto& pport = nc.list_p2p_ports.at(sock.p2p.port); - real_socket = pport.p2p_socket; - { - std::lock_guard lock(pport.bound_p2p_vports_mutex); - if (sock.p2p.vport == 0) - { - // Unassigned vport, assigns one - sys_net.warning("vport was unassigned before connect!"); - u16 found_vport = 30000; - while (true) - { - found_vport++; - if (pport.bound_p2p_vports.count(found_vport)) - continue; - if (pport.bound_p2p_streams.count(static_cast(found_vport) << 32)) - continue; - - break; - } - sock.p2p.vport = found_vport; - } - const u64 key = name.sin_addr.s_addr | (static_cast(sock.p2p.vport) << 32) | (static_cast(dst_vport) << 48); - pport.bound_p2p_streams.emplace(key, s); - } - } - - sock.socket = real_socket; - - send_hdr.src_port = sock.p2p.vport; - send_hdr.dst_port = dst_vport; - send_hdr.flags = lv2_socket::p2ps_i::SYN; - send_hdr.seq = rand(); - - // sock.socket = p2p_socket; - sock.p2ps.op_addr = name.sin_addr.s_addr; - sock.p2ps.op_port = dst_port; - sock.p2ps.op_vport = dst_vport; - sock.p2ps.cur_seq = send_hdr.seq + 1; - sock.p2ps.data_beg_seq = 0; - sock.p2ps.data_available = 0u; - sock.p2ps.received_data.clear(); - sock.p2ps.status = lv2_socket::p2ps_i::stream_status::stream_handshaking; - - std::vector packet = nt_p2p_port::generate_u2s_packet(send_hdr, nullptr, 0); - name.sin_port = std::bit_cast(psa_in_p2p->sin_vport); // not a bug - nt_p2p_port::send_u2s_packet(sock, s, std::move(packet), reinterpret_cast<::sockaddr_in*>(&name), send_hdr.seq); - } - - return true; - } - - std::lock_guard lock(sock.mutex); - - if (psa_in->sin_port == 53) - { - auto& dnshook = g_fxo->get(); - auto& nph = g_fxo->get>(); - - // Hack for DNS - name.sin_port = std::bit_cast>(53); - name.sin_addr.s_addr = nph.get_dns_ip(); - - sys_net.notice("sys_net_bnet_connect: using DNS..."); - - dnshook.add_dns_spy(s); - } - else if (_addr->sa_family != SYS_NET_AF_INET) - { - sys_net.error("sys_net_bnet_connect(s=%d): unsupported sa_family (%d)", s, _addr->sa_family); - } - - if (::connect(sock.socket, reinterpret_cast(&name), namelen) == 0) - { - return true; - } - - const bool is_blocking = !sock.so_nbio; - - result = get_last_error(is_blocking); - - if (result) - { - if (result == SYS_NET_EWOULDBLOCK) - { - result = SYS_NET_EINPROGRESS; - } - - if (result == SYS_NET_EINPROGRESS) - { -#ifdef _WIN32 - sock.is_connecting = true; -#endif - sock.events += lv2_socket::poll::write; - sock.queue.emplace_back(u32{0}, [&sock](bs_t events) -> bool - { - if (events & lv2_socket::poll::write) - { - int native_error; - ::socklen_t size = sizeof(native_error); - if (::getsockopt(sock.socket, SOL_SOCKET, SO_ERROR, reinterpret_cast(&native_error), &size) != 0 || size != sizeof(int)) - { - sock.so_error = 1; - } - else - { - // TODO: check error formats (both native and translated) - sock.so_error = native_error ? get_last_error(false, native_error) : 0; - } - - return true; - } - - sock.events += lv2_socket::poll::write; - return false; - }); - } - - return false; - } - -#ifdef _WIN32 - sock.is_connecting = true; -#endif - sock.events += lv2_socket::poll::write; - sock.queue.emplace_back(ppu.id, [&](bs_t events) -> bool - { - if (events & lv2_socket::poll::write) - { - int native_error; - ::socklen_t size = sizeof(native_error); - if (::getsockopt(sock.socket, SOL_SOCKET, SO_ERROR, reinterpret_cast(&native_error), &size) != 0 || size != sizeof(int)) - { - result = 1; - } - else - { - // TODO: check error formats (both native and translated) - result = native_error ? get_last_error(false, native_error) : 0; - } - - lv2_obj::awake(&ppu); + result = *success; return true; } - sock.events += lv2_socket::poll::write; + sock.poll_queue(ppu.id, lv2_socket::poll_t::write, [&](bs_t events) -> bool + { + if (events & lv2_socket::poll_t::write) + { + result = sock.connect_followup(); + + lv2_obj::awake(&ppu); + return true; + } + sock.set_poll_event(lv2_socket::poll_t::write); + return false; + }); + + lv2_obj::sleep(ppu); + return false; }); - lv2_obj::sleep(ppu); - return false; - }); - if (!sock) { return -SYS_NET_EBADF; } - if (!sock.ret && result) + if (sock.ret) { - if (result == SYS_NET_EWOULDBLOCK || result == SYS_NET_EINPROGRESS) - { - return not_an_error(-result); - } - - return -sys_net_error{result}; + return (result < 0) ? sys_net_error{result} : result; } if (!sock.ret) @@ -1926,15 +477,15 @@ error_code sys_net_bnet_connect(ppu_thread& ppu, s32 s, vm::ptr(-SYS_NET_EINTR)) { return -SYS_NET_EINTR; } + + if (result) + { + return (result < 0) ? sys_net_error{result} : result; + } } return CELL_OK; @@ -1952,24 +503,18 @@ error_code sys_net_bnet_getpeername(ppu_thread& ppu, s32 s, vm::ptr(s, [&](lv2_socket& sock) -> sys_net_error - { - std::lock_guard lock(sock.mutex); - - ensure(sock.type != SYS_NET_SOCK_DGRAM_P2P); - - if (::getpeername(sock.socket, reinterpret_cast(&native_addr), &native_addrlen) == 0) { - ensure(native_addr.ss_family == AF_INET); + auto [res, sn_addr] = sock.getpeername(); - return {}; - } + if (res == CELL_OK) + { + *paddrlen = sizeof(sys_net_sockaddr); + *addr = sn_addr; + } - return get_last_error(false); - }); + return sys_net_error{res}; + }); if (!sock) { @@ -1978,19 +523,9 @@ error_code sys_net_bnet_getpeername(ppu_thread& ppu, s32 s, vm::ptr paddr = vm::cast(addr.addr()); - - paddr->sin_len = sizeof(sys_net_sockaddr_in); - paddr->sin_family = SYS_NET_AF_INET; - paddr->sin_port = std::bit_cast, u16>(reinterpret_cast(&native_addr)->sin_port); - paddr->sin_addr = std::bit_cast, u32>(reinterpret_cast(&native_addr)->sin_addr.s_addr); - paddr->sin_zero = 0; - return CELL_OK; } @@ -2006,44 +541,18 @@ error_code sys_net_bnet_getsockname(ppu_thread& ppu, s32 s, vm::ptr(s, [&](lv2_socket& sock) -> sys_net_error - { - std::lock_guard lock(sock.mutex); - - type = sock.type; - p2p_vport = sock.p2p.vport; - - // Unbound P2P socket special case - if ((sock.type == SYS_NET_SOCK_DGRAM_P2P || sock.type == SYS_NET_SOCK_STREAM_P2P) && sock.socket == 0) + const auto sock = idm::check(s, [&](lv2_socket& sock) -> s32 { - return {}; - } + auto [res, sn_addr] = sock.getsockname(); - if (::getsockname(sock.socket, reinterpret_cast(&native_addr), &native_addrlen) == 0) - { - ensure(native_addr.ss_family == AF_INET); - - return {}; - } - #ifdef _WIN32 - else - { - // windows doesn't support getsockname for sockets that are not bound - if (get_native_error() == WSAEINVAL) + if (res == CELL_OK) { - return {}; + *paddrlen = sizeof(sys_net_sockaddr); + *addr = sn_addr; } - } - #endif - return get_last_error(false); - }); + return res; + }); if (!sock) { @@ -2052,25 +561,7 @@ error_code sys_net_bnet_getsockname(ppu_thread& ppu, s32 s, vm::ptr paddr = vm::cast(addr.addr()); - paddr->sin_len = sizeof(sys_net_sockaddr_in); - paddr->sin_family = SYS_NET_AF_INET; - paddr->sin_port = std::bit_cast, u16>(reinterpret_cast(&native_addr)->sin_port); - paddr->sin_addr = std::bit_cast, u32>(reinterpret_cast(&native_addr)->sin_addr.s_addr); - paddr->sin_zero = 0; - - if (type == SYS_NET_SOCK_DGRAM_P2P) - { - vm::ptr paddr_p2p = vm::cast(addr.addr()); - paddr_p2p->sin_vport = p2p_vport; - struct in_addr rep; - rep.s_addr = htonl(paddr->sin_addr); - sys_net.trace("[P2P] Reporting socket address as %s:%d:%d", rep, paddr_p2p->sin_port, paddr_p2p->sin_vport); + return sys_net_error{sock.ret}; } return CELL_OK; @@ -2108,283 +599,30 @@ error_code sys_net_bnet_getsockopt(ppu_thread& ppu, s32 s, s32 level, s32 optnam return -SYS_NET_EINVAL; } - int native_level = -1; - int native_opt = -1; - - union - { - char ch[128]; - int _int = 0; - ::timeval timeo; - ::linger linger; - } native_val; - ::socklen_t native_len = sizeof(native_val); - - union - { - char ch[128]; - be_t _int = 0; - sys_net_timeval timeo; - sys_net_linger linger; - } out_val; - u32 out_len = sizeof(out_val); - const auto sock = idm::check(s, [&](lv2_socket& sock) -> sys_net_error - { - std::lock_guard lock(sock.mutex); - - if (len < sizeof(s32)) { - return SYS_NET_EINVAL; - } + if (len < sizeof(s32)) + { + return -SYS_NET_EINVAL; + } - if (level == SYS_NET_SOL_SOCKET) - { - native_level = SOL_SOCKET; + const auto [res, out_val, out_len] = sock.getsockopt(level, optname, *optlen); - switch (optname) + if (res == CELL_OK) { - case SYS_NET_SO_NBIO: - { - // Special - out_val._int = sock.so_nbio; - out_len = sizeof(s32); - return {}; + std::memcpy(optval.get_ptr(), out_val.ch, out_len); + *optlen = out_len; } - case SYS_NET_SO_ERROR: - { - // Special - out_val._int = std::exchange(sock.so_error, 0); - out_len = sizeof(s32); - return {}; - } - case SYS_NET_SO_KEEPALIVE: - { - native_opt = SO_KEEPALIVE; - break; - } - case SYS_NET_SO_SNDBUF: - { - native_opt = SO_SNDBUF; - break; - } - case SYS_NET_SO_RCVBUF: - { - native_opt = SO_RCVBUF; - break; - } - case SYS_NET_SO_SNDLOWAT: - { - native_opt = SO_SNDLOWAT; - break; - } - case SYS_NET_SO_RCVLOWAT: - { - native_opt = SO_RCVLOWAT; - break; - } - case SYS_NET_SO_BROADCAST: - { - native_opt = SO_BROADCAST; - break; - } -#ifdef _WIN32 - case SYS_NET_SO_REUSEADDR: - { - out_val._int = sock.so_reuseaddr; - out_len = sizeof(s32); - return {}; - } - case SYS_NET_SO_REUSEPORT: - { - out_val._int = sock.so_reuseport; - out_len = sizeof(s32); - return {}; - } -#else - case SYS_NET_SO_REUSEADDR: - { - native_opt = SO_REUSEADDR; - break; - } - case SYS_NET_SO_REUSEPORT: - { - native_opt = SO_REUSEPORT; - break; - } -#endif - case SYS_NET_SO_SNDTIMEO: - case SYS_NET_SO_RCVTIMEO: - { - if (len < sizeof(sys_net_timeval)) - return SYS_NET_EINVAL; - native_opt = optname == SYS_NET_SO_SNDTIMEO ? SO_SNDTIMEO : SO_RCVTIMEO; - break; - } - case SYS_NET_SO_LINGER: - { - if (len < sizeof(sys_net_linger)) - return SYS_NET_EINVAL; - - native_opt = SO_LINGER; - break; - } - default: - { - sys_net.error("sys_net_bnet_getsockopt(s=%d, SOL_SOCKET): unknown option (0x%x)", s, optname); - return SYS_NET_EINVAL; - } - } - } - else if (level == SYS_NET_IPPROTO_TCP) - { - native_level = IPPROTO_TCP; - - switch (optname) - { - case SYS_NET_TCP_MAXSEG: - { - // Special (no effect) - out_val._int = sock.so_tcp_maxseg; - out_len = sizeof(s32); - return {}; - } - case SYS_NET_TCP_NODELAY: - { - native_opt = TCP_NODELAY; - break; - } - default: - { - sys_net.error("sys_net_bnet_getsockopt(s=%d, IPPROTO_TCP): unknown option (0x%x)", s, optname); - return SYS_NET_EINVAL; - } - } - } - else if (level == SYS_NET_IPPROTO_IP) - { - native_level = IPPROTO_IP; - switch (optname) - { - case SYS_NET_IP_HDRINCL: - { - native_opt = IP_HDRINCL; - break; - } - case SYS_NET_IP_TOS: - { - native_opt = IP_TOS; - break; - } - case SYS_NET_IP_TTL: - { - native_opt = IP_TTL; - break; - } - case SYS_NET_IP_MULTICAST_IF: - { - native_opt = IP_MULTICAST_IF; - break; - } - case SYS_NET_IP_MULTICAST_TTL: - { - native_opt = IP_MULTICAST_TTL; - break; - } - case SYS_NET_IP_MULTICAST_LOOP: - { - native_opt = IP_MULTICAST_LOOP; - break; - } - case SYS_NET_IP_ADD_MEMBERSHIP: - { - native_opt = IP_ADD_MEMBERSHIP; - break; - } - case SYS_NET_IP_DROP_MEMBERSHIP: - { - native_opt = IP_DROP_MEMBERSHIP; - break; - } - case SYS_NET_IP_TTLCHK: - { - sys_net.error("sys_net_bnet_getsockopt(s=%d, IPPROTO_IP): Stubbed option (0x%x) (SYS_NET_IP_TTLCHK)", s, optname); - return {}; - } - case SYS_NET_IP_MAXTTL: - { - sys_net.error("sys_net_bnet_getsockopt(s=%d, IPPROTO_IP): Stubbed option (0x%x) (SYS_NET_IP_MAXTTL)", s, optname); - return {}; - } - case SYS_NET_IP_DONTFRAG: - { - #ifdef _WIN32 - native_opt = IP_DONTFRAGMENT; - #else - native_opt = IP_DF; - #endif - break; - } - default: - { - sys_net.error("sys_net_bnet_getsockopt(s=%d, IPPROTO_IP): unknown option (0x%x)", s, optname); - return SYS_NET_EINVAL; - } - } - } - else - { - sys_net.error("sys_net_bnet_getsockopt(s=%d): unknown level (0x%x)", s, level); - return SYS_NET_EINVAL; - } - - if (::getsockopt(sock.socket, native_level, native_opt, native_val.ch, &native_len) != 0) - { - return get_last_error(false); - } - - if (level == SYS_NET_SOL_SOCKET) - { - switch (optname) - { - case SYS_NET_SO_SNDTIMEO: - case SYS_NET_SO_RCVTIMEO: - { - // TODO - out_val.timeo = { ::narrow(native_val.timeo.tv_sec), ::narrow(native_val.timeo.tv_usec) }; - out_len = sizeof(sys_net_timeval); - return {}; - } - case SYS_NET_SO_LINGER: - { - // TODO - out_val.linger = { ::narrow(native_val.linger.l_onoff), ::narrow(native_val.linger.l_linger) }; - out_len = sizeof(sys_net_linger); - return {}; - } - } - } - - // Fallback to int - out_val._int = native_val._int; - out_len = sizeof(s32); - return {}; - }); + return static_cast(res); + }); if (!sock) { return -SYS_NET_EBADF; } - if (sock.ret) - { - return -sock.ret; - } - - std::memcpy(optval.get_ptr(), out_val.ch, out_len); - *optlen = out_len; - return CELL_OK; + return sock.ret; } error_code sys_net_bnet_listen(ppu_thread& ppu, s32 s, s32 backlog) @@ -2399,24 +637,9 @@ error_code sys_net_bnet_listen(ppu_thread& ppu, s32 s, s32 backlog) } const auto sock = idm::check(s, [&](lv2_socket& sock) -> sys_net_error - { - std::lock_guard lock(sock.mutex); - - ensure(sock.type == SYS_NET_SOCK_STREAM_P2P || sock.type == SYS_NET_SOCK_STREAM); - - if (sock.type == SYS_NET_SOCK_STREAM_P2P) { - sock.p2ps.status = lv2_socket::p2ps_i::stream_status::stream_listening; - sock.p2ps.max_backlog = backlog; - return {}; - } - else if (::listen(sock.socket, backlog) == 0) - { - return {}; - } - - return get_last_error(false); - }); + return static_cast(sock.listen(backlog)); + }); if (!sock) { @@ -2425,7 +648,7 @@ error_code sys_net_bnet_listen(ppu_thread& ppu, s32 s, s32 backlog) if (sock.ret) { - return -sock.ret; + return sock.ret; } return CELL_OK; @@ -2448,246 +671,63 @@ error_code sys_net_bnet_recvfrom(ppu_thread& ppu, s32 s, vm::ptr buf, u32 fmt::throw_exception("sys_net_bnet_recvfrom(s=%d): unknown flags (0x%x)", flags); } - int native_flags = 0; - int native_result = -1; - ::sockaddr_storage native_addr; - ::socklen_t native_addrlen = sizeof(native_addr); - sys_net_error result{}; - std::vector _buf(len); - - if (flags & SYS_NET_MSG_PEEK) - { - native_flags |= MSG_PEEK; - } - - if (flags & SYS_NET_MSG_WAITALL) - { - native_flags |= MSG_WAITALL; - } - - s32 type = 0; + s32 result = 0; + sys_net_sockaddr sn_addr{}; const auto sock = idm::check(s, [&](lv2_socket& sock) - { - type = sock.type; - std::lock_guard lock(sock.mutex); - - //if (!(sock.events & lv2_socket::poll::read)) { - auto& dnshook = g_fxo->get(); - if (dnshook.is_dns(s) && dnshook.is_dns_queue(s)) + const auto success = sock.recvfrom(flags, len); + + if (success) { - auto& nph = g_fxo->get>(); - - const auto packet = dnshook.get_dns_packet(s); - ensure(packet.size() < len); - - memcpy(buf.get_ptr(), packet.data(), packet.size()); - native_result = ::narrow(packet.size()); - - native_addr.ss_family = AF_INET; - (reinterpret_cast<::sockaddr_in*>(&native_addr))->sin_port = std::bit_cast>(53); // htons(53) - (reinterpret_cast<::sockaddr_in*>(&native_addr))->sin_addr.s_addr = nph.get_dns_ip(); + const auto& [res, vec, res_addr] = *success; + if (res > 0) + { + sn_addr = res_addr; + std::memcpy(buf.get_ptr(), vec.data(), res); + sys_net_dump_data("recvfrom", vec.data(), res); + } + result = res; return true; } - if (sock.type == SYS_NET_SOCK_DGRAM_P2P) - { - sys_net.trace("[P2P] p2p_data for vport %d contains %d elements", sock.p2p.vport, sock.p2p.data.size()); - - if (sock.p2p.data.empty()) + sock.poll_queue(ppu.id, lv2_socket::poll_t::read, [&](bs_t events) -> bool { - result = SYS_NET_EWOULDBLOCK; - return false; - } - - const auto& p2p_data = sock.p2p.data.front(); - native_result = std::min(len, static_cast(p2p_data.second.size())); - memcpy(buf.get_ptr(), p2p_data.second.data(), native_result); - - if (addr) - { - *paddrlen = sizeof(sys_net_sockaddr_in); - memcpy(addr.get_ptr(), &p2p_data.first, addr.size()); - } - - sock.p2p.data.pop(); - - return true; - } - - if (sock.type == SYS_NET_SOCK_STREAM_P2P) - { - const auto get_data = [&](unsigned char *dest_buf) - { - const u32 to_give = std::min(sock.p2ps.data_available, len); - sys_net.trace("STREAM-P2P socket had %u available, given %u", sock.p2ps.data_available, to_give); - - u32 left_to_give = to_give; - while (left_to_give) + if (events & lv2_socket::poll_t::read) { - auto& cur_data = sock.p2ps.received_data.begin()->second; - auto to_give_for_this_packet = std::min(static_cast(cur_data.size()), left_to_give); - memcpy(reinterpret_cast(dest_buf) + (to_give - left_to_give), cur_data.data(), to_give_for_this_packet); - if (cur_data.size() != to_give_for_this_packet) + const auto success = sock.recvfrom(flags, len, false); + + if (success) { - auto amount_left = cur_data.size() - to_give_for_this_packet; - std::vector new_vec(amount_left); - memcpy(new_vec.data(), cur_data.data() + to_give_for_this_packet, amount_left); - auto new_key = (sock.p2ps.received_data.begin()->first) + to_give_for_this_packet; - sock.p2ps.received_data.emplace(new_key, std::move(new_vec)); - } - - sock.p2ps.received_data.erase(sock.p2ps.received_data.begin()); - - left_to_give -= to_give_for_this_packet; - } - - sock.p2ps.data_available -= to_give; - sock.p2ps.data_beg_seq += to_give; - native_result = to_give; - - if (addr) - { - sys_net_sockaddr_in_p2p* addr_p2p = reinterpret_cast(addr.get_ptr()); - addr_p2p->sin_family = AF_INET; - addr_p2p->sin_addr = std::bit_cast, u32>(sock.p2ps.op_addr); - addr_p2p->sin_port = sock.p2ps.op_vport; - addr_p2p->sin_vport = sock.p2ps.op_port; - addr_p2p->sin_len = sizeof(sys_net_sockaddr_in_p2p); - } - }; - - if (!sock.p2ps.data_available) - { - if (sock.so_nbio) - { - result = SYS_NET_EWOULDBLOCK; - return false; - } - - sock.events += lv2_socket::poll::read; - sock.queue.emplace_back(ppu.id, [&](bs_t events) -> bool - { - if (events & lv2_socket::poll::read) - { - if (sock.p2ps.data_available) + const auto& [res, vec, res_addr] = *success; + if (res > 0) { - get_data(_buf.data()); - lv2_obj::awake(&ppu); - return true; + sn_addr = res_addr; + std::memcpy(buf.get_ptr(), vec.data(), res); } + result = res; + lv2_obj::awake(&ppu); + return true; } - - sock.events += lv2_socket::poll::read; - return false; - }); - - lv2_obj::sleep(ppu); - return false; - } - - get_data(static_cast(buf.get_ptr())); - return true; - } - - native_result = ::recvfrom(sock.socket, static_cast(buf.get_ptr()), len, native_flags, reinterpret_cast(&native_addr), &native_addrlen); - - if (native_result >= 0) - { - if (sys_net_dump.enabled == logs::level::trace) - { - std::string datrace; - const char hex[16] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}; - - u8* dabuf = static_cast(buf.get_ptr()); - - for (s32 index = 0; index < native_result; index++) - { - if ((index % 16) == 0) - datrace += '\n'; - - datrace += hex[(dabuf[index] >> 4) & 15]; - datrace += hex[(dabuf[index]) & 15]; - datrace += ' '; } - sys_net.trace("recvfrom dump: %s", datrace); - } - return true; - } -#ifdef _WIN32 - else - { - // Windows returns an error when trying to peek at a message and buffer not long enough to contain the whole message, should be ignored - if ((native_flags & MSG_PEEK) && get_native_error() == WSAEMSGSIZE) - { - native_result = len; - return true; - } - // Windows will return WSASHUTDOWN when the connection is shutdown, POSIX just returns EOF (0) in this situation. - if( get_native_error() == WSAESHUTDOWN) - { - native_result = 0; - return true; - } - } -#endif + sock.set_poll_event(lv2_socket::poll_t::read); + return false; + }); - result = get_last_error(!sock.so_nbio && (flags & SYS_NET_MSG_DONTWAIT) == 0); - - if (result) - { - return false; - } - } - - // Enable read event - sock.events += lv2_socket::poll::read; - sock.queue.emplace_back(ppu.id, [&](bs_t events) -> bool - { - if (events & lv2_socket::poll::read) - { - native_result = ::recvfrom(sock.socket, reinterpret_cast(_buf.data()), len, native_flags, reinterpret_cast(&native_addr), &native_addrlen); - - if (native_result >= 0 || (result = get_last_error(!sock.so_nbio && (flags & SYS_NET_MSG_DONTWAIT) == 0))) - { - lv2_obj::awake(&ppu); - return true; - } - } - - sock.events += lv2_socket::poll::read; + lv2_obj::sleep(ppu); return false; }); - lv2_obj::sleep(ppu); - return false; - }); - if (!sock) { - if (type == SYS_NET_SOCK_DGRAM_P2P) - sys_net.error("[P2P] Error recvfrom(bad socket)"); return -SYS_NET_EBADF; } - if (!sock.ret && result) - { - if (result == SYS_NET_EWOULDBLOCK) - { - return not_an_error(-result); - } - - if (type == SYS_NET_SOCK_DGRAM_P2P) - sys_net.error("[P2P] Error recvfrom(result early): %d", result); - - return -result; - } - if (!sock.ret) { + while (auto state = ppu.state.fetch_sub(cpu_flag::signal)) { if (is_stopped(state)) @@ -2703,21 +743,10 @@ error_code sys_net_bnet_recvfrom(ppu_thread& ppu, s32 s, vm::ptr buf, u32 thread_ctrl::wait_on(ppu.state, state); } - if (result) - { - if (type == SYS_NET_SOCK_DGRAM_P2P) - sys_net.error("[P2P] Error recvfrom(result): %d", result); - return -result; - } - if (ppu.gpr[3] == static_cast(-SYS_NET_EINTR)) { - if (type == SYS_NET_SOCK_DGRAM_P2P) - sys_net.error("[P2P] Error recvfrom(interrupted)"); return -SYS_NET_EINTR; } - - std::memcpy(buf.get_ptr(), _buf.data(), len); } if (ppu.is_stopped()) @@ -2725,27 +754,23 @@ error_code sys_net_bnet_recvfrom(ppu_thread& ppu, s32 s, vm::ptr buf, u32 return {}; } - // addr is set earlier for P2P socket - if (addr && type != SYS_NET_SOCK_DGRAM_P2P && type != SYS_NET_SOCK_STREAM_P2P) + if (result == -SYS_NET_EWOULDBLOCK) { - ensure(native_addr.ss_family == AF_INET); - - vm::ptr paddr = vm::cast(addr.addr()); - - *paddrlen = sizeof(sys_net_sockaddr_in); - - paddr->sin_len = sizeof(sys_net_sockaddr_in); - paddr->sin_family = SYS_NET_AF_INET; - paddr->sin_port = std::bit_cast, u16>(reinterpret_cast(&native_addr)->sin_port); - paddr->sin_addr = std::bit_cast, u32>(reinterpret_cast(&native_addr)->sin_addr.s_addr); - paddr->sin_zero = 0; + return not_an_error(result); } - // Length - if (type == SYS_NET_SOCK_DGRAM_P2P || type == SYS_NET_SOCK_STREAM_P2P) - sys_net.trace("[P2P] %s Ok recvfrom: %d", type, native_result); + if (result > 0) + { + if (addr) + { + *paddrlen = sizeof(sys_net_sockaddr_in); + *addr = sn_addr; + } - return not_an_error(native_result); + return not_an_error(result); + } + + return result; } error_code sys_net_bnet_recvmsg(ppu_thread& ppu, s32 s, vm::ptr msg, s32 flags) @@ -2787,225 +812,69 @@ error_code sys_net_bnet_sendto(ppu_thread& ppu, s32 s, vm::cptr buf, u32 l return -SYS_NET_EAFNOSUPPORT; } - const auto psa_in = vm::_ptr(addr.addr()); + const std::optional sn_addr = addr ? std::optional(*addr) : std::nullopt; + const std::vector buf_copy(vm::_ptr(buf.addr()), vm::_ptr(buf.addr()) + len); + s32 result{}; - int native_flags = 0; - int native_result = -1; - ::sockaddr_in name{}; - std::vector _buf; - - if (idm::check(s)) - { - _buf.assign(vm::_ptr(buf.addr()), vm::_ptr(buf.addr()) + len); - } - else - { - return -SYS_NET_EBADF; - } - - if (addr) - { - name.sin_family = AF_INET; - name.sin_port = std::bit_cast(psa_in->sin_port); - name.sin_addr.s_addr = std::bit_cast(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 (name.sin_addr.s_addr == 0x00000000) - { - name.sin_addr.s_addr = 0x0100007F; - } -#endif - - char ip_str[16]; - inet_ntop(AF_INET, &name.sin_addr, ip_str, sizeof(ip_str)); - sys_net.trace("Sending to %s:%d", ip_str, psa_in->sin_port); - } - - ::socklen_t namelen = sizeof(name); - sys_net_error result{}; - - if (flags & SYS_NET_MSG_WAITALL) - { - native_flags |= MSG_WAITALL; - } - - std::vector p2p_data; - char *data = reinterpret_cast(_buf.data()); - u32 data_len = len; - - s32 type = 0; const auto sock = idm::check(s, [&](lv2_socket& sock) - { - std::lock_guard lock(sock.mutex); - type = sock.type; - - if (type == SYS_NET_SOCK_DGRAM_P2P) { - ensure(addr); - ensure(sock.socket); // ensures it has been bound - ensure(len <= (65535 - sizeof(u16))); // catch games using full payload for future fragmentation implementation if necessary - const u16 p2p_port = reinterpret_cast(addr.get_ptr())->sin_port; - const u16 p2p_vport = reinterpret_cast(addr.get_ptr())->sin_vport; + auto success = sock.sendto(flags, buf_copy, sn_addr); - char ip_str[16]; - inet_ntop(AF_INET, &name.sin_addr, ip_str, sizeof(ip_str)); - sys_net.trace("[P2P] Sending a packet to %s:%d:%d", ip_str, p2p_port, p2p_vport); - - p2p_data.resize(len + sizeof(u16)); - reinterpret_cast&>(p2p_data[0]) = p2p_vport; - memcpy(p2p_data.data()+sizeof(u16), _buf.data(), len); - - data = reinterpret_cast(p2p_data.data()); - data_len = len + sizeof(u16); - } - else if (type == SYS_NET_SOCK_STREAM_P2P) - { - constexpr u32 max_data_len = (65535 - (sizeof(u16) + sizeof(lv2_socket::p2ps_i::encapsulated_tcp))); - - // Prepare address - name.sin_family = AF_INET; - name.sin_port = std::bit_cast>(sock.p2ps.op_port); - name.sin_addr.s_addr = sock.p2ps.op_addr; - // Prepares encapsulated tcp - lv2_socket::p2ps_i::encapsulated_tcp tcp_header; - tcp_header.src_port = sock.p2p.vport; - tcp_header.dst_port = sock.p2ps.op_vport; - // chop it up - std::vector> stream_packets; - u32 cur_total_len = len; - while(cur_total_len > 0) + if (success) { - u32 cur_data_len; - if (cur_total_len >= max_data_len) - cur_data_len = max_data_len; - else - cur_data_len = cur_total_len; - - tcp_header.length = cur_data_len; - tcp_header.seq = sock.p2ps.cur_seq; - - auto packet = nt_p2p_port::generate_u2s_packet(tcp_header, &_buf[len - cur_total_len], cur_data_len); - nt_p2p_port::send_u2s_packet(sock, s, std::move(packet), &name, tcp_header.seq); - - cur_total_len -= cur_data_len; - sock.p2ps.cur_seq += cur_data_len; - } - - native_result = len; - return true; - } - - //if (!(sock.events & lv2_socket::poll::write)) - { - auto& dnshook = g_fxo->get(); - if (addr && type == SYS_NET_SOCK_DGRAM && psa_in->sin_port == 53) - { - dnshook.add_dns_spy(s); - } - - if (dnshook.is_dns(s)) - { - const s32 ret_analyzer = dnshook.analyze_dns_packet(s, reinterpret_cast(_buf.data()), len); - - // If we're not connected just never send the packet and pretend we did - auto& nph = g_fxo->get>(); - if (!nph.get_net_status()) - { - native_result = data_len; - return true; - } - - // Check if the packet is intercepted - if (ret_analyzer >= 0) - { - native_result = ret_analyzer; - return true; - } - } - - native_result = ::sendto(sock.socket, data, data_len, native_flags, addr ? reinterpret_cast(&name) : nullptr, addr ? namelen : 0); - - if (native_result >= 0) - { - if (sock.type == SYS_NET_SOCK_DGRAM_P2P) - { - native_result -= sizeof(u16); - } + result = *success; return true; } - result = get_last_error(!sock.so_nbio && (flags & SYS_NET_MSG_DONTWAIT) == 0); - - if (result) - { - return false; - } - } - - // Enable write event - sock.events += lv2_socket::poll::write; - sock.queue.emplace_back(ppu.id, [&](bs_t events) -> bool - { - if (events & lv2_socket::poll::write) - { - native_result = ::sendto(sock.socket, data, data_len, native_flags, addr ? reinterpret_cast(&name) : nullptr, addr ? namelen : 0); - - if (native_result >= 0 || (result = get_last_error(!sock.so_nbio && (flags & SYS_NET_MSG_DONTWAIT) == 0))) + // Enable write event + sock.poll_queue(ppu.id, lv2_socket::poll_t::write, [&](bs_t events) -> bool { - lv2_obj::awake(&ppu); - return true; - } - } + if (events & lv2_socket::poll_t::write) + { + auto success = sock.sendto(flags, buf_copy, sn_addr, false); + if (success) + { + result = *success; + lv2_obj::awake(&ppu); + return true; + } + } + sock.set_poll_event(lv2_socket::poll_t::write); + return false; + }); - sock.events += lv2_socket::poll::write; + lv2_obj::sleep(ppu); return false; }); - lv2_obj::sleep(ppu); - return false; - }); - if (!sock) { return -SYS_NET_EBADF; } - if (!sock.ret && result) - { - if (result == SYS_NET_EWOULDBLOCK) - { - return not_an_error(-result); - } - return -result; - } - if (!sock.ret) { while (true) { const auto state = ppu.state.fetch_sub(cpu_flag::signal); - if (is_stopped(state) || state & cpu_flag::signal) { break; } - thread_ctrl::wait_on(ppu.state, state); } - if (result) - { - return -result; - } - if (ppu.gpr[3] == static_cast(-SYS_NET_EINTR)) { return -SYS_NET_EINTR; } } - return not_an_error(native_result); + if (result > 0 || result == -SYS_NET_EWOULDBLOCK) + { + return not_an_error(result); + } + return sys_net_error{result}; } error_code sys_net_bnet_setsockopt(ppu_thread& ppu, s32 s, s32 level, s32 optname, vm::cptr optval, u32 optlen) @@ -3028,303 +897,40 @@ error_code sys_net_bnet_setsockopt(ppu_thread& ppu, s32 s, s32 level, s32 optnam break; } - switch(optlen) + switch (optlen) { - case 1: - sys_net.warning("optval: 0x%02X", *static_cast(optval.get_ptr())); - break; - case 2: - sys_net.warning("optval: 0x%04X", *static_cast *>(optval.get_ptr())); - break; - case 4: - sys_net.warning("optval: 0x%08X", *static_cast *>(optval.get_ptr())); - break; - case 8: - sys_net.warning("optval: 0x%016X", *static_cast *>(optval.get_ptr())); - break; + case 1: + sys_net.warning("optval: 0x%02X", *static_cast(optval.get_ptr())); + break; + case 2: + sys_net.warning("optval: 0x%04X", *static_cast*>(optval.get_ptr())); + break; + case 4: + sys_net.warning("optval: 0x%08X", *static_cast*>(optval.get_ptr())); + break; + case 8: + sys_net.warning("optval: 0x%016X", *static_cast*>(optval.get_ptr())); + break; } - int native_int = 0; - int native_level = -1; - int native_opt = -1; - const void* native_val = &native_int; - ::socklen_t native_len = sizeof(int); - ::linger native_linger; - ::ip_mreq native_mreq; - -#ifdef _WIN32 - u32 native_timeo; -#else - ::timeval native_timeo; -#endif - - std::vector optval_buf(vm::_ptr(optval.addr()), vm::_ptr(optval.addr() + optlen)); - - const auto sock = idm::check(s, [&](lv2_socket& sock) -> sys_net_error + if (optlen < sizeof(s32)) { - std::lock_guard lock(sock.mutex); + return -SYS_NET_EINVAL; + } - if (optlen >= sizeof(s32)) + std::vector optval_copy(vm::_ptr(optval.addr()), vm::_ptr(optval.addr() + optlen)); + + const auto sock = idm::check(s, [&](lv2_socket& sock) -> s32 { - native_int = *reinterpret_cast *>(optval_buf.data()); - } - else - { - return SYS_NET_EINVAL; - } - - if (sock.type == SYS_NET_SOCK_DGRAM_P2P || sock.type == SYS_NET_SOCK_STREAM_P2P) - { - if (level == SYS_NET_SOL_SOCKET && optname == SYS_NET_SO_NBIO) - { - sock.so_nbio = native_int; - } - - return {}; - } - - if (level == SYS_NET_SOL_SOCKET) - { - native_level = SOL_SOCKET; - - switch (optname) - { - case SYS_NET_SO_NBIO: - { - // Special - sock.so_nbio = native_int; - return {}; - } - case SYS_NET_SO_KEEPALIVE: - { - native_opt = SO_KEEPALIVE; - break; - } - case SYS_NET_SO_SNDBUF: - { - native_opt = SO_SNDBUF; - break; - } - case SYS_NET_SO_RCVBUF: - { - native_opt = SO_RCVBUF; - break; - } - case SYS_NET_SO_SNDLOWAT: - { - native_opt = SO_SNDLOWAT; - break; - } - case SYS_NET_SO_RCVLOWAT: - { - native_opt = SO_RCVLOWAT; - break; - } - case SYS_NET_SO_BROADCAST: - { - native_opt = SO_BROADCAST; - break; - } -#ifdef _WIN32 - case SYS_NET_SO_REUSEADDR: - { - native_opt = SO_REUSEADDR; - sock.so_reuseaddr = native_int; - native_int = sock.so_reuseaddr || sock.so_reuseport ? 1 : 0; - break; - } - case SYS_NET_SO_REUSEPORT: - { - native_opt = SO_REUSEADDR; - sock.so_reuseport = native_int; - native_int = sock.so_reuseaddr || sock.so_reuseport ? 1 : 0; - break; - } -#else - case SYS_NET_SO_REUSEADDR: - { - native_opt = SO_REUSEADDR; - break; - } - case SYS_NET_SO_REUSEPORT: - { - native_opt = SO_REUSEPORT; - break; - } -#endif - case SYS_NET_SO_SNDTIMEO: - case SYS_NET_SO_RCVTIMEO: - { - if (optlen < sizeof(sys_net_timeval)) - return SYS_NET_EINVAL; - - native_opt = optname == SYS_NET_SO_SNDTIMEO ? SO_SNDTIMEO : SO_RCVTIMEO; - native_val = &native_timeo; - native_len = sizeof(native_timeo); -#ifdef _WIN32 - native_timeo = ::narrow(reinterpret_cast(optval_buf.data())->tv_sec) * 1000; - native_timeo += ::narrow(reinterpret_cast(optval_buf.data())->tv_usec) / 1000; -#else - native_timeo.tv_sec = ::narrow(reinterpret_cast(optval_buf.data())->tv_sec); - native_timeo.tv_usec = ::narrow(reinterpret_cast(optval_buf.data())->tv_usec); -#endif - break; - } - case SYS_NET_SO_LINGER: - { - if (optlen < sizeof(sys_net_linger)) - return SYS_NET_EINVAL; - - // TODO - native_opt = SO_LINGER; - native_val = &native_linger; - native_len = sizeof(native_linger); - native_linger.l_onoff = reinterpret_cast(optval_buf.data())->l_onoff; - native_linger.l_linger = reinterpret_cast(optval_buf.data())->l_linger; - break; - } - case SYS_NET_SO_USECRYPTO: - { - //TODO - sys_net.error("sys_net_bnet_setsockopt(s=%d, SOL_SOCKET): Stubbed option (0x%x) (SYS_NET_SO_USECRYPTO)", s, optname); - return {}; - } - case SYS_NET_SO_USESIGNATURE: - { - //TODO - sys_net.error("sys_net_bnet_setsockopt(s=%d, SOL_SOCKET): Stubbed option (0x%x) (SYS_NET_SO_USESIGNATURE)", s, optname); - return {}; - } - default: - { - sys_net.error("sys_net_bnet_setsockopt(s=%d, SOL_SOCKET): unknown option (0x%x)", s, optname); - return SYS_NET_EINVAL; - } - } - } - else if (level == SYS_NET_IPPROTO_TCP) - { - native_level = IPPROTO_TCP; - - switch (optname) - { - case SYS_NET_TCP_MAXSEG: - { - // Special (no effect) - sock.so_tcp_maxseg = native_int; - return {}; - } - case SYS_NET_TCP_NODELAY: - { - native_opt = TCP_NODELAY; - break; - } - default: - { - sys_net.error("sys_net_bnet_setsockopt(s=%d, IPPROTO_TCP): unknown option (0x%x)", s, optname); - return SYS_NET_EINVAL; - } - } - } - else if (level == SYS_NET_IPPROTO_IP) - { - native_level = IPPROTO_IP; - switch (optname) - { - case SYS_NET_IP_HDRINCL: - { - native_opt = IP_HDRINCL; - break; - } - case SYS_NET_IP_TOS: - { - native_opt = IP_TOS; - break; - } - case SYS_NET_IP_TTL: - { - native_opt = IP_TTL; - break; - } - case SYS_NET_IP_MULTICAST_IF: - { - native_opt = IP_MULTICAST_IF; - break; - } - case SYS_NET_IP_MULTICAST_TTL: - { - native_opt = IP_MULTICAST_TTL; - break; - } - case SYS_NET_IP_MULTICAST_LOOP: - { - native_opt = IP_MULTICAST_LOOP; - break; - } - case SYS_NET_IP_ADD_MEMBERSHIP: - case SYS_NET_IP_DROP_MEMBERSHIP: - { - if (optlen < sizeof(sys_net_ip_mreq)) - return SYS_NET_EINVAL; - - native_opt = optname == SYS_NET_IP_ADD_MEMBERSHIP ? IP_ADD_MEMBERSHIP : IP_DROP_MEMBERSHIP; - native_val = &native_mreq; - native_len = sizeof(::ip_mreq); - native_mreq.imr_interface.s_addr = std::bit_cast(reinterpret_cast(optval_buf.data())->imr_interface); - native_mreq.imr_multiaddr.s_addr = std::bit_cast(reinterpret_cast(optval_buf.data())->imr_multiaddr); - break; - } - case SYS_NET_IP_TTLCHK: - { - sys_net.error("sys_net_bnet_setsockopt(s=%d, IPPROTO_IP): Stubbed option (0x%x) (SYS_NET_IP_TTLCHK)", s, optname); - break; - } - case SYS_NET_IP_MAXTTL: - { - sys_net.error("sys_net_bnet_setsockopt(s=%d, IPPROTO_IP): Stubbed option (0x%x) (SYS_NET_IP_MAXTTL)", s, optname); - break; - } - case SYS_NET_IP_DONTFRAG: - { - #ifdef _WIN32 - native_opt = IP_DONTFRAGMENT; - #else - native_opt = IP_DF; - #endif - break; - } - default: - { - sys_net.error("sys_net_bnet_setsockopt(s=%d, IPPROTO_IP): unknown option (0x%x)", s, optname); - return SYS_NET_EINVAL; - } - } - } - else - { - sys_net.error("sys_net_bnet_setsockopt(s=%d): unknown level (0x%x)", s, level); - return SYS_NET_EINVAL; - } - - if (::setsockopt(sock.socket, native_level, native_opt, static_cast(native_val), native_len) == 0) - { - return {}; - } - - return get_last_error(false); - }); + return sock.setsockopt(level, optname, optval_copy); + }); if (!sock) { return -SYS_NET_EBADF; } - if (sock.ret) - { - return -sock.ret; - } - - return CELL_OK; + return sock.ret; } error_code sys_net_bnet_shutdown(ppu_thread& ppu, s32 s, s32 how) @@ -3338,33 +944,10 @@ error_code sys_net_bnet_shutdown(ppu_thread& ppu, s32 s, s32 how) return -SYS_NET_EINVAL; } - const auto sock = idm::check(s, [&](lv2_socket& sock) -> sys_net_error - { - std::lock_guard lock(sock.mutex); - - // Shutdown of P2P socket is always successful - if (sock.type == SYS_NET_SOCK_DGRAM_P2P || sock.type == SYS_NET_SOCK_STREAM_P2P) + const auto sock = idm::check(s, [&](lv2_socket& sock) -> s32 { - return {}; - } - -#ifdef _WIN32 - const int native_how = - how == SYS_NET_SHUT_RD ? SD_RECEIVE : - how == SYS_NET_SHUT_WR ? SD_SEND : SD_BOTH; -#else - const int native_how = - how == SYS_NET_SHUT_RD ? SHUT_RD : - how == SYS_NET_SHUT_WR ? SHUT_WR : SHUT_RDWR; -#endif - - if (::shutdown(sock.socket, native_how) == 0) - { - return {}; - } - - return get_last_error(false); - }); + return sock.shutdown(how); + }); if (!sock) { @@ -3373,7 +956,7 @@ error_code sys_net_bnet_shutdown(ppu_thread& ppu, s32 s, s32 how) if (sock.ret) { - return -sock.ret; + return sock.ret; } return CELL_OK; @@ -3385,69 +968,48 @@ error_code sys_net_bnet_socket(ppu_thread& ppu, lv2_socket_family family, lv2_so sys_net.warning("sys_net_bnet_socket(family=%s, type=%s, protocol=%s)", family, type, protocol); - if (family != SYS_NET_AF_INET && family != SYS_NET_AF_UNSPEC) + if (family != SYS_NET_AF_INET) { sys_net.error("sys_net_bnet_socket(): unknown family (%d)", family); } - if (type != SYS_NET_SOCK_STREAM && type != SYS_NET_SOCK_DGRAM && type != SYS_NET_SOCK_DGRAM_P2P && type != SYS_NET_SOCK_STREAM_P2P) + if (type != SYS_NET_SOCK_STREAM && type != SYS_NET_SOCK_DGRAM && type != SYS_NET_SOCK_RAW && type != SYS_NET_SOCK_DGRAM_P2P && type != SYS_NET_SOCK_STREAM_P2P) { sys_net.error("sys_net_bnet_socket(): unsupported type (%d)", type); return -SYS_NET_EPROTONOSUPPORT; } - lv2_socket::socket_type native_socket = 0; + std::shared_ptr sock_lv2; - if (type != SYS_NET_SOCK_DGRAM_P2P && type != SYS_NET_SOCK_STREAM_P2P) + switch (type) { - const int native_domain = AF_INET; - - const int native_type = - type == SYS_NET_SOCK_STREAM ? SOCK_STREAM : - type == SYS_NET_SOCK_DGRAM ? SOCK_DGRAM : SOCK_RAW; - - int native_proto = - protocol == SYS_NET_IPPROTO_IP ? IPPROTO_IP : - protocol == SYS_NET_IPPROTO_ICMP ? IPPROTO_ICMP : - protocol == SYS_NET_IPPROTO_IGMP ? IPPROTO_IGMP : - protocol == SYS_NET_IPPROTO_TCP ? IPPROTO_TCP : - protocol == SYS_NET_IPPROTO_UDP ? IPPROTO_UDP : - protocol == SYS_NET_IPPROTO_ICMPV6 ? IPPROTO_ICMPV6 : 0; - - // TODO: native_domain is always AF_INET - if (native_domain == AF_UNSPEC && type == SYS_NET_SOCK_DGRAM) + case SYS_NET_SOCK_STREAM: + case SYS_NET_SOCK_DGRAM: + { + auto lv2_native = std::make_shared(family, type, protocol); + if (s32 result = lv2_native->create_socket(); result < 0) { - // Windows gets all errory if you try a unspec socket with protocol 0 - native_proto = IPPROTO_UDP; + return result; } - native_socket = ::socket(native_domain, native_type, native_proto); - - if (native_socket == -1) - { - return -get_last_error(false); - } - u32 default_RCVBUF = (type==SYS_NET_SOCK_STREAM) ? 65535 : 9216; - if (setsockopt(native_socket, SOL_SOCKET, SO_RCVBUF, reinterpret_cast(&default_RCVBUF), sizeof(default_RCVBUF)) != 0) - sys_net.error("Error setting defalult SO_RCVBUF on sys_net_bnet_socket socket"); - u32 default_SNDBUF = 131072; - if (setsockopt(native_socket, SOL_SOCKET, SO_SNDBUF, reinterpret_cast(&default_SNDBUF), sizeof(default_SNDBUF)) != 0) - sys_net.error("Error setting default SO_SNDBUF on sys_net_bnet_socket socket"); + sock_lv2 = std::move(lv2_native); + break; } - - const auto sock_lv2 = std::make_shared(native_socket, type, family); - if (type == SYS_NET_SOCK_STREAM_P2P) - { - sock_lv2->p2p.port = 3658; // Default value if unspecified later + case SYS_NET_SOCK_RAW: sock_lv2 = std::make_shared(family, type, protocol); break; + case SYS_NET_SOCK_DGRAM_P2P: sock_lv2 = std::make_shared(family, type, protocol); break; + case SYS_NET_SOCK_STREAM_P2P: sock_lv2 = std::make_shared(family, type, protocol); break; } const s32 s = idm::import_existing(sock_lv2); + // Can't allocate more than 1000 sockets if (s == id_manager::id_traits::invalid) { return -SYS_NET_EMFILE; } + sock_lv2->set_lv2_id(s); + return not_an_error(s); } @@ -3457,63 +1019,22 @@ error_code sys_net_bnet_close(ppu_thread& ppu, s32 s) sys_net.warning("sys_net_bnet_close(s=%d)", s); - const auto sock = idm::withdraw(s); + auto sock = idm::withdraw(s); if (!sock) { return -SYS_NET_EBADF; } - if (!sock->queue.empty()) + if (sock->get_queue_size()) sys_net.error("CLOSE"); - // If it's a bound socket we "close" the vport - if (sock->type == SYS_NET_SOCK_DGRAM_P2P || sock->type == SYS_NET_SOCK_STREAM_P2P) + sock->close(); + { - if (!sock->p2p.port || !sock->p2p.vport) - { - return CELL_OK; - } - - auto& nc = g_fxo->get(); - { - std::lock_guard lock(nc.list_p2p_ports_mutex); - ensure(nc.list_p2p_ports.contains(sock->p2p.port)); - auto& p2p_port = nc.list_p2p_ports.at(sock->p2p.port); - { - std::lock_guard lock(p2p_port.bound_p2p_vports_mutex); - if (sock->type == SYS_NET_SOCK_DGRAM_P2P) - { - p2p_port.bound_p2p_vports.erase(sock->p2p.vport); - } - else - { - for (auto it = p2p_port.bound_p2p_streams.begin(); it != p2p_port.bound_p2p_streams.end();) - { - if (it->second == s) - { - it = p2p_port.bound_p2p_streams.erase(it); - continue; - } - it++; - } - - } - } - } - } - else - { - auto& dnshook = g_fxo->get(); - dnshook.remove_dns_spy(s); - - std::lock_guard lock(sock->mutex); -#ifdef _WIN32 - ::closesocket(sock->socket); -#else - ::close(sock->socket); -#endif - sock->socket = 0; + // Ensures the socket has no lingering copy from the network thread + std::lock_guard nw_lock(g_fxo->get().s_nw_mutex); + sock.reset(); } return CELL_OK; @@ -3536,160 +1057,101 @@ error_code sys_net_bnet_poll(ppu_thread& ppu, vm::ptr fds, s32 n std::vector fds_buf; - if (true) + fds_buf.assign(fds.get_ptr(), fds.get_ptr() + nfds); + + std::unique_lock nw_lock(g_fxo->get().s_nw_mutex); + std::shared_lock lock(id_manager::g_mutex); + + ::pollfd _fds[1024]{}; +#ifdef _WIN32 + bool connecting[1024]{}; +#endif + + for (s32 i = 0; i < nfds; i++) { - fds_buf.assign(fds.get_ptr(), fds.get_ptr() + nfds); + _fds[i].fd = -1; + fds_buf[i].revents = 0; - std::unique_lock nw_lock(g_fxo->get().s_nw_mutex); - - std::shared_lock lock(id_manager::g_mutex); - - ::pollfd _fds[1024]{}; -#ifdef _WIN32 - bool connecting[1024]{}; -#endif - - for (s32 i = 0; i < nfds; i++) + if (fds_buf[i].fd < 0) { - _fds[i].fd = -1; - fds_buf[i].revents = 0; - - if (fds_buf[i].fd < 0) - { - continue; - } - - if (auto sock = idm::check_unlocked(fds_buf[i].fd)) - { - if (sock->type == SYS_NET_SOCK_DGRAM_P2P) - { - std::lock_guard lock(sock->mutex); - ensure(sock->p2p.vport); - sys_net.trace("[P2P] poll checking for 0x%X", fds[i].events); - // Check if it's a bound P2P socket - if ((fds[i].events & SYS_NET_POLLIN) && !sock->p2p.data.empty()) - { - sys_net.trace("[P2P] p2p_data for vport %d contains %d elements", sock->p2p.vport, sock->p2p.data.size()); - fds_buf[i].revents |= SYS_NET_POLLIN; - } - - // Data can always be written on a dgram socket - if (fds[i].events & SYS_NET_POLLOUT) - fds_buf[i].revents |= SYS_NET_POLLOUT; - - if (fds_buf[i].revents) - signaled++; - } - else if (sock->type == SYS_NET_SOCK_STREAM_P2P) - { - std::lock_guard lock(sock->mutex); - sys_net.trace("[P2PS] poll checking for 0x%X", fds[i].events); - if (sock->p2ps.status == lv2_socket::p2ps_i::stream_status::stream_connected) - { - if ((fds[i].events & SYS_NET_POLLIN) && sock->p2ps.data_available) - { - sys_net.trace("[P2PS] p2ps has %u bytes available", sock->p2ps.data_available); - fds_buf[i].revents |= SYS_NET_POLLIN; - } - - // Data can only be written if the socket is connected - if (fds[i].events & SYS_NET_POLLOUT && sock->p2ps.status == lv2_socket::p2ps_i::stream_status::stream_connected) - { - fds_buf[i].revents |= SYS_NET_POLLOUT; - } - - if (fds_buf[i].revents) - signaled++; - } - } - else - { - // Check for fake packet for dns interceptions - auto& dnshook = g_fxo->get(); - if (fds_buf[i].events & SYS_NET_POLLIN && dnshook.is_dns(fds_buf[i].fd) && dnshook.is_dns_queue(fds_buf[i].fd)) - fds_buf[i].revents |= SYS_NET_POLLIN; - - if (fds_buf[i].events & ~(SYS_NET_POLLIN | SYS_NET_POLLOUT | SYS_NET_POLLERR)) - sys_net.warning("sys_net_bnet_poll(fd=%d): events=0x%x", fds[i].fd, fds[i].events); - _fds[i].fd = sock->socket; - if (fds_buf[i].events & SYS_NET_POLLIN) - _fds[i].events |= POLLIN; - if (fds_buf[i].events & SYS_NET_POLLOUT) - _fds[i].events |= POLLOUT; - } -#ifdef _WIN32 - connecting[i] = sock->is_connecting; -#endif - } - else - { - fds_buf[i].revents |= SYS_NET_POLLNVAL; - signaled++; - } + continue; } + if (auto sock = idm::check_unlocked(fds_buf[i].fd)) + { + signaled += sock->poll(fds[i], _fds[i]); #ifdef _WIN32 - windows_poll(_fds, nfds, 0, connecting); + connecting[i] = sock->is_connecting(); +#endif + } + else + { + fds_buf[i].revents |= SYS_NET_POLLNVAL; + signaled++; + } + } + +#ifdef _WIN32 + windows_poll(_fds, nfds, 0, connecting); #else - ::poll(_fds, nfds, 0); + ::poll(_fds, nfds, 0); #endif - for (s32 i = 0; i < nfds; i++) - { - if (_fds[i].revents & (POLLIN | POLLHUP)) - fds_buf[i].revents |= SYS_NET_POLLIN; - if (_fds[i].revents & POLLOUT) - fds_buf[i].revents |= SYS_NET_POLLOUT; - if (_fds[i].revents & POLLERR) - fds_buf[i].revents |= SYS_NET_POLLERR; + for (s32 i = 0; i < nfds; i++) + { + if (_fds[i].revents & (POLLIN | POLLHUP)) + fds_buf[i].revents |= SYS_NET_POLLIN; + if (_fds[i].revents & POLLOUT) + fds_buf[i].revents |= SYS_NET_POLLOUT; + if (_fds[i].revents & POLLERR) + fds_buf[i].revents |= SYS_NET_POLLERR; - if (fds_buf[i].revents) - { - signaled++; - } + if (fds_buf[i].revents) + { + signaled++; + } + } + + if (ms == 0 || signaled) + { + lock.unlock(); + nw_lock.unlock(); + std::memcpy(fds.get_ptr(), fds_buf.data(), nfds * sizeof(fds[0])); + return not_an_error(signaled); + } + + for (s32 i = 0; i < nfds; i++) + { + if (fds_buf[i].fd < 0) + { + continue; } - if (ms == 0 || signaled) + if (auto sock = idm::check_unlocked(fds_buf[i].fd)) { - lock.unlock(); - nw_lock.unlock(); - std::memcpy(fds.get_ptr(), fds_buf.data(), nfds * sizeof(fds[0])); - return not_an_error(signaled); - } - - for (s32 i = 0; i < nfds; i++) - { - if (fds_buf[i].fd < 0) - { - continue; - } - - if (auto sock = idm::check_unlocked(fds_buf[i].fd)) - { - std::lock_guard lock(sock->mutex); + auto lock = sock->lock(); #ifdef _WIN32 - sock->is_connecting = connecting[i]; + sock->set_connecting(connecting[i]); #endif - bs_t selected = +lv2_socket::poll::error; + bs_t selected = +lv2_socket::poll_t::error; - if (fds_buf[i].events & SYS_NET_POLLIN) - selected += lv2_socket::poll::read; - if (fds_buf[i].events & SYS_NET_POLLOUT) - selected += lv2_socket::poll::write; - //if (fds_buf[i].events & SYS_NET_POLLPRI) // Unimplemented - // selected += lv2_socket::poll::error; + if (fds_buf[i].events & SYS_NET_POLLIN) + selected += lv2_socket::poll_t::read; + if (fds_buf[i].events & SYS_NET_POLLOUT) + selected += lv2_socket::poll_t::write; + // if (fds_buf[i].events & SYS_NET_POLLPRI) // Unimplemented + // selected += lv2_socket::poll::error; - sock->events += selected; - sock->queue.emplace_back(ppu.id, [sock, selected, &fds_buf, i, &signaled, &ppu](bs_t events) + sock->poll_queue(ppu.id, selected, [sock, selected, &fds_buf, i, &signaled, &ppu](bs_t events) { if (events & selected) { - if (events & selected & lv2_socket::poll::read) + if (events & selected & lv2_socket::poll_t::read) fds_buf[i].revents |= SYS_NET_POLLIN; - if (events & selected & lv2_socket::poll::write) + if (events & selected & lv2_socket::poll_t::write) fds_buf[i].revents |= SYS_NET_POLLOUT; - if (events & selected & lv2_socket::poll::error) + if (events & selected & lv2_socket::poll_t::error) fds_buf[i].revents |= SYS_NET_POLLERR; signaled++; @@ -3697,15 +1159,14 @@ error_code sys_net_bnet_poll(ppu_thread& ppu, vm::ptr fds, s32 n return true; } - sock->events += selected; + sock->set_poll_event(selected); return false; }); - } } - - lv2_obj::sleep(ppu, timeout); } + lv2_obj::sleep(ppu, timeout); + while (auto state = ppu.state.fetch_sub(cpu_flag::signal)) { if (is_stopped(state)) @@ -3796,18 +1257,18 @@ error_code sys_net_bnet_select(ppu_thread& ppu, s32 nfds, vm::ptr selected{}; + bs_t selected{}; if (readfds && _readfds.bit(i)) - selected += lv2_socket::poll::read; + selected += lv2_socket::poll_t::read; if (writefds && _writefds.bit(i)) - selected += lv2_socket::poll::write; - //if (exceptfds && _exceptfds.bit(i)) + selected += lv2_socket::poll_t::write; + // if (exceptfds && _exceptfds.bit(i)) // selected += lv2_socket::poll::error; if (selected) { - selected += lv2_socket::poll::error; + selected += lv2_socket::poll_t::error; } else { @@ -3816,28 +1277,14 @@ error_code sys_net_bnet_select(ppu_thread& ppu, s32 nfds, vm::ptr((lv2_socket::id_base & -1024) + i)) { + if (sock->select(selected, _fds[i])) + { + rread.set(i); + signaled++; + } - if (sock->type != SYS_NET_SOCK_DGRAM_P2P) - { - _fds[i].fd = sock->socket; - if (selected & lv2_socket::poll::read) - _fds[i].events |= POLLIN; - if (selected & lv2_socket::poll::write) - _fds[i].events |= POLLOUT; - } - else - { - std::lock_guard lock(sock->mutex); - // Check if it's a bound P2P socket - if ((selected & lv2_socket::poll::read) && sock->p2p.vport && !sock->p2p.data.empty()) - { - sys_net.trace("[P2P] p2p_data for vport %d contains %d elements", sock->p2p.vport, sock->p2p.data.size()); - rread.set(i); - signaled++; - } - } #ifdef _WIN32 - connecting[i] = sock->is_connecting; + connecting[i] = sock->is_connecting(); #endif } else @@ -3878,18 +1325,18 @@ error_code sys_net_bnet_select(ppu_thread& ppu, s32 nfds, vm::ptr selected{}; + bs_t selected{}; if (readfds && _readfds.bit(i)) - selected += lv2_socket::poll::read; + selected += lv2_socket::poll_t::read; if (writefds && _writefds.bit(i)) - selected += lv2_socket::poll::write; - //if (exceptfds && _exceptfds.bit(i)) - // selected += lv2_socket::poll::error; + selected += lv2_socket::poll_t::write; + // if (exceptfds && _exceptfds.bit(i)) + // selected += lv2_socket::poll_t::error; if (selected) { - selected += lv2_socket::poll::error; + selected += lv2_socket::poll_t::error; } else { @@ -3898,32 +1345,30 @@ error_code sys_net_bnet_select(ppu_thread& ppu, s32 nfds, vm::ptr((lv2_socket::id_base & -1024) + i)) { - std::lock_guard lock(sock->mutex); - + auto lock = sock->lock(); #ifdef _WIN32 - sock->is_connecting = connecting[i]; + sock->set_connecting(connecting[i]); #endif - sock->events += selected; - sock->queue.emplace_back(ppu.id, [sock, selected, i, &rread, &rwrite, &rexcept, &signaled, &ppu](bs_t events) - { - if (events & selected) + sock->poll_queue(ppu.id, selected, [sock, selected, i, &rread, &rwrite, &rexcept, &signaled, &ppu](bs_t events) { - if (selected & lv2_socket::poll::read && events & (lv2_socket::poll::read + lv2_socket::poll::error)) - rread.set(i); - if (selected & lv2_socket::poll::write && events & (lv2_socket::poll::write + lv2_socket::poll::error)) - rwrite.set(i); - //if (events & (selected & lv2_socket::poll::error)) - // rexcept.set(i); + if (events & selected) + { + if (selected & lv2_socket::poll_t::read && events & (lv2_socket::poll_t::read + lv2_socket::poll_t::error)) + rread.set(i); + if (selected & lv2_socket::poll_t::write && events & (lv2_socket::poll_t::write + lv2_socket::poll_t::error)) + rwrite.set(i); + // if (events & (selected & lv2_socket::poll::error)) + // rexcept.set(i); - signaled++; - g_fxo->get().s_to_awake.emplace_back(&ppu); - return true; - } + signaled++; + g_fxo->get().s_to_awake.emplace_back(&ppu); + return true; + } - sock->events += selected; - return false; - }); + sock->set_poll_event(selected); + return false; + }); } else { @@ -4050,7 +1495,7 @@ error_code sys_net_infoctl(ppu_thread& ppu, s32 cmd, vm::ptr arg) char buffer[nameserver.size() + 80]{}; std::memcpy(buffer, nameserver.data(), nameserver.size()); - auto& nph = g_fxo->get>(); + auto& nph = g_fxo->get>(); const auto dns_str = np::ip_to_string(nph.get_dns_ip()); std::memcpy(buffer + nameserver.size() - 1, dns_str.data(), dns_str.size()); diff --git a/rpcs3/Emu/Cell/lv2/sys_net.h b/rpcs3/Emu/Cell/lv2/sys_net.h index a6fa25b097..0411954787 100644 --- a/rpcs3/Emu/Cell/lv2/sys_net.h +++ b/rpcs3/Emu/Cell/lv2/sys_net.h @@ -322,130 +322,6 @@ struct sys_net_linger be_t l_linger; }; -// Custom structure for sockets -// We map host sockets to sequential IDs to return as descriptors because syscalls expect socket IDs to be under 1024. -struct lv2_socket final -{ -#ifdef _WIN32 - using socket_type = uptr; -#else - using socket_type = int; -#endif - - static const u32 id_base = 24; - static const u32 id_step = 1; - static const u32 id_count = 1000; - - // Poll events - enum class poll - { - read, - write, - error, - - __bitset_enum_max - }; - - lv2_socket(socket_type s, s32 s_type, s32 family); - ~lv2_socket(); - - shared_mutex mutex; - -#ifdef _WIN32 - // Tracks connect for WSAPoll workaround - bool is_connecting = false; -#endif - - // Native socket (must be non-blocking) - socket_type socket; - - // Events selected for polling - atomic_bs_t events{}; - - // Non-blocking IO option - s32 so_nbio = 0; - - // Connection result - s32 so_error = 0; - - // Unsupported option - s32 so_tcp_maxseg = 1500; - - const lv2_socket_type type; - const lv2_socket_family family; - - // SYS_NET_SOCK_DGRAM_P2P and SYS_NET_SOCK_STREAM_P2P socket specific information - struct p2p_i - { - // Port(actual bound port) and Virtual Port(indicated by u16 at the start of the packet) - u16 port = 0, vport = 0; - // Queue containing received packets from network_thread for SYS_NET_SOCK_DGRAM_P2P sockets - std::queue>> data{}; - } p2p; - - struct p2ps_i - { - enum tcp_flags : u8 - { - FIN = (1 << 0), - SYN = (1 << 1), - RST = (1 << 2), - PSH = (1 << 3), - ACK = (1 << 4), - URG = (1 << 5), - ECE = (1 << 6), - CWR = (1 << 7), - }; - - static constexpr be_t U2S_sig = (static_cast('U') << 24 | static_cast('2') << 16 | static_cast('S') << 8 | static_cast('0')); - static constexpr usz MAX_RECEIVED_BUFFER = (1024*1024*10); - - // P2P stream socket specific - struct encapsulated_tcp - { - be_t signature = lv2_socket::p2ps_i::U2S_sig; // Signature to verify it's P2P Stream data - be_t length = 0; // Length of data - be_t seq = 0; // This should be u32 but changed to u64 for simplicity - be_t ack = 0; - be_t src_port = 0; // fake source tcp port - be_t dst_port = 0; // fake dest tcp port(should be == vport) - be_t checksum = 0; - u8 flags = 0; - }; - - enum stream_status - { - stream_closed, // Default when port is not listening nor connected - stream_listening, // Stream is listening, accepting SYN packets - stream_handshaking, // Currently handshaking - stream_connected, // This is an established connection(after tcp handshake) - }; - - stream_status status = stream_status::stream_closed; - - usz max_backlog = 0; // set on listen - std::queue backlog; - - u16 op_port = 0, op_vport = 0; - u32 op_addr = 0; - - u64 data_beg_seq = 0; // Seq of first byte of received_data - u64 data_available = 0; // Amount of continuous data available(calculated on ACK send) - std::map> received_data; // holds seq/data of data received - - u64 cur_seq = 0; // SEQ of next packet to be sent - } p2ps; - - // Value keepers -#ifdef _WIN32 - s32 so_reuseaddr = 0; - s32 so_reuseport = 0; -#endif - - // Event processing workload (pair of thread id and the processing function) - std::vector)>>> queue; -}; - class ppu_thread; // Syscalls diff --git a/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket.cpp b/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket.cpp new file mode 100644 index 0000000000..d6daecde92 --- /dev/null +++ b/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket.cpp @@ -0,0 +1,128 @@ +#include "stdafx.h" +#include "lv2_socket.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 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 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::get_events() const +{ + return events.load(); +} + +void lv2_socket::set_poll_event(bs_t event) +{ + events += event; +} + +void lv2_socket::poll_queue(u32 ppu_id, bs_t event, std::function)> poll_cb) +{ + set_poll_event(event); + queue.emplace_back(ppu_id, poll_cb); +} + +void lv2_socket::clear_queue(u32 ppu_id) +{ + std::lock_guard lock(mutex); + + for (auto it = queue.begin(); it != queue.end();) + { + if (it->first == ppu_id) + { + it = queue.erase(it); + continue; + } + + it++; + } + + if (queue.empty()) + { + events.store({}); + } +} + +void lv2_socket::handle_events(const pollfd& native_pfd, [[maybe_unused]] bool unset_connecting) +{ + bs_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) + { + std::lock_guard lock(mutex); +#ifdef _WIN32 + if (unset_connecting) + set_connecting(false); +#endif + + for (auto it = queue.begin(); events_happening && it != queue.end();) + { + if (it->second(events_happening)) + { + it = queue.erase(it); + continue; + } + + it++; + } + + if (queue.empty()) + { + events.store({}); + } + } +} diff --git a/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket.h b/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket.h new file mode 100644 index 0000000000..a787147e83 --- /dev/null +++ b/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket.h @@ -0,0 +1,135 @@ +#pragma once + +#include +#include + +#include "Utilities/mutex.h" +#include "Emu/IdManager.h" +#include "Emu/Cell/lv2/sys_net.h" + +#ifdef _WIN32 +#include +#include +#else +#ifdef __clang__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wold-style-cast" +#endif +#include +#ifdef __clang__ +#pragma GCC diagnostic pop +#endif +#endif + + +#ifdef _WIN32 + using socket_type = uptr; +#else + using socket_type = int; +#endif + +class lv2_socket +{ +public: + // Poll events + enum class poll_t + { + read, + write, + error, + + __bitset_enum_max + }; + + union sockopt_data + { + char ch[128]; + be_t _int = 0; + sys_net_timeval timeo; + sys_net_linger linger; + }; + +public: + lv2_socket(lv2_socket_family family, lv2_socket_type type, lv2_ip_protocol protocol); + virtual ~lv2_socket() = default; + + std::unique_lock lock(); + + void set_lv2_id(u32 id); + bs_t get_events() const; + void set_poll_event(bs_t event); + void poll_queue(u32 ppu_id, bs_t event, std::function)> poll_cb); + void clear_queue(u32 ppu_id); + void handle_events(const pollfd& native_fd, bool unset_connecting = false); + + lv2_socket_family get_family() const; + lv2_socket_type get_type() const; + lv2_ip_protocol get_protocol() const; + std::size_t get_queue_size() const; + socket_type get_socket() const; +#ifdef _WIN32 + bool is_connecting() const; + void set_connecting(bool is_connecting); +#endif + +public: + + virtual std::tuple accept(bool is_lock = true) = 0; + virtual s32 bind(const sys_net_sockaddr &addr, s32 ps3_id) = 0; + + virtual std::optional connect(const sys_net_sockaddr &addr) = 0; + virtual s32 connect_followup() = 0; + + virtual std::pair getpeername() = 0; + virtual std::pair getsockname() = 0; + + virtual std::tuple getsockopt(s32 level, s32 optname, u32 len) = 0; + virtual s32 setsockopt(s32 level, s32 optname, const std::vector& optval) = 0; + + virtual s32 listen(s32 backlog) = 0; + + virtual std::optional, sys_net_sockaddr>> recvfrom(s32 flags, u32 len, bool is_lock = true) = 0; + virtual std::optional sendto(s32 flags, const std::vector& buf, std::optional opt_sn_addr, bool is_lock = true) = 0; + + virtual void close() = 0; + virtual s32 shutdown(s32 how) = 0; + + virtual s32 poll(sys_net_pollfd& sn_pfd, pollfd& native_pfd) = 0; + virtual s32 select(bs_t selected, pollfd& native_pfd) = 0; + +public: + // IDM data + static const u32 id_base = 24; + static const u32 id_step = 1; + static const u32 id_count = 1000; + +protected: + shared_mutex mutex; + u32 lv2_id = 0; + + socket_type socket = 0; + + lv2_socket_family family{}; + lv2_socket_type type{}; + lv2_ip_protocol protocol{}; + + // Events selected for polling + atomic_bs_t events{}; + // Event processing workload (pair of thread id and the processing function) + std::vector)>>> queue; + + // Socket options value keepers + // Non-blocking IO option + s32 so_nbio = 0; + // Error, only used for connection result for non blocking stream sockets + s32 so_error = 0; + // Unsupported option + s32 so_tcp_maxseg = 1500; +#ifdef _WIN32 + s32 so_reuseaddr = 0; + s32 so_reuseport = 0; + + // Tracks connect for WSAPoll workaround + bool connecting = false; +#endif +}; diff --git a/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket_native.cpp b/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket_native.cpp new file mode 100644 index 0000000000..4568d853c2 --- /dev/null +++ b/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket_native.cpp @@ -0,0 +1,1013 @@ +#include "stdafx.h" + +#include "Emu/Cell/lv2/sys_net.h" +#include "Emu/NP/np_dnshook.h" +#include "Emu/NP/np_handler.h" +#include "lv2_socket_native.h" +#include "sys_net_helpers.h" + +LOG_CHANNEL(sys_net); + +lv2_socket_native::lv2_socket_native(lv2_socket_family family, lv2_socket_type type, lv2_ip_protocol protocol) + : lv2_socket(family, type, protocol) +{ +} + +lv2_socket_native::~lv2_socket_native() +{ + close(); +} + +s32 lv2_socket_native::create_socket() +{ + ensure(family == SYS_NET_AF_INET); + ensure(type == SYS_NET_SOCK_STREAM || type == SYS_NET_SOCK_DGRAM); + ensure(protocol == SYS_NET_IPPROTO_IP || protocol == SYS_NET_IPPROTO_TCP || protocol == SYS_NET_IPPROTO_UDP); + + const int native_domain = AF_INET; + + const int native_type = type == SYS_NET_SOCK_STREAM ? SOCK_STREAM : SOCK_DGRAM; + + int native_proto = protocol == SYS_NET_IPPROTO_TCP ? IPPROTO_TCP : + protocol == SYS_NET_IPPROTO_UDP ? IPPROTO_UDP : + IPPROTO_IP; + + auto socket_res = ::socket(native_domain, native_type, native_proto); + + if (socket_res == -1) + { + return -get_last_error(false); + } + + set_socket(socket_res, family, type, protocol); + return CELL_OK; +} + +void lv2_socket_native::set_socket(socket_type socket, lv2_socket_family family, lv2_socket_type type, lv2_ip_protocol protocol) +{ + this->socket = socket; + this->family = family; + this->type = type; + this->protocol = protocol; + + set_default_buffers(); + set_non_blocking(); +} + +std::tuple lv2_socket_native::accept(bool is_lock) +{ + std::unique_lock lock(mutex, std::defer_lock); + + if (is_lock) + { + lock.lock(); + } + + ::sockaddr_storage native_addr; + ::socklen_t native_addrlen = sizeof(native_addr); + + socket_type native_socket = ::accept(socket, reinterpret_cast(&native_addr), &native_addrlen); + + if (native_socket != -1) + { + auto newsock = std::make_shared(family, type, protocol); + newsock->set_socket(native_socket, family, type, protocol); + s32 id_ps3 = idm::import_existing(newsock); + + if (id_ps3 == id_manager::id_traits::invalid) + { + return {true, -SYS_NET_EMFILE, {}}; + } + + sys_net_sockaddr ps3_addr = native_addr_to_sys_net_addr(native_addr); + + return {true, id_ps3, ps3_addr}; + } + + if (auto result = get_last_error(!so_nbio); result) + { + return {true, -result, {}}; + } + + return {false, {}, {}}; +} + +s32 lv2_socket_native::bind(const sys_net_sockaddr& addr, [[maybe_unused]] s32 ps3_id) +{ + std::lock_guard lock(mutex); + + const auto* psa_in = reinterpret_cast(&addr); + + ::sockaddr_in native_addr{}; + native_addr.sin_family = AF_INET; + native_addr.sin_port = std::bit_cast(psa_in->sin_port); + native_addr.sin_addr.s_addr = std::bit_cast(psa_in->sin_addr); + ::socklen_t native_addr_len = sizeof(native_addr); + + sys_net.warning("[Native] Trying to bind %s:%d", native_addr.sin_addr, std::bit_cast, u16>(native_addr.sin_port)); + + if (::bind(socket, reinterpret_cast(&native_addr), native_addr_len) == 0) + { + return CELL_OK; + } + return -get_last_error(false); +} + +std::optional lv2_socket_native::connect(const sys_net_sockaddr& addr) +{ + std::lock_guard lock(mutex); + + const auto* psa_in = reinterpret_cast(&addr); + + ::sockaddr_in native_addr = sys_net_addr_to_native_addr(addr); + ::socklen_t native_addr_len = sizeof(native_addr); + + sys_net.notice("[Native] Attempting to connect on %s:%d", native_addr.sin_addr, std::bit_cast, u16>(native_addr.sin_port)); + + if (psa_in->sin_port == 53) + { + // Add socket to the dns hook list + sys_net.notice("[Native] sys_net_bnet_connect: using DNS..."); + auto& dnshook = g_fxo->get(); + dnshook.add_dns_spy(socket); + } + + if (::connect(socket, reinterpret_cast(&native_addr), native_addr_len) == 0) + { + return CELL_OK; + } + + sys_net_error result = get_last_error(!so_nbio); + + if (result) + { + if (result == SYS_NET_EWOULDBLOCK || result == SYS_NET_EINPROGRESS) + { + result = SYS_NET_EINPROGRESS; +#ifdef _WIN32 + connecting = true; +#endif + this->poll_queue(u32{0}, lv2_socket::poll_t::write, [this](bs_t events) -> bool + { + if (events & lv2_socket::poll_t::write) + { + int native_error; + ::socklen_t size = sizeof(native_error); + if (::getsockopt(socket, SOL_SOCKET, SO_ERROR, reinterpret_cast(&native_error), &size) != 0 || size != sizeof(int)) + { + so_error = 1; + } + else + { + // TODO: check error formats (both native and translated) + so_error = native_error ? get_last_error(false, native_error) : 0; + } + + return true; + } + + events += lv2_socket::poll_t::write; + return false; + }); + } + + return -result; + } + +#ifdef _WIN32 + connecting = true; +#endif + + return std::nullopt; +} + +s32 lv2_socket_native::connect_followup() +{ + int native_error; + ::socklen_t size = sizeof(native_error); + if (::getsockopt(socket, SOL_SOCKET, SO_ERROR, reinterpret_cast(&native_error), &size) != 0 || size != sizeof(int)) + { + return -1; + } + + // TODO: check error formats (both native and translated) + return native_error ? -get_last_error(false, native_error) : 0; +} + +std::pair lv2_socket_native::getpeername() +{ + std::lock_guard lock(mutex); + + ::sockaddr_storage native_addr; + ::socklen_t native_addrlen = sizeof(native_addr); + + if (::getpeername(socket, reinterpret_cast(&native_addr), &native_addrlen) == 0) + { + ensure(native_addr.ss_family == AF_INET); + + sys_net_sockaddr sn_addr = native_addr_to_sys_net_addr(native_addr); + + return {CELL_OK, sn_addr}; + } + + return {-get_last_error(false), {}}; +} + +std::pair lv2_socket_native::getsockname() +{ + std::lock_guard lock(mutex); + + ::sockaddr_storage native_addr; + ::socklen_t native_addrlen = sizeof(native_addr); + + if (::getsockname(socket, reinterpret_cast(&native_addr), &native_addrlen) == 0) + { + ensure(native_addr.ss_family == AF_INET); + + sys_net_sockaddr sn_addr = native_addr_to_sys_net_addr(native_addr); + + return {CELL_OK, sn_addr}; + } +#ifdef _WIN32 + else + { + // windows doesn't support getsockname for sockets that are not bound + if (get_native_error() == WSAEINVAL) + { + return {CELL_OK, {}}; + } + } +#endif + + return {-get_last_error(false), {}}; +} + +std::tuple lv2_socket_native::getsockopt(s32 level, s32 optname, u32 len) +{ + std::lock_guard lock(mutex); + + sockopt_data out_val; + u32 out_len = sizeof(out_val); + + int native_level = -1; + int native_opt = -1; + + union + { + char ch[128]; + int _int = 0; + ::timeval timeo; + ::linger linger; + } native_val; + ::socklen_t native_len = sizeof(native_val); + + if (level == SYS_NET_SOL_SOCKET) + { + native_level = SOL_SOCKET; + + switch (optname) + { + case SYS_NET_SO_NBIO: + { + // Special + out_val._int = so_nbio; + out_len = sizeof(s32); + return {CELL_OK, out_val, out_len}; + } + case SYS_NET_SO_ERROR: + { + // Special + out_val._int = std::exchange(so_error, 0); + out_len = sizeof(s32); + return {CELL_OK, out_val, out_len}; + } + case SYS_NET_SO_KEEPALIVE: + { + native_opt = SO_KEEPALIVE; + break; + } + case SYS_NET_SO_SNDBUF: + { + native_opt = SO_SNDBUF; + break; + } + case SYS_NET_SO_RCVBUF: + { + native_opt = SO_RCVBUF; + break; + } + case SYS_NET_SO_SNDLOWAT: + { + native_opt = SO_SNDLOWAT; + break; + } + case SYS_NET_SO_RCVLOWAT: + { + native_opt = SO_RCVLOWAT; + break; + } + case SYS_NET_SO_BROADCAST: + { + native_opt = SO_BROADCAST; + break; + } +#ifdef _WIN32 + case SYS_NET_SO_REUSEADDR: + { + out_val._int = so_reuseaddr; + out_len = sizeof(s32); + return {CELL_OK, out_val, out_len}; + } + case SYS_NET_SO_REUSEPORT: + { + out_val._int = so_reuseport; + out_len = sizeof(s32); + return {CELL_OK, out_val, out_len}; + } +#else + case SYS_NET_SO_REUSEADDR: + { + native_opt = SO_REUSEADDR; + break; + } + case SYS_NET_SO_REUSEPORT: + { + native_opt = SO_REUSEPORT; + break; + } +#endif + case SYS_NET_SO_SNDTIMEO: + case SYS_NET_SO_RCVTIMEO: + { + if (len < sizeof(sys_net_timeval)) + return {-SYS_NET_EINVAL, {}, {}}; + + native_opt = optname == SYS_NET_SO_SNDTIMEO ? SO_SNDTIMEO : SO_RCVTIMEO; + break; + } + case SYS_NET_SO_LINGER: + { + if (len < sizeof(sys_net_linger)) + return {-SYS_NET_EINVAL, {}, {}}; + + native_opt = SO_LINGER; + break; + } + default: + { + sys_net.error("sys_net_bnet_getsockopt(s=%d, SOL_SOCKET): unknown option (0x%x)", lv2_id, optname); + return {-SYS_NET_EINVAL, {}, {}}; + } + } + } + else if (level == SYS_NET_IPPROTO_TCP) + { + native_level = IPPROTO_TCP; + + switch (optname) + { + case SYS_NET_TCP_MAXSEG: + { + // Special (no effect) + out_val._int = so_tcp_maxseg; + out_len = sizeof(s32); + return {CELL_OK, out_val, out_len}; + } + case SYS_NET_TCP_NODELAY: + { + native_opt = TCP_NODELAY; + break; + } + default: + { + sys_net.error("sys_net_bnet_getsockopt(s=%d, IPPROTO_TCP): unknown option (0x%x)", lv2_id, optname); + return {-SYS_NET_EINVAL, {}, {}}; + } + } + } + else if (level == SYS_NET_IPPROTO_IP) + { + native_level = IPPROTO_IP; + switch (optname) + { + case SYS_NET_IP_HDRINCL: + { + native_opt = IP_HDRINCL; + break; + } + case SYS_NET_IP_TOS: + { + native_opt = IP_TOS; + break; + } + case SYS_NET_IP_TTL: + { + native_opt = IP_TTL; + break; + } + case SYS_NET_IP_MULTICAST_IF: + { + native_opt = IP_MULTICAST_IF; + break; + } + case SYS_NET_IP_MULTICAST_TTL: + { + native_opt = IP_MULTICAST_TTL; + break; + } + case SYS_NET_IP_MULTICAST_LOOP: + { + native_opt = IP_MULTICAST_LOOP; + break; + } + case SYS_NET_IP_ADD_MEMBERSHIP: + { + native_opt = IP_ADD_MEMBERSHIP; + break; + } + case SYS_NET_IP_DROP_MEMBERSHIP: + { + native_opt = IP_DROP_MEMBERSHIP; + break; + } + case SYS_NET_IP_TTLCHK: + { + sys_net.error("sys_net_bnet_getsockopt(IPPROTO_IP, SYS_NET_IP_TTLCHK): stubbed option"); + return {CELL_OK, out_val, out_len}; + } + case SYS_NET_IP_MAXTTL: + { + sys_net.error("sys_net_bnet_getsockopt(IPPROTO_IP, SYS_NET_IP_MAXTTL): stubbed option"); + return {CELL_OK, out_val, out_len}; + } + case SYS_NET_IP_DONTFRAG: + { +#ifdef _WIN32 + native_opt = IP_DONTFRAGMENT; +#else + native_opt = IP_DF; +#endif + break; + } + default: + { + sys_net.error("sys_net_bnet_getsockopt(s=%d, IPPROTO_IP): unknown option (0x%x)", lv2_id, optname); + return {-SYS_NET_EINVAL, {}, {}}; + } + } + } + else + { + sys_net.error("sys_net_bnet_getsockopt(s=%d): unknown level (0x%x)", lv2_id, level); + return {-SYS_NET_EINVAL, {}, {}}; + } + + if (::getsockopt(socket, native_level, native_opt, native_val.ch, &native_len) != 0) + { + return {-get_last_error(false), {}, {}}; + } + + if (level == SYS_NET_SOL_SOCKET) + { + switch (optname) + { + case SYS_NET_SO_SNDTIMEO: + case SYS_NET_SO_RCVTIMEO: + { + // TODO + out_val.timeo = {::narrow(native_val.timeo.tv_sec), ::narrow(native_val.timeo.tv_usec)}; + out_len = sizeof(sys_net_timeval); + return {CELL_OK, out_val, out_len}; + } + case SYS_NET_SO_LINGER: + { + // TODO + out_val.linger = {::narrow(native_val.linger.l_onoff), ::narrow(native_val.linger.l_linger)}; + out_len = sizeof(sys_net_linger); + return {CELL_OK, out_val, out_len}; + } + default: break; + } + } + + // Fallback to int + out_val._int = native_val._int; + out_len = sizeof(s32); + return {CELL_OK, out_val, out_len}; +} + +s32 lv2_socket_native::setsockopt(s32 level, s32 optname, const std::vector& optval) +{ + std::lock_guard lock(mutex); + + int native_int = 0; + int native_level = -1; + int native_opt = -1; + const void* native_val = &native_int; + ::socklen_t native_len = sizeof(int); + ::linger native_linger; + ::ip_mreq native_mreq; + +#ifdef _WIN32 + u32 native_timeo; +#else + ::timeval native_timeo; +#endif + + native_int = *reinterpret_cast*>(optval.data()); + + if (level == SYS_NET_SOL_SOCKET) + { + native_level = SOL_SOCKET; + + switch (optname) + { + case SYS_NET_SO_NBIO: + { + // Special + so_nbio = native_int; + return {}; + } + case SYS_NET_SO_KEEPALIVE: + { + native_opt = SO_KEEPALIVE; + break; + } + case SYS_NET_SO_SNDBUF: + { + native_opt = SO_SNDBUF; + break; + } + case SYS_NET_SO_RCVBUF: + { + native_opt = SO_RCVBUF; + break; + } + case SYS_NET_SO_SNDLOWAT: + { + native_opt = SO_SNDLOWAT; + break; + } + case SYS_NET_SO_RCVLOWAT: + { + native_opt = SO_RCVLOWAT; + break; + } + case SYS_NET_SO_BROADCAST: + { + native_opt = SO_BROADCAST; + break; + } +#ifdef _WIN32 + case SYS_NET_SO_REUSEADDR: + { + native_opt = SO_REUSEADDR; + so_reuseaddr = native_int; + native_int = so_reuseaddr || so_reuseport ? 1 : 0; + break; + } + case SYS_NET_SO_REUSEPORT: + { + native_opt = SO_REUSEADDR; + so_reuseport = native_int; + native_int = so_reuseaddr || so_reuseport ? 1 : 0; + break; + } +#else + case SYS_NET_SO_REUSEADDR: + { + native_opt = SO_REUSEADDR; + break; + } + case SYS_NET_SO_REUSEPORT: + { + native_opt = SO_REUSEPORT; + break; + } +#endif + case SYS_NET_SO_SNDTIMEO: + case SYS_NET_SO_RCVTIMEO: + { + if (optval.size() < sizeof(sys_net_timeval)) + return -SYS_NET_EINVAL; + + native_opt = optname == SYS_NET_SO_SNDTIMEO ? SO_SNDTIMEO : SO_RCVTIMEO; + native_val = &native_timeo; + native_len = sizeof(native_timeo); +#ifdef _WIN32 + native_timeo = ::narrow(reinterpret_cast(optval.data())->tv_sec) * 1000; + native_timeo += ::narrow(reinterpret_cast(optval.data())->tv_usec) / 1000; +#else + native_timeo.tv_sec = ::narrow(reinterpret_cast(optval.data())->tv_sec); + native_timeo.tv_usec = ::narrow(reinterpret_cast(optval.data())->tv_usec); +#endif + break; + } + case SYS_NET_SO_LINGER: + { + if (optval.size() < sizeof(sys_net_linger)) + return -SYS_NET_EINVAL; + + // TODO + native_opt = SO_LINGER; + native_val = &native_linger; + native_len = sizeof(native_linger); + native_linger.l_onoff = reinterpret_cast(optval.data())->l_onoff; + native_linger.l_linger = reinterpret_cast(optval.data())->l_linger; + break; + } + case SYS_NET_SO_USECRYPTO: + { + // TODO + sys_net.error("sys_net_bnet_setsockopt(s=%d, SOL_SOCKET): Stubbed option (0x%x) (SYS_NET_SO_USECRYPTO)", lv2_id, optname); + return {}; + } + case SYS_NET_SO_USESIGNATURE: + { + // TODO + sys_net.error("sys_net_bnet_setsockopt(s=%d, SOL_SOCKET): Stubbed option (0x%x) (SYS_NET_SO_USESIGNATURE)", lv2_id, optname); + return {}; + } + default: + { + sys_net.error("sys_net_bnet_setsockopt(s=%d, SOL_SOCKET): unknown option (0x%x)", lv2_id, optname); + return -SYS_NET_EINVAL; + } + } + } + else if (level == SYS_NET_IPPROTO_TCP) + { + native_level = IPPROTO_TCP; + + switch (optname) + { + case SYS_NET_TCP_MAXSEG: + { + // Special (no effect) + so_tcp_maxseg = native_int; + return {}; + } + case SYS_NET_TCP_NODELAY: + { + native_opt = TCP_NODELAY; + break; + } + default: + { + sys_net.error("sys_net_bnet_setsockopt(s=%d, IPPROTO_TCP): unknown option (0x%x)", lv2_id, optname); + return -SYS_NET_EINVAL; + } + } + } + else if (level == SYS_NET_IPPROTO_IP) + { + native_level = IPPROTO_IP; + + switch (optname) + { + case SYS_NET_IP_HDRINCL: + { + native_opt = IP_HDRINCL; + break; + } + case SYS_NET_IP_TOS: + { + native_opt = IP_TOS; + break; + } + case SYS_NET_IP_TTL: + { + native_opt = IP_TTL; + break; + } + case SYS_NET_IP_MULTICAST_IF: + { + native_opt = IP_MULTICAST_IF; + break; + } + case SYS_NET_IP_MULTICAST_TTL: + { + native_opt = IP_MULTICAST_TTL; + break; + } + case SYS_NET_IP_MULTICAST_LOOP: + { + native_opt = IP_MULTICAST_LOOP; + break; + } + case SYS_NET_IP_ADD_MEMBERSHIP: + case SYS_NET_IP_DROP_MEMBERSHIP: + { + if (optval.size() < sizeof(sys_net_ip_mreq)) + return -SYS_NET_EINVAL; + + native_opt = optname == SYS_NET_IP_ADD_MEMBERSHIP ? IP_ADD_MEMBERSHIP : IP_DROP_MEMBERSHIP; + native_val = &native_mreq; + native_len = sizeof(::ip_mreq); + native_mreq.imr_interface.s_addr = std::bit_cast(reinterpret_cast(optval.data())->imr_interface); + native_mreq.imr_multiaddr.s_addr = std::bit_cast(reinterpret_cast(optval.data())->imr_multiaddr); + break; + } + case SYS_NET_IP_TTLCHK: + { + sys_net.error("sys_net_bnet_setsockopt(s=%d, IPPROTO_IP): Stubbed option (0x%x) (SYS_NET_IP_TTLCHK)", lv2_id, optname); + break; + } + case SYS_NET_IP_MAXTTL: + { + sys_net.error("sys_net_bnet_setsockopt(s=%d, IPPROTO_IP): Stubbed option (0x%x) (SYS_NET_IP_MAXTTL)", lv2_id, optname); + break; + } + case SYS_NET_IP_DONTFRAG: + { +#ifdef _WIN32 + native_opt = IP_DONTFRAGMENT; +#else + native_opt = IP_DF; +#endif + break; + } + default: + { + sys_net.error("sys_net_bnet_setsockopt(s=%d, IPPROTO_IP): unknown option (0x%x)", lv2_id, optname); + return -SYS_NET_EINVAL; + } + } + } + else + { + sys_net.error("sys_net_bnet_setsockopt(s=%d): unknown level (0x%x)", lv2_id, level); + return -SYS_NET_EINVAL; + } + + if (::setsockopt(socket, native_level, native_opt, static_cast(native_val), native_len) == 0) + { + return {}; + } + + return -get_last_error(false); +} + +s32 lv2_socket_native::listen(s32 backlog) +{ + std::lock_guard lock(mutex); + + if (::listen(socket, backlog) == 0) + { + return CELL_OK; + } + + return -get_last_error(false); +} + +std::optional, sys_net_sockaddr>> lv2_socket_native::recvfrom(s32 flags, u32 len, bool is_lock) +{ + std::unique_lock lock(mutex, std::defer_lock); + + if (is_lock) + { + lock.lock(); + } + + int native_flags = 0; + ::sockaddr_storage native_addr{}; + ::socklen_t native_addrlen = sizeof(native_addr); + std::vector res_buf(len); + + auto& dnshook = g_fxo->get(); + if (dnshook.is_dns(lv2_id) && dnshook.is_dns_queue(lv2_id)) + { + auto& nph = g_fxo->get>(); + const auto packet = dnshook.get_dns_packet(lv2_id); + ensure(packet.size() < len); + memcpy(res_buf.data(), packet.data(), packet.size()); + native_addr.ss_family = AF_INET; + (reinterpret_cast<::sockaddr_in*>(&native_addr))->sin_port = std::bit_cast>(53); // htons(53) + (reinterpret_cast<::sockaddr_in*>(&native_addr))->sin_addr.s_addr = nph.get_dns_ip(); + const auto sn_addr = native_addr_to_sys_net_addr(native_addr); + + return {{::narrow(packet.size()), res_buf, sn_addr}}; + } + + if (flags & SYS_NET_MSG_PEEK) + { + native_flags |= MSG_PEEK; + } + + if (flags & SYS_NET_MSG_WAITALL) + { + native_flags |= MSG_WAITALL; + } + + auto native_result = ::recvfrom(socket, reinterpret_cast(res_buf.data()), len, native_flags, reinterpret_cast(&native_addr), &native_addrlen); + + if (native_result >= 0) + { + const auto sn_addr = native_addr_to_sys_net_addr(native_addr); + return {{::narrow(native_result), res_buf, sn_addr}}; + } +#ifdef _WIN32 + else + { + // Windows returns an error when trying to peek at a message and buffer not long enough to contain the whole message, should be ignored + if ((native_flags & MSG_PEEK) && get_native_error() == WSAEMSGSIZE) + { + const auto sn_addr = native_addr_to_sys_net_addr(native_addr); + return {{len, res_buf, sn_addr}}; + } + // Windows will return WSASHUTDOWN when the connection is shutdown, POSIX just returns EOF (0) in this situation. + if (get_native_error() == WSAESHUTDOWN) + { + const auto sn_addr = native_addr_to_sys_net_addr(native_addr); + return {{0, {}, sn_addr}}; + } + } +#endif + + const auto result = get_last_error(!so_nbio && (flags & SYS_NET_MSG_DONTWAIT) == 0); + if (result) + { + return {{-result, {}, {}}}; + } + + return std::nullopt; +} + +std::optional lv2_socket_native::sendto(s32 flags, const std::vector& buf, std::optional opt_sn_addr, bool is_lock) +{ + std::unique_lock lock(mutex, std::defer_lock); + + if (is_lock) + { + lock.lock(); + } + + int native_flags = 0; + int native_result = -1; + std::optional native_addr = std::nullopt; + + if (opt_sn_addr) + { + native_addr = sys_net_addr_to_native_addr(*opt_sn_addr); + sys_net.trace("[Native] Attempting to send to %s:%d", (*native_addr).sin_addr, std::bit_cast, u16>((*native_addr).sin_port)); + } + + sys_net_error result{}; + + if (flags & SYS_NET_MSG_WAITALL) + { + native_flags |= MSG_WAITALL; + } + + auto& dnshook = g_fxo->get(); + if (opt_sn_addr && type == SYS_NET_SOCK_DGRAM && reinterpret_cast(&*opt_sn_addr)->sin_port == 53) + { + dnshook.add_dns_spy(lv2_id); + } + + if (dnshook.is_dns(lv2_id)) + { + const s32 ret_analyzer = dnshook.analyze_dns_packet(lv2_id, reinterpret_cast(buf.data()), buf.size()); + + // If we're not connected just never send the packet and pretend we did + auto& nph = g_fxo->get>(); + if (!nph.get_net_status()) + { + return {::narrow(buf.size())}; + } + + // Check if the packet is intercepted + if (ret_analyzer >= 0) + { + return {ret_analyzer}; + } + } + + native_result = ::sendto(socket, reinterpret_cast(buf.data()), buf.size(), native_flags, native_addr ? reinterpret_cast(&(*native_addr)) : nullptr, native_addr ? sizeof(*native_addr) : 0); + + if (native_result >= 0) + { + return {native_result}; + } + + 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; +} + +void lv2_socket_native::close() +{ + std::lock_guard lock(mutex); + if (socket) + { +#ifdef _WIN32 + ::closesocket(socket); +#else + ::close(socket); +#endif + socket = {}; + } + + auto& dnshook = g_fxo->get(); + dnshook.remove_dns_spy(lv2_id); +} + +s32 lv2_socket_native::shutdown(s32 how) +{ + std::lock_guard lock(mutex); + +#ifdef _WIN32 + const int native_how = + how == SYS_NET_SHUT_RD ? SD_RECEIVE : + how == SYS_NET_SHUT_WR ? SD_SEND : + SD_BOTH; +#else + const int native_how = + how == SYS_NET_SHUT_RD ? SHUT_RD : + how == SYS_NET_SHUT_WR ? SHUT_WR : + SHUT_RDWR; +#endif + + if (::shutdown(socket, native_how) == 0) + { + return CELL_OK; + } + + return -get_last_error(false); +} + +s32 lv2_socket_native::poll(sys_net_pollfd& sn_pfd, pollfd& native_pfd) +{ + // Check for fake packet for dns interceptions + auto& dnshook = g_fxo->get(); + if (sn_pfd.events & SYS_NET_POLLIN && dnshook.is_dns(sn_pfd.fd) && dnshook.is_dns_queue(sn_pfd.fd)) + { + sn_pfd.revents |= SYS_NET_POLLIN; + } + if (sn_pfd.events & ~(SYS_NET_POLLIN | SYS_NET_POLLOUT | SYS_NET_POLLERR)) + { + sys_net.warning("sys_net_bnet_poll(fd=%d): events=0x%x", sn_pfd.fd, sn_pfd.events); + } + + native_pfd.fd = socket; + + if (sn_pfd.events & SYS_NET_POLLIN) + { + native_pfd.events |= POLLIN; + } + if (sn_pfd.events & SYS_NET_POLLOUT) + { + native_pfd.events |= POLLOUT; + } + + return 0; +} + +s32 lv2_socket_native::select(bs_t selected, pollfd& native_pfd) +{ + native_pfd.fd = socket; + if (selected & lv2_socket::poll_t::read) + { + native_pfd.events |= POLLIN; + } + if (selected & lv2_socket::poll_t::write) + { + native_pfd.events |= POLLOUT; + } + + return 0; +} + +void lv2_socket_native::set_default_buffers() +{ + // Those are the default PS3 values + u32 default_RCVBUF = (type == SYS_NET_SOCK_STREAM) ? 65535 : 9216; + if (::setsockopt(socket, SOL_SOCKET, SO_RCVBUF, reinterpret_cast(&default_RCVBUF), sizeof(default_RCVBUF)) != 0) + { + sys_net.error("Error setting default SO_RCVBUF on sys_net_bnet_socket socket"); + } + u32 default_SNDBUF = 131072; + if (::setsockopt(socket, SOL_SOCKET, SO_SNDBUF, reinterpret_cast(&default_SNDBUF), sizeof(default_SNDBUF)) != 0) + { + sys_net.error("Error setting default SO_SNDBUF on sys_net_bnet_socket socket"); + } +} + +void lv2_socket_native::set_non_blocking() +{ + // Set non-blocking + // This is done to avoid having threads stuck on blocking socket functions + // Blocking functions just put the thread to sleep and delegate the waking up to network_thread which polls the sockets +#ifdef _WIN32 + u_long _true = 1; + ::ioctlsocket(socket, FIONBIO, &_true); +#else + ::fcntl(socket, F_SETFL, ::fcntl(socket, F_GETFL, 0) | O_NONBLOCK); +#endif +} diff --git a/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket_native.h b/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket_native.h new file mode 100644 index 0000000000..3ad3a0dedc --- /dev/null +++ b/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket_native.h @@ -0,0 +1,68 @@ + +#pragma once + +#ifdef _WIN32 +#include +#include +#else +#ifdef __clang__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wold-style-cast" +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef __clang__ +#pragma GCC diagnostic pop +#endif +#endif + +#include "lv2_socket.h" + +class lv2_socket_native final : public lv2_socket +{ +public: + lv2_socket_native(lv2_socket_family family, lv2_socket_type type, lv2_ip_protocol protocol); + ~lv2_socket_native(); + s32 create_socket(); + + std::tuple accept(bool is_lock = true) override; + s32 bind(const sys_net_sockaddr& addr, s32 ps3_id) override; + + std::optional connect(const sys_net_sockaddr& addr) override; + s32 connect_followup() override; + + std::pair getpeername() override; + std::pair getsockname() override; + std::tuple getsockopt(s32 level, s32 optname, u32 len) override; + s32 setsockopt(s32 level, s32 optname, const std::vector& optval) override; + std::optional, sys_net_sockaddr>> recvfrom(s32 flags, u32 len, bool is_lock = true) override; + std::optional sendto(s32 flags, const std::vector& buf, std::optional opt_sn_addr, bool is_lock = true) override; + + s32 poll(sys_net_pollfd& sn_pfd, pollfd& native_pfd) override; + s32 select(bs_t selected, pollfd& native_pfd) override; + + s32 listen(s32 backlog) override; + void close() override; + s32 shutdown(s32 how) override; + +private: + void set_socket(socket_type socket, lv2_socket_family family, lv2_socket_type type, lv2_ip_protocol protocol); + void set_default_buffers(); + void set_non_blocking(); + +private: + // Value keepers +#ifdef _WIN32 + s32 so_reuseaddr = 0; + s32 so_reuseport = 0; +#endif +}; diff --git a/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket_p2p.cpp b/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket_p2p.cpp new file mode 100644 index 0000000000..0f5724c434 --- /dev/null +++ b/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket_p2p.cpp @@ -0,0 +1,295 @@ +#include "stdafx.h" +#include "lv2_socket_p2p.h" +#include "Emu/NP/np_helpers.h" +#include "network_context.h" +#include "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) +{ +} + +void lv2_socket_p2p::handle_new_data(sys_net_sockaddr_in_p2p p2p_addr, std::vector 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 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 lv2_socket_p2p::accept([[maybe_unused]] bool is_lock) +{ + sys_net.fatal("[P2P] accept() called on a P2P socket"); + return {}; +} + +std::optional 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 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, s32 ps3_id) +{ + const auto* psa_in_p2p = reinterpret_cast(&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(psa_in_p2p->sin_addr)), p2p_port, p2p_vport); + + ensure(p2p_vport != 0); + if (p2p_port != 3658) + { + sys_net.warning("[P2P] Attempting to bind a socket to a port != 3658"); + } + + socket_type real_socket{}; + + auto& nc = g_fxo->get(); + { + std::lock_guard list_lock(nc.list_p2p_ports_mutex); + if (!nc.list_p2p_ports.contains(p2p_port)) + { + nc.list_p2p_ports.emplace(std::piecewise_construct, std::forward_as_tuple(p2p_port), std::forward_as_tuple(p2p_port)); + } + + auto& pport = nc.list_p2p_ports.at(p2p_port); + real_socket = pport.p2p_socket; + { + std::lock_guard lock(pport.bound_p2p_vports_mutex); + if (pport.bound_p2p_vports.count(p2p_vport) != 0) + { + return -SYS_NET_EADDRINUSE; + } + pport.bound_p2p_vports.insert(std::make_pair(p2p_vport, ps3_id)); + } + } + + { + std::lock_guard lock(mutex); + port = p2p_port; + vport = p2p_vport; + socket = real_socket; + } + + return CELL_OK; +} + +std::pair lv2_socket_p2p::getsockname() +{ + std::lock_guard lock(mutex); + + // Unbound socket + if (!socket) + { + return {CELL_OK, {}}; + } + + sys_net_sockaddr sn_addr{}; + sys_net_sockaddr_in_p2p* paddr = reinterpret_cast(&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 lv2_socket_p2p::getsockopt([[maybe_unused]] s32 level, [[maybe_unused]] s32 optname, [[maybe_unused]] u32 len) +{ + // TODO + return {}; +} + +s32 lv2_socket_p2p::setsockopt(s32 level, s32 optname, const std::vector& optval) +{ + // TODO + int native_int = *reinterpret_cast*>(optval.data()); + + if (level == SYS_NET_SOL_SOCKET && optname == SYS_NET_SO_NBIO) + { + so_nbio = native_int; + } + + return {}; +} + +std::optional, sys_net_sockaddr>> lv2_socket_p2p::recvfrom([[maybe_unused]] s32 flags, u32 len, bool is_lock) +{ + std::unique_lock lock(mutex, std::defer_lock); + + if (is_lock) + { + lock.lock(); + } + + sys_net.trace("[P2P] p2p_data for vport %d contains %d elements", vport, data.size()); + + if (data.empty()) + { + return {{-SYS_NET_EWOULDBLOCK, {}, {}}}; + } + + std::vector res_buf(len); + + const auto& p2p_data = data.front(); + s32 native_result = std::min(len, static_cast(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 lv2_socket_p2p::sendto(s32 flags, const std::vector& buf, std::optional opt_sn_addr, bool is_lock) +{ + std::unique_lock lock(mutex, std::defer_lock); + + if (is_lock) + { + lock.lock(); + } + + ensure(opt_sn_addr); + ensure(socket); // ensures it has been bound + ensure(buf.size() <= (65535 - sizeof(u16))); // catch games using full payload for future fragmentation implementation if necessary + const u16 p2p_port = reinterpret_cast(&*opt_sn_addr)->sin_port; + const u16 p2p_vport = reinterpret_cast(&*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 p2p_data(buf.size() + sizeof(u16)); + reinterpret_cast&>(p2p_data[0]) = p2p_vport; + memcpy(p2p_data.data() + sizeof(u16), buf.data(), buf.size()); + + int native_flags = 0; + if (flags & SYS_NET_MSG_WAITALL) + { + native_flags |= MSG_WAITALL; + } + + auto native_result = ::sendto(socket, reinterpret_cast(p2p_data.data()), p2p_data.size(), native_flags, reinterpret_cast(&native_addr), sizeof(native_addr)); + + if (native_result >= 0) + { + return {native_result}; + } + + 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; +} + +void lv2_socket_p2p::close() +{ + if (!port || !vport) + { + return; + } + + auto& nc = g_fxo->get(); + { + std::lock_guard lock(nc.list_p2p_ports_mutex); + ensure(nc.list_p2p_ports.contains(port)); + auto& p2p_port = nc.list_p2p_ports.at(port); + { + std::lock_guard lock(p2p_port.bound_p2p_vports_mutex); + 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); + sys_net.trace("[P2P] poll checking for 0x%X", sn_pfd.events); + // 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; +} + +s32 lv2_socket_p2p::select(bs_t selected, [[maybe_unused]] pollfd& native_pfd) +{ + std::lock_guard lock(mutex); + // 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()); + return 1; + } + + return 0; +} diff --git a/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket_p2p.h b/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket_p2p.h new file mode 100644 index 0000000000..a256e9fa81 --- /dev/null +++ b/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket_p2p.h @@ -0,0 +1,43 @@ +#pragma once + +#include "lv2_socket.h" + +class lv2_socket_p2p : public lv2_socket +{ + public: + lv2_socket_p2p(lv2_socket_family family, lv2_socket_type type, lv2_ip_protocol protocol); + + std::tuple accept(bool is_lock = true) override; + s32 bind(const sys_net_sockaddr &addr, s32 ps3_id) override; + + std::optional connect(const sys_net_sockaddr &addr) override; + s32 connect_followup() override; + + std::pair getpeername() override; + std::pair getsockname() override; + + std::tuple getsockopt(s32 level, s32 optname, u32 len) override; + s32 setsockopt(s32 level, s32 optname, const std::vector& optval) override; + + s32 listen(s32 backlog) override; + + std::optional, sys_net_sockaddr>> recvfrom(s32 flags, u32 len, bool is_lock = true) override; + std::optional sendto(s32 flags, const std::vector& buf, std::optional opt_sn_addr, bool is_lock = true) override; + + void close() override; + s32 shutdown(s32 how) override; + + s32 poll(sys_net_pollfd& sn_pfd, pollfd& native_pfd) override; + s32 select(bs_t selected, pollfd& native_pfd) override; + + void handle_new_data(sys_net_sockaddr_in_p2p p2p_addr, std::vector p2p_data); + + protected: + // Port(actual bound port) and Virtual Port(indicated by u16 at the start of the packet) + u16 port = 3658, vport = 0; + u32 bound_addr = 0; + // Queue containing received packets from network_thread for SYS_NET_SOCK_DGRAM_P2P sockets + std::queue>> data{}; + // ID of the real socket for the actual bound udp port + socket_type socket{}; +}; diff --git a/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket_p2ps.cpp b/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket_p2ps.cpp new file mode 100644 index 0000000000..f0023b11d1 --- /dev/null +++ b/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket_p2ps.cpp @@ -0,0 +1,798 @@ +#include "stdafx.h" + +#include + +#include "Utilities/Thread.h" +#include "util/asm.hpp" +#include "util/atomic.hpp" +#include "lv2_socket_p2ps.h" +#include "Emu/NP/np_helpers.h" +#include "nt_p2p_port.h" +#include "network_context.h" +#include "sys_net_helpers.h" + +LOG_CHANNEL(sys_net); + +// Object in charge of retransmiting packets for STREAM_P2P sockets +class tcp_timeout_monitor +{ +public: + void add_message(s32 sock_id, const sockaddr_in* dst, std::vector data, u64 seq) + { + { + std::lock_guard lock(data_mutex); + + const auto now = steady_clock::now(); + + message msg; + msg.dst_addr = *dst; + msg.sock_id = sock_id; + msg.data = std::move(data); + msg.seq = seq; + msg.initial_sendtime = now; + + rtt_info rtt = rtts[sock_id]; + + const auto expected_time = now + rtt.rtt_time; + + msgs.insert(std::make_pair(expected_time, std::move(msg))); + } + wakey.notify_one(); // TODO: Should be improved to only wake if new timeout < old timeout + } + + void confirm_data_received(s32 sock_id, u64 ack) + { + std::lock_guard lock(data_mutex); + rtts[sock_id].num_retries = 0; + + const auto now = steady_clock::now(); + + for (auto it = msgs.begin(); it != msgs.end();) + { + auto& msg = it->second; + if (msg.sock_id == sock_id && msg.seq < ack) + { + // Decreases RTT if msg is early + if (now < it->first) + { + const auto actual_rtt = std::chrono::duration_cast(now - it->second.initial_sendtime); + const auto cur_rtt = rtts[sock_id].rtt_time; + if (cur_rtt > actual_rtt) + { + rtts[sock_id].rtt_time = (actual_rtt + cur_rtt) / 2; + } + } + it = msgs.erase(it); + continue; + } + it++; + } + } + + void operator()() + { + while (thread_ctrl::state() != thread_state::aborting) + { + std::unique_lock lock(data_mutex); + if (msgs.size()) + wakey.wait_until(lock, msgs.begin()->first); + else + wakey.wait(lock); + + if (thread_ctrl::state() == thread_state::aborting) + return; + + const auto now = steady_clock::now(); + // Check for messages that haven't been acked + std::set rtt_increased; + for (auto it = msgs.begin(); it != msgs.end();) + { + if (it->first > now) + break; + + // reply is late, increases rtt + auto& msg = it->second; + const auto addr = msg.dst_addr.sin_addr.s_addr; + rtt_info rtt = rtts[msg.sock_id]; + // Only increases rtt once per loop(in case a big number of packets are sent at once) + if (!rtt_increased.count(msg.sock_id)) + { + rtt.num_retries += 1; + // Increases current rtt by 10% + rtt.rtt_time += (rtt.rtt_time / 10); + rtts[addr] = rtt; + + rtt_increased.emplace(msg.sock_id); + } + + if (rtt.num_retries >= 10) + { + // Too many retries, need to notify the socket that the connection is dead + idm::check(msg.sock_id, [&](lv2_socket& sock) + { + ensure(sock.get_type() == SYS_NET_SOCK_STREAM_P2P); + auto& sock_p2ps = reinterpret_cast(sock); + sock_p2ps.set_status(p2ps_stream_status::stream_closed); + }); + it = msgs.erase(it); + continue; + } + + // resend the message + const auto res = idm::check(msg.sock_id, [&](lv2_socket& sock) -> bool + { + ensure(sock.get_type() == SYS_NET_SOCK_STREAM_P2P); + auto& sock_p2ps = reinterpret_cast(sock); + + if (sendto(sock_p2ps.get_socket(), reinterpret_cast(msg.data.data()), msg.data.size(), 0, reinterpret_cast(&msg.dst_addr), sizeof(msg.dst_addr)) == -1) + { + sock_p2ps.set_status(p2ps_stream_status::stream_closed); + return false; + } + return true; + }); + + if (!res || !res.ret) + { + it = msgs.erase(it); + continue; + } + + // Update key timeout + msgs.insert(std::make_pair(now + rtt.rtt_time, std::move(msg))); + it = msgs.erase(it); + } + } + } + + tcp_timeout_monitor& operator=(thread_state) + { + wakey.notify_one(); + return *this; + } + +public: + static constexpr auto thread_name = "Tcp Over Udp Timeout Manager Thread"sv; + +private: + std::condition_variable wakey; + std::mutex data_mutex; + // List of outgoing messages + struct message + { + s32 sock_id = 0; + ::sockaddr_in dst_addr{}; + std::vector data; + u64 seq = 0; + steady_clock::time_point initial_sendtime{}; + }; + std::map msgs; // (wakeup time, msg) + // List of rtts + struct rtt_info + { + unsigned long num_retries = 0; + std::chrono::milliseconds rtt_time = 50ms; + }; + std::unordered_map rtts; // (sock_id, rtt) +}; + +void initialize_tcp_timeout_monitor() +{ + g_fxo->need>(); +} + +u16 u2s_tcp_checksum(const u16* buffer, usz size) +{ + u32 cksum = 0; + while (size > 1) + { + cksum += *buffer++; + size -= sizeof(u16); + } + if (size) + cksum += *reinterpret_cast(buffer); + + cksum = (cksum >> 16) + (cksum & 0xffff); + cksum += (cksum >> 16); + return static_cast(~cksum); +} + +std::vector generate_u2s_packet(const p2ps_encapsulated_tcp& header, const u8* data, const u32 datasize) +{ + const u32 packet_size = (sizeof(u16) + sizeof(p2ps_encapsulated_tcp) + datasize); + ensure(packet_size < 65535); // packet size shouldn't be bigger than possible UDP payload + std::vector packet(packet_size); + u8* packet_data = packet.data(); + + *reinterpret_cast*>(packet_data) = header.dst_port; + memcpy(packet_data + sizeof(u16), &header, sizeof(p2ps_encapsulated_tcp)); + if (datasize) + memcpy(packet_data + sizeof(u16) + sizeof(p2ps_encapsulated_tcp), data, datasize); + + auto* hdr_ptr = reinterpret_cast(packet_data + sizeof(u16)); + hdr_ptr->checksum = 0; + hdr_ptr->checksum = u2s_tcp_checksum(utils::bless(hdr_ptr), sizeof(p2ps_encapsulated_tcp) + datasize); + + return packet; +} + +lv2_socket_p2ps::lv2_socket_p2ps(lv2_socket_family family, lv2_socket_type type, lv2_ip_protocol protocol) + : lv2_socket_p2p(family, type, protocol) +{ +} + +lv2_socket_p2ps::lv2_socket_p2ps(socket_type socket, u16 port, u16 vport, u32 op_addr, u16 op_port, u16 op_vport, u64 cur_seq, u64 data_beg_seq) + : lv2_socket_p2p(SYS_NET_AF_INET, SYS_NET_SOCK_STREAM_P2P, SYS_NET_IPPROTO_IP) +{ + this->socket = socket; + this->port = port; + this->vport = vport; + this->op_addr = op_addr; + this->op_port = op_port; + this->op_vport = op_vport; + this->cur_seq = cur_seq; + this->data_beg_seq = data_beg_seq; + status = p2ps_stream_status::stream_connected; +} + +bool lv2_socket_p2ps::handle_connected(p2ps_encapsulated_tcp* tcp_header, u8* data, ::sockaddr_storage* op_addr) +{ + std::lock_guard lock(mutex); + + if (status != p2ps_stream_status::stream_connected && status != p2ps_stream_status::stream_handshaking) + return false; + + nt_p2p_port::dump_packet(tcp_header); + + if (tcp_header->flags == p2ps_tcp_flags::ACK) + { + auto& tcpm = g_fxo->get>(); + tcpm.confirm_data_received(lv2_id, tcp_header->ack); + } + + auto send_ack = [&]() + { + auto final_ack = data_beg_seq; + while (received_data.contains(final_ack)) + { + final_ack += received_data.at(final_ack).size(); + } + data_available = final_ack - data_beg_seq; + + 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::ACK; + send_hdr.ack = final_ack; + auto packet = generate_u2s_packet(send_hdr, nullptr, 0); + sys_net.trace("Sent ack %d", final_ack); + send_u2s_packet(std::move(packet), reinterpret_cast<::sockaddr_in*>(op_addr), 0, false); + + // check if polling is happening + if (data_available && events.test_and_reset(lv2_socket::poll_t::read)) + { + bs_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({}); + } + } + }; + + if (status == p2ps_stream_status::stream_handshaking) + { + // Only expect SYN|ACK + if (tcp_header->flags == (p2ps_tcp_flags::SYN | p2ps_tcp_flags::ACK)) + { + sys_net.trace("Received SYN|ACK, status is now connected"); + data_beg_seq = tcp_header->seq + 1; + status = p2ps_stream_status::stream_connected; + send_ack(); + } + + return true; + } + else if (status == p2ps_stream_status::stream_connected) + { + if (tcp_header->seq < data_beg_seq) + { + // Data has already been processed + sys_net.trace("Data has already been processed"); + if (tcp_header->flags != p2ps_tcp_flags::ACK && tcp_header->flags != p2ps_tcp_flags::RST) + send_ack(); + return true; + } + + switch (tcp_header->flags) + { + case p2ps_tcp_flags::PSH: + case 0: + { + if (!received_data.count(tcp_header->seq)) + { + // New data + received_data.emplace(tcp_header->seq, std::vector(data, data + tcp_header->length)); + } + else + { + sys_net.trace("Data was not new!"); + } + + send_ack(); + return true; + } + case p2ps_tcp_flags::RST: + case p2ps_tcp_flags::FIN: + { + status = p2ps_stream_status::stream_closed; + return false; + } + default: + { + sys_net.error("Unknown U2S TCP flag received"); + return true; + } + } + } + + return true; +} + +bool lv2_socket_p2ps::handle_listening(p2ps_encapsulated_tcp* tcp_header, [[maybe_unused]] u8* data, ::sockaddr_storage* op_addr) +{ + std::lock_guard lock(mutex); + + if (status != p2ps_stream_status::stream_listening) + return false; + + // Only valid packet + if (tcp_header->flags == p2ps_tcp_flags::SYN && backlog.size() < max_backlog) + { + if (backlog.size() >= max_backlog) + { + // Send a RST packet on backlog full + sys_net.trace("Backlog was full, sent 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); + send_u2s_packet(std::move(packet), reinterpret_cast<::sockaddr_in*>(op_addr), 0, false); + } + + // Yes, new connection and a backlog is available, create a new lv2_socket for it and send SYN|ACK + // Prepare reply packet + sys_net.notice("Received connection on listening STREAM-P2P socket!"); + 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::SYN | p2ps_tcp_flags::ACK; + send_hdr.ack = tcp_header->seq + 1; + // Generates random starting SEQ + send_hdr.seq = rand(); + + // Create new socket + const u32 new_op_addr = reinterpret_cast(op_addr)->sin_addr.s_addr; + const u16 new_op_port = std::bit_cast>((reinterpret_cast(op_addr)->sin_port)); + const u16 new_op_vport = tcp_header->src_port; + const u64 new_cur_seq = send_hdr.seq + 1; + const u64 new_data_beg_seq = send_hdr.ack; + auto sock_lv2 = std::make_shared(socket, port, vport, new_op_addr, new_op_port, new_op_vport, new_cur_seq, new_data_beg_seq); + const s32 new_sock_id = idm::import_existing(sock_lv2); + sock_lv2->set_lv2_id(new_sock_id); + const u64 key_connected = (reinterpret_cast(op_addr)->sin_addr.s_addr) | (static_cast(tcp_header->src_port) << 48) | (static_cast(tcp_header->dst_port) << 32); + + { + auto& nc = g_fxo->get(); + auto& pport = nc.list_p2p_ports.at(port); + pport.bound_p2p_streams.emplace(key_connected, new_sock_id); + } + + auto packet = generate_u2s_packet(send_hdr, nullptr, 0); + { + std::lock_guard lock(sock_lv2->mutex); + send_u2s_packet(std::move(packet), reinterpret_cast<::sockaddr_in*>(op_addr), send_hdr.seq, true); + } + + backlog.push(new_sock_id); + if (events.test_and_reset(lv2_socket::poll_t::read)) + { + bs_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({}); + } + } + } + else if (tcp_header->flags == p2ps_tcp_flags::SYN) + { + // Send a RST packet on backlog full + sys_net.trace("Backlog was full, sent 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); + send_u2s_packet(std::move(packet), reinterpret_cast<::sockaddr_in*>(op_addr), 0, false); + } + + // Ignore other packets? + + return true; +} + +void lv2_socket_p2ps::send_u2s_packet(std::vector data, const ::sockaddr_in* dst, u32 seq, bool require_ack) +{ + char ip_str[16]; + inet_ntop(AF_INET, &dst->sin_addr, ip_str, sizeof(ip_str)); + sys_net.trace("Sending U2S packet on socket %d(id:%d): data(%d, seq %d, require_ack %d) to %s:%d", socket, lv2_id, data.size(), seq, require_ack, ip_str, std::bit_cast>(dst->sin_port)); + if (::sendto(socket, reinterpret_cast(data.data()), data.size(), 0, reinterpret_cast(dst), sizeof(sockaddr_in)) == -1) + { + sys_net.error("Attempting to send a u2s packet failed(%s), closing socket!", get_last_error(false)); + status = p2ps_stream_status::stream_closed; + return; + } + + // Adds to tcp timeout monitor to resend the message until an ack is received + if (require_ack) + { + auto& tcpm = g_fxo->get>(); + tcpm.add_message(lv2_id, dst, std::move(data), seq); + } +} + +p2ps_stream_status lv2_socket_p2ps::get_status() const +{ + return status; +} + +void lv2_socket_p2ps::set_status(p2ps_stream_status new_status) +{ + status = new_status; +} + +std::tuple lv2_socket_p2ps::accept(bool is_lock) +{ + std::unique_lock lock(mutex, std::defer_lock); + + if (is_lock) + { + lock.lock(); + } + + if (backlog.size() == 0) + { + if (so_nbio) + { + return {true, -SYS_NET_EWOULDBLOCK, {}}; + } + + return {false, {}, {}}; + } + + auto p2ps_client = backlog.front(); + backlog.pop(); + + sys_net_sockaddr ps3_addr{}; + auto* paddr = reinterpret_cast(&ps3_addr); + + lv2_socket_p2ps* sock_client = reinterpret_cast(idm::check_unlocked(p2ps_client)); + { + std::lock_guard lock(sock_client->mutex); + paddr->sin_family = SYS_NET_AF_INET; + paddr->sin_addr = std::bit_cast, u32>(sock_client->op_addr); + paddr->sin_port = sock_client->op_vport; + paddr->sin_vport = sock_client->op_port; + paddr->sin_len = sizeof(sys_net_sockaddr_in_p2p); + } + + return {true, p2ps_client, ps3_addr}; +} + +s32 lv2_socket_p2ps::bind(const sys_net_sockaddr& addr, s32 ps3_id) +{ + const auto* psa_in_p2p = reinterpret_cast(&addr); + + // For SYS_NET_SOCK_STREAM_P2P sockets, the port is the "fake" tcp port and the vport is the udp port it's bound to + u16 p2p_port = psa_in_p2p->sin_vport; + u16 p2p_vport = psa_in_p2p->sin_port; + + sys_net.notice("[P2PS] Trying to bind %s:%d:%d", np::ip_to_string(std::bit_cast(psa_in_p2p->sin_addr)), p2p_port, p2p_vport); + + ensure(p2p_vport != 0); + if (p2p_port != 3658) + { + sys_net.warning("[P2PS] Attempting to bind a socket to a port != 3658"); + } + + socket_type real_socket{}; + + auto& nc = g_fxo->get(); + { + std::lock_guard list_lock(nc.list_p2p_ports_mutex); + if (!nc.list_p2p_ports.contains(p2p_port)) + { + nc.list_p2p_ports.emplace(std::piecewise_construct, std::forward_as_tuple(p2p_port), std::forward_as_tuple(p2p_port)); + } + + auto& pport = nc.list_p2p_ports.at(p2p_port); + real_socket = pport.p2p_socket; + { + // Ensures the socket & the bound list are updated at the same time to avoid races + std::lock_guard vport_lock(pport.bound_p2p_vports_mutex); + std::lock_guard sock_lock(mutex); + + const u64 key = (static_cast(p2p_vport) << 32); + pport.bound_p2p_streams.emplace(key, ps3_id); + + port = p2p_port; + vport = p2p_vport; + socket = real_socket; + } + } + + return CELL_OK; +} + +std::optional lv2_socket_p2ps::connect(const sys_net_sockaddr& addr) +{ + std::lock_guard lock(mutex); + + p2ps_encapsulated_tcp send_hdr; + const auto psa_in_p2p = reinterpret_cast(&addr); + auto name = sys_net_addr_to_native_addr(addr); + + // This is purposefully inverted, not a bug + const u16 dst_vport = psa_in_p2p->sin_port; + const u16 dst_port = psa_in_p2p->sin_vport; + + socket_type real_socket{}; + + auto& nc = g_fxo->get(); + { + std::lock_guard list_lock(nc.list_p2p_ports_mutex); + if (!nc.list_p2p_ports.contains(port)) + nc.list_p2p_ports.emplace(std::piecewise_construct, std::forward_as_tuple(port), std::forward_as_tuple(port)); + + auto& pport = nc.list_p2p_ports.at(port); + real_socket = pport.p2p_socket; + { + std::lock_guard lock(pport.bound_p2p_vports_mutex); + if (vport == 0) + { + // Unassigned vport, assigns one + sys_net.warning("[P2PS] vport was unassigned before connect!"); + u16 found_vport = 30000; + while (true) + { + found_vport++; + if (pport.bound_p2p_vports.count(found_vport)) + continue; + if (pport.bound_p2p_streams.count(static_cast(found_vport) << 32)) + continue; + + break; + } + vport = found_vport; + } + const u64 key = name.sin_addr.s_addr | (static_cast(vport) << 32) | (static_cast(dst_vport) << 48); + pport.bound_p2p_streams.emplace(key, lv2_id); + } + } + + socket = real_socket; + + send_hdr.src_port = vport; + send_hdr.dst_port = dst_vport; + send_hdr.flags = p2ps_tcp_flags::SYN; + send_hdr.seq = rand(); + + // sock.socket = p2p_socket; + op_addr = name.sin_addr.s_addr; + op_port = dst_port; + op_vport = dst_vport; + cur_seq = send_hdr.seq + 1; + data_beg_seq = 0; + data_available = 0u; + received_data.clear(); + status = p2ps_stream_status::stream_handshaking; + + std::vector packet = generate_u2s_packet(send_hdr, nullptr, 0); + name.sin_port = std::bit_cast(psa_in_p2p->sin_vport); // not a bug + send_u2s_packet(std::move(packet), reinterpret_cast<::sockaddr_in*>(&name), send_hdr.seq, true); + + return true; +} + +s32 lv2_socket_p2ps::listen(s32 backlog) +{ + std::lock_guard lock(mutex); + + status = p2ps_stream_status::stream_listening; + max_backlog = backlog; + + return CELL_OK; +} + +std::optional, sys_net_sockaddr>> lv2_socket_p2ps::recvfrom([[maybe_unused]] s32 flags, u32 len, bool is_lock) +{ + std::unique_lock lock(mutex, std::defer_lock); + + if (is_lock) + { + lock.lock(); + } + + if (!data_available) + { + if (so_nbio) + { + return {{-SYS_NET_EWOULDBLOCK, {}, {}}}; + } + + return std::nullopt; + } + + const u32 to_give = std::min(data_available, len); + sys_net_sockaddr addr{}; + std::vector dest_buf(to_give); + + sys_net.trace("STREAM-P2P socket had %u available, given %u", data_available, to_give); + + u32 left_to_give = to_give; + while (left_to_give) + { + auto& cur_data = received_data.begin()->second; + auto to_give_for_this_packet = std::min(static_cast(cur_data.size()), left_to_give); + memcpy(dest_buf.data() + (to_give - left_to_give), cur_data.data(), to_give_for_this_packet); + if (cur_data.size() != to_give_for_this_packet) + { + auto amount_left = cur_data.size() - to_give_for_this_packet; + std::vector new_vec(amount_left); + memcpy(new_vec.data(), cur_data.data() + to_give_for_this_packet, amount_left); + auto new_key = (received_data.begin()->first) + to_give_for_this_packet; + received_data.emplace(new_key, std::move(new_vec)); + } + + received_data.erase(received_data.begin()); + + left_to_give -= to_give_for_this_packet; + } + + data_available -= to_give; + data_beg_seq += to_give; + + sys_net_sockaddr_in_p2p* addr_p2p = reinterpret_cast(&addr); + addr_p2p->sin_family = AF_INET; + addr_p2p->sin_addr = std::bit_cast, u32>(op_addr); + addr_p2p->sin_port = op_vport; + addr_p2p->sin_vport = op_port; + addr_p2p->sin_len = sizeof(sys_net_sockaddr_in_p2p); + + return {{to_give, dest_buf, addr}}; +} + +std::optional lv2_socket_p2ps::sendto([[maybe_unused]] s32 flags, const std::vector& buf, std::optional opt_sn_addr, bool is_lock) +{ + std::unique_lock lock(mutex, std::defer_lock); + + if (is_lock) + { + lock.lock(); + } + + constexpr u32 max_data_len = (65535 - (sizeof(u16) + sizeof(p2ps_encapsulated_tcp))); + + ::sockaddr_in name{}; + if (opt_sn_addr) + { + name = sys_net_addr_to_native_addr(*opt_sn_addr); + } + + // Prepare address + name.sin_family = AF_INET; + name.sin_port = std::bit_cast>(op_port); + name.sin_addr.s_addr = op_addr; + // Prepares encapsulated tcp + p2ps_encapsulated_tcp tcp_header; + tcp_header.src_port = vport; + tcp_header.dst_port = op_vport; + // chop it up + std::vector> stream_packets; + u32 cur_total_len = buf.size(); + while (cur_total_len > 0) + { + u32 cur_data_len = std::min(cur_total_len, max_data_len); + + tcp_header.length = cur_data_len; + tcp_header.seq = cur_seq; + + auto packet = generate_u2s_packet(tcp_header, &buf[buf.size() - cur_total_len], cur_data_len); + send_u2s_packet(std::move(packet), &name, tcp_header.seq, true); + + cur_total_len -= cur_data_len; + cur_seq += cur_data_len; + } + + return {buf.size()}; +} + +void lv2_socket_p2ps::close() +{ + if (!port || !vport) + { + return; + } + + auto& nc = g_fxo->get(); + { + std::lock_guard lock(nc.list_p2p_ports_mutex); + ensure(nc.list_p2p_ports.contains(port)); + auto& p2p_port = nc.list_p2p_ports.at(port); + { + std::lock_guard lock(p2p_port.bound_p2p_vports_mutex); + for (auto it = p2p_port.bound_p2p_streams.begin(); it != p2p_port.bound_p2p_streams.end();) + { + if (static_cast(it->second) == lv2_id) + { + it = p2p_port.bound_p2p_streams.erase(it); + continue; + } + it++; + } + } + } +} + +s32 lv2_socket_p2ps::shutdown([[maybe_unused]] s32 how) +{ + sys_net.todo("[P2PS] shutdown"); + return CELL_OK; +} + +s32 lv2_socket_p2ps::poll(sys_net_pollfd& sn_pfd, [[maybe_unused]] pollfd& native_pfd) +{ + std::lock_guard lock(mutex); + sys_net.trace("[P2PS] poll checking for 0x%X", sn_pfd.events); + if (status == p2ps_stream_status::stream_connected) + { + if ((sn_pfd.events & SYS_NET_POLLIN) && data_available) + { + sys_net.trace("[P2PS] p2ps has %u bytes available", data_available); + sn_pfd.revents |= SYS_NET_POLLIN; + } + + // Data can only be written if the socket is connected + if (sn_pfd.events & SYS_NET_POLLOUT && status == p2ps_stream_status::stream_connected) + { + sn_pfd.revents |= SYS_NET_POLLOUT; + } + + if (sn_pfd.revents) + { + return 1; + } + } + + return 0; +} diff --git a/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket_p2ps.h b/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket_p2ps.h new file mode 100644 index 0000000000..9893f37db1 --- /dev/null +++ b/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket_p2ps.h @@ -0,0 +1,102 @@ +#pragma once + +#ifdef _WIN32 +#include +#include +#else +#ifdef __clang__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wold-style-cast" +#endif +#include +#ifdef __clang__ +#pragma GCC diagnostic pop +#endif +#endif + +#include "lv2_socket_p2p.h" + +constexpr be_t P2PS_U2S_SIG = (static_cast('U') << 24 | static_cast('2') << 16 | static_cast('S') << 8 | static_cast('0')); + +struct p2ps_encapsulated_tcp +{ + be_t signature = P2PS_U2S_SIG; // Signature to verify it's P2P Stream data + be_t length = 0; // Length of data + be_t seq = 0; // This should be u32 but changed to u64 for simplicity + be_t ack = 0; + be_t src_port = 0; // fake source tcp port + be_t dst_port = 0; // fake dest tcp port(should be == vport) + be_t checksum = 0; + u8 flags = 0; +}; + +enum p2ps_stream_status +{ + stream_closed, // Default when port is not listening nor connected + stream_listening, // Stream is listening, accepting SYN packets + stream_handshaking, // Currently handshaking + stream_connected, // This is an established connection(after tcp handshake) +}; + +enum p2ps_tcp_flags : u8 +{ + FIN = (1 << 0), + SYN = (1 << 1), + RST = (1 << 2), + PSH = (1 << 3), + ACK = (1 << 4), + URG = (1 << 5), + ECE = (1 << 6), + CWR = (1 << 7), +}; + +void initialize_tcp_timeout_monitor(); +u16 u2s_tcp_checksum(const u16* buffer, usz size); +std::vector generate_u2s_packet(const p2ps_encapsulated_tcp& header, const u8* data, const u32 datasize); + +class lv2_socket_p2ps final : public lv2_socket_p2p +{ +public: + lv2_socket_p2ps(lv2_socket_family family, lv2_socket_type type, lv2_ip_protocol protocol); + lv2_socket_p2ps(socket_type socket, u16 port, u16 vport, u32 op_addr, u16 op_port, u16 op_vport, u64 cur_seq, u64 data_beg_seq); + + p2ps_stream_status get_status() const; + void set_status(p2ps_stream_status new_status); + bool handle_connected(p2ps_encapsulated_tcp* tcp_header, u8* data, ::sockaddr_storage* op_addr); + bool handle_listening(p2ps_encapsulated_tcp* tcp_header, u8* data, ::sockaddr_storage* op_addr); + void send_u2s_packet(std::vector data, const ::sockaddr_in* dst, u32 seq, bool require_ack); + + std::tuple accept(bool is_lock = true) override; + s32 bind(const sys_net_sockaddr& addr, s32 ps3_id) override; + + std::optional connect(const sys_net_sockaddr& addr) override; + + //std::pair getsockname() override; + + s32 listen(s32 backlog) override; + + std::optional, sys_net_sockaddr>> recvfrom(s32 flags, u32 len, bool is_lock = true) override; + std::optional sendto(s32 flags, const std::vector& buf, std::optional opt_sn_addr, bool is_lock = true) override; + + void close() override; + s32 shutdown(s32 how) override; + + s32 poll(sys_net_pollfd& sn_pfd, pollfd& native_pfd) override; + +protected: + static constexpr usz MAX_RECEIVED_BUFFER = (1024 * 1024 * 10); + + p2ps_stream_status status = p2ps_stream_status::stream_closed; + + usz max_backlog = 0; // set on listen + std::queue backlog; + + u16 op_port = 0, op_vport = 0; + u32 op_addr = 0; + + u64 data_beg_seq = 0; // Seq of first byte of received_data + u64 data_available = 0; // Amount of continuous data available(calculated on ACK send) + std::map> received_data; // holds seq/data of data received + + u64 cur_seq = 0; // SEQ of next packet to be sent +}; diff --git a/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket_raw.cpp b/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket_raw.cpp new file mode 100644 index 0000000000..2a1be4b4c9 --- /dev/null +++ b/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket_raw.cpp @@ -0,0 +1,98 @@ +#include "stdafx.h" +#include "lv2_socket_raw.h" + +LOG_CHANNEL(sys_net); + +lv2_socket_raw::lv2_socket_raw(lv2_socket_family family, lv2_socket_type type, lv2_ip_protocol protocol) + : lv2_socket(family, type, protocol) +{ +} + +std::tuple lv2_socket_raw::accept([[maybe_unused]] bool is_lock) +{ + sys_net.todo("lv2_socket_raw::accept"); + return {}; +} + +s32 lv2_socket_raw::bind([[maybe_unused]] const sys_net_sockaddr& addr, [[maybe_unused]] s32 ps3_id) +{ + sys_net.todo("lv2_socket_raw::bind"); + return {}; +} + +std::optional lv2_socket_raw::connect([[maybe_unused]] const sys_net_sockaddr& addr) +{ + sys_net.todo("lv2_socket_raw::connect"); + return {}; +} + +s32 lv2_socket_raw::connect_followup() +{ + sys_net.todo("lv2_socket_raw::connect_followup"); + return {}; +} + +std::pair lv2_socket_raw::getpeername() +{ + sys_net.todo("lv2_socket_raw::getpeername"); + return {{}, {}}; +} + +std::pair lv2_socket_raw::getsockname() +{ + sys_net.todo("lv2_socket_raw::getsockname"); + return {}; +} + +std::tuple lv2_socket_raw::getsockopt([[maybe_unused]] s32 level, [[maybe_unused]] s32 optname, [[maybe_unused]] u32 len) +{ + sys_net.todo("lv2_socket_raw::getsockopt"); + return {}; +} + +s32 lv2_socket_raw::setsockopt([[maybe_unused]] s32 level, [[maybe_unused]] s32 optname, [[maybe_unused]] const std::vector& optval) +{ + sys_net.todo("lv2_socket_raw::setsockopt"); + return {}; +} + +s32 lv2_socket_raw::listen([[maybe_unused]] s32 backlog) +{ + sys_net.todo("lv2_socket_raw::listen"); + return {}; +} + +std::optional, sys_net_sockaddr>> lv2_socket_raw::recvfrom([[maybe_unused]] s32 flags, [[maybe_unused]] u32 len, [[maybe_unused]] bool is_lock) +{ + sys_net.todo("lv2_socket_raw::recvfrom"); + return {}; +} + +std::optional lv2_socket_raw::sendto([[maybe_unused]] s32 flags, [[maybe_unused]] const std::vector& buf, [[maybe_unused]] std::optional opt_sn_addr, [[maybe_unused]] bool is_lock) +{ + sys_net.todo("lv2_socket_raw::sendto"); + return {}; +} + +void lv2_socket_raw::close() +{ + sys_net.todo("lv2_socket_raw::close"); +} + +s32 lv2_socket_raw::shutdown([[maybe_unused]] s32 how) +{ + sys_net.todo("lv2_socket_raw::shutdown"); + return {}; +} + +s32 lv2_socket_raw::poll([[maybe_unused]] sys_net_pollfd& sn_pfd, [[maybe_unused]] pollfd& native_pfd) +{ + sys_net.todo("lv2_socket_raw::poll"); + return {}; +} + +s32 lv2_socket_raw::select([[maybe_unused]] bs_t selected, [[maybe_unused]] pollfd& native_pfd) +{ + sys_net.todo("lv2_socket_raw::select"); + return {}; +} diff --git a/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket_raw.h b/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket_raw.h new file mode 100644 index 0000000000..c20a6d8194 --- /dev/null +++ b/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket_raw.h @@ -0,0 +1,32 @@ +#pragma once + +#include "lv2_socket.h" + +class lv2_socket_raw final : public lv2_socket +{ +public: + lv2_socket_raw(lv2_socket_family family, lv2_socket_type type, lv2_ip_protocol protocol); + + std::tuple accept(bool is_lock = true) override; + s32 bind(const sys_net_sockaddr& addr, s32 ps3_id) override; + + std::optional connect(const sys_net_sockaddr& addr) override; + s32 connect_followup() override; + + std::pair getpeername() override; + std::pair getsockname() override; + + std::tuple getsockopt(s32 level, s32 optname, u32 len) override; + s32 setsockopt(s32 level, s32 optname, const std::vector& optval) override; + + s32 listen(s32 backlog) override; + + std::optional, sys_net_sockaddr>> recvfrom(s32 flags, u32 len, bool is_lock = true) override; + std::optional sendto(s32 flags, const std::vector& buf, std::optional opt_sn_addr, bool is_lock = true) override; + + void close() override; + s32 shutdown(s32 how) override; + + s32 poll(sys_net_pollfd& sn_pfd, pollfd& native_pfd) override; + s32 select(bs_t selected, pollfd& native_pfd) override; +}; diff --git a/rpcs3/Emu/Cell/lv2/sys_net/network_context.cpp b/rpcs3/Emu/Cell/lv2/sys_net/network_context.cpp new file mode 100644 index 0000000000..6fd6e6398e --- /dev/null +++ b/rpcs3/Emu/Cell/lv2/sys_net/network_context.cpp @@ -0,0 +1,212 @@ +#include "stdafx.h" +#include "Emu/Cell/lv2/sys_sync.h" + +#include "network_context.h" +#include "Emu/system_config.h" +#include "sys_net_helpers.h" + +LOG_CHANNEL(sys_net); + +// Used by RPCN to send signaling packets to RPCN server(for UDP hole punching) +s32 send_packet_from_p2p_port(const std::vector& data, const sockaddr_in& addr) +{ + s32 res{}; + auto& nc = g_fxo->get(); + { + std::lock_guard list_lock(nc.list_p2p_ports_mutex); + if (nc.list_p2p_ports.contains(3658)) + { + auto& def_port = nc.list_p2p_ports.at(3658); + res = ::sendto(def_port.p2p_socket, reinterpret_cast(data.data()), data.size(), 0, reinterpret_cast(&addr), sizeof(sockaddr_in)); + } + else + { + sys_net.error("send_packet_from_p2p_port: port %d not present", 3658); + } + } + + return res; +} + +std::vector> get_rpcn_msgs() +{ + std::vector> msgs; + auto& nc = g_fxo->get(); + { + std::lock_guard list_lock(nc.list_p2p_ports_mutex); + if (nc.list_p2p_ports.contains(3658)) + { + auto& def_port = nc.list_p2p_ports.at(3658); + { + 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", 3658); + } + } + + return msgs; +} + +std::vector, std::vector>> get_sign_msgs() +{ + std::vector, std::vector>> msgs; + auto& nc = g_fxo->get(); + { + std::lock_guard list_lock(nc.list_p2p_ports_mutex); + if (nc.list_p2p_ports.contains(3658)) + { + auto& def_port = nc.list_p2p_ports.at(3658); + { + 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", 3658); + } + } + + return msgs; +} + +void need_network() +{ + g_fxo->need(); + initialize_tcp_timeout_monitor(); +} + +network_thread::network_thread() noexcept +{ + if (g_cfg.net.psn_status == np_psn_status::psn_rpcn) + list_p2p_ports.emplace(std::piecewise_construct, std::forward_as_tuple(3658), std::forward_as_tuple(3658)); +} + +network_thread::~network_thread() +{ +} + +void network_thread::operator()() +{ + std::vector> socklist; + socklist.reserve(lv2_socket::id_count); + + s_to_awake.clear(); + + ::pollfd fds[lv2_socket::id_count]{}; +#ifdef _WIN32 + bool connecting[lv2_socket::id_count]{}; + bool was_connecting[lv2_socket::id_count]{}; +#endif + + ::pollfd p2p_fd[lv2_socket::id_count]{}; + + while (thread_ctrl::state() != thread_state::aborting) + { + // Wait with 1ms timeout +#ifdef _WIN32 + windows_poll(fds, ::size32(socklist), 1, connecting); +#else + ::poll(fds, socklist.size(), 1); +#endif + + // Check P2P sockets for incoming packets(timeout could probably be set at 0) + { + std::lock_guard lock(list_p2p_ports_mutex); + std::memset(p2p_fd, 0, sizeof(p2p_fd)); + auto num_p2p_sockets = 0; + for (const auto& p2p_port : list_p2p_ports) + { + p2p_fd[num_p2p_sockets].events = POLLIN; + p2p_fd[num_p2p_sockets].revents = 0; + p2p_fd[num_p2p_sockets].fd = p2p_port.second.p2p_socket; + num_p2p_sockets++; + } + + if (num_p2p_sockets) + { +#ifdef _WIN32 + const auto ret_p2p = WSAPoll(p2p_fd, num_p2p_sockets, 1); +#else + const auto ret_p2p = ::poll(p2p_fd, num_p2p_sockets, 1); +#endif + if (ret_p2p > 0) + { + auto fd_index = 0; + for (auto& p2p_port : list_p2p_ports) + { + if ((p2p_fd[fd_index].revents & POLLIN) == POLLIN || (p2p_fd[fd_index].revents & POLLRDNORM) == POLLRDNORM) + { + while (p2p_port.second.recv_data()) + ; + } + fd_index++; + } + } + else if (ret_p2p < 0) + { + sys_net.error("[P2P] Error poll on master P2P socket: %d", get_last_error(false)); + } + } + } + + std::lock_guard lock(s_nw_mutex); + + 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 + } + + s_to_awake.erase(std::unique(s_to_awake.begin(), s_to_awake.end()), s_to_awake.end()); + + for (ppu_thread* ppu : s_to_awake) + { + network_clear_queue(*ppu); + lv2_obj::append(ppu); + } + + if (!s_to_awake.empty()) + { + lv2_obj::awake_all(); + } + + s_to_awake.clear(); + socklist.clear(); + + // Obtain all non P2P active sockets + idm::select([&](u32 id, lv2_socket& s) + { + if (s.get_type() != SYS_NET_SOCK_DGRAM_P2P && s.get_type() != SYS_NET_SOCK_STREAM_P2P) + { + socklist.emplace_back(idm::get_unlocked(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] = connecting; + connecting[i] = connecting; +#endif + } + } +} diff --git a/rpcs3/Emu/Cell/lv2/sys_net/network_context.h b/rpcs3/Emu/Cell/lv2/sys_net/network_context.h new file mode 100644 index 0000000000..75897ed52e --- /dev/null +++ b/rpcs3/Emu/Cell/lv2/sys_net/network_context.h @@ -0,0 +1,27 @@ +#pragma once + +#include +#include +#include "Utilities/mutex.h" +#include "Emu/Cell/PPUThread.h" + +#include "nt_p2p_port.h" + +struct network_thread +{ + std::vector s_to_awake; + shared_mutex s_nw_mutex; + + shared_mutex list_p2p_ports_mutex; + std::map list_p2p_ports{}; + + static constexpr auto thread_name = "Network Thread"; + + network_thread() noexcept; + + ~network_thread(); + + void operator()(); +}; + +using network_context = named_thread; diff --git a/rpcs3/Emu/Cell/lv2/sys_net/nt_p2p_port.cpp b/rpcs3/Emu/Cell/lv2/sys_net/nt_p2p_port.cpp new file mode 100644 index 0000000000..7748557e94 --- /dev/null +++ b/rpcs3/Emu/Cell/lv2/sys_net/nt_p2p_port.cpp @@ -0,0 +1,241 @@ +#include "stdafx.h" + +#include + +#include "nt_p2p_port.h" +#include "lv2_socket_native.h" +#include "lv2_socket_p2ps.h" +#include "util/asm.hpp" +#include "sys_net_helpers.h" +#include "Emu/NP/signaling_handler.h" + +LOG_CHANNEL(sys_net); + +nt_p2p_port::nt_p2p_port(u16 port) + : port(port) +{ + // Creates and bind P2P Socket + p2p_socket = ::socket(AF_INET, SOCK_DGRAM, IPPROTO_IP); + + if (p2p_socket == -1) + sys_net.fatal("Failed to create DGRAM socket for P2P socket!"); + +#ifdef _WIN32 + u_long _true = 1; + ::ioctlsocket(p2p_socket, FIONBIO, &_true); +#else + ::fcntl(p2p_socket, F_SETFL, ::fcntl(p2p_socket, F_GETFL, 0) | O_NONBLOCK); +#endif + + 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(&optval), sizeof(optval)) != 0) + sys_net.fatal("Error setsockopt SO_RCVBUF on P2P socket"); + + ::sockaddr_in p2p_saddr{}; + p2p_saddr.sin_family = AF_INET; + p2p_saddr.sin_port = std::bit_cast>(port); // htons(port); + p2p_saddr.sin_addr.s_addr = 0; // binds to 0.0.0.0 + const auto ret_bind = ::bind(p2p_socket, reinterpret_cast(&p2p_saddr), sizeof(p2p_saddr)); + + if (ret_bind == -1) + sys_net.fatal("Failed to bind DGRAM socket to %d for P2P!", port); + + sys_net.notice("P2P port %d was bound!", port); +} + +nt_p2p_port::~nt_p2p_port() +{ + if (p2p_socket) + { +#ifdef _WIN32 + ::closesocket(p2p_socket); +#else + ::close(p2p_socket); +#endif + } +} + +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); +} + +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(sock_id, [&](lv2_socket& sock) -> bool + { + ensure(sock.get_type() == SYS_NET_SOCK_STREAM_P2P); + auto& sock_p2ps = reinterpret_cast(sock); + + return sock_p2ps.handle_connected(tcp_header, data, op_addr); + }); + + if (!sock || !sock.ret) + 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(sock_id); + if (!sock) + return false; + + auto& sock_p2ps = reinterpret_cast(*sock.get()); + 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(p2p_recv_data.data()), p2p_recv_data.size(), 0, reinterpret_cast(&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 P2P socket: %d", lerr); + + return false; + } + + if (recv_res < static_cast(sizeof(u16))) + { + sys_net.error("Received badly formed packet on P2P port(no vport)!"); + return true; + } + + u16 dst_vport = reinterpret_cast&>(p2p_recv_data[0]); + + if (dst_vport == 0) // Reserved for messages from RPCN server + { + std::vector rpcn_msg(recv_res - sizeof(u16)); + memcpy(rpcn_msg.data(), p2p_recv_data.data() + sizeof(u16), recv_res - sizeof(u16)); + + std::lock_guard lock(s_rpcn_mutex); + rpcn_msgs.push_back(std::move(rpcn_msg)); + return true; + } + + if (dst_vport == 65535) // Reserved for signaling + { + std::vector sign_msg(recv_res - sizeof(u16)); + memcpy(sign_msg.data(), p2p_recv_data.data() + sizeof(u16), recv_res - sizeof(u16)); + + std::pair, std::vector> msg; + msg.first.first = reinterpret_cast(&native_addr)->sin_addr.s_addr; + msg.first.second = std::bit_cast>(reinterpret_cast(&native_addr)->sin_port); + msg.second = std::move(sign_msg); + + { + std::lock_guard lock(s_sign_mutex); + sign_msgs.push_back(std::move(msg)); + } + + auto& sigh = g_fxo->get>(); + sigh.wake_up(); + + return true; + } + + { + 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, u32>(reinterpret_cast(&native_addr)->sin_addr.s_addr); + p2p_addr.sin_vport = dst_vport; + p2p_addr.sin_port = std::bit_cast, u16>(reinterpret_cast(&native_addr)->sin_port); + + std::vector p2p_data(recv_res - sizeof(u16)); + memcpy(p2p_data.data(), p2p_recv_data.data() + sizeof(u16), recv_res - sizeof(u16)); + + const auto sock = idm::check(bound_p2p_vports.at(dst_vport), [&](lv2_socket& sock) + { + ensure(sock.get_type() == SYS_NET_SOCK_DGRAM_P2P); + auto& sock_p2p = reinterpret_cast(sock); + + sock_p2p.handle_new_data(std::move(p2p_addr), std::move(p2p_data)); + }); + + // Should not happen in theory + if (!sock) + bound_p2p_vports.erase(dst_vport); + + return true; + } + } + + // Not directed at a bound DGRAM_P2P vport so check if the packet is a STREAM-P2P packet + + const auto sp_size = recv_res - sizeof(u16); + u8* sp_data = p2p_recv_data.data() + sizeof(u16); + + if (sp_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(sp_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 != (sp_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(sp_data), sp_size)) + { + sys_net.error("Checksum is invalid, dropping packet!"); + return true; + } + + // The packet is valid, check if it's bound + const u64 key_connected = (reinterpret_cast(&native_addr)->sin_addr.s_addr) | (static_cast(tcp_header->src_port) << 48) | (static_cast(tcp_header->dst_port) << 32); + const u64 key_listening = (static_cast(tcp_header->dst_port) << 32); + + { + std::lock_guard lock(bound_p2p_vports_mutex); + if (bound_p2p_streams.contains(key_connected)) + { + const auto sock_id = bound_p2p_streams.at(key_connected); + sys_net.trace("Received packet for connected STREAM-P2P socket(s=%d)", sock_id); + handle_connected(sock_id, tcp_header, sp_data + sizeof(p2ps_encapsulated_tcp), &native_addr); + return true; + } + + if (bound_p2p_streams.contains(key_listening)) + { + const auto sock_id = bound_p2p_streams.at(key_listening); + sys_net.trace("Received packet for listening STREAM-P2P socket(s=%d)", sock_id); + handle_listening(sock_id, tcp_header, sp_data + sizeof(p2ps_encapsulated_tcp), &native_addr); + return true; + } + } + + sys_net.notice("Received a STREAM-P2P packet with no bound target"); + return true; +} diff --git a/rpcs3/Emu/Cell/lv2/sys_net/nt_p2p_port.h b/rpcs3/Emu/Cell/lv2/sys_net/nt_p2p_port.h new file mode 100644 index 0000000000..b17f7c2531 --- /dev/null +++ b/rpcs3/Emu/Cell/lv2/sys_net/nt_p2p_port.h @@ -0,0 +1,51 @@ +#pragma once + +#include "lv2_socket_p2ps.h" + +#ifdef _WIN32 +#include +#include +#else +#ifdef __clang__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wold-style-cast" +#endif +#include +#include +#include +#ifdef __clang__ +#pragma GCC diagnostic pop +#endif +#endif + +struct nt_p2p_port +{ + // Real socket where P2P packets are received/sent + socket_type p2p_socket = 0; + u16 port = 0; + + shared_mutex bound_p2p_vports_mutex; + // For DGRAM_P2P sockets(vport, sock_id) + std::map bound_p2p_vports{}; + // For STREAM_P2P sockets(key, sock_id) + // key is ( (src_vport) << 48 | (dst_vport) << 32 | addr ) with src_vport and addr being 0 for listening sockets + std::map bound_p2p_streams{}; + + // Queued messages from RPCN + shared_mutex s_rpcn_mutex; + std::vector> rpcn_msgs{}; + // Queued signaling messages + shared_mutex s_sign_mutex; + std::vector, std::vector>> sign_msgs{}; + + std::array p2p_recv_data{}; + + nt_p2p_port(u16 port); + ~nt_p2p_port(); + + static void dump_packet(p2ps_encapsulated_tcp* tcph); + + bool handle_connected(s32 sock_id, p2ps_encapsulated_tcp* tcp_header, u8* data, ::sockaddr_storage* op_addr); + bool handle_listening(s32 sock_id, p2ps_encapsulated_tcp* tcp_header, u8* data, ::sockaddr_storage* op_addr); + bool recv_data(); +}; diff --git a/rpcs3/Emu/Cell/lv2/sys_net/sys_net_helpers.cpp b/rpcs3/Emu/Cell/lv2/sys_net/sys_net_helpers.cpp new file mode 100644 index 0000000000..784d8dd1d6 --- /dev/null +++ b/rpcs3/Emu/Cell/lv2/sys_net/sys_net_helpers.cpp @@ -0,0 +1,212 @@ +#include "stdafx.h" + +#include "Emu/IdManager.h" +#include "Emu/Cell/PPUThread.h" + +#include "lv2_socket.h" +#include "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 get_last_error(bool is_blocking, int native_error) +{ + // Convert the error code for socket functions to a one for sys_net + sys_net_error result{}; + const char* name{}; + + if (!native_error) + { + native_error = get_native_error(); + } + +#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); + default: + fmt::throw_exception("sys_net get_last_error(is_blocking=%d, native_error=%d): Unknown/illegal socket error", is_blocking, native_error); + } + + 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_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(&sn_addr); + + paddr->sin_len = sizeof(sys_net_sockaddr_in); + paddr->sin_family = SYS_NET_AF_INET; + paddr->sin_port = std::bit_cast, u16>(reinterpret_cast(&native_addr)->sin_port); + paddr->sin_addr = std::bit_cast, u32>(reinterpret_cast(&native_addr)->sin_addr.s_addr); + paddr->sin_zero = 0; + + 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(&sn_addr); + + ::sockaddr_in native_addr{}; + native_addr.sin_family = AF_INET; + native_addr.sin_port = std::bit_cast(psa_in->sin_port); + native_addr.sin_addr.s_addr = std::bit_cast(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>(0x7F000001); + } +#endif + + return native_addr; +} + +void network_clear_queue(ppu_thread& ppu) +{ + idm::select([&](u32, lv2_socket& sock) + { + sock.clear_queue(ppu.id); + }); +} + +#ifdef _WIN32 +// Workaround function for WSAPoll not reporting failed connections +void windows_poll(pollfd* fds, unsigned long nfds, int timeout, bool* connecting) +{ + ensure(connecting); + + // Don't call WSAPoll with zero nfds (errors 10022 or 10038) + if (std::none_of(fds, fds + nfds, [](pollfd& pfd) + { + return pfd.fd != INVALID_SOCKET; + })) + { + if (timeout > 0) + { + Sleep(timeout); + } + + return; + } + + int r = ::WSAPoll(fds, nfds, timeout); + + if (r == SOCKET_ERROR) + { + sys_net.error("WSAPoll failed: %u", WSAGetLastError()); + 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(&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 diff --git a/rpcs3/Emu/Cell/lv2/sys_net/sys_net_helpers.h b/rpcs3/Emu/Cell/lv2/sys_net/sys_net_helpers.h new file mode 100644 index 0000000000..fb081baff0 --- /dev/null +++ b/rpcs3/Emu/Cell/lv2/sys_net/sys_net_helpers.h @@ -0,0 +1,29 @@ +#pragma once + +#ifdef _WIN32 +#include +#include +#else +#ifdef __clang__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wold-style-cast" +#endif +#include +#include +#ifdef __clang__ +#pragma GCC diagnostic pop +#endif +#endif + + +#include "Emu/Cell/lv2/sys_net.h" + +int get_native_error(); +sys_net_error get_last_error(bool is_blocking, int native_error = 0); +sys_net_sockaddr native_addr_to_sys_net_addr(const ::sockaddr_storage& native_addr); +::sockaddr_in sys_net_addr_to_native_addr(const sys_net_sockaddr& sn_addr); +void network_clear_queue(ppu_thread& ppu); + +#ifdef _WIN32 +void windows_poll(pollfd* fds, unsigned long nfds, int timeout, bool* connecting); +#endif diff --git a/rpcs3/Emu/GDB.cpp b/rpcs3/Emu/GDB.cpp index 14ffa1ffaa..f5778ae52b 100644 --- a/rpcs3/Emu/GDB.cpp +++ b/rpcs3/Emu/GDB.cpp @@ -809,10 +809,6 @@ bool gdb_thread::cmd_remove_breakpoint(gdb_cmd& cmd) gdb_thread::gdb_thread() noexcept { -#ifdef _WIN32 - WSADATA wsa_data; - WSAStartup(MAKEWORD(2, 2), &wsa_data); -#endif } gdb_thread::~gdb_thread() @@ -826,10 +822,6 @@ gdb_thread::~gdb_thread() { closesocket(client_socket); } - -#ifdef _WIN32 - WSACleanup(); -#endif } void gdb_thread::operator()() diff --git a/rpcs3/Emu/NP/rpcn_client.cpp b/rpcs3/Emu/NP/rpcn_client.cpp index 5e69b7214c..2981d1b50e 100644 --- a/rpcs3/Emu/NP/rpcn_client.cpp +++ b/rpcs3/Emu/NP/rpcn_client.cpp @@ -60,10 +60,6 @@ namespace rpcn thread_rpcn(std::thread(&rpcn_client::rpcn_thread, this)), thread_rpcn_reader(std::thread(&rpcn_client::rpcn_reader_thread, this)), thread_rpcn_writer(std::thread(&rpcn_client::rpcn_writer_thread, this)) { -#ifdef _WIN32 - WSADATA wsa_data; - WSAStartup(MAKEWORD(2, 2), &wsa_data); -#endif g_cfg_rpcn.load(); sem_rpcn.release(); diff --git a/rpcs3/emucore.vcxproj b/rpcs3/emucore.vcxproj index e66e1b7849..28805ff1dc 100644 --- a/rpcs3/emucore.vcxproj +++ b/rpcs3/emucore.vcxproj @@ -161,6 +161,14 @@ + + + + + + + + diff --git a/rpcs3/emucore.vcxproj.filters b/rpcs3/emucore.vcxproj.filters index 5d3320c644..0d425369dc 100644 --- a/rpcs3/emucore.vcxproj.filters +++ b/rpcs3/emucore.vcxproj.filters @@ -762,6 +762,30 @@ Emu\Cell\lv2 + + Emu\Cell\lv2 + + + Emu\Cell\lv2 + + + Emu\Cell\lv2 + + + Emu\Cell\lv2 + + + Emu\Cell\lv2 + + + Emu\Cell\lv2 + + + Emu\Cell\lv2 + + + Emu\Cell\lv2 + Emu\Io diff --git a/rpcs3/main.cpp b/rpcs3/main.cpp index 53d6bd5ac5..0a1f8c7bf2 100644 --- a/rpcs3/main.cpp +++ b/rpcs3/main.cpp @@ -446,6 +446,9 @@ int main(int argc, char** argv) { report_fatal_error("Not enough memory for RPCS3 process."); } + + WSADATA wsa_data; + WSAStartup(MAKEWORD(2, 2), &wsa_data); #endif ensure(thread_ctrl::is_main(), "Not main thread"); diff --git a/rpcs3/rpcs3qt/kernel_explorer.cpp b/rpcs3/rpcs3qt/kernel_explorer.cpp index 7efd940c85..98428aa5ed 100644 --- a/rpcs3/rpcs3qt/kernel_explorer.cpp +++ b/rpcs3/rpcs3qt/kernel_explorer.cpp @@ -26,6 +26,7 @@ #include "Emu/Cell/lv2/sys_rsx.h" #include "Emu/Cell/lv2/sys_vm.h" #include "Emu/Cell/lv2/sys_net.h" +#include "Emu/Cell/lv2/sys_net/lv2_socket.h" #include "Emu/Cell/lv2/sys_fs.h" #include "Emu/Cell/lv2/sys_interrupt.h" #include "Emu/Cell/Modules/cellSpurs.h" @@ -547,7 +548,7 @@ void kernel_explorer::update() idm::select([&](u32 id, lv2_socket& sock) { - add_leaf(find_node(root, additional_nodes::sockets), qstr(fmt::format("Socket %u: Type: %s, Family: %s, Wq: %zu", id, sock.type, sock.family, sock.queue.size()))); + add_leaf(find_node(root, additional_nodes::sockets), qstr(fmt::format("Socket %u: Type: %s, Family: %s, Wq: %zu", id, sock.get_type(), sock.get_family(), sock.get_queue_size()))); }); idm::select([&](u32 id, lv2_memory_container& container)