From e763e90c2a1fb8acb4b1a65afcb219ab83b3d9b0 Mon Sep 17 00:00:00 2001 From: Gursukh Date: Sat, 4 Apr 2026 14:05:19 +0100 Subject: [PATCH] Feat: P2PEpoll Refinement --- src/core/libraries/network/net.cpp | 68 +++-- src/core/libraries/network/net.h | 2 +- src/core/libraries/network/net_epoll.cpp | 68 ++++- src/core/libraries/network/net_epoll.h | 39 +-- src/core/libraries/network/p2p_sockets.cpp | 301 +++---------------- src/core/libraries/network/posix_sockets.cpp | 66 ++-- src/core/libraries/network/sockets.h | 115 ++++--- src/core/libraries/network/unix_sockets.cpp | 49 +-- 8 files changed, 267 insertions(+), 441 deletions(-) diff --git a/src/core/libraries/network/net.cpp b/src/core/libraries/network/net.cpp index 3af0bea2c..76f67841d 100644 --- a/src/core/libraries/network/net.cpp +++ b/src/core/libraries/network/net.cpp @@ -34,7 +34,7 @@ using FDTable = Common::Singleton; static thread_local int32_t net_errno = 0; -static bool g_isNetInitialized = false; +static bool g_isNetInitialized = true; // TODO init it properly static int ConvertFamilies(int family) { switch (family) { @@ -617,8 +617,14 @@ int PS4_SYSV_ABI sceNetDuplicateIpStop() { return ORBIS_OK; } -int PS4_SYSV_ABI sceNetEpollAbort() { - LOG_ERROR(Lib_Net, "(STUBBED) called"); +int PS4_SYSV_ABI sceNetEpollAbort(OrbisNetId epollid, u32 flags) { + auto file = FDTable::Instance()->GetEpoll(epollid); + if (!file) { + *sceNetErrnoLoc() = ORBIS_NET_EBADF; + return ORBIS_NET_ERROR_EBADF; + } + LOG_DEBUG(Lib_Net, "called, epollid = {} ({}), flags = {}", epollid, file->epoll->name, flags); + file->epoll->Abort(); return ORBIS_OK; } @@ -664,11 +670,18 @@ int PS4_SYSV_ABI sceNetEpollControl(OrbisNetId epollid, OrbisNetEpollFlag op, Or return ORBIS_NET_ERROR_EBADF; } + // P2P dgram sockets only support EPOLLIN (matches PS4 kernel behavior) + auto epoll_events = event->events; + if (file->socket->socket_type == ORBIS_NET_SOCK_DGRAM_P2P) { + epoll_events &= ORBIS_NET_EPOLLIN; + } + #ifndef __FreeBSD__ - epoll_event native_event = {.events = ConvertEpollEventsIn(event->events), + epoll_event native_event = {.events = ConvertEpollEventsIn(epoll_events), .data = {.fd = id}}; ASSERT(epoll_ctl(epoll->epoll_fd, EPOLL_CTL_ADD, *native_handle, &native_event) == 0); #endif + // Emulator-level event tracking epoll->events.emplace_back(id, *event); break; } @@ -713,11 +726,18 @@ int PS4_SYSV_ABI sceNetEpollControl(OrbisNetId epollid, OrbisNetEpollFlag op, Or return ORBIS_NET_ERROR_EBADF; } + // P2P dgram sockets only support EPOLLIN (matches PS4 kernel behavior) + auto epoll_events_mod = event->events; + if (file->socket->socket_type == ORBIS_NET_SOCK_DGRAM_P2P) { + epoll_events_mod &= ORBIS_NET_EPOLLIN; + } + #ifndef __FreeBSD__ - epoll_event native_event = {.events = ConvertEpollEventsIn(event->events), + epoll_event native_event = {.events = ConvertEpollEventsIn(epoll_events_mod), .data = {.fd = id}}; ASSERT(epoll_ctl(epoll->epoll_fd, EPOLL_CTL_MOD, *native_handle, &native_event) == 0); #endif + // Emulator-level event tracking *it = {id, *event}; break; } @@ -827,20 +847,29 @@ int PS4_SYSV_ABI sceNetEpollWait(OrbisNetId epollid, OrbisNetEpollEvent* events, LOG_DEBUG(Lib_Net, "called, epollid = {} ({}), maxevents = {}, timeout = {}", epollid, epoll->name, maxevents, timeout); - int sockets_waited_on = (epoll->events.size() - epoll->async_resolutions.size()) > 0; + if (epoll->aborted.load(std::memory_order_acquire)) { + epoll->ClearAbort(); + *sceNetErrnoLoc() = ORBIS_NET_ECANCELED; + return ORBIS_NET_ERROR_ECANCELED; + } - std::vector native_events{static_cast(maxevents)}; + // +1 for the abort fd which is also registered in the epoll instance + std::vector native_events{static_cast(maxevents + 1)}; int result = ORBIS_OK; - if (sockets_waited_on) { #ifdef __linux__ - const timespec epoll_timeout{.tv_sec = timeout / 1000000, - .tv_nsec = (timeout % 1000000) * 1000}; - result = epoll_pwait2(epoll->epoll_fd, native_events.data(), maxevents, - timeout < 0 ? nullptr : &epoll_timeout, nullptr); + const timespec epoll_timeout{.tv_sec = timeout / 1000000, + .tv_nsec = (timeout % 1000000) * 1000}; + result = epoll_pwait2(epoll->epoll_fd, native_events.data(), maxevents + 1, + timeout < 0 ? nullptr : &epoll_timeout, nullptr); #else - result = epoll_wait(epoll->epoll_fd, native_events.data(), maxevents, - timeout < 0 ? timeout : timeout / 1000); + result = epoll_wait(epoll->epoll_fd, native_events.data(), maxevents + 1, + timeout < 0 ? timeout : timeout / 1000); #endif + + if (epoll->aborted.load(std::memory_order_acquire)) { + epoll->ClearAbort(); + *sceNetErrnoLoc() = ORBIS_NET_ECANCELED; + return ORBIS_NET_ERROR_ECANCELED; } int i = 0; @@ -863,9 +892,13 @@ int PS4_SYSV_ABI sceNetEpollWait(OrbisNetId epollid, OrbisNetEpollEvent* events, } else if (result == 0) { LOG_TRACE(Lib_Net, "timed out"); } else { - for (; i < result; ++i) { - const auto& current_event = native_events[i]; - LOG_DEBUG(Lib_Net, "native_event[{}] = ( .events = {}, .data = {:#x} )", i, + for (int j = 0; j < result; ++j) { + const auto& current_event = native_events[j]; + // Skip the abort fd event (registered with data.fd = -1) + if (current_event.data.fd == -1) { + continue; + } + LOG_DEBUG(Lib_Net, "native_event[{}] = ( .events = {}, .data = {:#x} )", j, current_event.events, current_event.data.u64); const auto it = std::ranges::find_if( epoll->events, [&](auto& el) { return el.first == current_event.data.fd; }); @@ -877,6 +910,7 @@ int PS4_SYSV_ABI sceNetEpollWait(OrbisNetId epollid, OrbisNetEpollEvent* events, }; LOG_DEBUG(Lib_Net, "event[{}] = ( .events = {:#x}, .ident = {}, .data = {:#x} )", i, events[i].events, events[i].ident, events[i].data.data_u64); + ++i; } } diff --git a/src/core/libraries/network/net.h b/src/core/libraries/network/net.h index 4cb7afdda..7ba88a62a 100644 --- a/src/core/libraries/network/net.h +++ b/src/core/libraries/network/net.h @@ -367,7 +367,7 @@ int PS4_SYSV_ABI sceNetDumpDestroy(); int PS4_SYSV_ABI sceNetDumpRead(); int PS4_SYSV_ABI sceNetDuplicateIpStart(); int PS4_SYSV_ABI sceNetDuplicateIpStop(); -int PS4_SYSV_ABI sceNetEpollAbort(); +int PS4_SYSV_ABI sceNetEpollAbort(OrbisNetId epollid, u32 flags); int PS4_SYSV_ABI sceNetEpollControl(OrbisNetId epollid, OrbisNetEpollFlag op, OrbisNetId id, OrbisNetEpollEvent* event); int PS4_SYSV_ABI sceNetEpollCreate(const char* name, int flags); diff --git a/src/core/libraries/network/net_epoll.cpp b/src/core/libraries/network/net_epoll.cpp index 4f1b521ce..189b5bc72 100644 --- a/src/core/libraries/network/net_epoll.cpp +++ b/src/core/libraries/network/net_epoll.cpp @@ -1,9 +1,14 @@ // SPDX-FileCopyrightText: Copyright 2025 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include #include "common/assert.h" +#include "common/logging/log.h" #include "common/types.h" #include "net_epoll.h" +#ifdef __linux__ +#include +#endif namespace Libraries::Net { @@ -43,17 +48,70 @@ u32 ConvertEpollEventsOut(u32 epoll_events) { return ret; } +Epoll::Epoll(const char* name_) : name(name_ ? name_ : "anon"), epoll_fd(epoll_create1(0)) { +#ifdef _WIN32 + ASSERT(epoll_fd != nullptr); +#else + ASSERT(epoll_fd != -1); +#endif +#ifdef __linux__ + abort_fd = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC); + ASSERT(abort_fd != -1); + epoll_event ev = {.events = EPOLLIN, .data = {.fd = -1}}; + ASSERT(epoll_ctl(epoll_fd, EPOLL_CTL_ADD, abort_fd, &ev) == 0); +#endif +} + +void Epoll::Abort() { + aborted.store(true, std::memory_order_release); +#ifdef __linux__ + if (abort_fd != -1) { + uint64_t val = 1; + ::write(abort_fd, &val, sizeof(val)); + } +#endif +} + +void Epoll::ClearAbort() { + aborted.store(false, std::memory_order_release); +#ifdef __linux__ + if (abort_fd != -1) { + uint64_t val; + ::read(abort_fd, &val, sizeof(val)); + } +#endif +} + +void Epoll::Destroy() noexcept { + events.clear(); +#ifdef _WIN32 + epoll_close(epoll_fd); + epoll_fd = nullptr; +#else + close(epoll_fd); + epoll_fd = -1; +#ifdef __linux__ + if (abort_fd != -1) { + close(abort_fd); + abort_fd = -1; + } +#endif +#endif + name = ""; + destroyed = true; +} + int EpollTable::CreateHandle(const char* name) { std::scoped_lock lock{m_mutex}; - if (auto it = std::ranges::find_if(epolls, [](const Epoll& e) { return e.Destroyed(); }); + if (auto it = std::ranges::find_if(epolls, [](const auto& e) { return e && e->Destroyed(); }); it != epolls.end()) { - *it = Epoll(name); + *it = std::make_unique(name); const auto ret = std::distance(epolls.begin(), it); LOG_DEBUG(Lib_Net, "epollid = {}", ret); return ret; } else { - epolls.emplace_back(name); + epolls.push_back(std::make_unique(name)); const auto ret = epolls.size() - 1; LOG_DEBUG(Lib_Net, "epollid = {}", ret); return ret; @@ -66,11 +124,11 @@ void EpollTable::DeleteHandle(int d) { Epoll* EpollTable::GetEpoll(int epollid) { std::scoped_lock lock{m_mutex}; - if (epollid >= epolls.size() || epolls[epollid].Destroyed()) { + if (epollid >= epolls.size() || !epolls[epollid] || epolls[epollid]->Destroyed()) { return nullptr; } - return &epolls[epollid]; + return epolls[epollid].get(); } } // namespace Libraries::Net diff --git a/src/core/libraries/network/net_epoll.h b/src/core/libraries/network/net_epoll.h index 17716b36e..aa5801195 100644 --- a/src/core/libraries/network/net_epoll.h +++ b/src/core/libraries/network/net_epoll.h @@ -6,7 +6,9 @@ #include "common/types.h" #include "core/libraries/network/net.h" +#include #include +#include #include #include @@ -15,7 +17,6 @@ #endif #if defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) -// ADD libepoll-shim if using freebsd! #include #include #endif @@ -33,34 +34,24 @@ struct Epoll { std::string name; epoll_handle epoll_fd; std::deque async_resolutions{}; - - explicit Epoll(const char* name_) : name(name_), epoll_fd(epoll_create1(0)) { -#ifdef _WIN32 - ASSERT(epoll_fd != nullptr); -#else - ASSERT(epoll_fd != -1); + std::atomic aborted{false}; +#ifdef __linux__ + int abort_fd = -1; #endif - if (name_ == nullptr) { - name = "anon"; - } - } + + explicit Epoll(const char* name_); + + // Signal the epoll to abort any blocking wait + void Abort(); + + // Drain the abort fd after waking up, reset aborted flag + void ClearAbort(); bool Destroyed() const noexcept { return destroyed; } - void Destroy() noexcept { - events.clear(); -#ifdef _WIN32 - epoll_close(epoll_fd); - epoll_fd = nullptr; -#else - close(epoll_fd); - epoll_fd = -1; -#endif - name = ""; - destroyed = true; - } + void Destroy() noexcept; private: bool destroyed{}; @@ -79,7 +70,7 @@ public: Epoll* GetEpoll(int d); private: - std::vector epolls; + std::vector> epolls; std::mutex m_mutex; }; diff --git a/src/core/libraries/network/p2p_sockets.cpp b/src/core/libraries/network/p2p_sockets.cpp index 4f6a7f3d7..c8a1f27af 100644 --- a/src/core/libraries/network/p2p_sockets.cpp +++ b/src/core/libraries/network/p2p_sockets.cpp @@ -1,246 +1,66 @@ // SPDX-FileCopyrightText: Copyright 2024-2026 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#include -#include "core/libraries/kernel/kernel.h" #include "net.h" #include "net_error.h" #include "sockets.h" -#ifndef _WIN32 -#include -#include -#endif - namespace Libraries::Net { -P2PSocket::P2PSocket(int domain, int type, int protocol) : Socket(domain, type, protocol) { +P2PSocket::P2PSocket(int domain, int type, int protocol) + : PosixSocket(AF_INET, type == ORBIS_NET_SOCK_DGRAM_P2P ? SOCK_DGRAM : SOCK_STREAM, + type == ORBIS_NET_SOCK_DGRAM_P2P ? IPPROTO_UDP : IPPROTO_TCP) { + // Store the original PS4 socket type so GetSocketOptions(SO_TYPE) returns the P2P value socket_type = type; - // Map P2P socket types to real OS socket types - int os_type; - int os_protocol; - if (type == ORBIS_NET_SOCK_DGRAM_P2P) { - os_type = SOCK_DGRAM; - os_protocol = IPPROTO_UDP; - } else { - // ORBIS_NET_SOCK_STREAM_P2P - os_type = SOCK_STREAM; - os_protocol = IPPROTO_TCP; - } - sock = ::socket(AF_INET, os_type, os_protocol); - LOG_INFO(Lib_Net, "P2P socket created: type={} (os_type={}), fd={}", type, os_type, sock); -} - -P2PSocket::~P2PSocket() { -#ifdef _WIN32 - if (sock != INVALID_SOCKET) { - closesocket(sock); - } -#else - if (sock != -1) { - ::close(sock); - } -#endif -} - -bool P2PSocket::IsValid() const { -#ifdef _WIN32 - return sock != INVALID_SOCKET; -#else - return sock != -1; -#endif -} - -int P2PSocket::Close() { - std::scoped_lock lock{m_mutex}; -#ifdef _WIN32 - auto out = closesocket(sock); - sock = INVALID_SOCKET; -#else - auto out = ::close(sock); - sock = -1; -#endif - return ConvertReturnErrorCode(out); -} - -int P2PSocket::SetSocketOptions(int level, int optname, const void* optval, u32 optlen) { - std::scoped_lock lock{m_mutex}; - int native_level = ConvertLevels(level); - - // Handle PS4-specific options that have no OS equivalent - if (native_level == SOL_SOCKET) { - switch (optname) { - case ORBIS_NET_SO_NBIO: { - sockopt_so_nbio = *(int*)optval; - int val = sockopt_so_nbio; -#ifdef _WIN32 - u_long mode = val; - return ConvertReturnErrorCode(ioctlsocket(sock, FIONBIO, &mode)); -#else - return ConvertReturnErrorCode(ioctl(sock, FIONBIO, &val)); -#endif - } - case ORBIS_NET_SO_USECRYPTO: - case ORBIS_NET_SO_USESIGNATURE: - case ORBIS_NET_SO_REUSEPORT: - LOG_DEBUG(Lib_Net, "P2P setsockopt: storing PS4-specific option {}", optname); - return 0; - case ORBIS_NET_SO_SNDTIMEO: - case ORBIS_NET_SO_RCVTIMEO: { - int us = *(int*)optval; -#ifdef _WIN32 - DWORD timeout_ms = us / 1000; - int native_opt = (optname == ORBIS_NET_SO_SNDTIMEO) ? SO_SNDTIMEO : SO_RCVTIMEO; - return ConvertReturnErrorCode( - ::setsockopt(sock, SOL_SOCKET, native_opt, (char*)&timeout_ms, sizeof(timeout_ms))); -#else - timeval tv{.tv_sec = us / 1000000, .tv_usec = us % 1000000}; - int native_opt = (optname == ORBIS_NET_SO_SNDTIMEO) ? SO_SNDTIMEO : SO_RCVTIMEO; - return ConvertReturnErrorCode( - ::setsockopt(sock, SOL_SOCKET, native_opt, &tv, sizeof(tv))); -#endif - } - default: - break; - } - } - - if (native_level < 0) { - LOG_WARNING(Lib_Net, "P2P setsockopt: unknown level {}", level); - return 0; - } - - return ConvertReturnErrorCode( - ::setsockopt(sock, native_level, optname, (const char*)optval, optlen)); + LOG_INFO(Lib_Net, "P2P socket created: type={} (os_type={}), fd={}", type, + type == ORBIS_NET_SOCK_DGRAM_P2P ? int(SOCK_DGRAM) : int(SOCK_STREAM), sock); } int P2PSocket::GetSocketOptions(int level, int optname, void* optval, u32* optlen) { - std::scoped_lock lock{m_mutex}; - int native_level = ConvertLevels(level); - - if (native_level == SOL_SOCKET) { - switch (optname) { - case ORBIS_NET_SO_NBIO: - *(int*)optval = sockopt_so_nbio; - *optlen = sizeof(int); - return 0; - case ORBIS_NET_SO_TYPE: - *(int*)optval = socket_type; - *optlen = sizeof(int); - return 0; - default: - break; - } - } - - if (native_level < 0) { - LOG_WARNING(Lib_Net, "P2P getsockopt: unknown level {}", level); + // Intercept SO_TYPE to return the PS4 P2P socket type, not the native OS type + if (ConvertLevels(level) == SOL_SOCKET && optname == ORBIS_NET_SO_TYPE) { + *(int*)optval = socket_type; + *optlen = sizeof(int); return 0; } - - socklen_t native_optlen = *optlen; - int ret = ::getsockopt(sock, native_level, optname, (char*)optval, &native_optlen); - *optlen = native_optlen; - return ConvertReturnErrorCode(ret); + return PosixSocket::GetSocketOptions(level, optname, optval, optlen); } int P2PSocket::Bind(const OrbisNetSockaddr* addr, u32 addrlen) { - std::scoped_lock lock{m_mutex}; + // TODO: vport multiplexing not implemented, on PS4, P2P sockets multiplex + // virtual ports over a single UDP association. We extract the vport here for + // future use but don't perform any demuxing. const auto* orbis_addr = reinterpret_cast(addr); - if (orbis_addr) { + if (orbis_addr && orbis_addr->sin_family == ORBIS_NET_AF_INET) { vport = orbis_addr->sin_vport; + if (vport != 0) { + LOG_WARNING(Lib_Net, "P2P bind with vport={} — vport multiplexing is not implemented", + vport); + } LOG_INFO(Lib_Net, "P2P bind: port={}, vport={}", ntohs(orbis_addr->sin_port), vport); } - sockaddr native_addr; - convertOrbisNetSockaddrToPosix(addr, &native_addr); - return ConvertReturnErrorCode(::bind(sock, &native_addr, sizeof(sockaddr_in))); + return PosixSocket::Bind(addr, addrlen); } -int P2PSocket::Listen(int backlog) { - std::scoped_lock lock{m_mutex}; - return ConvertReturnErrorCode(::listen(sock, backlog)); -} - -int P2PSocket::SendMessage(const OrbisNetMsghdr* msg, int flags) { - std::scoped_lock lock{m_mutex}; - int native_flags = convertOrbisFlagsToPosix(socket_type, flags); -#ifdef _WIN32 - // Windows: loop through buffers with send() - int total_sent = 0; - for (int i = 0; i < msg->msg_iovlen; i++) { - auto& iov = msg->msg_iov[i]; - int sent = ::send(sock, (const char*)iov.iov_base, iov.iov_len, native_flags); - if (sent < 0) { - return total_sent > 0 ? total_sent : ConvertReturnErrorCode(sent); +int P2PSocket::Connect(const OrbisNetSockaddr* addr, u32 namelen) { + // TODO: vport multiplexing not implemented + const auto* orbis_addr = reinterpret_cast(addr); + if (orbis_addr && orbis_addr->sin_family == ORBIS_NET_AF_INET) { + vport = orbis_addr->sin_vport; + if (vport != 0) { + LOG_WARNING(Lib_Net, + "P2P connect with vport={} — vport multiplexing is not implemented", vport); } - total_sent += sent; + LOG_INFO(Lib_Net, "P2P connect: vport={}", vport); } - return total_sent; -#else - return ConvertReturnErrorCode( - ::sendmsg(sock, reinterpret_cast(msg), native_flags)); -#endif -} - -int P2PSocket::SendPacket(const void* msg, u32 len, int flags, const OrbisNetSockaddr* to, - u32 tolen) { - std::scoped_lock lock{m_mutex}; - int native_flags = convertOrbisFlagsToPosix(socket_type, flags); - if (to == nullptr) { - return ConvertReturnErrorCode(::send(sock, (const char*)msg, len, native_flags)); - } - sockaddr native_addr; - convertOrbisNetSockaddrToPosix(to, &native_addr); - return ConvertReturnErrorCode( - ::sendto(sock, (const char*)msg, len, native_flags, &native_addr, sizeof(sockaddr_in))); -} - -int P2PSocket::ReceiveMessage(OrbisNetMsghdr* msg, int flags) { - std::scoped_lock lock{receive_mutex}; - int native_flags = convertOrbisFlagsToPosix(socket_type, flags); -#ifdef _WIN32 - // Windows: loop through buffers with recv() - int total_recv = 0; - for (int i = 0; i < msg->msg_iovlen; i++) { - auto& iov = msg->msg_iov[i]; - int recvd = ::recv(sock, (char*)iov.iov_base, iov.iov_len, native_flags); - if (recvd < 0) { - return total_recv > 0 ? total_recv : ConvertReturnErrorCode(recvd); - } - total_recv += recvd; - if (recvd < (int)iov.iov_len) { - break; - } - } - return total_recv; -#else - return ConvertReturnErrorCode(::recvmsg(sock, reinterpret_cast(msg), native_flags)); -#endif -} - -int P2PSocket::ReceivePacket(void* buf, u32 len, int flags, OrbisNetSockaddr* from, u32* fromlen) { - std::scoped_lock lock{receive_mutex}; - int native_flags = convertOrbisFlagsToPosix(socket_type, flags); - if (from == nullptr) { - return ConvertReturnErrorCode(::recv(sock, (char*)buf, len, native_flags)); - } - sockaddr native_addr; - socklen_t native_addrlen = sizeof(native_addr); - int ret = ::recvfrom(sock, (char*)buf, len, native_flags, &native_addr, &native_addrlen); - if (ret >= 0) { - convertPosixSockaddrToOrbis(&native_addr, from); - if (fromlen) { - *fromlen = sizeof(OrbisNetSockaddrIn); - } - } - return ConvertReturnErrorCode(ret); + return PosixSocket::Connect(addr, namelen); } SocketPtr P2PSocket::Accept(OrbisNetSockaddr* addr, u32* addrlen) { std::scoped_lock lock{m_mutex}; sockaddr native_addr; socklen_t len = sizeof(native_addr); - auto new_sock = ::accept(sock, &native_addr, &len); + net_socket new_sock = ::accept(sock, &native_addr, &len); #ifdef _WIN32 if (new_sock == INVALID_SOCKET) { #else @@ -249,64 +69,11 @@ SocketPtr P2PSocket::Accept(OrbisNetSockaddr* addr, u32* addrlen) { ConvertReturnErrorCode(-1); return nullptr; } - if (addr) { + if (addr && addrlen) { convertPosixSockaddrToOrbis(&native_addr, addr); - if (addrlen) { - *addrlen = sizeof(OrbisNetSockaddrIn); - } + *addrlen = sizeof(OrbisNetSockaddrIn); } - return std::make_shared(new_sock); -} - -int P2PSocket::Connect(const OrbisNetSockaddr* addr, u32 namelen) { - std::scoped_lock lock{m_mutex}; - const auto* orbis_addr = reinterpret_cast(addr); - if (orbis_addr) { - vport = orbis_addr->sin_vport; - LOG_INFO(Lib_Net, "P2P connect: vport={}", vport); - } - sockaddr native_addr; - convertOrbisNetSockaddrToPosix(addr, &native_addr); - int ret = ::connect(sock, &native_addr, sizeof(sockaddr_in)); -#ifdef _WIN32 - if (ret < 0 && WSAGetLastError() == WSAEWOULDBLOCK) { - WSASetLastError(WSAEINPROGRESS); - } -#endif - return ConvertReturnErrorCode(ret); -} - -int P2PSocket::GetSocketAddress(OrbisNetSockaddr* name, u32* namelen) { - std::scoped_lock lock{m_mutex}; - sockaddr native_addr; - socklen_t len = sizeof(native_addr); - int ret = ::getsockname(sock, &native_addr, &len); - if (ret == 0) { - convertPosixSockaddrToOrbis(&native_addr, name); - if (namelen) { - *namelen = sizeof(OrbisNetSockaddrIn); - } - } - return ConvertReturnErrorCode(ret); -} - -int P2PSocket::GetPeerName(OrbisNetSockaddr* addr, u32* namelen) { - std::scoped_lock lock{m_mutex}; - sockaddr native_addr; - socklen_t len = sizeof(native_addr); - int ret = ::getpeername(sock, &native_addr, &len); - if (ret == 0) { - convertPosixSockaddrToOrbis(&native_addr, addr); - if (namelen) { - *namelen = sizeof(OrbisNetSockaddrIn); - } - } - return ConvertReturnErrorCode(ret); -} - -int P2PSocket::fstat(Libraries::Kernel::OrbisKernelStat* stat) { - LOG_DEBUG(Lib_Net, "(STUBBED) called"); - return 0; + return std::make_shared(new_sock, socket_type, vport); } } // namespace Libraries::Net diff --git a/src/core/libraries/network/posix_sockets.cpp b/src/core/libraries/network/posix_sockets.cpp index 8171cf589..896009b1d 100644 --- a/src/core/libraries/network/posix_sockets.cpp +++ b/src/core/libraries/network/posix_sockets.cpp @@ -154,24 +154,39 @@ void convertPosixSockaddrToOrbis(sockaddr* src, OrbisNetSockaddr* dst) { memcpy(&dst_in->sin_addr, &src_in->sin_addr, 4); } -bool PosixSocket::IsValid() const { -#ifdef _WIN32 - return sock != INVALID_SOCKET; -#else - return sock != -1; -#endif -} - -int PosixSocket::Close() { +int NativeSocket::Close() { std::scoped_lock lock{m_mutex}; #ifdef _WIN32 auto out = closesocket(sock); + sock = INVALID_SOCKET; #else auto out = ::close(sock); + sock = -1; #endif return ConvertReturnErrorCode(out); } +int NativeSocket::Listen(int backlog) { + std::scoped_lock lock{m_mutex}; + return ConvertReturnErrorCode(::listen(sock, backlog)); +} + +int NativeSocket::fstat(Libraries::Kernel::OrbisKernelStat* sb) { +#ifdef _WIN32 + LOG_ERROR(Lib_Net, "(STUBBED) called"); + sb->st_mode = 0000777u | 0140000u; + return 0; +#else + struct stat st {}; + int result = ::fstat(sock, &st); + sb->st_mode = 0000777u | 0140000u; + sb->st_size = st.st_size; + sb->st_blocks = st.st_blocks; + sb->st_blksize = st.st_blksize; + return ConvertReturnErrorCode(result); +#endif +} + int PosixSocket::Bind(const OrbisNetSockaddr* addr, u32 addrlen) { std::scoped_lock lock{m_mutex}; sockaddr addr2; @@ -179,11 +194,6 @@ int PosixSocket::Bind(const OrbisNetSockaddr* addr, u32 addrlen) { return ConvertReturnErrorCode(::bind(sock, &addr2, sizeof(sockaddr_in))); } -int PosixSocket::Listen(int backlog) { - std::scoped_lock lock{m_mutex}; - return ConvertReturnErrorCode(::listen(sock, backlog)); -} - int convertOrbisFlagsToPosix(int sock_type, int sce_flags) { int posix_flags = 0; @@ -281,7 +291,8 @@ int PosixSocket::SendMessage(const OrbisNetMsghdr* msg, int flags) { #else int native_flags = convertOrbisFlagsToPosix(socket_type, flags); - int res = sendmsg(sock, reinterpret_cast(msg), native_flags); + msghdr native_msg = ConvertOrbisToNativeMsghdr(msg); + int res = sendmsg(sock, &native_msg, native_flags); return ConvertReturnErrorCode(res); #endif } @@ -374,7 +385,9 @@ int PosixSocket::ReceiveMessage(OrbisNetMsghdr* msg, int flags) { #else int native_flags = convertOrbisFlagsToPosix(socket_type, flags); - int res = recvmsg(sock, reinterpret_cast(msg), native_flags); + msghdr native_msg = ConvertOrbisToNativeMsghdr(msg); + int res = recvmsg(sock, &native_msg, native_flags); + msg->msg_flags = native_msg.msg_flags; return ConvertReturnErrorCode(res); #endif } @@ -523,7 +536,7 @@ int PosixSocket::SetSocketOptions(int level, int optname, const void* optval, u3 return -1; } case ORBIS_NET_SO_LINGER: { - if (socket_type != ORBIS_NET_SOCK_STREAM) { + if (socket_type != ORBIS_NET_SOCK_STREAM && socket_type != ORBIS_NET_SOCK_STREAM_P2P) { *Libraries::Kernel::__Error() = ORBIS_NET_EPROCUNAVAIL; return -1; } @@ -689,21 +702,4 @@ int PosixSocket::GetPeerName(OrbisNetSockaddr* name, u32* namelen) { return ConvertReturnErrorCode(res); } -int PosixSocket::fstat(Libraries::Kernel::OrbisKernelStat* sb) { -#ifdef _WIN32 - LOG_ERROR(Lib_Net, "(STUBBED) called"); - sb->st_mode = 0000777u | 0140000u; - return 0; -#else - struct stat st{}; - int result = ::fstat(sock, &st); - sb->st_mode = 0000777u | 0140000u; - sb->st_size = st.st_size; - sb->st_blocks = st.st_blocks; - sb->st_blksize = st.st_blksize; - // sb->st_flags = st.st_flags; - return ConvertReturnErrorCode(result); -#endif -} - -} // namespace Libraries::Net \ No newline at end of file +} // namespace Libraries::Net diff --git a/src/core/libraries/network/sockets.h b/src/core/libraries/network/sockets.h index 74600f792..abe4946eb 100644 --- a/src/core/libraries/network/sockets.h +++ b/src/core/libraries/network/sockets.h @@ -63,6 +63,22 @@ int ConvertLevels(int level); void convertOrbisNetSockaddrToPosix(const OrbisNetSockaddr* src, sockaddr* dst); void convertPosixSockaddrToOrbis(sockaddr* src, OrbisNetSockaddr* dst); int convertOrbisFlagsToPosix(int sock_type, int sce_flags); +#ifndef _WIN32 +// Build a native msghdr from OrbisNetMsghdr. OrbisNetMsghdr uses +// int-sized fields (msg_iovlen, msg_controllen) where native msghdr uses size_t, +// so a reinterpret_cast would corrupt the layout on 64-bit systems. +inline msghdr ConvertOrbisToNativeMsghdr(const OrbisNetMsghdr* msg) { + msghdr native_msg{}; + native_msg.msg_name = msg->msg_name; + native_msg.msg_namelen = msg->msg_namelen; + native_msg.msg_iov = reinterpret_cast(msg->msg_iov); + native_msg.msg_iovlen = msg->msg_iovlen; + native_msg.msg_control = msg->msg_control; + native_msg.msg_controllen = msg->msg_controllen; + native_msg.msg_flags = msg->msg_flags; + return native_msg; +} +#endif struct OrbisNetLinger { s32 l_onoff; @@ -94,8 +110,40 @@ struct Socket { int socket_type; }; -struct PosixSocket : public Socket { +// Owns the OS socket fd and implements methods identical across all socket types. +struct NativeSocket : public Socket { net_socket sock; + + NativeSocket(int domain, int type, int protocol) + : Socket(domain, type, protocol), sock(::socket(domain, type, protocol)) {} + + // Wrap an already-existing fd. Queries SO_TYPE from the OS automatically. + // Note: Socket base domain/protocol remain 0 — only socket_type is recovered. + explicit NativeSocket(net_socket sock) : Socket(0, 0, 0), sock(sock) { + int type = 0; + socklen_t len = sizeof(type); + ::getsockopt(sock, SOL_SOCKET, SO_TYPE, (char*)&type, &len); + socket_type = type; + } + + bool IsValid() const override { +#ifdef _WIN32 + return sock != INVALID_SOCKET; +#else + return sock != -1; +#endif + } + + int Close() override; + int Listen(int backlog) override; + int fstat(Libraries::Kernel::OrbisKernelStat* stat) override; + + std::optional Native() override { + return sock; + } +}; + +struct PosixSocket : public NativeSocket { int sockopt_so_connecttimeo = 0; int sockopt_so_reuseport = 0; int sockopt_so_onesbcast = 0; @@ -105,18 +153,14 @@ struct PosixSocket : public Socket { int sockopt_ip_ttlchk = 0; int sockopt_ip_maxttl = 0; int sockopt_tcp_mss_to_advertise = 0; - int socket_type; + explicit PosixSocket(int domain, int type, int protocol) - : Socket(domain, type, protocol), sock(socket(domain, type, protocol)) { - socket_type = type; - } - explicit PosixSocket(net_socket sock) : Socket(0, 0, 0), sock(sock) {} - bool IsValid() const override; - int Close() override; + : NativeSocket(domain, type, protocol) {} + explicit PosixSocket(net_socket sock) : NativeSocket(sock) {} + int SetSocketOptions(int level, int optname, const void* optval, u32 optlen) override; int GetSocketOptions(int level, int optname, void* optval, u32* optlen) override; int Bind(const OrbisNetSockaddr* addr, u32 addrlen) override; - int Listen(int backlog) override; int SendMessage(const OrbisNetMsghdr* msg, int flags) override; int SendPacket(const void* msg, u32 len, int flags, const OrbisNetSockaddr* to, u32 tolen) override; @@ -126,55 +170,32 @@ struct PosixSocket : public Socket { int Connect(const OrbisNetSockaddr* addr, u32 namelen) override; int GetSocketAddress(OrbisNetSockaddr* name, u32* namelen) override; int GetPeerName(OrbisNetSockaddr* addr, u32* namelen) override; - int fstat(Libraries::Kernel::OrbisKernelStat* stat) override; - std::optional Native() override { - return sock; - } }; -struct P2PSocket : public Socket { - net_socket sock; - int sockopt_so_nbio = 0; - int socket_type; +struct P2PSocket : public PosixSocket { u16 vport = 0; // PS4 virtual port explicit P2PSocket(int domain, int type, int protocol); - ~P2PSocket() override; - bool IsValid() const override; - int Close() override; - int SetSocketOptions(int level, int optname, const void* optval, u32 optlen) override; + // Wrap an already-accepted fd. The PS4 type must be passed explicitly because + // the OS only knows SOCK_STREAM/SOCK_DGRAM, not PS4-specific P2P socket types. + P2PSocket(net_socket accepted_sock, int ps4_type, u16 parent_vport) + : PosixSocket(accepted_sock), vport(parent_vport) { + socket_type = ps4_type; + } int GetSocketOptions(int level, int optname, void* optval, u32* optlen) override; int Bind(const OrbisNetSockaddr* addr, u32 addrlen) override; - int Listen(int backlog) override; - int SendMessage(const OrbisNetMsghdr* msg, int flags) override; - int SendPacket(const void* msg, u32 len, int flags, const OrbisNetSockaddr* to, - u32 tolen) override; - int ReceiveMessage(OrbisNetMsghdr* msg, int flags) override; - int ReceivePacket(void* buf, u32 len, int flags, OrbisNetSockaddr* from, u32* fromlen) override; - SocketPtr Accept(OrbisNetSockaddr* addr, u32* addrlen) override; int Connect(const OrbisNetSockaddr* addr, u32 namelen) override; - int GetSocketAddress(OrbisNetSockaddr* name, u32* namelen) override; - int GetPeerName(OrbisNetSockaddr* addr, u32* namelen) override; - int fstat(Libraries::Kernel::OrbisKernelStat* stat) override; - std::optional Native() override { - return sock; - } + SocketPtr Accept(OrbisNetSockaddr* addr, u32* addrlen) override; }; -struct UnixSocket : public Socket { - net_socket sock; - int socket_type; +struct UnixSocket : public NativeSocket { explicit UnixSocket(int domain, int type, int protocol) - : Socket(domain, type, protocol), sock(socket(domain, type, protocol)) { - socket_type = type; - } - explicit UnixSocket(net_socket sock) : Socket(0, 0, 0), sock(sock) {} - bool IsValid() const override; - int Close() override; + : NativeSocket(domain, type, protocol) {} + explicit UnixSocket(net_socket sock) : NativeSocket(sock) {} + int SetSocketOptions(int level, int optname, const void* optval, u32 optlen) override; int GetSocketOptions(int level, int optname, void* optval, u32* optlen) override; int Bind(const OrbisNetSockaddr* addr, u32 addrlen) override; - int Listen(int backlog) override; int SendMessage(const OrbisNetMsghdr* msg, int flags) override; int SendPacket(const void* msg, u32 len, int flags, const OrbisNetSockaddr* to, u32 tolen) override; @@ -184,10 +205,6 @@ struct UnixSocket : public Socket { int Connect(const OrbisNetSockaddr* addr, u32 namelen) override; int GetSocketAddress(OrbisNetSockaddr* name, u32* namelen) override; int GetPeerName(OrbisNetSockaddr* addr, u32* namelen) override; - int fstat(Libraries::Kernel::OrbisKernelStat* stat) override; - std::optional Native() override { - return sock; - } }; -} // namespace Libraries::Net \ No newline at end of file +} // namespace Libraries::Net diff --git a/src/core/libraries/network/unix_sockets.cpp b/src/core/libraries/network/unix_sockets.cpp index 0914384f3..18c7ccf42 100644 --- a/src/core/libraries/network/unix_sockets.cpp +++ b/src/core/libraries/network/unix_sockets.cpp @@ -36,24 +36,6 @@ static void convertUnixSockaddrToOrbis(sockaddr* src, OrbisNetSockaddr* dst) { memcpy(&dst_in->sun_path, &src_in->sun_path, dst_in->sun_len); } -bool UnixSocket::IsValid() const { -#ifdef _WIN32 - return sock != INVALID_SOCKET; -#else - return sock != -1; -#endif -} - -int UnixSocket::Close() { - std::scoped_lock lock{m_mutex}; -#ifdef _WIN32 - auto out = closesocket(sock); -#else - auto out = ::close(sock); -#endif - return ConvertReturnErrorCode(out); -} - int UnixSocket::Bind(const OrbisNetSockaddr* addr, u32 addrlen) { std::scoped_lock lock{m_mutex}; sockaddr_un addr2; @@ -61,11 +43,6 @@ int UnixSocket::Bind(const OrbisNetSockaddr* addr, u32 addrlen) { return ConvertReturnErrorCode(::bind(sock, (const sockaddr*)&addr2, sizeof(sockaddr_un))); } -int UnixSocket::Listen(int backlog) { - std::scoped_lock lock{m_mutex}; - return ConvertReturnErrorCode(::listen(sock, backlog)); -} - int UnixSocket::SendMessage(const OrbisNetMsghdr* msg, int flags) { std::scoped_lock lock{m_mutex}; #ifdef _WIN32 @@ -87,7 +64,8 @@ int UnixSocket::SendMessage(const OrbisNetMsghdr* msg, int flags) { } return static_cast(bytesSent); #else - int res = sendmsg(sock, reinterpret_cast(msg), flags); + msghdr native_msg = ConvertOrbisToNativeMsghdr(msg); + int res = sendmsg(sock, &native_msg, flags); return ConvertReturnErrorCode(res); #endif } @@ -126,7 +104,9 @@ int UnixSocket::ReceiveMessage(OrbisNetMsghdr* msg, int flags) { } return static_cast(bytesReceived); #else - int res = recvmsg(sock, reinterpret_cast(msg), flags); + msghdr native_msg = ConvertOrbisToNativeMsghdr(msg); + int res = recvmsg(sock, &native_msg, flags); + msg->msg_flags = native_msg.msg_flags; return ConvertReturnErrorCode(res); #endif } @@ -256,21 +236,4 @@ int UnixSocket::GetPeerName(OrbisNetSockaddr* name, u32* namelen) { return ConvertReturnErrorCode(res); } -int UnixSocket::fstat(Libraries::Kernel::OrbisKernelStat* sb) { -#ifdef _WIN32 - LOG_ERROR(Lib_Net, "(STUBBED) called"); - sb->st_mode = 0000777u | 0140000u; - return 0; -#else - struct stat st{}; - int result = ::fstat(sock, &st); - sb->st_mode = 0000777u | 0140000u; - sb->st_size = st.st_size; - sb->st_blocks = st.st_blocks; - sb->st_blksize = st.st_blksize; - // sb->st_flags = st.st_flags; - return ConvertReturnErrorCode(result); -#endif -} - -} // namespace Libraries::Net \ No newline at end of file +} // namespace Libraries::Net