diff --git a/src/core/libraries/network/net.cpp b/src/core/libraries/network/net.cpp index baf45b64f..d4e09505e 100644 --- a/src/core/libraries/network/net.cpp +++ b/src/core/libraries/network/net.cpp @@ -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; } @@ -615,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; } @@ -657,18 +665,24 @@ 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; } + // 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); - epoll->events.emplace_back(id, *event); #endif + // Emulator-level event tracking + epoll->events.emplace_back(id, *event); break; } case Core::FileSys::FileType::Resolver: { @@ -707,18 +721,24 @@ 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; } + // 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_MOD, *native_handle, &native_event) == 0); - *it = {id, *event}; #endif + // Emulator-level event tracking + *it = {id, *event}; break; } default: @@ -751,15 +771,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: { @@ -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,16 @@ 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) { + 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) { + 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 +913,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..de9ad121a 100644 --- a/src/core/libraries/network/net_epoll.cpp +++ b/src/core/libraries/network/net_epoll.cpp @@ -1,9 +1,19 @@ // 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 _WIN32 +#include +#include +#elif defined(__linux__) +#include +#else +#include +#endif namespace Libraries::Net { @@ -43,17 +53,137 @@ 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 + + // 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 _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; + (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 _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; + (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 +} + +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 +#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; +} + 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 +196,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..9c778e68e 100644 --- a/src/core/libraries/network/net_epoll.h +++ b/src/core/libraries/network/net_epoll.h @@ -6,16 +6,19 @@ #include "common/types.h" #include "core/libraries/network/net.h" +#include #include +#include #include #include #ifdef _WIN32 #include +#include #endif +// TODO: FreeBSD requires libepoll-shim for epoll support #if defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) -// ADD libepoll-shim if using freebsd! #include #include #endif @@ -33,34 +36,28 @@ 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)) { + std::atomic aborted{false}; #ifdef _WIN32 - ASSERT(epoll_fd != nullptr); + SOCKET abort_sock = INVALID_SOCKET; // loopback UDP socket for abort wake +#elif defined(__linux__) + int abort_fd = -1; // eventfd for abort wake #else - ASSERT(epoll_fd != -1); + int abort_pipe[2] = {-1, -1}; // self-pipe for abort wake (macOS/BSD) #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 +76,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 cfdd1bda7..ad6e42ee8 100644 --- a/src/core/libraries/network/p2p_sockets.cpp +++ b/src/core/libraries/network/p2p_sockets.cpp @@ -1,88 +1,79 @@ // SPDX-FileCopyrightText: Copyright 2024 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" namespace Libraries::Net { -int P2PSocket::Close() { - LOG_ERROR(Lib_Net, "(STUBBED) called"); - return 0; -} - -int P2PSocket::SetSocketOptions(int level, int optname, const void* optval, u32 optlen) { - LOG_ERROR(Lib_Net, "(STUBBED) called"); - return 0; +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; + 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) { - LOG_ERROR(Lib_Net, "(STUBBED) called"); - return 0; + // 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; + } + return PosixSocket::GetSocketOptions(level, optname, optval, optlen); } int P2PSocket::Bind(const OrbisNetSockaddr* addr, u32 addrlen) { - LOG_ERROR(Lib_Net, "(STUBBED) called"); - return 0; -} - -int P2PSocket::Listen(int backlog) { - LOG_ERROR(Lib_Net, "(STUBBED) called"); - return 0; -} - -int P2PSocket::SendMessage(const OrbisNetMsghdr* msg, int flags) { - LOG_ERROR(Lib_Net, "(STUBBED) called"); - *Libraries::Kernel::__Error() = ORBIS_NET_EAGAIN; - return -1; -} - -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; -} - -int P2PSocket::ReceiveMessage(OrbisNetMsghdr* msg, int flags) { - 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) { - LOG_ERROR(Lib_Net, "(STUBBED) called"); - *Libraries::Kernel::__Error() = ORBIS_NET_EAGAIN; - return -1; -} - -SocketPtr P2PSocket::Accept(OrbisNetSockaddr* addr, u32* addrlen) { - LOG_ERROR(Lib_Net, "(STUBBED) called"); - *Libraries::Kernel::__Error() = ORBIS_NET_EAGAIN; - return nullptr; + // 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 && 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); + } + return PosixSocket::Bind(addr, addrlen); } int P2PSocket::Connect(const OrbisNetSockaddr* addr, u32 namelen) { - LOG_ERROR(Lib_Net, "(STUBBED) called"); - return 0; + // 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); + } + LOG_INFO(Lib_Net, "P2P connect: vport={}", vport); + } + return PosixSocket::Connect(addr, namelen); } -int P2PSocket::GetSocketAddress(OrbisNetSockaddr* name, u32* namelen) { - LOG_ERROR(Lib_Net, "(STUBBED) called"); - return 0; +SocketPtr P2PSocket::Accept(OrbisNetSockaddr* addr, u32* addrlen) { + std::scoped_lock lock{m_mutex}; + sockaddr native_addr; + socklen_t len = sizeof(native_addr); + net_socket 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 && addrlen) { + convertPosixSockaddrToOrbis(&native_addr, addr); + *addrlen = sizeof(OrbisNetSockaddrIn); + } + return std::make_shared(new_sock, socket_type, vport); } -int P2PSocket::GetPeerName(OrbisNetSockaddr* addr, u32* namelen) { - LOG_ERROR(Lib_Net, "(STUBBED) called"); - return 0; -} - -int P2PSocket::fstat(Libraries::Kernel::OrbisKernelStat* stat) { - LOG_ERROR(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..266436c7f 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)); @@ -154,24 +154,39 @@ static 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,12 +194,7 @@ 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)); -} - -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) @@ -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 86661c71c..abe4946eb 100644 --- a/src/core/libraries/network/sockets.h +++ b/src/core/libraries/network/sockets.h @@ -57,6 +57,29 @@ 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); +#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; s32 l_linger; @@ -87,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; @@ -98,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; @@ -119,51 +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 { - explicit P2PSocket(int domain, int type, int protocol) : Socket(domain, type, protocol) {} - bool IsValid() const override { - return true; +struct P2PSocket : public PosixSocket { + u16 vport = 0; // PS4 virtual port + + explicit P2PSocket(int domain, int type, int protocol); + // 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 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; 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 {}; - } + 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; @@ -173,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 29bbc5f16..18c7ccf42 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; @@ -153,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; @@ -178,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 @@ -204,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 } @@ -243,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 } @@ -373,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