Skip to content
Open
Show file tree
Hide file tree
Changes from 10 commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
e887616
fix(libSceNet): read back SO_REUSEADDR and SO_BROADCAST in sceNetGets…
EagleBearerYS Oct 21, 2026
aea804d
fix(libkernel): getsockopt reads options of libSceNet sockets
EagleBearerYS Oct 21, 2026
528ede3
Merge #9494 and #9515 into feat/libscenet-ex-forwards
EagleBearerYS Oct 22, 2026
3cd3328
feat(libSceNet): forward sceNetSocketInternal and the Ex socket optio…
EagleBearerYS Oct 22, 2026
7a48107
feat(libSceNet): forward sceNetSysctl to libkernel's sysctl
EagleBearerYS Oct 22, 2026
b44ee56
feat(libSceNet): convert IPv6 text with a numeric scope ID
EagleBearerYS Oct 22, 2026
a19d110
feat(libSceNet): report the IPv4 default route in sceNetConfigGetDefa…
EagleBearerYS Oct 22, 2026
43ea777
Merge main into feat/libscenet-ex-forwards
EagleBearerYS Oct 22, 2026
efd999c
Merge main into fix/libkernel-getsockopt-net-sockets
EagleBearerYS Oct 23, 2026
dda8c45
Merge #9515's main merge into feat/libscenet-ex-forwards
EagleBearerYS Oct 23, 2026
c0caad0
Merge main into feat/libscenet-ex-forwards
EagleBearerYS Oct 24, 2026
abbb7ac
Merge main into fix/libkernel-getsockopt-net-sockets
EagleBearerYS Oct 24, 2026
5cffcf5
Merge #9515's main merge into feat/libscenet-ex-forwards
EagleBearerYS Oct 24, 2026
fc47c2f
Merge main into fix/libkernel-getsockopt-net-sockets
EagleBearerYS Oct 25, 2026
6c938db
Merge #9515's main merge into feat/libscenet-ex-forwards
EagleBearerYS Oct 25, 2026
7f30082
Merge main into feat/libscenet-ex-forwards
EagleBearerYS Oct 25, 2026
89da0c7
Merge main into feat/libscenet-ex-forwards
EagleBearerYS Oct 25, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
93 changes: 76 additions & 17 deletions core/libs/prx/libSceNet/Export.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
#include <set>
#include <stdexcept>
#include <string>
#include <string_view>
#include <utility>
#include "SceTypes.hpp"
#include "prx/libc/include/General.hpp"
Expand Down Expand Up @@ -484,7 +485,11 @@ int* errno_slot() {
}

extern "C" int* APS5_VABI __error_nid_postfix();
extern "C" int APS5_VABI sysctl_nid_postfix(const int* name, std::uint32_t nameLength, void* oldValue, std::size_t* oldLength, const void* newValue, std::size_t newLength);
extern "C" int APS5_VABI __inet_pton_nid_postfix(int family, const char* text, void* destination);
extern "C" const char* APS5_VABI __inet_ntop_nid_postfix(int family, const void* source, char* destination, std::uint32_t capacity);
extern "C" std::size_t NetCtlHostDnsServers_nid_no_patch(std::uint32_t* servers, std::size_t capacity);
extern "C" bool NetCtlHostIpv4Gateway_nid_no_patch(std::uint32_t* gateway);

