This commit is contained in:
Gursukh Sembi 2026-04-08 12:42:36 +02:00 committed by GitHub
commit d2d966229a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 391 additions and 366 deletions

View File

@ -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<epoll_event> native_events{static_cast<size_t>(maxevents)};
// +1 for the abort fd which is also registered in the epoll instance
std::vector<epoll_event> native_events{static_cast<size_t>(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;
}
}

View File

@ -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);

View File

@ -1,9 +1,19 @@
// SPDX-FileCopyrightText: Copyright 2025 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <memory>
#include "common/assert.h"
#include "common/logging/log.h"
#include "common/types.h"
#include "net_epoll.h"
#ifdef _WIN32
#include <winsock2.h>
#include <ws2tcpip.h>
#elif defined(__linux__)
#include <sys/eventfd.h>
#else
#include <fcntl.h>
#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<sockaddr*>(&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<sockaddr*>(&addr), &addrlen) == 0);
ASSERT(::connect(abort_sock, reinterpret_cast<sockaddr*>(&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<Epoll>(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<Epoll>(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

View File

@ -6,16 +6,19 @@
#include "common/types.h"
#include "core/libraries/network/net.h"
#include <atomic>
#include <deque>
#include <memory>
#include <mutex>
#include <vector>
#ifdef _WIN32
#include <wepoll.h>
#include <winsock2.h>
#endif
// TODO: FreeBSD requires libepoll-shim for epoll support
#if defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__)
// ADD libepoll-shim if using freebsd!
#include <sys/epoll.h>
#include <unistd.h>
#endif
@ -33,34 +36,28 @@ struct Epoll {
std::string name;
epoll_handle epoll_fd;
std::deque<u32> async_resolutions{};
explicit Epoll(const char* name_) : name(name_), epoll_fd(epoll_create1(0)) {
std::atomic<bool> 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<Epoll> epolls;
std::vector<std::unique_ptr<Epoll>> epolls;
std::mutex m_mutex;
};

View File

@ -1,88 +1,79 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <common/assert.h>
#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<const OrbisNetSockaddrIn*>(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<const OrbisNetSockaddrIn*>(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<P2PSocket>(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
} // namespace Libraries::Net

View File

@ -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<const msghdr*>(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<msghdr*>(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
} // namespace Libraries::Net

View File

@ -57,6 +57,29 @@ struct Socket;
typedef std::shared_ptr<Socket> 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<iovec*>(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<net_socket> 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<net_socket> 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<net_socket> 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<net_socket> Native() override {
return sock;
}
};
} // namespace Libraries::Net
} // namespace Libraries::Net

View File

@ -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<int>(bytesSent);
#else
int res = sendmsg(sock, reinterpret_cast<const msghdr*>(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<int>(bytesReceived);
#else
int res = recvmsg(sock, reinterpret_cast<msghdr*>(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
} // namespace Libraries::Net