From 6781dbe8e9210529f11d711767a02e849322aba7 Mon Sep 17 00:00:00 2001 From: Gursukh Date: Thu, 2 Apr 2026 23:17:59 +0100 Subject: [PATCH 1/7] Feat: Epoll P2P Implementation --- src/core/libraries/network/net.cpp | 29 +- src/core/libraries/network/p2p_sockets.cpp | 296 ++++++++++++++++--- src/core/libraries/network/posix_sockets.cpp | 10 +- src/core/libraries/network/sockets.h | 21 +- src/core/libraries/network/unix_sockets.cpp | 117 -------- 5 files changed, 298 insertions(+), 175 deletions(-) diff --git a/src/core/libraries/network/net.cpp b/src/core/libraries/network/net.cpp index baf45b64f..d5d74c1a3 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 = true; // TODO init it properly +static bool g_isNetInitialized = false; static int ConvertFamilies(int family) { switch (family) { @@ -127,6 +127,8 @@ int PS4_SYSV_ABI sce_net_in6addr_nodelocal_allnodes() { } OrbisNetId PS4_SYSV_ABI sceNetAccept(OrbisNetId s, OrbisNetSockaddr* addr, u32* paddrlen) { + LOG_DEBUG(Lib_Net, "called, s = {}, addr = {}, paddrlen = {}", s, fmt::ptr(addr), + fmt::ptr(paddrlen)); if (!g_isNetInitialized) { return ORBIS_NET_ERROR_ENOTINIT; } @@ -657,8 +659,7 @@ int PS4_SYSV_ABI sceNetEpollControl(OrbisNetId epollid, OrbisNetEpollFlag op, Or case Core::FileSys::FileType::Socket: { auto native_handle = file->socket->Native(); if (!native_handle) { - // P2P socket, cannot be added to epoll - LOG_ERROR(Lib_Net, "P2P socket cannot be added to epoll (unimplemented)"); + LOG_ERROR(Lib_Net, "socket {} has no native handle", id); *sceNetErrnoLoc() = ORBIS_NET_EBADF; return ORBIS_NET_ERROR_EBADF; } @@ -666,9 +667,10 @@ int PS4_SYSV_ABI sceNetEpollControl(OrbisNetId epollid, OrbisNetEpollFlag op, Or #ifndef __FreeBSD__ epoll_event native_event = {.events = ConvertEpollEventsIn(event->events), .data = {.fd = id}}; - ASSERT(epoll_ctl(epoll->epoll_fd, EPOLL_CTL_ADD, *native_handle, &native_event) == 0); - epoll->events.emplace_back(id, *event); + ASSERT(epoll_ctl(epoll->epoll_fd, EPOLL_CTL_ADD, *native_handle, + &native_event) == 0); #endif + epoll->events.emplace_back(id, *event); break; } case Core::FileSys::FileType::Resolver: { @@ -707,8 +709,7 @@ int PS4_SYSV_ABI sceNetEpollControl(OrbisNetId epollid, OrbisNetEpollFlag op, Or case Core::FileSys::FileType::Socket: { auto native_handle = file->socket->Native(); if (!native_handle) { - // P2P socket, cannot be modified in epoll - LOG_ERROR(Lib_Net, "P2P socket cannot be modified in epoll (unimplemented)"); + LOG_ERROR(Lib_Net, "socket {} has no native handle", id); *sceNetErrnoLoc() = ORBIS_NET_EBADF; return ORBIS_NET_ERROR_EBADF; } @@ -716,9 +717,10 @@ int PS4_SYSV_ABI sceNetEpollControl(OrbisNetId epollid, OrbisNetEpollFlag op, Or #ifndef __FreeBSD__ epoll_event native_event = {.events = ConvertEpollEventsIn(event->events), .data = {.fd = id}}; - ASSERT(epoll_ctl(epoll->epoll_fd, EPOLL_CTL_MOD, *native_handle, &native_event) == 0); - *it = {id, *event}; + ASSERT(epoll_ctl(epoll->epoll_fd, EPOLL_CTL_MOD, *native_handle, + &native_event) == 0); #endif + *it = {id, *event}; break; } default: @@ -751,15 +753,15 @@ int PS4_SYSV_ABI sceNetEpollControl(OrbisNetId epollid, OrbisNetEpollFlag op, Or case Core::FileSys::FileType::Socket: { auto native_handle = file->socket->Native(); if (!native_handle) { - // P2P socket, cannot be removed from epoll - LOG_ERROR(Lib_Net, "P2P socket cannot be removed from epoll (unimplemented)"); + LOG_ERROR(Lib_Net, "socket {} has no native handle", id); *sceNetErrnoLoc() = ORBIS_NET_EBADF; return ORBIS_NET_ERROR_EBADF; } + #ifndef __FreeBSD__ ASSERT(epoll_ctl(epoll->epoll_fd, EPOLL_CTL_DEL, *native_handle, nullptr) == 0); - epoll->events.erase(it); #endif + epoll->events.erase(it); break; } case Core::FileSys::FileType::Resolver: { @@ -1700,7 +1702,8 @@ int PS4_SYSV_ABI sceNetSysctl() { } int PS4_SYSV_ABI sceNetTerm() { - LOG_ERROR(Lib_Net, "(STUBBED) called"); + LOG_DEBUG(Lib_Net, "called"); + g_isNetInitialized = false; return ORBIS_OK; } diff --git a/src/core/libraries/network/p2p_sockets.cpp b/src/core/libraries/network/p2p_sockets.cpp index cfdd1bda7..6ef21852e 100644 --- a/src/core/libraries/network/p2p_sockets.cpp +++ b/src/core/libraries/network/p2p_sockets.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-FileCopyrightText: Copyright 2024-2026 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #include @@ -7,82 +7,308 @@ #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) { + 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() { - LOG_ERROR(Lib_Net, "(STUBBED) called"); - return 0; + 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) { - LOG_ERROR(Lib_Net, "(STUBBED) called"); - return 0; + 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)); } int P2PSocket::GetSocketOptions(int level, int optname, void* optval, u32* optlen) { - LOG_ERROR(Lib_Net, "(STUBBED) called"); - return 0; + 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); + return 0; + } + + socklen_t native_optlen = *optlen; + int ret = ::getsockopt(sock, native_level, optname, (char*)optval, &native_optlen); + *optlen = native_optlen; + return ConvertReturnErrorCode(ret); } int P2PSocket::Bind(const OrbisNetSockaddr* addr, u32 addrlen) { - LOG_ERROR(Lib_Net, "(STUBBED) called"); - return 0; + 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 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))); } int P2PSocket::Listen(int backlog) { - LOG_ERROR(Lib_Net, "(STUBBED) called"); - return 0; + std::scoped_lock lock{m_mutex}; + return ConvertReturnErrorCode(::listen(sock, backlog)); } int P2PSocket::SendMessage(const OrbisNetMsghdr* msg, int flags) { - LOG_ERROR(Lib_Net, "(STUBBED) called"); - *Libraries::Kernel::__Error() = ORBIS_NET_EAGAIN; - return -1; + 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); + } + total_sent += sent; + } + 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) { - LOG_ERROR(Lib_Net, "(STUBBED) called"); - *Libraries::Kernel::__Error() = ORBIS_NET_EAGAIN; - return -1; + 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) { - LOG_ERROR(Lib_Net, "(STUBBED) called"); - *Libraries::Kernel::__Error() = ORBIS_NET_EAGAIN; - return -1; + 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) { - LOG_ERROR(Lib_Net, "(STUBBED) called"); - *Libraries::Kernel::__Error() = ORBIS_NET_EAGAIN; - return -1; +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); } SocketPtr P2PSocket::Accept(OrbisNetSockaddr* addr, u32* addrlen) { - LOG_ERROR(Lib_Net, "(STUBBED) called"); - *Libraries::Kernel::__Error() = ORBIS_NET_EAGAIN; - return nullptr; + std::scoped_lock lock{m_mutex}; + sockaddr native_addr; + socklen_t len = sizeof(native_addr); + auto new_sock = ::accept(sock, &native_addr, &len); +#ifdef _WIN32 + if (new_sock == INVALID_SOCKET) { +#else + if (new_sock < 0) { +#endif + ConvertReturnErrorCode(-1); + return nullptr; + } + if (addr) { + convertPosixSockaddrToOrbis(&native_addr, addr); + if (addrlen) { + *addrlen = sizeof(OrbisNetSockaddrIn); + } + } + return std::make_shared(new_sock); } int P2PSocket::Connect(const OrbisNetSockaddr* addr, u32 namelen) { - LOG_ERROR(Lib_Net, "(STUBBED) called"); - return 0; + 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) { - LOG_ERROR(Lib_Net, "(STUBBED) called"); - return 0; + 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) { - LOG_ERROR(Lib_Net, "(STUBBED) called"); - return 0; + 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_ERROR(Lib_Net, "(STUBBED) called"); + LOG_DEBUG(Lib_Net, "(STUBBED) called"); return 0; } -} // namespace Libraries::Net \ No newline at end of file +} // namespace Libraries::Net diff --git a/src/core/libraries/network/posix_sockets.cpp b/src/core/libraries/network/posix_sockets.cpp index 164d85896..8171cf589 100644 --- a/src/core/libraries/network/posix_sockets.cpp +++ b/src/core/libraries/network/posix_sockets.cpp @@ -27,7 +27,7 @@ namespace Libraries::Net { return -1; #endif -static int ConvertReturnErrorCode(int retval) { +int ConvertReturnErrorCode(int retval) { if (retval < 0) { #ifdef _WIN32 switch (WSAGetLastError()) { @@ -118,7 +118,7 @@ static int ConvertReturnErrorCode(int retval) { return retval; } -static int ConvertLevels(int level) { +int ConvertLevels(int level) { switch (level) { case ORBIS_NET_SOL_SOCKET: return SOL_SOCKET; @@ -132,7 +132,7 @@ static int ConvertLevels(int level) { return -1; } -static void convertOrbisNetSockaddrToPosix(const OrbisNetSockaddr* src, sockaddr* dst) { +void convertOrbisNetSockaddrToPosix(const OrbisNetSockaddr* src, sockaddr* dst) { if (src == nullptr || dst == nullptr) return; memset(dst, 0, sizeof(sockaddr)); @@ -143,7 +143,7 @@ static void convertOrbisNetSockaddrToPosix(const OrbisNetSockaddr* src, sockaddr memcpy(&dst_in->sin_addr, &src_in->sin_addr, 4); } -static void convertPosixSockaddrToOrbis(sockaddr* src, OrbisNetSockaddr* dst) { +void convertPosixSockaddrToOrbis(sockaddr* src, OrbisNetSockaddr* dst) { if (src == nullptr || dst == nullptr) return; memset(dst, 0, sizeof(OrbisNetSockaddr)); @@ -184,7 +184,7 @@ int PosixSocket::Listen(int backlog) { return ConvertReturnErrorCode(::listen(sock, backlog)); } -static int convertOrbisFlagsToPosix(int sock_type, int sce_flags) { +int convertOrbisFlagsToPosix(int sock_type, int sce_flags) { int posix_flags = 0; if (sce_flags & ORBIS_NET_MSG_PEEK) diff --git a/src/core/libraries/network/sockets.h b/src/core/libraries/network/sockets.h index 86661c71c..74600f792 100644 --- a/src/core/libraries/network/sockets.h +++ b/src/core/libraries/network/sockets.h @@ -57,6 +57,13 @@ struct Socket; typedef std::shared_ptr SocketPtr; +// Shared socket helper functions defined in posix_sockets.cpp +int ConvertReturnErrorCode(int retval); +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); + struct OrbisNetLinger { s32 l_onoff; s32 l_linger; @@ -126,10 +133,14 @@ struct PosixSocket : public Socket { }; struct P2PSocket : public Socket { - explicit P2PSocket(int domain, int type, int protocol) : Socket(domain, type, protocol) {} - bool IsValid() const override { - return true; - } + net_socket sock; + int sockopt_so_nbio = 0; + int socket_type; + 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; int GetSocketOptions(int level, int optname, void* optval, u32* optlen) override; @@ -146,7 +157,7 @@ struct P2PSocket : public Socket { int GetPeerName(OrbisNetSockaddr* addr, u32* namelen) override; int fstat(Libraries::Kernel::OrbisKernelStat* stat) override; std::optional Native() override { - return {}; + return sock; } }; diff --git a/src/core/libraries/network/unix_sockets.cpp b/src/core/libraries/network/unix_sockets.cpp index 29bbc5f16..0914384f3 100644 --- a/src/core/libraries/network/unix_sockets.cpp +++ b/src/core/libraries/network/unix_sockets.cpp @@ -15,123 +15,6 @@ namespace Libraries::Net { -#ifdef _WIN32 -#define ERROR_CASE(errname) \ - case (WSA##errname): \ - *Libraries::Kernel::__Error() = ORBIS_NET_##errname; \ - return -1; -#else -#define ERROR_CASE(errname) \ - case (errname): \ - *Libraries::Kernel::__Error() = ORBIS_NET_##errname; \ - return -1; -#endif - -static int ConvertReturnErrorCode(int retval) { - if (retval < 0) { -#ifdef _WIN32 - switch (WSAGetLastError()) { -#else - switch (errno) { -#endif -#ifndef _WIN32 // These errorcodes don't exist in WinSock - ERROR_CASE(EPERM) - ERROR_CASE(ENOENT) - // ERROR_CASE(ESRCH) - // ERROR_CASE(EIO) - // ERROR_CASE(ENXIO) - // ERROR_CASE(E2BIG) - // ERROR_CASE(ENOEXEC) - // ERROR_CASE(EDEADLK) - ERROR_CASE(ENOMEM) - // ERROR_CASE(ECHILD) - // ERROR_CASE(EBUSY) - ERROR_CASE(EEXIST) - // ERROR_CASE(EXDEV) - ERROR_CASE(ENODEV) - // ERROR_CASE(ENOTDIR) - // ERROR_CASE(EISDIR) - ERROR_CASE(ENFILE) - // ERROR_CASE(ENOTTY) - // ERROR_CASE(ETXTBSY) - // ERROR_CASE(EFBIG) - ERROR_CASE(ENOSPC) - // ERROR_CASE(ESPIPE) - // ERROR_CASE(EROFS) - // ERROR_CASE(EMLINK) - ERROR_CASE(EPIPE) - // ERROR_CASE(EDOM) - // ERROR_CASE(ERANGE) - // ERROR_CASE(ENOLCK) - // ERROR_CASE(ENOSYS) - // ERROR_CASE(EIDRM) - // ERROR_CASE(EOVERFLOW) - // ERROR_CASE(EILSEQ) - // ERROR_CASE(ENOTSUP) - ERROR_CASE(ECANCELED) - // ERROR_CASE(EBADMSG) - ERROR_CASE(ENODATA) - // ERROR_CASE(ENOSR) - // ERROR_CASE(ENOSTR) - // ERROR_CASE(ETIME) -#endif - ERROR_CASE(EINTR) - ERROR_CASE(EBADF) - ERROR_CASE(EACCES) - ERROR_CASE(EFAULT) - ERROR_CASE(EINVAL) - ERROR_CASE(EMFILE) - ERROR_CASE(EWOULDBLOCK) - ERROR_CASE(EINPROGRESS) - ERROR_CASE(EALREADY) - ERROR_CASE(ENOTSOCK) - ERROR_CASE(EDESTADDRREQ) - ERROR_CASE(EMSGSIZE) - ERROR_CASE(EPROTOTYPE) - ERROR_CASE(ENOPROTOOPT) - ERROR_CASE(EPROTONOSUPPORT) -#if defined(__APPLE__) || defined(_WIN32) - ERROR_CASE(EOPNOTSUPP) -#endif - ERROR_CASE(EAFNOSUPPORT) - ERROR_CASE(EADDRINUSE) - ERROR_CASE(EADDRNOTAVAIL) - ERROR_CASE(ENETDOWN) - ERROR_CASE(ENETUNREACH) - ERROR_CASE(ENETRESET) - ERROR_CASE(ECONNABORTED) - ERROR_CASE(ECONNRESET) - ERROR_CASE(ENOBUFS) - ERROR_CASE(EISCONN) - ERROR_CASE(ENOTCONN) - ERROR_CASE(ETIMEDOUT) - ERROR_CASE(ECONNREFUSED) - ERROR_CASE(ELOOP) - ERROR_CASE(ENAMETOOLONG) - ERROR_CASE(EHOSTUNREACH) - ERROR_CASE(ENOTEMPTY) - } - *Libraries::Kernel::__Error() = ORBIS_NET_EINTERNAL; - return -1; - } - // if it is 0 or positive return it as it is - return retval; -} - -static int ConvertLevels(int level) { - switch (level) { - case ORBIS_NET_SOL_SOCKET: - return SOL_SOCKET; - case ORBIS_NET_IPPROTO_IP: - return IPPROTO_IP; - case ORBIS_NET_IPPROTO_TCP: - return IPPROTO_TCP; - case ORBIS_NET_IPPROTO_IPV6: - return IPPROTO_IPV6; - } - return -1; -} - static void convertOrbisNetSockaddrToUnix(const OrbisNetSockaddr* src, sockaddr_un* dst) { if (src == nullptr || dst == nullptr) return; From 2616af16455dbc3d1c997185f975db3ae0fd2c83 Mon Sep 17 00:00:00 2001 From: Gursukh Date: Thu, 2 Apr 2026 23:24:17 +0100 Subject: [PATCH 2/7] Fix: Clang format --- src/core/libraries/network/net.cpp | 6 ++---- src/core/libraries/network/p2p_sockets.cpp | 6 ++---- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/src/core/libraries/network/net.cpp b/src/core/libraries/network/net.cpp index d5d74c1a3..3af0bea2c 100644 --- a/src/core/libraries/network/net.cpp +++ b/src/core/libraries/network/net.cpp @@ -667,8 +667,7 @@ int PS4_SYSV_ABI sceNetEpollControl(OrbisNetId epollid, OrbisNetEpollFlag op, Or #ifndef __FreeBSD__ epoll_event native_event = {.events = ConvertEpollEventsIn(event->events), .data = {.fd = id}}; - ASSERT(epoll_ctl(epoll->epoll_fd, EPOLL_CTL_ADD, *native_handle, - &native_event) == 0); + ASSERT(epoll_ctl(epoll->epoll_fd, EPOLL_CTL_ADD, *native_handle, &native_event) == 0); #endif epoll->events.emplace_back(id, *event); break; @@ -717,8 +716,7 @@ int PS4_SYSV_ABI sceNetEpollControl(OrbisNetId epollid, OrbisNetEpollFlag op, Or #ifndef __FreeBSD__ epoll_event native_event = {.events = ConvertEpollEventsIn(event->events), .data = {.fd = id}}; - ASSERT(epoll_ctl(epoll->epoll_fd, EPOLL_CTL_MOD, *native_handle, - &native_event) == 0); + ASSERT(epoll_ctl(epoll->epoll_fd, EPOLL_CTL_MOD, *native_handle, &native_event) == 0); #endif *it = {id, *event}; break; diff --git a/src/core/libraries/network/p2p_sockets.cpp b/src/core/libraries/network/p2p_sockets.cpp index 6ef21852e..4f6a7f3d7 100644 --- a/src/core/libraries/network/p2p_sockets.cpp +++ b/src/core/libraries/network/p2p_sockets.cpp @@ -214,13 +214,11 @@ int P2PSocket::ReceiveMessage(OrbisNetMsghdr* msg, int flags) { } return total_recv; #else - return ConvertReturnErrorCode( - ::recvmsg(sock, reinterpret_cast(msg), native_flags)); + return ConvertReturnErrorCode(::recvmsg(sock, reinterpret_cast(msg), native_flags)); #endif } -int P2PSocket::ReceivePacket(void* buf, u32 len, int flags, OrbisNetSockaddr* from, - u32* fromlen) { +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) { From e763e90c2a1fb8acb4b1a65afcb219ab83b3d9b0 Mon Sep 17 00:00:00 2001 From: Gursukh Date: Sat, 4 Apr 2026 14:05:19 +0100 Subject: [PATCH 3/7] 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 From c576aefa9fb5d172b33fd5b6d9b5b2e4668831d6 Mon Sep 17 00:00:00 2001 From: Gursukh Date: Sat, 4 Apr 2026 14:29:29 +0100 Subject: [PATCH 4/7] Feat: Further refinemets --- src/core/libraries/network/net.cpp | 12 ++-- src/core/libraries/network/net_epoll.cpp | 88 +++++++++++++++++++++--- src/core/libraries/network/net_epoll.h | 9 ++- 3 files changed, 94 insertions(+), 15 deletions(-) diff --git a/src/core/libraries/network/net.cpp b/src/core/libraries/network/net.cpp index 76f67841d..d4e09505e 100644 --- a/src/core/libraries/network/net.cpp +++ b/src/core/libraries/network/net.cpp @@ -727,13 +727,13 @@ int PS4_SYSV_ABI sceNetEpollControl(OrbisNetId epollid, OrbisNetEpollFlag op, Or } // P2P dgram sockets only support EPOLLIN (matches PS4 kernel behavior) - auto epoll_events_mod = event->events; + auto epoll_events = event->events; if (file->socket->socket_type == ORBIS_NET_SOCK_DGRAM_P2P) { - epoll_events_mod &= ORBIS_NET_EPOLLIN; + epoll_events &= ORBIS_NET_EPOLLIN; } #ifndef __FreeBSD__ - epoll_event native_event = {.events = ConvertEpollEventsIn(epoll_events_mod), + epoll_event native_event = {.events = ConvertEpollEventsIn(epoll_events), .data = {.fd = id}}; ASSERT(epoll_ctl(epoll->epoll_fd, EPOLL_CTL_MOD, *native_handle, &native_event) == 0); #endif @@ -893,6 +893,9 @@ int PS4_SYSV_ABI sceNetEpollWait(OrbisNetId epollid, OrbisNetEpollEvent* events, LOG_TRACE(Lib_Net, "timed out"); } else { for (int j = 0; j < result; ++j) { + if (i >= maxevents) { + break; + } const auto& current_event = native_events[j]; // Skip the abort fd event (registered with data.fd = -1) if (current_event.data.fd == -1) { @@ -1734,8 +1737,7 @@ int PS4_SYSV_ABI sceNetSysctl() { } int PS4_SYSV_ABI sceNetTerm() { - LOG_DEBUG(Lib_Net, "called"); - g_isNetInitialized = false; + LOG_ERROR(Lib_Net, "(STUBBED) called"); return ORBIS_OK; } diff --git a/src/core/libraries/network/net_epoll.cpp b/src/core/libraries/network/net_epoll.cpp index 189b5bc72..de9ad121a 100644 --- a/src/core/libraries/network/net_epoll.cpp +++ b/src/core/libraries/network/net_epoll.cpp @@ -6,8 +6,13 @@ #include "common/logging/log.h" #include "common/types.h" #include "net_epoll.h" -#ifdef __linux__ +#ifdef _WIN32 +#include +#include +#elif defined(__linux__) #include +#else +#include #endif namespace Libraries::Net { @@ -54,30 +59,85 @@ Epoll::Epoll(const char* name_) : name(name_ ? name_ : "anon"), epoll_fd(epoll_c #else ASSERT(epoll_fd != -1); #endif -#ifdef __linux__ + + // Set up the abort wake mechanism and register it in epoll with sentinel data.fd = -1. + // Linux: eventfd, macOS/BSD: self-pipe, Windows: loopback UDP socket. +#ifdef _WIN32 + abort_sock = ::socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + ASSERT(abort_sock != INVALID_SOCKET); + sockaddr_in addr{}; + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + addr.sin_port = 0; // OS picks an ephemeral port + ASSERT(::bind(abort_sock, reinterpret_cast(&addr), sizeof(addr)) == 0); + // Connect to self so we can use send() in Abort() + socklen_t addrlen = sizeof(addr); + ASSERT(::getsockname(abort_sock, reinterpret_cast(&addr), &addrlen) == 0); + ASSERT(::connect(abort_sock, reinterpret_cast(&addr), sizeof(addr)) == 0); + u_long nonblocking = 1; + ASSERT(::ioctlsocket(abort_sock, FIONBIO, &nonblocking) == 0); + epoll_event ev = {.events = EPOLLIN, .data = {.fd = -1}}; + ASSERT(epoll_ctl(epoll_fd, EPOLL_CTL_ADD, abort_sock, &ev) == 0); +#elif defined(__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); +#else + // Self-pipe for macOS/BSD + int r = ::pipe(abort_pipe); + ASSERT(r == 0); + // Set both ends non-blocking + for (int i = 0; i < 2; ++i) { + int flags = ::fcntl(abort_pipe[i], F_GETFL); + ::fcntl(abort_pipe[i], F_SETFL, flags | O_NONBLOCK); + ::fcntl(abort_pipe[i], F_SETFD, FD_CLOEXEC); + } + epoll_event ev = {.events = EPOLLIN, .data = {.fd = -1}}; + ASSERT(epoll_ctl(epoll_fd, EPOLL_CTL_ADD, abort_pipe[0], &ev) == 0); #endif } void Epoll::Abort() { aborted.store(true, std::memory_order_release); -#ifdef __linux__ +#ifdef _WIN32 + if (abort_sock != INVALID_SOCKET) { + char byte = 1; + (void)::send(abort_sock, &byte, 1, 0); + } +#elif defined(__linux__) if (abort_fd != -1) { uint64_t val = 1; - ::write(abort_fd, &val, sizeof(val)); + (void)::write(abort_fd, &val, sizeof(val)); + } +#else + if (abort_pipe[1] != -1) { + char byte = 1; + (void)::write(abort_pipe[1], &byte, 1); } #endif } void Epoll::ClearAbort() { aborted.store(false, std::memory_order_release); -#ifdef __linux__ +#ifdef _WIN32 + if (abort_sock != INVALID_SOCKET) { + char buf[64]; + // Drain any pending data + while (::recv(abort_sock, buf, sizeof(buf), 0) > 0) { + } + } +#elif defined(__linux__) if (abort_fd != -1) { uint64_t val; - ::read(abort_fd, &val, sizeof(val)); + (void)::read(abort_fd, &val, sizeof(val)); + } +#else + if (abort_pipe[0] != -1) { + char buf[64]; + // Drain the pipe + while (::read(abort_pipe[0], buf, sizeof(buf)) > 0) { + } } #endif } @@ -85,17 +145,29 @@ void Epoll::ClearAbort() { void Epoll::Destroy() noexcept { events.clear(); #ifdef _WIN32 + if (abort_sock != INVALID_SOCKET) { + epoll_ctl(epoll_fd, EPOLL_CTL_DEL, abort_sock, nullptr); + ::closesocket(abort_sock); + abort_sock = INVALID_SOCKET; + } 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; } +#else + for (int i = 0; i < 2; ++i) { + if (abort_pipe[i] != -1) { + close(abort_pipe[i]); + abort_pipe[i] = -1; + } + } #endif + close(epoll_fd); + epoll_fd = -1; #endif name = ""; destroyed = true; diff --git a/src/core/libraries/network/net_epoll.h b/src/core/libraries/network/net_epoll.h index aa5801195..bccb4ba25 100644 --- a/src/core/libraries/network/net_epoll.h +++ b/src/core/libraries/network/net_epoll.h @@ -16,6 +16,7 @@ #include #endif +// TODO: FreeBSD requires libepoll-shim for epoll support #if defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) #include #include @@ -35,8 +36,12 @@ struct Epoll { epoll_handle epoll_fd; std::deque async_resolutions{}; std::atomic aborted{false}; -#ifdef __linux__ - int abort_fd = -1; +#ifdef _WIN32 + SOCKET abort_sock = INVALID_SOCKET; // loopback UDP socket for abort wake +#elif defined(__linux__) + int abort_fd = -1; // eventfd for abort wake +#else + int abort_pipe[2] = {-1, -1}; // self-pipe for abort wake (macOS/BSD) #endif explicit Epoll(const char* name_); From 0170c93c3f94e41b6ca21f80397d1f7edca2f8f4 Mon Sep 17 00:00:00 2001 From: Gursukh Sembi <63640543+Gursukh@users.noreply.github.com> Date: Sat, 4 Apr 2026 14:35:23 +0100 Subject: [PATCH 5/7] Minor Change --- src/core/libraries/network/p2p_sockets.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/libraries/network/p2p_sockets.cpp b/src/core/libraries/network/p2p_sockets.cpp index c8a1f27af..ad6e42ee8 100644 --- a/src/core/libraries/network/p2p_sockets.cpp +++ b/src/core/libraries/network/p2p_sockets.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2024-2026 shadPS4 Emulator Project +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #include "net.h" From 98d223d9266ba80b167ea0a11bd2cd3c1d264719 Mon Sep 17 00:00:00 2001 From: Gursukh Date: Sat, 4 Apr 2026 14:54:21 +0100 Subject: [PATCH 6/7] Fix: Missed windows socket import --- src/core/libraries/network/net_epoll.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/core/libraries/network/net_epoll.h b/src/core/libraries/network/net_epoll.h index bccb4ba25..9c778e68e 100644 --- a/src/core/libraries/network/net_epoll.h +++ b/src/core/libraries/network/net_epoll.h @@ -14,6 +14,7 @@ #ifdef _WIN32 #include +#include #endif // TODO: FreeBSD requires libepoll-shim for epoll support From 309396c1fdbfd7ba55d7ed75c677c8f6e47cb3ef Mon Sep 17 00:00:00 2001 From: Gursukh Date: Sat, 4 Apr 2026 15:09:08 +0100 Subject: [PATCH 7/7] Fix: Clang format --- src/core/libraries/network/posix_sockets.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/libraries/network/posix_sockets.cpp b/src/core/libraries/network/posix_sockets.cpp index 896009b1d..266436c7f 100644 --- a/src/core/libraries/network/posix_sockets.cpp +++ b/src/core/libraries/network/posix_sockets.cpp @@ -177,7 +177,7 @@ int NativeSocket::fstat(Libraries::Kernel::OrbisKernelStat* sb) { sb->st_mode = 0000777u | 0140000u; return 0; #else - struct stat st {}; + struct stat st{}; int result = ::fstat(sock, &st); sb->st_mode = 0000777u | 0140000u; sb->st_size = st.st_size;