void set_errno(int err) {
*errno_slot() = err;
Expand Down Expand Up @@ -892,6 +897,19 @@ bool SetSocketOptionForKernel(int descriptor, int level, int option, const void*

const bool g_socketOptionSetterRegistered = (KernelSetSocketOptionSetter_nid_no_patch(&SetSocketOptionForKernel), true);

extern "C" int APS5_VABI sceNetGetsockopt(int s, int level, int optname, void* optval, uint32_t* optlen);

bool GetSocketOptionForKernel(int descriptor, int level, int option, void* value, std::uint32_t* length, int* error) {
{
std::lock_guard lk(g_mutex);
if (g_socks.count(descriptor) == 0) return false;
}
*error = sceNetGetsockopt(descriptor, level, option, value, length) == 0 ? 0 : *errno_slot();
return true;
}

const bool g_socketOptionGetterRegistered = (KernelSetSocketOptionGetter_nid_no_patch(&GetSocketOptionForKernel), true);

constexpr int GuestFcntlGetStatus = 3;
constexpr int GuestFcntlSetStatus = 4;
constexpr int GuestReadWrite = 2;
Expand Down Expand Up @@ -1563,6 +1581,15 @@ int APS5_VABI sceNetGetsockopt(int s, int level, int optname, void* optval, uint
case NET_SO_RCVTIMEO: value = socket.native->receiveTimeout; break;
case NET_SO_SNDTIMEO: value = socket.native->sendTimeout; break;
case NET_SO_ONESBCAST: value = socket.onesbcast; break;
case NET_SO_REUSEADDR:
case NET_SO_BROADCAST: {
int enabled = 0;
NativeLength size = sizeof(enabled);
if (::getsockopt(socket.native->value, SOL_SOCKET, optname == NET_SO_REUSEADDR ? SO_REUSEADDR : SO_BROADCAST, reinterpret_cast<char*>(&enabled), &size) != 0)
return fail(native_error());
value = enabled != 0 ? optname : 0;
break;
}
case NET_SO_ERROR: {
int pending = 0;
NativeLength size = sizeof(pending);
Expand Down Expand Up @@ -1942,9 +1969,9 @@ int APS5_VABI sceNetInetPton(int af, const char* src, void* dst) {
const std::string text = af == NET_AF_INET ? std::string(src, std::strcspn(src, " \t\n\v\f\r")) : std::string(src);
std::array<unsigned char, 16> address{};
#ifdef _WIN32
const int converted = InetPtonA(af == NET_AF_INET ? AF_INET : AF_INET6, text.c_str(), address.data());
const int converted = af == NET_AF_INET6 ? __inet_pton_nid_postfix(af, text.c_str(), address.data()) : InetPtonA(AF_INET, text.c_str(), address.data());
#else
const int converted = ::inet_pton(af == NET_AF_INET ? AF_INET : AF_INET6, text.c_str(), address.data());
const int converted = af == NET_AF_INET6 ? __inet_pton_nid_postfix(af, text.c_str(), address.data()) : ::inet_pton(AF_INET, text.c_str(), address.data());
#endif
if (converted == 1) {
std::memcpy(dst, address.data(), af == NET_AF_INET ? 4 : 16);
Expand All @@ -1963,9 +1990,9 @@ const char* APS5_VABI sceNetInetNtop(int af, const void* src, char* dst, uint32_
}
char text[INET6_ADDRSTRLEN];
#ifdef _WIN32
const auto* result = InetNtopA(af == NET_AF_INET ? AF_INET : AF_INET6, const_cast<void*>(src), text, sizeof(text));
const auto* result = af == NET_AF_INET6 ? __inet_ntop_nid_postfix(af, src, text, sizeof(text)) : InetNtopA(AF_INET, const_cast<void*>(src), text, sizeof(text));
#else
const auto* result = ::inet_ntop(af == NET_AF_INET ? AF_INET : AF_INET6, src, text, sizeof(text));
const auto* result = af == NET_AF_INET6 ? __inet_ntop_nid_postfix(af, src, text, sizeof(text)) : ::inet_ntop(AF_INET, src, text, sizeof(text));
#endif
if (!result) {
set_errno(native_error());
Expand Down Expand Up @@ -2393,8 +2420,7 @@ int APS5_VABI sceNetGetRouteInfo(std::int32_t family, NetRouteInfo* routes, std:
}

int APS5_VABI sceNetGetsockoptEx(int socketId, int level, int option, void* value, std::uint32_t* valueSize) {
NotImplemented_nid_no_patch(__func__);
return 0;
return sceNetGetsockopt(socketId, level, option, value, valueSize);
}

void APS5_VABI sceNetGetSystemTime(std::uint64_t* output) {
Expand Down Expand Up @@ -2460,13 +2486,11 @@ int APS5_VABI sceNetSetDnsInfoToKernel(const NetDnsInfo* info, std::uint32_t fla
}

int APS5_VABI sceNetSetsockoptEx(int socketId, int level, int option, const void* value, std::uint32_t valueSize) {
NotImplemented_nid_no_patch(__func__);
return 0;
return sceNetSetsockopt(socketId, level, option, value, valueSize);
}

int APS5_VABI sceNetSocketInternal(const char* name, int domain, int type, int protocol) {
NotImplemented_nid_no_patch(__func__);
return 0;
return sceNetSocket(name, domain, type, protocol);
}

int APS5_VABI sceNetSyncCreate(NetSyncObject* sync, std::uint32_t signals, const char* name) {
Expand Down Expand Up @@ -2495,8 +2519,8 @@ int APS5_VABI sceNetSyncWait(NetSyncObject* sync, std::uint32_t* timeout) {
}

int APS5_VABI sceNetSysctl(const int* name, std::uint32_t nameLength, void* oldValue, std::size_t* oldSize, const void* newValue, std::size_t newSize) {
NotImplemented_nid_no_patch(__func__);
return 0;
if (sysctl_nid_postfix(name, nameLength, oldValue, oldSize, newValue, newSize) == 0) return 0;
return fail(*__error_nid_postfix());
}

int APS5_VABI sceNetThreadCreate(Pthread* thread, const NetThreadParameter* parameter, const char* name) {
Expand Down Expand Up @@ -2548,13 +2572,45 @@ int APS5_VABI sceNetShowNetstatExForBuffer(char* buffer, std::int32_t bufferSize
}

const char* APS5_VABI sceNetInetNtopWithScopeId(std::int32_t family, const void* address, char* text, std::uint32_t size, std::uint32_t scopeId) {
NotImplemented_nid_no_patch(__func__);
return 0;
if (address == nullptr || text == nullptr) {
set_errno(NET_EINVAL);
return nullptr;
}
if (family != NET_AF_INET6) throw std::runtime_error("sceNetInetNtopWithScopeId: family " + std::to_string(family) + " is not implemented");
std::array<char, 64> unscoped{};
if (sceNetInetNtop(family, address, unscoped.data(), static_cast<std::uint32_t>(unscoped.size())) == nullptr) return nullptr;
std::string scoped(unscoped.data());
if (scopeId != 0) scoped += "%" + std::to_string(scopeId);
if (scoped.size() >= size) {
set_errno(NET_ENOSPC);
return nullptr;
}
std::memcpy(text, scoped.c_str(), scoped.size() + 1);
return text;
}

int APS5_VABI sceNetInetPtonWithScopeId(std::int32_t family, const char* text, void* address, std::uint32_t* scopeId) {
NotImplemented_nid_no_patch(__func__);
return 0;
if (text == nullptr || address == nullptr || scopeId == nullptr) return fail(NET_EINVAL);
if (family != NET_AF_INET6) throw std::runtime_error("sceNetInetPtonWithScopeId: family " + std::to_string(family) + " is not implemented");
const char* percent = std::strchr(text, '%');
std::uint64_t zone = 0;
if (percent != nullptr) {
const std::string_view digits(percent + 1);
if (digits.empty()) return 0;
if (!std::all_of(digits.begin(), digits.end(), [](char digit) { return digit >= '0' && digit <= '9'; }))
throw std::runtime_error("sceNetInetPtonWithScopeId: the interface name zone in " + std::string(text) + " is not implemented");
for (const char digit : digits) {
zone = zone * 10 + static_cast<std::uint64_t>(digit - '0');
if (zone > 0xffffffffull) return 0;
}
}
const std::string unscoped = percent != nullptr ? std::string(text, percent) : std::string(text);
std::array<unsigned char, 16> parsed{};
const int converted = sceNetInetPton(family, unscoped.c_str(), parsed.data());
if (converted != 1) return converted;
std::memcpy(address, parsed.data(), parsed.size());
*scopeId = static_cast<std::uint32_t>(zone);
return 1;
}

int APS5_VABI sceNetGetDns6Info(NetDns6Info* info, std::uint32_t flags) {
Expand Down Expand Up @@ -2713,7 +2769,10 @@ int APS5_VABI sceNetConfigDelRoute(const std::uint32_t* destination, const std::
}

int APS5_VABI sceNetConfigGetDefaultRoute(std::uint32_t* gateway) {
NotImplemented_nid_no_patch(__func__);
if (!gateway) throw std::runtime_error("sceNetConfigGetDefaultRoute: a null gateway is not supported");
std::uint32_t found = 0;
if (!NetCtlHostIpv4Gateway_nid_no_patch(&found)) throw std::runtime_error("sceNetConfigGetDefaultRoute: the answer without an IPv4 default route is not implemented");
*gateway = found;
return 0;
}

Expand Down
7 changes: 7 additions & 0 deletions core/libs/prx/libSceNetCtl/Export.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -378,6 +378,13 @@ extern "C" std::size_t NetCtlHostDnsServers_nid_no_patch(std::uint32_t* servers,
return host.dns.size();
}

extern "C" bool NetCtlHostIpv4Gateway_nid_no_patch(std::uint32_t* gateway) {
HostIpv4Interface host;
if (!FindHostIpv4Interface(host) || host.gateway.s_addr == 0) return false;
*gateway = host.gateway.s_addr;
return true;
}

static void WriteInfoWord(NetCtlInfo* info, std::uint32_t value) {
std::memcpy(info->opaque, &value, sizeof(value));
}
Expand Down
3 changes: 3 additions & 0 deletions core/libs/prx/libkernel/Socket/include/SocketPoll.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ struct Entry {
using Poller = int (*)(Entry* entries, int count, int timeoutMilliseconds);
using Closer = bool (*)(int descriptor, int* error);
using OptionSetter = bool (*)(int descriptor, int level, int option, const void* value, std::uint32_t length, int* error);
using OptionGetter = bool (*)(int descriptor, int level, int option, void* value, std::uint32_t* length, int* error);
using StatusControl = bool (*)(int descriptor, int command, int flags, int* result, int* error);
struct NativeOwner {
int (*transfer)(NativeOwner* owner, bool send, void* buffer, int length) = nullptr;
Expand All @@ -32,6 +33,8 @@ extern "C" void KernelSetSocketCloser_nid_no_patch(KernelSocketPoll::Closer clos
extern "C" bool KernelCloseSocket_nid_no_patch(int descriptor, int* error);
extern "C" void KernelSetSocketOptionSetter_nid_no_patch(KernelSocketPoll::OptionSetter setter);
extern "C" bool KernelSetSocketOption_nid_no_patch(int descriptor, int level, int option, const void* value, std::uint32_t length, int* error);
extern "C" void KernelSetSocketOptionGetter_nid_no_patch(KernelSocketPoll::OptionGetter getter);
extern "C" bool KernelGetSocketOption_nid_no_patch(int descriptor, int level, int option, void* value, std::uint32_t* length, int* error);
extern "C" void KernelSetSocketStatusControl_nid_no_patch(KernelSocketPoll::StatusControl control);
extern "C" bool KernelControlSocketStatus_nid_no_patch(int descriptor, int command, int flags, int* result, int* error);
extern "C" void KernelSetSocketNativeLookup_nid_no_patch(KernelSocketPoll::NativeLookup lookup);
Expand Down
10 changes: 10 additions & 0 deletions core/libs/prx/libkernel/Socket/src/Socket.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ struct GuestTimeval {
std::atomic<KernelSocketPoll::Poller> g_socketPoller{nullptr};
std::atomic<KernelSocketPoll::Closer> g_socketCloser{nullptr};
std::atomic<KernelSocketPoll::OptionSetter> g_socketOptionSetter{nullptr};
std::atomic<KernelSocketPoll::OptionGetter> g_socketOptionGetter{nullptr};
std::atomic<KernelSocketPoll::StatusControl> g_socketStatusControl{nullptr};
std::atomic<KernelSocketPoll::NativeLookup> g_socketNativeLookup{nullptr};

Expand Down Expand Up @@ -161,6 +162,15 @@ void KernelSetSocketOptionSetter_nid_no_patch(KernelSocketPoll::OptionSetter set
g_socketOptionSetter.store(setter);
}

void KernelSetSocketOptionGetter_nid_no_patch(KernelSocketPoll::OptionGetter getter) {
g_socketOptionGetter.store(getter);
}

bool KernelGetSocketOption_nid_no_patch(int descriptor, int level, int option, void* value, std::uint32_t* length, int* error) {
const auto getter = g_socketOptionGetter.load();
return getter != nullptr && getter(descriptor, level, option, value, length, error);
}

bool KernelSetSocketOption_nid_no_patch(int descriptor, int level, int option, const void* value, std::uint32_t length, int* error) {
const auto setter = g_socketOptionSetter.load();
return setter != nullptr && setter(descriptor, level, option, value, length, error);
Expand Down
4 changes: 4 additions & 0 deletions core/libs/prx/libkernel/Socket/src/Udp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1102,6 +1102,10 @@ int APS5_VABI setsockopt_nid_postfix(int descriptor, int level, int option,
}
int APS5_VABI getsockopt_nid_postfix(int descriptor, int level, int option,
void* value, std::uint32_t* length) {
int netError = 0;
if (!GuestSockets::IsOpen(descriptor) && KernelGetSocketOption_nid_no_patch(descriptor, level, option, value, length, &netError)) {
return netError == 0 ? 0 : Fail(netError);
}
const auto socket = Lookup(descriptor);
if (!socket) return -1;
if (level != 0xffff && level != 6 && level != 41) return Fail(42);
Expand Down
Loading
Loading