Files
AnyPS5/core/libs/tests/GuestSceNet.cpp
T
2026-10-21 20:19:56 +13:00

1952 lines
104 KiB
C++

#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
#include <io.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#else
#include <cerrno>
#include <netdb.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <unistd.h>
#endif
#include "prx/libc/include/general/VabiMacros.hpp"
#include "prx/libkernel/Socket/include/SocketSelectDeadline.hpp"
#include "prx/libkernel/Time/include/Time.hpp"
#include "SceTypes.hpp"
#include <algorithm>
#include <array>
#include <chrono>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <future>
#include <initializer_list>
#include <stdexcept>
#include <string>
#include <thread>
#include <vector>
extern "C" {
extern const std::uint8_t in6addr_any_nid_postfix[16];
extern const std::uint8_t in6addr_loopback_nid_postfix[16];
int APS5_VABI sceNetInit_nid_postfix(void);
int APS5_VABI sceNetShowNetstatWithMemory(int);
int APS5_VABI sceNetShowNetstat(void);
int APS5_VABI sceNetShowIfconfig(void);
int APS5_VABI sceNetShowRoute(void);
int APS5_VABI sceNetSocket(const char*, int, int, int);
int APS5_VABI sceNetBind_nid_postfix(int, const void*, std::uint32_t);
int APS5_VABI sceNetListen(int, int);
int APS5_VABI sceNetGetsockname(int, void*, std::uint32_t*);
int APS5_VABI sceNetGetpeername(int, void*, std::uint32_t*);
int APS5_VABI sceNetGetSockInfo(int, void*, int, int);
int APS5_VABI sceNetConnect(int, const void*, std::uint32_t);
int APS5_VABI sceNetAccept(int, void*, std::uint32_t*);
std::int64_t APS5_VABI sceNetSend(int, const void*, std::size_t, int);
std::int64_t APS5_VABI sceNetRecv(int, void*, std::size_t, int);
std::int64_t APS5_VABI sceNetSendto(int, const void*, std::size_t, int, const void*, std::uint32_t);
std::int64_t APS5_VABI sceNetRecvfrom(int, void*, std::size_t, int, void*, std::uint32_t*);
int APS5_VABI sceNetSocketClose(int);
int APS5_VABI sceNetSocketAbort(int, int);
int APS5_VABI sceNetShutdown(int, int);
int APS5_VABI sceNetSetsockopt(int, int, int, const void*, std::uint32_t);
int APS5_VABI sceNetGetsockopt(int, int, int, void*, std::uint32_t*);
int* APS5_VABI sceNetErrnoLoc(void);
int APS5_VABI sceNetEpollCreate(const char*, int);
int APS5_VABI sceNetEpollControl(int, int, int, const NetEpollEvent*);
int APS5_VABI sceNetEpollWait(int, NetEpollEvent*, int, int);
int APS5_VABI sceNetEpollDestroy(int);
int APS5_VABI sceNetEpollAbort(int, int);
int APS5_VABI sceNetGetsockopt(int, int, int, void*, std::uint32_t*);
extern const std::uint32_t sce_net_in6addr_any[4];
extern const std::uint8_t sce_net_in6addr_linklocal_allnodes[16];
int APS5_VABI sceNetResolverCreate(const char*, int, int);
int APS5_VABI sceNetResolverStartNtoa(int, const char*, void*, int, int, int);
int APS5_VABI sceNetResolverStartNtoa6(int, const char*, void*, int, int, int);
int APS5_VABI sceNetResolverStartNtoaMultipleRecordsEx(int, const char*, void*, int, int, int);
int APS5_VABI sceNetResolverStartNtoaMultipleRecords(int, const char*, void*, int, int, int);
int APS5_VABI sceNetResolverStartAton(int, const void*, char*, int, int, int, int);
int APS5_VABI sceNetResolverStartAton6(int, const void*, char*, int, int, int, int);
int APS5_VABI sceNetResolverAbort(int, int);
int APS5_VABI sceNetResolverDestroy(int);
int APS5_VABI sceNetResolverGetError(int, int*);
int APS5_VABI sceNetClearDnsCache(int);
int APS5_VABI sceNetGetDnsInfo(std::uint32_t*, int);
int APS5_VABI sceNetCtlGetState(int*);
int APS5_VABI sceNetCtlGetInfo(int, NetCtlInfo*);
int APS5_VABI sceNetCtlGetIfStat(NetCtlIfStat*);
int APS5_VABI sceNetCtlRegisterCallback(NetCtlCallback, void*, int*);
int APS5_VABI sceNetCtlUnregisterCallback(int);
int APS5_VABI sceNetCtlRegisterCallbackV6(NetCtlCallback, void*, int*);
int APS5_VABI sceNetCtlUnregisterCallbackV6(int);
std::uint64_t NetCtlReadByteCounter_nid_no_patch(const char*);
int APS5_VABI select_nid_postfix(int, void*, void*, void*, const void*);
int APS5_VABI open_nid_postfix(const char*, int, int);
int APS5_VABI socket_nid_postfix(int, int, int);
int APS5_VABI bind_nid_postfix(int, const void*, std::uint32_t);
int APS5_VABI close_nid_postfix(int);
bool GuestSocketIsOpen_nid_no_patch(int);
int APS5_VABI setsockopt_nid_postfix(int, int, int, const void*, std::uint32_t);
int APS5_VABI fcntl_nid_postfix(int, int, ...);
struct GuestPollEntry {
int descriptor;
short events;
short revents;
};
int APS5_VABI poll_nid_postfix(GuestPollEntry*, std::uint32_t, int);
int* APS5_VABI __error_nid_postfix();
int APS5_VABI sceNetInetPton(int, const char*, void*);
int APS5_VABI sceNetInetPtonEx(int, const char*, void*, int);
int APS5_VABI sceNetUsleep(int);
void APS5_VABI sceNetGetSystemTime(std::uint64_t*);
const char* APS5_VABI sceNetInetNtop(int, const void*, char*, std::uint32_t);
int* APS5_VABI sceNetErrnoLoc(void);
}
struct NetIovec {
void* base;
std::uint64_t length;
};
struct NetMsghdr {
void* name;
std::uint32_t name_length;
NetIovec* iov;
int iov_length;
void* control;
std::uint32_t control_length;
int flags;
};
struct NetMemoryPoolStats {
std::size_t pool_size;
std::size_t max_inuse_size;
std::size_t current_inuse_size;
};
extern "C" {
std::int64_t APS5_VABI sceNetSendmsg(int, const NetMsghdr*, int);
std::int64_t APS5_VABI sceNetRecvmsg(int, NetMsghdr*, int);
int APS5_VABI sceNetPoolCreate(const char*, int, int);
int APS5_VABI sceNetPoolDestroy(int);
int APS5_VABI sceNetGetMemoryPoolStats(int, NetMemoryPoolStats*);
const char* APS5_VABI sceNetGetIfName(std::uint32_t);
int APS5_VABI sceNetGetRandom(std::uint32_t*);
void* APS5_VABI sceNetMemoryAllocate(std::int64_t, int);
void APS5_VABI sceNetMemoryFree(void*);
}
static void Require(bool condition) {
if (!condition) std::abort();
}
static void APS5_VABI IgnoreNetCtlEvent(int, void*) {}
static bool Failed(std::int64_t result, int error) {
return result == static_cast<int>(0x80410100u | static_cast<unsigned>(error)) && *sceNetErrnoLoc() == error;
}
static void CheckSocketHostErrors() {
Require(Failed(sceNetSocket(nullptr, 2, 1, 17), 43));
}
static void CheckStreamSender(int family) {
const auto addressSize = family == 2 ? 16u : 28u;
const int listener = sceNetSocket(nullptr, family, 1, 6);
Require(listener >= 0);
std::array<std::uint8_t, 28> destination{};
destination[0] = static_cast<std::uint8_t>(addressSize);
destination[1] = static_cast<std::uint8_t>(family);
if (family == 2) { destination[4] = 127; destination[7] = 1; }
else destination[23] = 1;
Require(sceNetBind_nid_postfix(listener, destination.data(), addressSize) == 0 && sceNetListen(listener, 1) == 0);
std::uint32_t size = destination.size();
Require(sceNetGetsockname(listener, destination.data(), &size) == 0 && size == addressSize);
const int client = sceNetSocket(nullptr, family, 1, 6);
Require(client >= 0 && sceNetConnect(client, destination.data(), addressSize) == 0);
const int accepted = sceNetAccept(listener, nullptr, nullptr);
Require(accepted >= 0);
const char payload[] = "stream";
for (const auto endpoints : {std::array{accepted, client}, std::array{client, accepted}}) {
const auto receiver = endpoints[0], sender = endpoints[1];
for (const std::uint32_t capacity : {0u, 1u, 16u, 28u, UINT32_MAX}) {
Require(sceNetSend(sender, payload, sizeof(payload), 0) == sizeof(payload));
std::array<std::uint8_t, 28> address;
address.fill(0xa5);
char received[sizeof(payload)]{};
size = capacity;
*sceNetErrnoLoc() = 123;
Require(sceNetRecvfrom(receiver, received, 0, 2, address.data(), &size) == 0);
Require(size == 0 && received[0] == 0 && *sceNetErrnoLoc() == 123);
Require(std::all_of(address.begin(), address.end(), [](auto byte) { return byte == 0xa5; }));
for (const int flags : {2, 0}) {
size = capacity;
*sceNetErrnoLoc() = 123;
Require(sceNetRecvfrom(receiver, received, sizeof(received), flags, address.data(), &size) == sizeof(payload));
Require(size == 0 && std::memcmp(received, payload, sizeof(payload)) == 0 && *sceNetErrnoLoc() == 123);
Require(std::all_of(address.begin(), address.end(), [](auto byte) { return byte == 0xa5; }));
}
}
Require(sceNetSend(sender, payload, sizeof(payload), 0) == sizeof(payload));
std::array<std::uint8_t, 28> address;
address.fill(0xa5);
char received[sizeof(payload)]{};
Require(Failed(sceNetRecvfrom(receiver, received, sizeof(received), 0, address.data(), nullptr), 22));
size = 123;
Require(sceNetRecvfrom(receiver, received, sizeof(received), 0, nullptr, &size) == sizeof(payload));
Require(size == 123 && std::memcmp(received, payload, sizeof(payload)) == 0);
Require(sceNetShutdown(sender, 1) == 0);
for (const int flags : {2, 0}) {
size = address.size();
*sceNetErrnoLoc() = 123;
Require(sceNetRecvfrom(receiver, received, sizeof(received), flags, address.data(), &size) == 0);
Require(size == 0 && *sceNetErrnoLoc() == 123);
Require(std::all_of(address.begin(), address.end(), [](auto byte) { return byte == 0xa5; }));
}
}
for (const int descriptor : {client, accepted, listener}) Require(sceNetSocketClose(descriptor) == 0);
}
static std::string CaptureNetstat(int pool) {
auto* file = std::tmpfile();
Require(file != nullptr);
Require(std::fflush(stdout) == 0);
#ifdef _WIN32
const int output = _fileno(stdout);
const int saved = _dup(output);
Require(saved >= 0 && _dup2(_fileno(file), output) == 0);
#else
const int output = fileno(stdout);
const int saved = dup(output);
Require(saved >= 0 && dup2(fileno(file), output) == output);
#endif
const int result = sceNetShowNetstatWithMemory(pool);
Require(std::fflush(stdout) == 0);
#ifdef _WIN32
Require(_dup2(saved, output) == 0 && _close(saved) == 0);
#else
Require(dup2(saved, output) == output && close(saved) == 0);
#endif
Require(result == 0 && std::fseek(file, 0, SEEK_SET) == 0);
std::string text;
std::array<char, 256> bytes{};
for (std::size_t count; (count = std::fread(bytes.data(), 1, bytes.size(), file)) != 0;) text.append(bytes.data(), count);
Require(std::ferror(file) == 0 && std::fclose(file) == 0);
return text;
}
static void CheckNetstat() {
Require(sceNetShowNetstat() == 0);
Require(sceNetShowIfconfig() == 0);
Require(sceNetShowRoute() == 0);
Require(Failed(sceNetShowNetstatWithMemory(-1), 9));
const int pool = sceNetPoolCreate("netstat", 4096, 0);
Require(pool > 0);
const auto empty = CaptureNetstat(pool);
Require(empty.find("bytes=4096 sockets=0") != std::string::npos);
const int socket = sceNetSocket("netstat", 2, 1, 0);
Require(socket >= 0);
const int nonblocking = 1;
Require(sceNetSetsockopt(socket, 0xffff, 0x1200, &nonblocking, sizeof(nonblocking)) == 0);
std::array<std::uint8_t, 16> address{16, 2, 0, 0, 127, 0, 0, 1};
Require(sceNetBind_nid_postfix(socket, address.data(), address.size()) == 0);
Require(sceNetListen(socket, 1) == 0);
std::uint32_t size = address.size();
Require(sceNetGetsockname(socket, address.data(), &size) == 0 && size == address.size());
const auto before = address;
const auto snapshot = CaptureNetstat(pool);
Require(snapshot.find("bytes=4096 sockets=1") != std::string::npos);
Require(snapshot.find("socket=" + std::to_string(socket) + " family=2 type=1 nonblock=1 bound=1 listening=1") != std::string::npos);
Require(sceNetGetsockname(socket, address.data(), &size) == 0 && address == before);
Require(sceNetSocketClose(socket) == 0);
Require(CaptureNetstat(pool).find("sockets=0") != std::string::npos);
Require(sceNetPoolDestroy(pool) == 0);
Require(Failed(sceNetShowNetstatWithMemory(pool), 9));
}
static void CheckPoolStats() {
const int pool = sceNetPoolCreate("stats", 0x4000, 0);
Require(pool > 0);
NetMemoryPoolStats stats{1, 1, 1};
Require(sceNetGetMemoryPoolStats(pool, &stats) == 0);
Require(stats.pool_size == 0x4000 && stats.max_inuse_size == 0 && stats.current_inuse_size == 0);
Require(Failed(sceNetGetMemoryPoolStats(pool, nullptr), 22));
Require(sceNetPoolDestroy(pool) == 0);
Require(Failed(sceNetGetMemoryPoolStats(pool, &stats), 9));
}
static void CheckIfName() {
constexpr std::array<const char*, 10> names = {
"lo0", "eth0", "eth1", "dbg0", "wlan0", "wlan1", "gbe0", "bt0", "phone0", "pppoe0"};
for (std::uint32_t index = 0; index < names.size(); ++index) {
Require(std::strcmp(sceNetGetIfName(index), names[index]) == 0);
}
*sceNetErrnoLoc() = 0;
const char* missing = sceNetGetIfName(10);
Require(missing != nullptr && missing[0] == '\0' && *sceNetErrnoLoc() == 22);
*sceNetErrnoLoc() = 0;
missing = sceNetGetIfName(0xffffffffu);
Require(missing != nullptr && missing[0] == '\0' && *sceNetErrnoLoc() == 22);
}
static void CheckRandom() {
Require(Failed(sceNetGetRandom(nullptr), 22));
std::array<std::uint32_t, 8> values{};
for (auto& value : values) Require(sceNetGetRandom(&value) == 0);
Require(std::any_of(values.begin() + 1, values.end(), [&](std::uint32_t value) { return value != values[0]; }));
}
static void CheckNetMemory() {
Require(sceNetMemoryAllocate(0, 0) == nullptr);
Require(sceNetMemoryAllocate(-1, 2) == nullptr);
auto* zeroed = static_cast<std::uint8_t*>(sceNetMemoryAllocate(4096, 2));
Require(zeroed != nullptr);
Require(std::all_of(zeroed, zeroed + 4096, [](std::uint8_t byte) { return byte == 0; }));
std::memset(zeroed, 0xa5, 4096);
auto* plain = static_cast<std::uint8_t*>(sceNetMemoryAllocate(64, 0));
Require(plain != nullptr && plain != zeroed);
std::memset(plain, 0x5a, 64);
Require(zeroed[4095] == 0xa5);
sceNetMemoryFree(plain);
sceNetMemoryFree(zeroed);
sceNetMemoryFree(nullptr);
bool threw = false;
try {
sceNetMemoryAllocate(16, 1);
} catch (const std::runtime_error&) {
threw = true;
}
Require(threw);
}
static void CheckUnboundSocketName() {
for (const int family : {2, 28}) {
for (const int type : {1, 2}) {
const int socket = sceNetSocket("unbound", family, type, type == 1 ? 6 : 17);
if (socket < 0 && family == 28 && *sceNetErrnoLoc() == 47) continue;
Require(socket >= 0);
const std::uint32_t addressSize = family == 2 ? 16 : 28;
std::array<std::uint8_t, 28> address;
address.fill(0xa5);
std::uint32_t size = address.size();
Require(sceNetGetsockname(socket, address.data(), &size) == 0 && size == addressSize);
Require(address[0] == addressSize && address[1] == family);
Require(std::all_of(address.begin() + 2, address.begin() + addressSize, [](auto byte) { return byte == 0; }));
Require(sceNetSocketClose(socket) == 0);
}
}
}
static void CheckUnboundDatagramReceive() {
const int waiting = sceNetSocket("unbound-receive", 2, 2, 17);
const int peer = sceNetSocket("unbound-peer", 2, 2, 17);
Require(waiting >= 0 && peer >= 0);
const int timeout = 50000;
Require(sceNetSetsockopt(waiting, 0xffff, 0x1106, &timeout, sizeof(timeout)) == 0);
char buffer[16]{};
NetIovec in[] = {{buffer, sizeof(buffer)}};
NetMsghdr receive{nullptr, 0, in, 1, nullptr, 0, 0};
for (int call = 0; call < 3; ++call) {
const auto start = std::chrono::steady_clock::now();
const std::int64_t result = call == 0 ? sceNetRecv(waiting, buffer, sizeof(buffer), 0)
: call == 1 ? sceNetRecvfrom(waiting, buffer, sizeof(buffer), 0, nullptr, nullptr)
: sceNetRecvmsg(waiting, &receive, 0);
Require(Failed(result, 35));
Require(std::chrono::steady_clock::now() - start >= std::chrono::milliseconds(40));
}
const int on = 1;
const int off = 0;
Require(sceNetSetsockopt(waiting, 0xffff, 0x1200, &on, sizeof(on)) == 0);
Require(Failed(sceNetRecvfrom(waiting, buffer, sizeof(buffer), 0, nullptr, nullptr), 35));
Require(sceNetSetsockopt(waiting, 0xffff, 0x1200, &off, sizeof(off)) == 0);
const int longTimeout = 2000000;
Require(sceNetSetsockopt(waiting, 0xffff, 0x1106, &longTimeout, sizeof(longTimeout)) == 0);
std::array<std::uint8_t, 16> address{16, 2, 0, 0, 127, 0, 0, 1};
Require(sceNetBind_nid_postfix(peer, address.data(), address.size()) == 0);
std::uint32_t size = address.size();
Require(sceNetGetsockname(peer, address.data(), &size) == 0);
auto reply = std::async(std::launch::async, [waiting] {
char answer[16]{};
return sceNetRecvfrom(waiting, answer, sizeof(answer), 0, nullptr, nullptr) == 5 && std::strcmp(answer, "pong") == 0;
});
std::this_thread::sleep_for(std::chrono::milliseconds(20));
Require(reply.wait_for(std::chrono::seconds(0)) == std::future_status::timeout);
Require(sceNetSendto(waiting, "ping", 5, 0, address.data(), address.size()) == 5);
std::array<std::uint8_t, 16> source{};
size = source.size();
Require(sceNetRecvfrom(peer, buffer, sizeof(buffer), 0, source.data(), &size) == 5 && std::strcmp(buffer, "ping") == 0);
Require(sceNetSendto(peer, "pong", 5, 0, source.data(), size) == 5);
Require(reply.wait_for(std::chrono::seconds(1)) == std::future_status::ready && reply.get());
Require(Failed(sceNetBind_nid_postfix(waiting, address.data(), address.size()), 22));
for (const int descriptor : {waiting, peer}) Require(sceNetSocketClose(descriptor) == 0);
#ifdef _WIN32
for (const bool abort : {true, false}) {
const int socket = sceNetSocket("unbound-abort", 2, 2, 17);
Require(socket >= 0);
auto waiter = std::async(std::launch::async, [socket] {
char ignored[4];
return sceNetRecvfrom(socket, ignored, sizeof(ignored), 0, nullptr, nullptr);
});
std::this_thread::sleep_for(std::chrono::milliseconds(20));
Require(waiter.wait_for(std::chrono::seconds(0)) == std::future_status::timeout);
Require((abort ? sceNetSocketAbort(socket, 0) : sceNetSocketClose(socket)) == 0);
Require(waiter.wait_for(std::chrono::seconds(1)) == std::future_status::ready && waiter.get() < 0);
if (abort) Require(sceNetSocketClose(socket) == 0);
}
#endif
}
static void CheckUnboundDatagramShutdown() {
const int socket = sceNetSocket("unbound-shutdown", 2, 2, 17);
Require(socket >= 0);
auto waiter = std::async(std::launch::async, [socket] {
char ignored[4];
return sceNetRecvfrom(socket, ignored, sizeof(ignored), 0, nullptr, nullptr);
});
std::this_thread::sleep_for(std::chrono::milliseconds(20));
Require(waiter.wait_for(std::chrono::seconds(0)) == std::future_status::timeout);
sceNetShutdown(socket, 0);
Require(waiter.wait_for(std::chrono::seconds(1)) == std::future_status::ready && waiter.get() == 0);
char buffer[4]{};
Require(sceNetRecv(socket, buffer, sizeof(buffer), 0) == 0);
std::array<std::uint8_t, 16> source{};
NetIovec in[] = {{buffer, sizeof(buffer)}};
NetMsghdr receive{source.data(), static_cast<std::uint32_t>(source.size()), in, 1, nullptr, 0, 0};
Require(sceNetRecvmsg(socket, &receive, 0) == 0 && receive.name_length == 0);
#ifdef _WIN32
std::uint32_t size = source.size();
Require(sceNetRecvfrom(socket, buffer, sizeof(buffer), 0, source.data(), &size) == 0 && size == 0);
#endif
Require(sceNetSocketClose(socket) == 0);
#ifdef _WIN32
const int empty = sceNetSocket("unbound-empty", 2, 2, 17);
Require(empty >= 0);
Require(sceNetRecv(empty, buffer, 0, 0) == 0);
Require(sceNetRecvfrom(empty, buffer, 0, 0, nullptr, nullptr) == 0);
Require(sceNetSocketClose(empty) == 0);
#endif
}
static void CheckUnboundDatagramLaterAddress() {
for (const bool connect : {false, true}) {
const int waiting = sceNetSocket("unbound-later", 2, 2, 17);
const int peer = sceNetSocket("unbound-later-peer", 2, 2, 17);
Require(waiting >= 0 && peer >= 0);
std::array<std::uint8_t, 16> peerAddress{16, 2, 0, 0, 127, 0, 0, 1};
Require(sceNetBind_nid_postfix(peer, peerAddress.data(), peerAddress.size()) == 0);
std::uint32_t size = peerAddress.size();
Require(sceNetGetsockname(peer, peerAddress.data(), &size) == 0);
auto reply = std::async(std::launch::async, [waiting] {
char answer[16]{};
return sceNetRecv(waiting, answer, sizeof(answer), 0) == 5 && std::strcmp(answer, "pong") == 0;
});
std::this_thread::sleep_for(std::chrono::milliseconds(20));
Require(reply.wait_for(std::chrono::seconds(0)) == std::future_status::timeout);
std::array<std::uint8_t, 16> waitingAddress{16, 2, 0, 0, 127, 0, 0, 1};
Require((connect ? sceNetConnect(waiting, peerAddress.data(), peerAddress.size())
: sceNetBind_nid_postfix(waiting, waitingAddress.data(), waitingAddress.size())) == 0);
size = waitingAddress.size();
Require(sceNetGetsockname(waiting, waitingAddress.data(), &size) == 0);
Require(sceNetSendto(peer, "pong", 5, 0, waitingAddress.data(), size) == 5);
Require(reply.wait_for(std::chrono::seconds(1)) == std::future_status::ready && reply.get());
for (const int descriptor : {waiting, peer}) Require(sceNetSocketClose(descriptor) == 0);
}
}
static void CheckUnboundDatagramTimeoutAfterAddress() {
const int waiting = sceNetSocket("unbound-timeout", 2, 2, 17);
Require(waiting >= 0);
const int timeout = 500000;
Require(sceNetSetsockopt(waiting, 0xffff, 0x1106, &timeout, sizeof(timeout)) == 0);
auto bound = std::async(std::launch::async, [waiting] {
std::this_thread::sleep_for(std::chrono::milliseconds(300));
std::array<std::uint8_t, 16> address{16, 2, 0, 0, 127, 0, 0, 1};
return sceNetBind_nid_postfix(waiting, address.data(), address.size()) == 0;
});
char buffer[4];
const auto start = std::chrono::steady_clock::now();
Require(Failed(sceNetRecv(waiting, buffer, sizeof(buffer), 0), 35));
const auto elapsed = std::chrono::steady_clock::now() - start;
Require(bound.get());
Require(elapsed >= std::chrono::milliseconds(450) && elapsed < std::chrono::milliseconds(750));
Require(sceNetSocketClose(waiting) == 0);
}
static void CheckEmptyEpoll() {
const int epoll = sceNetEpollCreate("empty-epoll", 0);
Require(epoll >= 0);
NetEpollEvent event{};
Require(sceNetEpollWait(epoll, &event, 1, 0) == 0);
std::array<std::chrono::steady_clock::duration, 9> elapsed{};
for (auto& sample : elapsed) {
const auto start = std::chrono::steady_clock::now();
Require(sceNetEpollWait(epoll, &event, 1, 1000) == 0);
sample = std::chrono::steady_clock::now() - start;
Require(sample >= std::chrono::microseconds(1000));
}
#ifdef _WIN32
if (std::getenv("APS5_NO_TIMER_RESOLUTION") == nullptr) {
std::sort(elapsed.begin(), elapsed.end());
Require(elapsed[elapsed.size() / 2] < std::chrono::milliseconds(8));
}
#endif
Require(sceNetEpollAbort(epoll, 0) == 0);
Require(Failed(sceNetEpollWait(epoll, &event, 1, 1000), 53));
Require(sceNetEpollWait(epoll, &event, 1, 0) == 0);
Require(sceNetEpollDestroy(epoll) == 0);
for (const bool abort : {true, false}) {
const int waiting_epoll = sceNetEpollCreate("waiting-epoll", 0);
Require(waiting_epoll >= 0);
auto waiter = std::async(std::launch::async, [waiting_epoll, abort] {
NetEpollEvent ready{};
Require(Failed(sceNetEpollWait(waiting_epoll, &ready, 1, -1), abort ? 53 : 9));
});
std::this_thread::sleep_for(std::chrono::milliseconds(20));
Require((abort ? sceNetEpollAbort(waiting_epoll, 0) : sceNetEpollDestroy(waiting_epoll)) == 0);
Require(waiter.wait_for(std::chrono::seconds(1)) == std::future_status::ready);
waiter.get();
if (abort) Require(sceNetEpollDestroy(waiting_epoll) == 0);
}
}
static void CheckDontWait() {
constexpr int DontWait = 0x80;
constexpr int Peek = 0x2;
const int receiver = sceNetSocket("dontwait-receiver", 2, 2, 17);
const int sender = sceNetSocket("dontwait-sender", 2, 2, 17);
Require(receiver >= 0 && sender >= 0);
std::array<std::uint8_t, 16> address{16, 2, 0, 0, 127, 0, 0, 1};
Require(sceNetBind_nid_postfix(receiver, address.data(), address.size()) == 0);
std::uint32_t size = address.size();
Require(sceNetGetsockname(receiver, address.data(), &size) == 0);
const int timeout = 2000000;
Require(sceNetSetsockopt(receiver, 0xffff, 0x1106, &timeout, sizeof(timeout)) == 0);
char buffer[16]{};
NetIovec in[] = {{buffer, sizeof(buffer)}};
NetMsghdr receive{nullptr, 0, in, 1, nullptr, 0, 0};
const auto start = std::chrono::steady_clock::now();
const int unbound = sceNetSocket("dontwait-unbound", 2, 2, 17);
Require(unbound >= 0);
const auto unboundStart = std::chrono::steady_clock::now();
Require(Failed(sceNetRecv(unbound, buffer, sizeof(buffer), DontWait), 35));
Require(Failed(sceNetRecvfrom(unbound, buffer, sizeof(buffer), DontWait, nullptr, nullptr), 35));
Require(Failed(sceNetRecvmsg(unbound, &receive, DontWait), 35));
Require(std::chrono::steady_clock::now() - unboundStart < std::chrono::seconds(1));
Require(sceNetSocketClose(unbound) == 0);
Require(Failed(sceNetRecv(receiver, buffer, sizeof(buffer), DontWait), 35));
Require(Failed(sceNetRecvfrom(receiver, buffer, sizeof(buffer), DontWait, nullptr, nullptr), 35));
Require(Failed(sceNetRecvmsg(receiver, &receive, DontWait), 35));
Require(Failed(sceNetRecv(receiver, buffer, sizeof(buffer), DontWait | Peek), 35));
Require(std::chrono::steady_clock::now() - start < std::chrono::seconds(1));
const char datagram[] = "nowait";
Require(sceNetSendto(sender, datagram, sizeof(datagram), DontWait, address.data(), address.size()) == sizeof(datagram));
Require(sceNetRecvfrom(receiver, buffer, sizeof(buffer), Peek, nullptr, nullptr) == sizeof(datagram));
std::memset(buffer, 0, sizeof(buffer));
Require(sceNetRecv(receiver, buffer, sizeof(buffer), DontWait | Peek) == sizeof(datagram) && std::strcmp(buffer, datagram) == 0);
std::memset(buffer, 0, sizeof(buffer));
Require(sceNetRecv(receiver, buffer, sizeof(buffer), DontWait) == sizeof(datagram) && std::strcmp(buffer, datagram) == 0);
Require(sceNetConnect(sender, address.data(), address.size()) == 0);
Require(sceNetSend(sender, datagram, sizeof(datagram), DontWait) == sizeof(datagram));
Require(sceNetRecvmsg(receiver, &receive, 0) == sizeof(datagram) && std::strcmp(buffer, datagram) == 0);
Require(Failed(sceNetRecv(receiver, buffer, sizeof(buffer), DontWait), 35));
Require(Failed(sceNetRecv(receiver, buffer, sizeof(buffer), 0x1), 45));
Require(Failed(sceNetSend(sender, "x", 1, Peek), 45));
Require(sceNetSocketClose(receiver) == 0 && sceNetSocketClose(sender) == 0);
}
static void CheckReceiveTimeout(int receiver, int sender, const std::array<std::uint8_t, 16>& address) {
const KernelTimeval timeout{0, 50000};
Require(sceNetSetsockopt(receiver, 0xffff, 0x1006, &timeout, sizeof(timeout)) == 0);
int microseconds = -1;
std::uint32_t length = sizeof(microseconds);
Require(sceNetGetsockopt(receiver, 0xffff, 0x1106, &microseconds, &length) == 0 && microseconds == 50000);
char buffer[8]{};
std::array<std::uint8_t, 16> source{};
std::uint32_t size = source.size();
NetIovec in[] = {{buffer, sizeof(buffer)}};
NetMsghdr receive{source.data(), static_cast<std::uint32_t>(source.size()), in, 1, nullptr, 0, 0};
Require(Failed(sceNetRecv(receiver, buffer, sizeof(buffer), 0), 35));
Require(Failed(sceNetRecvfrom(receiver, buffer, sizeof(buffer), 0, source.data(), &size), 35));
Require(Failed(sceNetRecvmsg(receiver, &receive, 0), 35));
const char datagram[] = "late";
Require(sceNetSendto(sender, datagram, sizeof(datagram), 0, address.data(), address.size()) == sizeof(datagram));
Require(sceNetRecv(receiver, buffer, sizeof(buffer), 0) == sizeof(datagram) && std::strcmp(buffer, datagram) == 0);
const KernelTimeval disabled{0, 0};
Require(sceNetSetsockopt(receiver, 0xffff, 0x1006, &disabled, sizeof(disabled)) == 0);
}
static void CheckMicrosecondTimeouts(int receiver) {
const int receiveTimeout = 50000;
const int sendTimeout = 70000;
Require(sceNetSetsockopt(receiver, 0xffff, 0x1106, &receiveTimeout, sizeof(receiveTimeout)) == 0);
Require(sceNetSetsockopt(receiver, 0xffff, 0x1105, &sendTimeout, sizeof(sendTimeout)) == 0);
for (const auto [option, expected] : {std::array{0x1106, receiveTimeout}, std::array{0x1105, sendTimeout}}) {
int value = -1;
std::uint32_t size = sizeof(value);
Require(sceNetGetsockopt(receiver, 0xffff, option, &value, &size) == 0 && size == sizeof(value) && value == expected);
}
char buffer[8]{};
Require(Failed(sceNetRecv(receiver, buffer, sizeof(buffer), 0), 35));
const int disabled = 0;
Require(sceNetSetsockopt(receiver, 0xffff, 0x1106, &disabled, sizeof(disabled)) == 0);
Require(sceNetSetsockopt(receiver, 0xffff, 0x1105, &disabled, sizeof(disabled)) == 0);
}
static void CheckMessages(int receiver, int sender, const std::array<std::uint8_t, 16>& address) {
char head[] = "scatter ";
char tail[] = "gather";
NetIovec out[] = {{head, 8}, {nullptr, 0}, {tail, 6}};
std::array<std::uint8_t, 16> destination = address;
NetMsghdr send{destination.data(), 16, out, 3, nullptr, 0, 0};
Require(sceNetSendmsg(sender, &send, 0) == 14);
char first[5]{};
char second[20]{};
NetIovec in[] = {{first, sizeof(first)}, {second, sizeof(second)}};
std::array<std::uint8_t, 28> source{};
char control[16]{};
NetMsghdr receive{source.data(), static_cast<std::uint32_t>(source.size()), in, 2, control, sizeof(control), -1};
Require(sceNetRecvmsg(receiver, &receive, 2) == 14);
Require(std::memcmp(first, "scatt", 5) == 0 && std::memcmp(second, "er gather", 9) == 0);
Require(receive.flags == 0 && receive.control_length == 0 && receive.name_length == 16);
Require(source[0] == 16 && source[1] == 2 && source[4] == 127 && source[7] == 1);
std::memset(second, 0, sizeof(second));
receive.iov_length = 1;
receive.flags = -1;
Require(sceNetRecvmsg(receiver, &receive, 0) == 5);
Require(receive.flags == 0x10 && std::memcmp(first, "scatt", 5) == 0 && second[0] == 0);
NetMsghdr bad = send;
Require(Failed(sceNetSendmsg(sender, nullptr, 0), 14));
Require(Failed(sceNetRecvmsg(receiver, nullptr, 0), 14));
bad.iov_length = -1;
Require(Failed(sceNetSendmsg(sender, &bad, 0), 40));
bad.iov_length = 1025;
Require(Failed(sceNetRecvmsg(receiver, &bad, 0), 40));
bad.iov_length = 1;
bad.iov = nullptr;
Require(Failed(sceNetSendmsg(sender, &bad, 0), 14));
NetIovec hole[] = {{nullptr, 1}};
bad.iov = hole;
Require(Failed(sceNetRecvmsg(receiver, &bad, 0), 14));
NetIovec huge[] = {{head, 0x7fffffff}, {tail, 1}};
bad.iov = huge;
bad.iov_length = 2;
Require(Failed(sceNetSendmsg(sender, &bad, 0), 22));
Require(Failed(sceNetRecvmsg(receiver, &receive, 1), 45));
Require(Failed(sceNetSendmsg(sender, &send, 1), 45));
Require(Failed(sceNetRecvmsg(12345, &receive, 0), 9));
Require(Failed(sceNetSendmsg(12345, &send, 0), 9));
NetMsghdr with_control = send;
with_control.control = control;
with_control.control_length = sizeof(control);
bool threw = false;
try {
sceNetSendmsg(sender, &with_control, 0);
} catch (const std::runtime_error&) {
threw = true;
}
Require(threw);
}
static void CheckEpollWritable() {
std::array<std::uint8_t, 16> address{16, 2, 0, 0, 127, 0, 0, 1};
const int receiver = sceNetSocket("epoll-receiver", 2, 2, 17);
const int sender = sceNetSocket("epoll-sender", 2, 2, 17);
Require(receiver >= 0 && sender >= 0);
Require(sceNetBind_nid_postfix(receiver, address.data(), address.size()) == 0);
std::uint32_t size = address.size();
Require(sceNetGetsockname(receiver, address.data(), &size) == 0 && size == address.size());
const int epoll = sceNetEpollCreate("epoll-writable", 0);
Require(epoll >= 0);
NetEpollEvent registration{};
registration.events = 2;
registration.ident = static_cast<std::uint64_t>(sender);
registration.data.u32 = 41;
Require(sceNetEpollControl(epoll, 1, sender, &registration) == 0);
NetEpollEvent ready{};
Require(sceNetEpollWait(epoll, &ready, 1, 1000000) == 1);
Require(ready.events == 2 && ready.ident == static_cast<std::uint64_t>(sender) && ready.data.u32 == 41);
Require(sceNetEpollControl(epoll, 3, sender, nullptr) == 0);
const char payload[] = "epoll";
char peeked[sizeof(payload)]{};
Require(sceNetSendto(sender, payload, sizeof(payload), 0, address.data(), address.size()) == sizeof(payload));
Require(sceNetRecv(receiver, peeked, sizeof(peeked), 2) == sizeof(payload));
registration.events = 3;
registration.ident = static_cast<std::uint64_t>(receiver);
Require(sceNetEpollControl(epoll, 1, receiver, &registration) == 0);
Require(sceNetEpollWait(epoll, &ready, 1, 1000000) == 1);
Require(ready.events == 3 && ready.ident == static_cast<std::uint64_t>(receiver) && ready.data.u32 == 41);
Require(sceNetEpollDestroy(epoll) == 0);
Require(sceNetSocketClose(sender) == 0 && sceNetSocketClose(receiver) == 0);
}
static void RequireTimeout(int socket, int option, int expected) {
KernelTimeval timeout{-1, -1};
std::uint32_t size = sizeof(timeout);
Require(sceNetGetsockopt(socket, 0xffff, option, &timeout, &size) == 0 && size == sizeof(timeout));
int microseconds = -1;
std::uint32_t length = sizeof(microseconds);
Require(sceNetGetsockopt(socket, 0xffff, option == 0x1006 ? 0x1106 : 0x1105, &microseconds, &length) == 0 && length == sizeof(microseconds));
if (timeout.tv_sec * 1000000 + timeout.tv_usec != expected || microseconds != expected) {
std::fprintf(stderr, "option 0x%x on a libSceNet socket reads back {%lld, %lld} and %d microseconds, expected %d\n",
option, static_cast<long long>(timeout.tv_sec), static_cast<long long>(timeout.tv_usec), microseconds, expected);
std::abort();
}
}
static void CheckKernelTimeouts() {
const int descriptor = sceNetSocket("kernel-timeouts", 2, 2, 17);
Require(descriptor >= 0);
const KernelTimeval receive{1, 250000};
Require(setsockopt_nid_postfix(descriptor, 0xffff, 0x1006, &receive, sizeof(receive)) == 0);
RequireTimeout(descriptor, 0x1006, 1250000);
const KernelTimeval send{0, 500000};
Require(setsockopt_nid_postfix(descriptor, 0xffff, 0x1005, &send, sizeof(send)) == 0);
RequireTimeout(descriptor, 0x1005, 500000);
const KernelTimeval invalid{0, 1000000};
Require(setsockopt_nid_postfix(descriptor, 0xffff, 0x1006, &invalid, sizeof(invalid)) == -1 && *__error_nid_postfix() == 33);
const KernelTimeval negative{-1, 0};
Require(setsockopt_nid_postfix(descriptor, 0xffff, 0x1005, &negative, sizeof(negative)) == -1 && *__error_nid_postfix() == 33);
const int microseconds = 1000;
Require(setsockopt_nid_postfix(descriptor, 0xffff, 0x1006, &microseconds, sizeof(microseconds)) == -1 && *__error_nid_postfix() == 22);
RequireTimeout(descriptor, 0x1006, 1250000);
RequireTimeout(descriptor, 0x1005, 500000);
Require(Failed(sceNetSetsockopt(descriptor, 0xffff, 0x1006, &microseconds, sizeof(microseconds)), 22));
Require(Failed(sceNetSetsockopt(descriptor, 0xffff, 0x1006, &invalid, sizeof(invalid)), 33));
Require(Failed(sceNetSetsockopt(descriptor, 0xffff, 0x1005, &negative, sizeof(negative)), 33));
RequireTimeout(descriptor, 0x1006, 1250000);
RequireTimeout(descriptor, 0x1005, 500000);
int seconds = -1;
std::uint32_t truncated = sizeof(seconds);
Require(sceNetGetsockopt(descriptor, 0xffff, 0x1006, &seconds, &truncated) == 0 && truncated == sizeof(seconds) && seconds == 1);
const KernelTimeval quarter{0, 250000};
Require(sceNetSetsockopt(descriptor, 0xffff, 0x1006, &quarter, sizeof(quarter)) == 0);
RequireTimeout(descriptor, 0x1006, 250000);
Require(sceNetSetsockopt(descriptor, 0xffff, 0x1106, &microseconds, sizeof(microseconds)) == 0);
RequireTimeout(descriptor, 0x1006, microseconds);
#ifndef _WIN32
std::array<std::uint8_t, 16> loopback{16, 2, 0, 0, 127, 0, 0, 1};
Require(sceNetBind_nid_postfix(descriptor, loopback.data(), loopback.size()) == 0);
const KernelTimeval brief{0, 50000};
Require(setsockopt_nid_postfix(descriptor, 0xffff, 0x1006, &brief, sizeof(brief)) == 0);
char buffer[8]{};
const auto start = std::chrono::steady_clock::now();
Require(Failed(sceNetRecv(descriptor, buffer, sizeof(buffer), 0), 35));
Require(std::chrono::steady_clock::now() - start >= std::chrono::milliseconds(40));
#endif
Require(sceNetSocketClose(descriptor) == 0);
}
static void CheckKernelStatusFlags() {
const int descriptor = sceNetSocket("kernel-status", 2, 1, 6);
Require(descriptor >= 0);
Require(fcntl_nid_postfix(descriptor, 3) == 2);
Require(fcntl_nid_postfix(descriptor, 4, 2 | 4) == 0);
Require(fcntl_nid_postfix(descriptor, 3) == (2 | 4));
int enabled = 0;
std::uint32_t length = sizeof(enabled);
Require(sceNetGetsockopt(descriptor, 0xffff, 0x1200, &enabled, &length) == 0 && enabled == 1);
Require(fcntl_nid_postfix(descriptor, 4, 2) == 0);
Require(fcntl_nid_postfix(descriptor, 3) == 2);
length = sizeof(enabled);
Require(sceNetGetsockopt(descriptor, 0xffff, 0x1200, &enabled, &length) == 0 && enabled == 0);
*__error_nid_postfix() = 0;
Require(fcntl_nid_postfix(descriptor, 4, 0x100) == -1 && *__error_nid_postfix() == 45);
*__error_nid_postfix() = 0;
Require(fcntl_nid_postfix(descriptor, 1) == -1 && *__error_nid_postfix() == 22);
Require(close_nid_postfix(descriptor) == 0);
}
static void CheckAddressText(int family, const char* text) {
std::array<std::uint8_t, 16> address{};
Require(sceNetInetPton(family, text, address.data()) == 1);
std::array<char, 64> output{};
const auto length = static_cast<std::uint32_t>(std::strlen(text));
for (std::uint32_t size = 0; size <= length; ++size) {
*sceNetErrnoLoc() = 123;
Require(sceNetInetNtop(family, address.data(), output.data(), size) == nullptr);
Require(*sceNetErrnoLoc() == 28);
}
output.fill('x');
*sceNetErrnoLoc() = 123;
Require(sceNetInetNtop(family, address.data(), output.data(), length + 1) == output.data());
Require(std::strcmp(output.data(), text) == 0 && output[length + 1] == 'x');
Require(*sceNetErrnoLoc() == 123);
Require(sceNetInetNtop(99, address.data(), output.data(), output.size()) == nullptr && *sceNetErrnoLoc() == 47);
Require(sceNetInetNtop(family, nullptr, output.data(), output.size()) == nullptr && *sceNetErrnoLoc() == 22);
Require(sceNetInetNtop(family, address.data(), nullptr, output.size()) == nullptr && *sceNetErrnoLoc() == 22);
}
static void CheckInetPtonRejects(int family, const char* text) {
std::array<std::uint8_t, 16> address{};
address.fill(0xaa);
*sceNetErrnoLoc() = 123;
Require(sceNetInetPton(family, text, address.data()) == 0 && *sceNetErrnoLoc() == 123);
Require(std::all_of(address.begin(), address.end(), [](std::uint8_t byte) { return byte == 0xaa; }));
}
static void CheckInetPtonWhitespace() {
for (const char* text : {"1.2.3.4 ", "1.2.3.4\t", "1.2.3.4\n", "1.2.3.4\v", "1.2.3.4\f", "1.2.3.4\r", "1.2.3.4 5", "1.2.3.4 x"}) {
std::array<std::uint8_t, 16> address{};
address.fill(0xaa);
*sceNetErrnoLoc() = 123;
Require(sceNetInetPton(2, text, address.data()) == 1 && *sceNetErrnoLoc() == 123);
Require(address[0] == 1 && address[1] == 2 && address[2] == 3 && address[3] == 4 && address[4] == 0xaa);
}
for (const char* text : {" 1.2.3.4", "1.2.3.4x", "1.2.3.4.", "1.2.3.4\x01", "1.2.3.4\xa0", "1.2. 3.4", "1.2.3", "1.2.3 ", "01.2.3.4", "256.0.0.1"}) {
CheckInetPtonRejects(2, text);
}
for (const char* text : {"::1 ", "::1\t", " ::1", "::1x"}) {
CheckInetPtonRejects(28, text);
}
}
struct AtonCase {
const char* text;
std::uint32_t address;
};
static void CheckInetPtonExParts() {
for (const AtonCase& c : {AtonCase{"0", 0}, AtonCase{"1", 1}, AtonCase{"4294967295", 0xffffffff}, AtonCase{"4294967296", 0},
AtonCase{"0xffffffff", 0xffffffff}, AtonCase{"0x100000000", 0}, AtonCase{"1.16777215", 0x01ffffff},
AtonCase{"1.2.65535", 0x0102ffff}, AtonCase{"1.2.3.255", 0x010203ff}, AtonCase{"0x7F.1", 0x7f000001},
AtonCase{"0X7f.1", 0x7f000001}, AtonCase{"0x", 0}, AtonCase{"0x.1.2.3", 0x00010203}, AtonCase{"0x8.1", 0x08000001}, AtonCase{"0x9", 9}, AtonCase{"1.2.3", 0x01020003},
AtonCase{"127.1", 0x7f000001}, AtonCase{"010.0.0.1", 0x08000001}, AtonCase{"0377.1", 0xff000001},
AtonCase{"00000000001.2.3.4", 0x01020304}, AtonCase{"1.2.3.4 x", 0x01020304}, AtonCase{"1.2.3.4\tx", 0x01020304},
AtonCase{"1.2.3.4\v", 0x01020304}, AtonCase{"1 .2.3.4", 1}, AtonCase{"0.8", 8}, AtonCase{"00.1", 1},
AtonCase{"09", 9}, AtonCase{"09.1", 0x09000001}, AtonCase{"1.09", 0x01000009}, AtonCase{"0.09", 9},
AtonCase{"09.09", 0x09000009}, AtonCase{"0919", 919}, AtonCase{"0989", 989}, AtonCase{"0998", 998},
AtonCase{"1.4294967296", 0x01000000}, AtonCase{"4294967297.1", 0x01000001}, AtonCase{"1.2.3.4294967297", 0x01020301},
AtonCase{"18446744073709551617", 1}, AtonCase{"0x1ffffffff", 0xffffffff}, AtonCase{"0x100000001.1", 0x01000001},
AtonCase{"1.0xffffff", 0x01ffffff}, AtonCase{"1.2.0xffff", 0x0102ffff}}) {
std::array<std::uint8_t, 16> address{};
address.fill(0xaa);
*sceNetErrnoLoc() = 123;
Require(sceNetInetPtonEx(2, c.text, address.data(), 0) == 1 && *sceNetErrnoLoc() == 123);
Require(address[0] == (c.address >> 24) && address[1] == ((c.address >> 16) & 0xff) && address[2] == ((c.address >> 8) & 0xff) &&
address[3] == (c.address & 0xff) && address[4] == 0xaa);
}
for (const char* text : {"", "1.16777216", "1.2.65536", "1.2.3.256", "256.1", "0400.1", "0xg.1", "08", "018", "019", "0199", "00919",
"080", "08.1.2.3", "1.08", "0.08", "0919.1", "09x", "1.0x1000000", "1.2.0x10000", "1.2.3.4.5", "1..2.3",
".1.2.3", "1.2.3.", "1.2.3.4x", " 1.2.3.4", "1.2.3.4\x01", "1.2.3.4\xa0", "+1.2.3.4", "-1.2.3.4"}) {
std::array<std::uint8_t, 16> address{};
address.fill(0xaa);
*sceNetErrnoLoc() = 123;
Require(sceNetInetPtonEx(2, text, address.data(), 0) == 0 && *sceNetErrnoLoc() == 123);
Require(std::all_of(address.begin(), address.end(), [](std::uint8_t byte) { return byte == 0xaa; }));
}
for (const char* text : {"::1 ", "::1x", " ::1", "1.2.3.4", "::1.2.3", "1:2:3:4:5:6:7:8:9"}) {
std::array<std::uint8_t, 16> address{};
address.fill(0xaa);
Require(sceNetInetPtonEx(28, text, address.data(), 0) == 0);
Require(std::all_of(address.begin(), address.end(), [](std::uint8_t byte) { return byte == 0xaa; }));
}
}
static void CheckInetPtonEx() {
std::array<std::uint8_t, 16> address{};
Require(sceNetInetPtonEx(2, "192.168.1.20", address.data(), 0) == 1);
Require(address[0] == 192 && address[1] == 168 && address[2] == 1 && address[3] == 20);
std::array<std::uint8_t, 16> expected{};
Require(sceNetInetPton(28, "fe80::1:2", expected.data()) == 1);
address.fill(0);
Require(sceNetInetPtonEx(28, "fe80::1:2", address.data(), 0) == 1 && address == expected);
Require(sceNetInetPtonEx(2, "not an address", address.data(), 0) == 0);
Require(Failed(sceNetInetPtonEx(99, "127.0.0.1", address.data(), 0), 47));
Require(Failed(sceNetInetPtonEx(2, nullptr, address.data(), 0), 22));
Require(Failed(sceNetInetPtonEx(2, "127.0.0.1", nullptr, 0), 22));
for (const int flags : {1, 0x100, -1}) {
address.fill(0);
Require(sceNetInetPtonEx(2, "1.2.3", address.data(), flags) == 1);
Require(address[0] == 1 && address[1] == 2 && address[2] == 0 && address[3] == 3);
}
}
static void CheckSystemTime() {
*sceNetErrnoLoc() = 22;
sceNetGetSystemTime(nullptr);
Require(*sceNetErrnoLoc() == 22);
std::array<std::uint64_t, 3> output{0x123456789abcdef0ULL, UINT64_MAX, 0xfedcba9876543210ULL};
std::uint64_t previous = 0;
for (int i = 0; i < 64; ++i) {
output[1] = UINT64_MAX;
const auto before = sceKernelGetProcessTime();
sceNetGetSystemTime(&output[1]);
const auto after = sceKernelGetProcessTime();
Require(before <= output[1] && output[1] <= after && previous <= output[1]);
Require(output[0] == 0x123456789abcdef0ULL && output[2] == 0xfedcba9876543210ULL);
Require(*sceNetErrnoLoc() == 22);
previous = output[1];
}
std::this_thread::sleep_for(std::chrono::milliseconds(2));
sceNetGetSystemTime(&output[1]);
Require(output[1] > previous);
}
static void CheckUsleep() {
Require(sceNetUsleep(0) == 0);
const auto start = std::chrono::steady_clock::now();
Require(sceNetUsleep(2000) == 0);
Require(std::chrono::steady_clock::now() - start >= std::chrono::microseconds(2000));
*sceNetErrnoLoc() = 0;
Require(Failed(sceNetUsleep(-1), 22));
}
static void CheckEpollIdent() {
std::array<std::uint8_t, 16> address{16, 2, 0, 0, 127, 0, 0, 1};
const int receiver = sceNetSocket("epoll-ident", 2, 2, 17);
const int sender = sceNetSocket("epoll-ident-sender", 2, 2, 17);
Require(receiver >= 0 && sender >= 0);
Require(sceNetBind_nid_postfix(receiver, address.data(), address.size()) == 0);
std::uint32_t size = address.size();
Require(sceNetGetsockname(receiver, address.data(), &size) == 0 && size == address.size());
const int epoll = sceNetEpollCreate("epoll-ident", 0);
Require(epoll >= 0);
NetEpollEvent registration{};
registration.events = 1;
registration.reserved = 0x5a5a5a5a;
registration.ident = 0x123456789abcdef0;
registration.data.u64 = 0x1122334455667788;
Require(sceNetEpollControl(epoll, 1, receiver, &registration) == 0);
const char payload[] = "ident";
Require(sceNetSendto(sender, payload, sizeof(payload), 0, address.data(), address.size()) == sizeof(payload));
NetEpollEvent ready{};
Require(sceNetEpollWait(epoll, &ready, 1, 1000000) == 1);
Require(ready.events == 1 && ready.reserved == 0 && ready.ident == static_cast<std::uint64_t>(receiver));
Require(ready.data.u64 == 0x1122334455667788);
Require(sceNetEpollDestroy(epoll) == 0);
Require(sceNetSocketClose(sender) == 0 && sceNetSocketClose(receiver) == 0);
}
static void CheckTcpNoDelay() {
const int stream = sceNetSocket("tcp-nodelay", 2, 1, 6);
const int datagram = sceNetSocket("tcp-nodelay-udp", 2, 2, 17);
Require(stream >= 0 && datagram >= 0);
int value = -1;
std::uint32_t size = sizeof(value);
Require(sceNetGetsockopt(stream, 6, 1, &value, &size) == 0 && size == sizeof(value) && value == 0);
const int on = 1;
const int off = 0;
Require(sceNetSetsockopt(stream, 6, 1, &on, sizeof(on)) == 0);
Require(sceNetGetsockopt(stream, 6, 1, &value, &size) == 0 && value == 4);
Require(setsockopt_nid_postfix(stream, 6, 1, &off, sizeof(off)) == 0);
Require(sceNetGetsockopt(stream, 6, 1, &value, &size) == 0 && value == 0);
Require(Failed(sceNetSetsockopt(stream, 6, 2, &on, sizeof(on)), 42));
Require(Failed(sceNetGetsockopt(stream, 6, 2, &value, &size), 42));
Require(Failed(sceNetSetsockopt(datagram, 6, 1, &on, sizeof(on)), 22));
Require(Failed(sceNetGetsockopt(datagram, 6, 1, &value, &size), 22));
Require(Failed(sceNetSetsockopt(datagram, 6, 2, &on, sizeof(on)), 22));
Require(sceNetSocketClose(stream) == 0 && sceNetSocketClose(datagram) == 0);
}
static bool ipv6Unavailable = false;
static bool HostIpv6LoopbackUnavailable() {
const auto hostSocket = ::socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
#ifdef _WIN32
if (hostSocket == INVALID_SOCKET) {
const int error = WSAGetLastError();
return error == WSAEAFNOSUPPORT || error == WSAEPROTONOSUPPORT;
}
#else
if (hostSocket < 0) return errno == EAFNOSUPPORT || errno == EPROTONOSUPPORT;
#endif
sockaddr_in6 loopback{};
loopback.sin6_family = AF_INET6;
loopback.sin6_port = htons(9);
loopback.sin6_addr = in6addr_loopback;
const char payload = 0;
const auto sent = ::sendto(hostSocket, &payload, sizeof(payload), 0,
reinterpret_cast<const sockaddr*>(&loopback), sizeof(loopback));
#ifdef _WIN32
const int error = sent < 0 ? WSAGetLastError() : 0;
closesocket(hostSocket);
return sent < 0 && (error == WSAEADDRNOTAVAIL || error == WSAENETUNREACH || error == WSAEHOSTUNREACH);
#else
const int error = sent < 0 ? errno : 0;
close(hostSocket);
return sent < 0 && (error == EADDRNOTAVAIL || error == ENETUNREACH || error == EHOSTUNREACH);
#endif
}
static void CheckLinkLocalAllNodesIpv6() {
Require(reinterpret_cast<std::uintptr_t>(sce_net_in6addr_linklocal_allnodes) % alignof(std::uint32_t) == 0);
std::array<std::uint8_t, 16> address{};
Require(sceNetInetPton(28, "ff02::1", address.data()) == 1);
Require(std::memcmp(sce_net_in6addr_linklocal_allnodes, address.data(), address.size()) == 0);
std::array<char, 9> text;
text.fill('x');
Require(sceNetInetNtop(28, sce_net_in6addr_linklocal_allnodes, text.data(), 7) == nullptr && *sceNetErrnoLoc() == 28);
Require(sceNetInetNtop(28, sce_net_in6addr_linklocal_allnodes, text.data(), 8) == text.data());
Require(std::strcmp(text.data(), "ff02::1") == 0 && text[8] == 'x');
address.fill(0xa5);
Require(sceNetInetPton(28, text.data(), address.data()) == 1);
Require(std::memcmp(sce_net_in6addr_linklocal_allnodes, address.data(), address.size()) == 0);
}
template <std::size_t TSize>
static bool Untouched(const std::array<std::uint8_t, TSize>& bytes, std::uint32_t from) {
return std::all_of(bytes.begin() + from, bytes.end(), [](auto byte) { return byte == 0xa5; });
}
static void CheckDatagramAddressLengths(int family) {
if (family == 28 && ipv6Unavailable) return;
const std::uint32_t addressSize = family == 2 ? 16 : 28;
std::array<std::uint8_t, 28> destination{};
destination[0] = static_cast<std::uint8_t>(addressSize);
destination[1] = static_cast<std::uint8_t>(family);
if (family == 2) { destination[4] = 127; destination[7] = 1; }
else destination[23] = 1;
const int receiver = sceNetSocket("length-receiver", family, 2, 17);
const int sender = sceNetSocket("length-sender", family, 2, 17);
Require(receiver >= 0 && sender >= 0);
Require(sceNetBind_nid_postfix(receiver, destination.data(), addressSize) == 0);
std::uint32_t size = destination.size();
Require(sceNetGetsockname(receiver, destination.data(), &size) == 0 && size == addressSize);
Require(sceNetConnect(sender, destination.data(), addressSize) == 0);
std::array<std::uint8_t, 28> source{};
size = source.size();
Require(sceNetGetsockname(sender, source.data(), &size) == 0 && size == addressSize);
const char payload[] = "address length";
for (const std::uint32_t capacity : {0u, 1u, 2u, addressSize - 1, addressSize, 32u, UINT32_MAX}) {
const auto copied = std::min(capacity, addressSize);
std::array<std::uint8_t, 32> address;
address.fill(0xa5);
size = capacity;
Require(sceNetGetsockname(receiver, address.data(), &size) == 0 && size == copied);
Require(std::memcmp(address.data(), destination.data(), copied) == 0 && Untouched(address, copied));
char received[sizeof(payload)]{};
Require(sceNetSend(sender, payload, sizeof(payload), 0) == sizeof(payload));
address.fill(0xa5);
size = capacity;
Require(sceNetRecvfrom(receiver, received, sizeof(received), 2, address.data(), &size) == sizeof(payload));
Require(size == copied && std::memcmp(address.data(), source.data(), copied) == 0 && Untouched(address, copied));
address.fill(0xa5);
NetIovec in[] = {{received, sizeof(received)}};
NetMsghdr message{address.data(), capacity, in, 1, nullptr, 0, -1};
Require(sceNetRecvmsg(receiver, &message, 0) == sizeof(payload) && std::memcmp(received, payload, sizeof(payload)) == 0);
Require(message.name_length == copied && std::memcmp(address.data(), source.data(), copied) == 0 && Untouched(address, copied));
}
Require(sceNetSocketClose(sender) == 0 && sceNetSocketClose(receiver) == 0);
}
static void CheckStreamAddressLengths() {
const int listener = sceNetSocket("length-listener", 2, 1, 6);
Require(listener >= 0);
std::array<std::uint8_t, 16> listening{16, 2, 0, 0, 127, 0, 0, 1};
Require(sceNetBind_nid_postfix(listener, listening.data(), listening.size()) == 0 && sceNetListen(listener, 1) == 0);
std::uint32_t size = listening.size();
Require(sceNetGetsockname(listener, listening.data(), &size) == 0 && size == 16);
const int client = sceNetSocket("length-client", 2, 1, 6);
Require(client >= 0 && sceNetConnect(client, listening.data(), listening.size()) == 0);
std::array<std::uint8_t, 16> clientAddress{};
size = clientAddress.size();
Require(sceNetGetsockname(client, clientAddress.data(), &size) == 0 && size == 16);
std::array<std::uint8_t, 28> peer;
peer.fill(0xa5);
size = 1;
const int accepted = sceNetAccept(listener, peer.data(), &size);
Require(accepted >= 0 && size == 1 && peer[0] == 16 && Untouched(peer, 1));
for (const std::uint32_t capacity : {0u, 1u, 15u, 16u, 28u, UINT32_MAX}) {
const auto copied = std::min(capacity, std::uint32_t{16});
peer.fill(0xa5);
size = capacity;
Require(sceNetGetpeername(accepted, peer.data(), &size) == 0 && size == copied);
Require(std::memcmp(peer.data(), clientAddress.data(), copied) == 0 && Untouched(peer, copied));
}
for (const int descriptor : {accepted, client, listener}) Require(sceNetSocketClose(descriptor) == 0);
}
static void CheckKernelSocketOption() {
const int kernel = socket_nid_postfix(2, 1, 0);
Require(kernel >= 0);
const int size = 65536;
Require(sceNetSetsockopt(kernel, 0xffff, 0x1001, &size, sizeof(size)) == 0);
const int one = 1;
Require(sceNetSetsockopt(kernel, 6, 1, &one, sizeof(one)) == 0);
Require(close_nid_postfix(kernel) == 0);
Require(Failed(sceNetSetsockopt(kernel, 0xffff, 0x1001, &size, sizeof(size)), 9));
}
static void CheckBufferSizes() {
const int socket = sceNetSocket("buffer-sizes", 2, 2, 17);
Require(socket >= 0);
for (const int option : {0x1001, 0x1002}) {
int value = -1;
std::uint32_t size = sizeof(value);
Require(sceNetGetsockopt(socket, 0xffff, option, &value, &size) == 0 && size == sizeof(value) && value > 0);
}
for (const auto [option, requested] : {std::array{0x1001, 32768}, std::array{0x1002, 65536}}) {
Require(sceNetSetsockopt(socket, 0xffff, option, &requested, sizeof(requested)) == 0);
int value = -1;
std::uint32_t size = sizeof(value);
Require(sceNetGetsockopt(socket, 0xffff, option, &value, &size) == 0 && size == sizeof(value) && value == requested);
}
Require(sceNetSocketClose(socket) == 0);
}
static void CheckOnesBroadcast() {
const auto read = [](int socket) {
int value = -1;
std::uint32_t size = sizeof(value);
Require(sceNetGetsockopt(socket, 0xffff, 0x10000, &value, &size) == 0 && size == sizeof(value));
return value;
};
const int socket = sceNetSocket("ones-broadcast", 2, 2, 17);
const int other = sceNetSocket("ones-broadcast-other", 2, 2, 17);
Require(socket >= 0 && other >= 0);
Require(read(socket) == 0);
for (const int requested : {1, 0, 7}) {
Require(sceNetSetsockopt(socket, 0xffff, 0x10000, &requested, sizeof(requested)) == 0);
Require(read(socket) == requested);
}
Require(read(other) == 0);
const std::uint8_t truncated = 1;
Require(Failed(sceNetSetsockopt(socket, 0xffff, 0x10000, &truncated, sizeof(truncated)), 22));
Require(read(socket) == 7);
for (const int descriptor : {socket, other}) Require(sceNetSocketClose(descriptor) == 0);
}
static void CheckUnspecifiedIpv6() {
std::array<std::uint8_t, 16> unspecified{};
Require(sceNetInetPton(28, "::", unspecified.data()) == 1);
Require(std::memcmp(sce_net_in6addr_any, unspecified.data(), unspecified.size()) == 0);
char text[4] = {'x', 'x', 'x', 'x'};
Require(sceNetInetNtop(28, sce_net_in6addr_any, text, 3) == text);
Require(std::strcmp(text, "::") == 0 && text[3] == 'x');
const int receiver = sceNetSocket("ipv6-any", 28, 2, 17);
if (receiver < 0 && *sceNetErrnoLoc() == 47) {
ipv6Unavailable = true;
return;
}
const int sender = sceNetSocket("ipv6-loopback", 28, 2, 17);
Require(receiver >= 0 && sender >= 0);
std::array<std::uint8_t, 28> address{28, 28};
std::memcpy(address.data() + 8, sce_net_in6addr_any, 16);
Require(sceNetBind_nid_postfix(receiver, address.data(), address.size()) == 0);
std::uint32_t size = address.size();
address.fill(0xa5);
Require(sceNetGetsockname(receiver, address.data(), &size) == 0 && size == address.size());
Require(address[0] == 28 && address[1] == 28 && (address[2] != 0 || address[3] != 0));
Require(std::memcmp(address.data() + 8, unspecified.data(), unspecified.size()) == 0);
Require(sceNetInetPton(28, "::1", address.data() + 8) == 1);
const char payload[] = "IPv6 wildcard receive";
const auto sent = sceNetSendto(sender, payload, sizeof(payload), 0, address.data(), address.size());
if (sent < 0 && (*sceNetErrnoLoc() == 49 || *sceNetErrnoLoc() == 51 || *sceNetErrnoLoc() == 65) && HostIpv6LoopbackUnavailable()) {
Require(sceNetSocketClose(sender) == 0);
Require(sceNetSocketClose(receiver) == 0);
ipv6Unavailable = true;
return;
}
Require(sent == sizeof(payload));
char received[sizeof(payload)]{};
std::array<std::uint8_t, 28> peer{};
size = peer.size();
Require(sceNetRecvfrom(receiver, received, sizeof(received), 0, peer.data(), &size) == sizeof(received));
Require(std::memcmp(received, payload, sizeof(payload)) == 0 && size == peer.size() && peer[1] == 28);
Require(std::memcmp(peer.data() + 8, address.data() + 8, 16) == 0);
address[0] = 0;
Require(sceNetSendto(sender, payload, sizeof(payload), 0, address.data(), address.size()) == sizeof(payload));
Require(sceNetRecvfrom(receiver, received, sizeof(received), 0, nullptr, nullptr) == sizeof(received));
Require(std::memcmp(sce_net_in6addr_any, unspecified.data(), unspecified.size()) == 0);
Require(sceNetSocketClose(sender) == 0);
Require(sceNetSocketClose(receiver) == 0);
}
static void CheckAcceptedNonblocking(int family, bool nonblocking) {
const auto addressSize = family == 2 ? 16u : 28u;
std::array<std::uint8_t, 28> address{};
address[0] = static_cast<std::uint8_t>(addressSize);
address[1] = static_cast<std::uint8_t>(family);
if (family == 2) { address[4] = 127; address[7] = 1; }
else address[23] = 1;
const int listener = sceNetSocket("accept-mode", family, 1, 6);
Require(listener >= 0 && sceNetBind_nid_postfix(listener, address.data(), addressSize) == 0);
Require(sceNetListen(listener, 1) == 0);
const int mode = nonblocking ? 1 : 0;
Require(sceNetSetsockopt(listener, 0xffff, 0x1200, &mode, sizeof(mode)) == 0);
std::uint32_t size = address.size();
Require(sceNetGetsockname(listener, address.data(), &size) == 0 && size == addressSize);
const int client = sceNetSocket("accept-client", family, 1, 6);
Require(client >= 0 && sceNetConnect(client, address.data(), addressSize) == 0);
int accepted = -1;
const auto deadline = std::chrono::steady_clock::now() + std::chrono::seconds(2);
do {
accepted = sceNetAccept(listener, nullptr, nullptr);
if (accepted >= 0) break;
Require(nonblocking && Failed(accepted, 35));
std::this_thread::sleep_for(std::chrono::milliseconds(1));
} while (std::chrono::steady_clock::now() < deadline);
Require(accepted >= 0);
int inherited = -1;
size = sizeof(inherited);
Require(sceNetGetsockopt(accepted, 0xffff, 0x1200, &inherited, &size) == 0 && size == sizeof(inherited));
const int status = fcntl_nid_postfix(accepted, 3);
std::printf("Accepted IPv%d mode=%d SO_NBIO=%d F_GETFL=%d\n", family == 2 ? 4 : 6, mode, inherited, status);
std::fflush(stdout);
Require(inherited == mode && status == (2 | (nonblocking ? 4 : 0)));
const int changed = 1 - mode;
Require(sceNetSetsockopt(listener, 0xffff, 0x1200, &changed, sizeof(changed)) == 0);
size = sizeof(inherited);
Require(sceNetGetsockopt(accepted, 0xffff, 0x1200, &inherited, &size) == 0 && inherited == mode);
struct ReceiveResult { std::int64_t bytes; int error; char value; };
auto receive = std::async(std::launch::async, [accepted] {
char byte = '?';
const auto received = sceNetRecv(accepted, &byte, sizeof(byte), 0);
return ReceiveResult{received, *sceNetErrnoLoc(), byte};
});
const bool ready = receive.wait_for(nonblocking ? std::chrono::milliseconds(2000) : std::chrono::milliseconds(50)) == std::future_status::ready;
const char payload = 'n';
Require(sceNetSend(client, &payload, sizeof(payload), 0) == sizeof(payload));
const auto received = receive.get();
Require(ready == nonblocking);
if (nonblocking) {
Require(received.bytes == static_cast<int>(0x80410123) && received.error == 35 && received.value == '?');
char byte = '?';
std::int64_t count = -1;
const auto arrival = std::chrono::steady_clock::now() + std::chrono::seconds(2);
do {
count = sceNetRecv(accepted, &byte, sizeof(byte), 0);
if (count >= 0) break;
Require(Failed(count, 35));
std::this_thread::sleep_for(std::chrono::milliseconds(1));
} while (std::chrono::steady_clock::now() < arrival);
Require(count == sizeof(byte) && byte == payload);
} else {
Require(received.bytes == sizeof(payload) && received.value == payload);
}
for (const int descriptor : {accepted, client, listener}) Require(sceNetSocketClose(descriptor) == 0);
}
static void CheckIpv6SocketsAreIpv6Only() {
const int inet = sceNetSocket("ipv4-any", 2, 2, 17);
Require(inet >= 0);
std::array<std::uint8_t, 16> any{16, 2};
Require(sceNetBind_nid_postfix(inet, any.data(), any.size()) == 0);
std::uint32_t size = any.size();
Require(sceNetGetsockname(inet, any.data(), &size) == 0 && size == any.size());
const int inet6 = sceNetSocket("ipv6-any-same-port", 28, 2, 17);
Require(inet6 >= 0);
std::array<std::uint8_t, 28> any6{28, 28, any[2], any[3]};
if (sceNetBind_nid_postfix(inet6, any6.data(), any6.size()) != 0) {
std::fprintf(stderr, "sceNetBind of [::]:%d failed with errno %d while an IPv4 socket holds 0.0.0.0:%d\n",
(any[2] << 8) | any[3], *sceNetErrnoLoc(), (any[2] << 8) | any[3]);
std::abort();
}
Require(sceNetSocketClose(inet6) == 0);
Require(sceNetSocketClose(inet) == 0);
}
static void CheckSocketError() {
std::array<std::uint8_t, 16> address{16, 2, 0, 0, 127, 0, 0, 1};
const int listener = sceNetSocket(nullptr, 2, 1, 6);
Require(listener >= 0 && sceNetBind_nid_postfix(listener, address.data(), address.size()) == 0 && sceNetListen(listener, 1) == 0);
std::uint32_t size = address.size();
Require(sceNetGetsockname(listener, address.data(), &size) == 0 && size == address.size());
const int connected = sceNetSocket(nullptr, 2, 1, 6);
Require(connected >= 0 && sceNetConnect(connected, address.data(), address.size()) == 0);
int error = -1;
size = sizeof(error);
Require(sceNetGetsockopt(connected, 0xffff, 0x1007, &error, &size) == 0 && size == sizeof(error) && error == 0);
for (const int descriptor : {connected, listener}) Require(sceNetSocketClose(descriptor) == 0);
const int refused = sceNetSocket(nullptr, 2, 1, 6);
const int nonblocking = 1;
Require(refused >= 0 && sceNetSetsockopt(refused, 0xffff, 0x1200, &nonblocking, sizeof(nonblocking)) == 0);
const int started = sceNetConnect(refused, address.data(), address.size());
Require(started == static_cast<int>(0x80410124) && *sceNetErrnoLoc() == 36);
error = 0;
for (int attempt = 0; attempt < 500 && error == 0; ++attempt) {
size = sizeof(error);
Require(sceNetGetsockopt(refused, 0xffff, 0x1007, &error, &size) == 0 && size == sizeof(error));
if (error == 0) std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
Require(error == 61);
size = sizeof(error);
Require(sceNetGetsockopt(refused, 0xffff, 0x1007, &error, &size) == 0 && error == 0);
Require(sceNetSocketClose(refused) == 0);
}
static void CheckCloseKernelSocket() {
const int kernel = socket_nid_postfix(2, 1, 0);
Require(kernel >= 0 && GuestSocketIsOpen_nid_no_patch(kernel));
Require(sceNetSocketClose(kernel) == 0);
Require(!GuestSocketIsOpen_nid_no_patch(kernel));
Require(Failed(sceNetSocketClose(kernel), 9));
}
static std::array<std::uint8_t, 0xa0> RequireSockInfo(int socket, int type, int flags, const std::array<std::uint8_t, 16>& local,
const std::array<std::uint8_t, 16>& remote) {
std::array<std::uint8_t, 0x140> records;
records.fill(0xa5);
Require(sceNetGetSockInfo(socket, records.data(), 2, 0) == 1);
std::array<std::uint8_t, 0xa0> expected{};
std::memcpy(expected.data() + 0x24, &socket, sizeof(socket));
expected[0x28] = static_cast<std::uint8_t>(type);
std::memcpy(expected.data() + 0x34, local.data() + 4, 4);
std::memcpy(expected.data() + 0x38, remote.data() + 4, 4);
std::memcpy(expected.data() + 0x3c, local.data() + 2, 2);
std::memcpy(expected.data() + 0x3e, remote.data() + 2, 2);
std::memcpy(expected.data() + 0x48, &flags, sizeof(flags));
for (const auto [offset, option] : {std::array{0x64, 0x1002}, std::array{0x68, 0x1001}}) {
int size = 0;
std::uint32_t length = sizeof(size);
Require(sceNetGetsockopt(socket, 0xffff, option, &size, &length) == 0 && size > 0);
std::memcpy(expected.data() + offset, &size, sizeof(size));
}
Require(std::equal(expected.begin(), expected.end(), records.begin()));
Require(std::all_of(records.begin() + 0xa0, records.end(), [](auto byte) { return byte == 0xa5; }));
return expected;
}
static std::vector<int> RequireSockInfoList(int flags) {
const int count = sceNetGetSockInfo(-1, nullptr, 0, flags);
Require(count >= 0);
std::vector<std::uint8_t> records(static_cast<std::size_t>(count + 1) * 0xa0, 0xa5);
Require(sceNetGetSockInfo(-1, records.data(), count + 1, flags) == count);
Require(std::all_of(records.end() - 0xa0, records.end(), [](auto byte) { return byte == 0xa5; }));
std::vector<int> sockets;
for (int index = 0; index < count; ++index) {
const auto listed = records.begin() + index * 0xa0;
int socket = 0;
std::memcpy(&socket, &listed[0x24], sizeof(socket));
Require(sockets.empty() || socket > sockets.back());
std::array<std::uint8_t, 0xa0> single;
Require(sceNetGetSockInfo(socket, single.data(), 1, 0) == 1 && std::equal(single.begin(), single.end(), listed));
sockets.push_back(socket);
}
const int one = std::min(count, 1);
std::array<std::uint8_t, 0x140> first;
first.fill(0xa5);
Require(sceNetGetSockInfo(-1, first.data(), 1, flags) == one && std::equal(first.begin(), first.begin() + one * 0xa0, records.begin()));
Require(std::all_of(first.begin() + one * 0xa0, first.end(), [](auto byte) { return byte == 0xa5; }));
return sockets;
}
static void CheckSockInfo() {
const auto earlier = RequireSockInfoList(0);
const std::array<std::uint8_t, 16> unnamed{};
const int datagram = sceNetSocket("sockinfo-datagram", 2, 2, 17);
Require(datagram >= 0);
RequireSockInfo(datagram, 2, 1, unnamed, unnamed);
std::array<std::uint8_t, 16> datagramAddress{16, 2, 0, 0, 127, 0, 0, 1};
Require(sceNetBind_nid_postfix(datagram, datagramAddress.data(), datagramAddress.size()) == 0);
std::uint32_t size = datagramAddress.size();
Require(sceNetGetsockname(datagram, datagramAddress.data(), &size) == 0 && size == 16);
RequireSockInfo(datagram, 2, 1, datagramAddress, unnamed);
const int listener = sceNetSocket("sockinfo-listener", 2, 1, 6);
std::array<std::uint8_t, 16> listening{16, 2, 0, 0, 127, 0, 0, 1};
Require(listener >= 0 && sceNetBind_nid_postfix(listener, listening.data(), listening.size()) == 0 && sceNetListen(listener, 1) == 0);
size = listening.size();
Require(sceNetGetsockname(listener, listening.data(), &size) == 0 && size == 16);
RequireSockInfo(listener, 1, 1, listening, unnamed);
const int client = sceNetSocket("sockinfo-client", 2, 1, 6);
Require(client >= 0 && sceNetConnect(client, listening.data(), listening.size()) == 0);
const int accepted = sceNetAccept(listener, nullptr, nullptr);
Require(accepted >= 0);
std::array<std::uint8_t, 16> clientAddress{};
size = clientAddress.size();
Require(sceNetGetsockname(client, clientAddress.data(), &size) == 0 && size == 16);
RequireSockInfo(client, 1, 1, clientAddress, listening);
RequireSockInfo(accepted, 1, 1, listening, clientAddress);
Require(sceNetConnect(datagram, listening.data(), listening.size()) == 0);
RequireSockInfo(datagram, 2, 1, datagramAddress, listening);
const int nonblocking = 1;
const std::array<int, 2> buffers{65536, 32768};
Require(sceNetSetsockopt(datagram, 0xffff, 0x1200, &nonblocking, sizeof(nonblocking)) == 0);
Require(sceNetSetsockopt(datagram, 0xffff, 0x1002, &buffers[0], sizeof(buffers[0])) == 0);
Require(sceNetSetsockopt(datagram, 0xffff, 0x1001, &buffers[1], sizeof(buffers[1])) == 0);
const auto configured = RequireSockInfo(datagram, 2, 0x200001, datagramAddress, listening);
Require(std::memcmp(configured.data() + 0x64, buffers.data(), sizeof(buffers)) == 0);
std::array<std::uint8_t, 0xa0> record;
record.fill(0xa5);
Require(Failed(sceNetGetSockInfo(datagram, nullptr, 1, 0), 22));
Require(Failed(sceNetGetSockInfo(datagram, record.data(), 0, 0), 22));
Require(Failed(sceNetGetSockInfo(-1, record.data(), 0, 0), 22));
for (const int flags : {1, 0x41, 0x1000}) {
Require(Failed(sceNetGetSockInfo(datagram, record.data(), 1, flags), 22));
Require(Failed(sceNetGetSockInfo(-1, nullptr, 0, flags), 22));
}
const int inet6 = sceNetSocket("sockinfo-inet6", 28, 2, 17);
Require(inet6 >= 0 ? Failed(sceNetGetSockInfo(inet6, record.data(), 1, 0), 47) : *sceNetErrnoLoc() == 47);
Require(std::all_of(record.begin(), record.end(), [](auto byte) { return byte == 0xa5; }));
std::vector<int> expected = earlier;
expected.insert(expected.end(), {datagram, listener, client, accepted});
std::sort(expected.begin(), expected.end());
Require(RequireSockInfoList(0) == expected && RequireSockInfoList(0x40) == expected);
Require(sceNetGetSockInfo(datagram, record.data(), 1, 0x40) == 1 && std::equal(configured.begin(), configured.end(), record.begin()));
if (inet6 >= 0) Require(sceNetSocketClose(inet6) == 0);
for (const int descriptor : {accepted, client, listener, datagram}) Require(sceNetSocketClose(descriptor) == 0);
Require(Failed(sceNetGetSockInfo(datagram, record.data(), 1, 0), 9));
Require(RequireSockInfoList(0) == earlier);
}
static void CheckDatagramDisconnect() {
const int peer = sceNetSocket("disconnect-peer", 2, 2, 17);
const int other = sceNetSocket("disconnect-other", 2, 2, 17);
const int dissolved = sceNetSocket("disconnect", 2, 2, 17);
Require(peer >= 0 && other >= 0 && dissolved >= 0);
std::array<std::uint8_t, 16> peerAddress{16, 2, 0, 0, 127, 0, 0, 1};
std::array<std::uint8_t, 16> dissolvedAddress{16, 2, 0, 0, 127, 0, 0, 1};
Require(sceNetBind_nid_postfix(peer, peerAddress.data(), peerAddress.size()) == 0);
Require(sceNetBind_nid_postfix(dissolved, dissolvedAddress.data(), dissolvedAddress.size()) == 0);
std::uint32_t size = peerAddress.size();
Require(sceNetGetsockname(peer, peerAddress.data(), &size) == 0);
size = dissolvedAddress.size();
Require(sceNetGetsockname(dissolved, dissolvedAddress.data(), &size) == 0);
Require(sceNetConnect(dissolved, peerAddress.data(), peerAddress.size()) == 0);
std::array<std::uint8_t, 16> name{};
size = name.size();
Require(sceNetGetpeername(dissolved, name.data(), &size) == 0 && name[2] == peerAddress[2] && name[3] == peerAddress[3]);
const std::array<std::uint8_t, 16> unspecified{16, 0};
Require(Failed(sceNetConnect(dissolved, unspecified.data(), unspecified.size()), 47));
size = name.size();
Require(Failed(sceNetGetpeername(dissolved, name.data(), &size), 57));
std::array<std::uint8_t, 16> local{};
size = local.size();
Require(sceNetGetsockname(dissolved, local.data(), &size) == 0 && local[2] == dissolvedAddress[2] && local[3] == dissolvedAddress[3]);
#ifndef _WIN32
Require(local[4] == 0 && local[5] == 0 && local[6] == 0 && local[7] == 0);
#endif
const int timeout = 2000000;
Require(sceNetSetsockopt(dissolved, 0xffff, 0x1106, &timeout, sizeof(timeout)) == 0);
const char unrelated[] = "unrelated sender";
Require(sceNetSendto(other, unrelated, sizeof(unrelated), 0, dissolvedAddress.data(), dissolvedAddress.size()) == sizeof(unrelated));
char buffer[32]{};
Require(sceNetRecvfrom(dissolved, buffer, sizeof(buffer), 0, nullptr, nullptr) == sizeof(unrelated));
Require(std::strcmp(buffer, unrelated) == 0);
for (const int descriptor : {peer, other, dissolved}) Require(sceNetSocketClose(descriptor) == 0);
}
int main() {
const auto epoch = std::chrono::steady_clock::time_point{};
Require(KernelSocketSelect::DeadlineAfter(epoch, 2, 250000) == epoch + std::chrono::milliseconds(2250));
Require(KernelSocketSelect::DeadlineAfter(epoch, INT64_MAX, 0) == std::chrono::steady_clock::time_point::max());
Require(KernelSocketSelect::DeadlineAfter(epoch, 10000000000LL, 0) == std::chrono::steady_clock::time_point::max());
const auto almostMax = std::chrono::steady_clock::time_point::max() - std::chrono::microseconds(1);
Require(KernelSocketSelect::DeadlineAfter(almostMax, 0, 1) == std::chrono::steady_clock::time_point::max());
Require(KernelSocketSelect::DeadlineAfter(almostMax, 0, 2) == std::chrono::steady_clock::time_point::max());
for (int i = 0; i < 16; ++i) {
Require(in6addr_any_nid_postfix[i] == 0);
Require(in6addr_loopback_nid_postfix[i] == (i == 15 ? 1 : 0));
}
Require(sceNetInit_nid_postfix() == 0);
CheckEpollWritable();
CheckSocketHostErrors();
CheckPoolStats();
CheckIfName();
CheckRandom();
CheckNetMemory();
CheckNetstat();
CheckEmptyEpoll();
CheckDontWait();
CheckAddressText(2, "127.0.0.1");
CheckAddressText(2, "255.255.255.255");
CheckAddressText(28, "::1");
CheckAddressText(28, "1234:5678:9abc:def0:1234:5678:9abc:def0");
CheckInetPtonWhitespace();
CheckInetPtonExParts();
CheckInetPtonEx();
CheckUsleep();
CheckSystemTime();
CheckEpollIdent();
CheckTcpNoDelay();
CheckLinkLocalAllNodesIpv6();
CheckUnboundSocketName();
CheckUnboundDatagramReceive();
CheckUnboundDatagramShutdown();
CheckUnboundDatagramLaterAddress();
CheckUnboundDatagramTimeoutAfterAddress();
CheckUnspecifiedIpv6();
if (!ipv6Unavailable) CheckIpv6SocketsAreIpv6Only();
for (const int family : {2, 28}) {
if (family == 28 && ipv6Unavailable) continue;
CheckAcceptedNonblocking(family, false);
CheckAcceptedNonblocking(family, true);
}
CheckDatagramDisconnect();
CheckCloseKernelSocket();
CheckSockInfo();
const int closed = sceNetSocket("closed-by-kernel", 2, 2, 17);
Require(closed >= 0);
const int enabled = 1;
Require(setsockopt_nid_postfix(closed, 0xffff, 0x20, &enabled, sizeof(enabled)) == 0);
Require(sceNetSetsockopt(closed, 0xffff, 0x20, &enabled, sizeof(enabled)) == 0);
Require(sceNetSetsockopt(closed, 0xffff, 0x4, &enabled, sizeof(enabled)) == 0);
Require(close_nid_postfix(closed) == 0);
Require(Failed(sceNetSocketClose(closed), 9));
CheckKernelTimeouts();
CheckSocketError();
CheckKernelStatusFlags();
const int listener = sceNetSocket(nullptr, 2, 1, 6);
Require(listener >= 0);
std::array<std::uint8_t, 16> address{16, 2, 0, 0, 127, 0, 0, 1};
Require(sceNetBind_nid_postfix(listener, address.data(), address.size()) == 0);
Require(sceNetListen(listener, 4) == 0);
std::uint32_t address_size = address.size();
Require(sceNetGetsockname(listener, address.data(), &address_size) == 0 && address_size == 16);
Require(address[2] != 0 || address[3] != 0);
const int client = sceNetSocket(nullptr, 2, 1, 6);
Require(client >= 0);
Require(sceNetConnect(client, address.data(), address.size()) == 0);
std::array<std::uint8_t, 16> peer{};
address_size = peer.size();
const int accepted = sceNetAccept(listener, peer.data(), &address_size);
Require(accepted >= 0 && address_size == 16);
Require(sceNetConnect(client, address.data(), address.size()) == static_cast<int>(0x80410138) && *sceNetErrnoLoc() == 56);
const int nonblocking = 1;
Require(sceNetSetsockopt(accepted, 0xffff, 0x1200, &nonblocking, sizeof(nonblocking)) == 0);
char pending = 0;
Require(sceNetRecv(accepted, &pending, sizeof(pending), 0) == static_cast<int>(0x80410123) && *sceNetErrnoLoc() == 35);
const int connecting = sceNetSocket(nullptr, 2, 1, 6);
Require(connecting >= 0);
Require(sceNetSetsockopt(connecting, 0xffff, 0x1200, &nonblocking, sizeof(nonblocking)) == 0);
const int started = sceNetConnect(connecting, address.data(), address.size());
Require(started == 0 || (started == static_cast<int>(0x80410124) && *sceNetErrnoLoc() == 36));
Require(sceNetSocketClose(connecting) == 0);
CheckDatagramAddressLengths(2);
CheckDatagramAddressLengths(28);
CheckStreamAddressLengths();
CheckBufferSizes();
CheckOnesBroadcast();
CheckKernelSocketOption();
const int epoll = sceNetEpollCreate("guest-sce-net", 0);
Require(epoll >= 0);
NetEpollEvent registration{};
registration.events = 1;
registration.ident = static_cast<std::uint64_t>(accepted);
registration.data.u32 = 77;
NetEpollEvent ready{};
auto waiter = std::async(std::launch::async, [&] { return sceNetEpollWait(epoll, &ready, 1, -1); });
std::this_thread::sleep_for(std::chrono::milliseconds(20));
Require(sceNetEpollControl(epoll, 1, accepted, &registration) == 0);
const char request[] = "guest tcp loopback";
char response[sizeof(request)]{};
Require(sceNetSend(client, request, sizeof(request), 0) == sizeof(request));
Require(waiter.wait_for(std::chrono::seconds(1)) == std::future_status::ready);
const int ready_count = waiter.get();
Require(ready_count == 1 && (ready.events & 1) && ready.ident == static_cast<std::uint64_t>(accepted) && ready.data.u32 == 77);
Require(sceNetEpollWait(epoll, &ready, 1, 1000000) == 1);
Require(sceNetRecv(accepted, response, sizeof(response), 0) == sizeof(response));
Require(std::strcmp(request, response) == 0);
char reply[] = "stream reply";
NetIovec reply_out[] = {{reply, 7}, {reply + 7, sizeof(reply) - 7}};
NetMsghdr reply_send{nullptr, 0, reply_out, 2, nullptr, 0, 0};
Require(sceNetSendmsg(accepted, &reply_send, 0) == sizeof(reply));
char reply_result[sizeof(reply)]{};
NetIovec reply_in[] = {{reply_result, sizeof(reply_result)}};
std::array<std::uint8_t, 16> reply_name{};
NetMsghdr reply_receive{reply_name.data(), 16, reply_in, 1, nullptr, 0, -1};
Require(sceNetRecvmsg(client, &reply_receive, 0) == sizeof(reply));
Require(std::strcmp(reply, reply_result) == 0 && reply_receive.flags == 0 && reply_receive.name_length == 0);
Require(sceNetSend(client, request, sizeof(request), 0) == sizeof(request));
ready = {};
Require(sceNetEpollWait(epoll, &ready, 1, 1000000) == 1 && (ready.events & 1) && ready.ident == static_cast<std::uint64_t>(accepted));
Require(sceNetShutdown(accepted, 1) == 0);
Require(Failed(sceNetSend(accepted, request, sizeof(request), 0), 32));
registration.data.u32 = 78;
Require(sceNetEpollControl(epoll, 2, accepted, &registration) == 0);
Require(Failed(sceNetEpollControl(epoll, 2, accepted, nullptr), 22));
Require(sceNetEpollControl(epoll, 3, accepted, nullptr) == 0);
Require(Failed(sceNetEpollControl(epoll, 3, accepted, nullptr), 2));
Require(Failed(sceNetEpollControl(epoll, 2, accepted, &registration), 2));
Require(sceNetEpollDestroy(epoll) == 0);
Require(sceNetSocketClose(accepted) == 0);
bool send_failed = false;
for (int attempt = 0; attempt < 100 && !send_failed; ++attempt) {
send_failed = sceNetSend(client, request, sizeof(request), 0) < 0;
if (!send_failed) std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
Require(send_failed);
Require(sceNetSocketClose(client) == 0);
Require(sceNetSocketClose(listener) == 0);
*__error_nid_postfix() = 0;
Require(sceNetSocketClose(listener) == static_cast<int>(0x80410109) && *sceNetErrnoLoc() == 9);
Require(*__error_nid_postfix() == 9);
const int udp_receiver = sceNetSocket(nullptr, 2, 2, 17);
const int udp_sender = sceNetSocket(nullptr, 2, 2, 17);
Require(udp_receiver >= 0 && udp_sender >= 0 && udp_receiver < 1024 && udp_sender < 1024);
address = {16, 2, 0, 0, 127, 0, 0, 1};
Require(sceNetBind_nid_postfix(udp_receiver, address.data(), address.size()) == 0);
address_size = address.size();
Require(sceNetGetsockname(udp_receiver, address.data(), &address_size) == 0);
const char datagram[] = "guest udp loopback";
char datagram_result[sizeof(datagram)]{};
Require(sceNetSendto(udp_sender, datagram, sizeof(datagram), 0, address.data(), address.size()) == sizeof(datagram));
std::array<std::uint8_t, 16> source{};
address_size = source.size();
Require(sceNetRecvfrom(udp_receiver, datagram_result, sizeof(datagram_result), 0,
source.data(), &address_size) == sizeof(datagram_result));
Require(std::strcmp(datagram, datagram_result) == 0 && source[1] == 2);
std::array<std::uint8_t, 16> unsized = address;
unsized[0] = 0;
Require(sceNetSendto(udp_sender, datagram, sizeof(datagram), 0, unsized.data(), unsized.size()) == sizeof(datagram));
Require(sceNetRecvfrom(udp_receiver, datagram_result, sizeof(datagram_result), 0, nullptr, nullptr) == sizeof(datagram_result));
std::uint64_t readable[16]{};
readable[udp_receiver / 64] |= std::uint64_t{1} << (udp_receiver % 64);
const std::int64_t poll_now[2]{0, 0};
Require(select_nid_postfix(udp_receiver + 1, readable, nullptr, nullptr, poll_now) == 0);
Require(readable[udp_receiver / 64] == 0);
Require(sceNetSendto(udp_sender, datagram, sizeof(datagram), 0, address.data(), address.size()) == sizeof(datagram));
readable[udp_receiver / 64] |= std::uint64_t{1} << (udp_receiver % 64);
const std::int64_t wait_second[2]{1, 0};
Require(select_nid_postfix(udp_receiver + 1, readable, nullptr, nullptr, wait_second) == 1);
Require(((readable[udp_receiver / 64] >> (udp_receiver % 64)) & 1u) != 0);
Require(sceNetRecvfrom(udp_receiver, datagram_result, sizeof(datagram_result), 0, nullptr, nullptr) == sizeof(datagram_result));
const std::int64_t bad_timeout[2]{0, 1000000};
Require(select_nid_postfix(udp_receiver + 1, readable, nullptr, nullptr, bad_timeout) == -1 && *__error_nid_postfix() == 22);
Require(select_nid_postfix(1025, nullptr, nullptr, nullptr, poll_now) == -1 && *__error_nid_postfix() == 22);
const int random = open_nid_postfix("/dev/urandom", 0, 0);
Require(random >= 0 && random < 1024);
std::uint64_t random_readable[16]{};
std::uint64_t random_urgent[16]{};
random_readable[random / 64] |= std::uint64_t{1} << (random % 64);
random_readable[udp_receiver / 64] |= std::uint64_t{1} << (udp_receiver % 64);
random_urgent[random / 64] |= std::uint64_t{1} << (random % 64);
Require(select_nid_postfix(std::max(random, udp_receiver) + 1, random_readable, nullptr, random_urgent, nullptr) == 1);
Require(((random_readable[random / 64] >> (random % 64)) & 1u) != 0);
Require(((random_readable[udp_receiver / 64] >> (udp_receiver % 64)) & 1u) == 0);
Require(random_urgent[random / 64] == 0);
Require(close_nid_postfix(random) == 0);
const int kernel_socket = socket_nid_postfix(2, 2, 0);
Require(kernel_socket >= 0 && kernel_socket < 1024 && kernel_socket != udp_receiver && kernel_socket != udp_sender);
std::array<std::uint8_t, 16> kernel_address{16, 2, 0, 0, 127, 0, 0, 1};
Require(bind_nid_postfix(kernel_socket, kernel_address.data(), kernel_address.size()) == 0);
std::uint64_t mixed_writable[16]{};
mixed_writable[kernel_socket / 64] |= std::uint64_t{1} << (kernel_socket % 64);
mixed_writable[udp_sender / 64] |= std::uint64_t{1} << (udp_sender % 64);
Require(select_nid_postfix(std::max(kernel_socket, udp_sender) + 1, nullptr, mixed_writable, nullptr, wait_second) == 2);
Require(((mixed_writable[kernel_socket / 64] >> (kernel_socket % 64)) & 1u) != 0);
Require(((mixed_writable[udp_sender / 64] >> (udp_sender % 64)) & 1u) != 0);
GuestPollEntry net_read{udp_receiver, 1, 0};
Require(poll_nid_postfix(&net_read, 1, 0) == 0 && net_read.revents == 0);
Require(sceNetSendto(udp_sender, datagram, sizeof(datagram), 0, address.data(), address.size()) == sizeof(datagram));
Require(poll_nid_postfix(&net_read, 1, 1000) == 1 && net_read.revents == 1);
Require(sceNetRecvfrom(udp_receiver, datagram_result, sizeof(datagram_result), 0, nullptr, nullptr) == sizeof(datagram_result));
GuestPollEntry mixed_poll[2]{{kernel_socket, 4, 0}, {udp_sender, 4, 0}};
Require(poll_nid_postfix(mixed_poll, 2, 1000) == 2 && mixed_poll[0].revents == 4 && mixed_poll[1].revents == 4);
Require(close_nid_postfix(kernel_socket) == 0);
std::uint64_t stdio_readable[16]{1};
std::uint64_t stdio_writable[16]{6};
Require(select_nid_postfix(3, stdio_readable, stdio_writable, nullptr, poll_now) == 2);
Require(stdio_readable[0] == 0 && stdio_writable[0] == 6);
stdio_readable[0] = 1;
Require(select_nid_postfix(1, stdio_readable, nullptr, nullptr, poll_now) == 0 && stdio_readable[0] == 0);
CheckReceiveTimeout(udp_receiver, udp_sender, address);
CheckMicrosecondTimeouts(udp_receiver);
CheckMessages(udp_receiver, udp_sender, address);
Require(sceNetSocketClose(udp_sender) == 0);
Require(sceNetSocketClose(udp_receiver) == 0);
const int resolver = sceNetResolverCreate("guest-sce-net", 0, 0);
Require(resolver >= 0);
int resolver_error = -1;
Require(sceNetResolverGetError(resolver, &resolver_error) == 0 && resolver_error == 0);
std::array<std::uint8_t, 4> ipv4{};
Require(sceNetResolverStartNtoa(resolver, "guest-sce-net.invalid.", ipv4.data(), 5000000, 1, 0) ==
static_cast<int>(0x804101E1));
Require(sceNetResolverGetError(resolver, &resolver_error) == 0 &&
resolver_error == static_cast<int>(0x804101E1));
Require(sceNetResolverStartNtoa(resolver, nullptr, ipv4.data(), 5000000, 1, 0) == static_cast<int>(0x80410116));
Require(sceNetResolverGetError(resolver, &resolver_error) == 0 &&
resolver_error == static_cast<int>(0x804101E1));
Require(sceNetResolverStartNtoa(resolver, "localhost", ipv4.data(), 5000000, 1, 0) == 0);
Require(ipv4[0] == 127);
std::array<std::uint8_t, 16> resolved6{};
resolved6.fill(0xA5);
Require(sceNetResolverStartNtoa6(resolver, "::1", resolved6.data(), 5000000, 1, 0) == 0);
Require(std::all_of(resolved6.begin(), resolved6.end() - 1, [](std::uint8_t byte) { return byte == 0; }) && resolved6[15] == 1);
Require(sceNetResolverGetError(resolver, &resolver_error) == 0 && resolver_error == 0);
Require(sceNetResolverStartNtoa6(resolver, nullptr, resolved6.data(), 5000000, 1, 0) == static_cast<int>(0x80410116));
Require(sceNetResolverStartNtoa6(resolver, "127.0.0.1", resolved6.data(), 5000000, 1, 0) == static_cast<int>(0x804101E1));
Require(sceNetResolverGetError(resolver, &resolver_error) == 0 && resolver_error == static_cast<int>(0x804101E1));
Require(sceNetResolverStartNtoa(resolver, "localhost", ipv4.data(), 5000000, 1, 0) == 0);
Require(sceNetResolverGetError(resolver, &resolver_error) == 0 && resolver_error == 0);
std::array<std::uint8_t, 512> records{};
records.fill(0xA5);
Require(sceNetResolverStartNtoaMultipleRecordsEx(resolver, "localhost", records.data(), 5000000, 1, 0) == 0);
std::int32_t record_family = 0;
std::int32_t record_count = 0;
std::int32_t record_count4 = 0;
std::memcpy(&record_family, records.data() + 16, sizeof(record_family));
std::memcpy(&record_count, records.data() + 320, sizeof(record_count));
std::memcpy(&record_count4, records.data() + 324, sizeof(record_count4));
Require(records[0] == 127 && record_family == 2 && record_count >= 1 && record_count <= 10 && record_count4 == record_count);
Require(std::all_of(records.begin() + 32 * record_count, records.begin() + 320, [](std::uint8_t byte) { return byte == 0; }));
Require(std::all_of(records.begin() + 328, records.begin() + 384, [](std::uint8_t byte) { return byte == 0; }));
Require(std::all_of(records.begin() + 384, records.end(), [](std::uint8_t byte) { return byte == 0xA5; }));
for (int first = 0; first < record_count; ++first) {
for (int second = first + 1; second < record_count; ++second) {
Require(std::memcmp(records.data() + 32 * first, records.data() + 32 * second, 4) != 0);
}
}
const auto resolved = records;
Require(sceNetResolverStartNtoaMultipleRecordsEx(resolver, nullptr, records.data(), 5000000, 1, 0) ==
static_cast<int>(0x80410116) && *sceNetErrnoLoc() == 22 && records == resolved);
Require(sceNetResolverStartNtoaMultipleRecordsEx(resolver, "localhost", nullptr, 5000000, 1, 0) ==
static_cast<int>(0x80410116) && *sceNetErrnoLoc() == 22);
Require(sceNetResolverStartNtoaMultipleRecordsEx(resolver, "guest-sce-net.invalid.", records.data(), 5000000, 1, 0) ==
static_cast<int>(0x804101E1) && records == resolved);
std::array<std::uint8_t, 512> plain{};
plain.fill(0xA5);
Require(sceNetResolverStartNtoaMultipleRecords(resolver, "localhost", plain.data(), 5000000, 1, 0) == 0);
Require(plain == resolved);
Require(sceNetResolverGetError(resolver, &resolver_error) == 0 && resolver_error == 0);
Require(sceNetResolverStartNtoaMultipleRecords(resolver, nullptr, plain.data(), 5000000, 1, 0) ==
static_cast<int>(0x80410116) && *sceNetErrnoLoc() == 22 && plain == resolved);
Require(sceNetResolverStartNtoaMultipleRecords(resolver, "localhost", nullptr, 5000000, 1, 0) ==
static_cast<int>(0x80410116) && *sceNetErrnoLoc() == 22);
Require(sceNetResolverStartNtoaMultipleRecords(resolver, "guest-sce-net.invalid", plain.data(), 5000000, 1, 0) ==
static_cast<int>(0x804101E1) && plain == resolved);
Require(sceNetResolverGetError(resolver, &resolver_error) == 0 && resolver_error == static_cast<int>(0x804101E1));
for (const int flags : {0x1, 0x10000}) {
bool threw = false;
try { sceNetResolverStartNtoaMultipleRecords(resolver, "localhost", plain.data(), 5000000, 1, flags); }
catch (const std::runtime_error&) { threw = true; }
Require(threw && plain == resolved);
}
const std::array<std::uint8_t, 4> loopback{127, 0, 0, 1};
char reverse[256]{};
Require(sceNetResolverAbort(resolver, 0) == 0);
Require(sceNetResolverGetError(resolver, &resolver_error) == 0 && resolver_error == static_cast<int>(0x804101E1));
Require(sceNetResolverStartNtoa(resolver, "localhost", ipv4.data(), 5000000, 1, 0) == 0);
Require(sceNetResolverGetError(resolver, &resolver_error) == 0 && resolver_error == 0);
Require(sceNetResolverAbort(resolver, 1) == 0);
Require(sceNetResolverAbort(resolver, 1) == 0);
Require(sceNetResolverGetError(resolver, &resolver_error) == 0 && resolver_error == 0);
const int reverse_result = sceNetResolverStartAton(resolver, loopback.data(), reverse, sizeof(reverse), 5000000, 1, 0);
Require(reverse_result == 0 || reverse_result == static_cast<int>(0x804101E1));
Require(sceNetResolverStartNtoa(resolver, nullptr, ipv4.data(), 5000000, 1, 0) == static_cast<int>(0x80410116));
ipv4.fill(0xA5);
Require(sceNetResolverStartNtoa(resolver, "localhost", ipv4.data(), 5000000, 1, 0) == static_cast<int>(0x80410104) &&
*sceNetErrnoLoc() == 4 && ipv4[0] == 0xA5);
Require(sceNetResolverGetError(resolver, &resolver_error) == 0 && resolver_error == static_cast<int>(0x80410104));
Require(sceNetResolverStartNtoa(resolver, "localhost", ipv4.data(), 5000000, 1, 0) == 0 && ipv4[0] == 127);
Require(sceNetResolverGetError(resolver, &resolver_error) == 0 && resolver_error == 0);
Require(sceNetResolverAbort(resolver, 1) == 0);
Require(sceNetResolverStartNtoaMultipleRecords(resolver, "localhost", plain.data(), 5000000, 1, 0) ==
static_cast<int>(0x80410104) && *sceNetErrnoLoc() == 4);
Require(sceNetResolverStartNtoaMultipleRecords(resolver, "localhost", plain.data(), 5000000, 1, 0) == 0 && plain == resolved);
Require(sceNetResolverAbort(resolver, 2) == 0);
Require(sceNetResolverStartNtoaMultipleRecordsEx(resolver, "localhost", records.data(), 5000000, 1, 0) == 0 && records == resolved);
std::memset(reverse, 0x5A, sizeof(reverse));
Require(sceNetResolverStartAton(resolver, loopback.data(), reverse, sizeof(reverse), 5000000, 1, 0) ==
static_cast<int>(0x80410104) && *sceNetErrnoLoc() == 4 && reverse[0] == 0x5A);
Require(sceNetResolverGetError(resolver, &resolver_error) == 0 && resolver_error == static_cast<int>(0x80410104));
Require(sceNetResolverStartAton(resolver, loopback.data(), reverse, sizeof(reverse), 5000000, 1, 0) == reverse_result);
Require(sceNetResolverAbort(resolver, 3) == 0);
Require(sceNetResolverStartAton(resolver, loopback.data(), reverse, sizeof(reverse), 5000000, 1, 0) ==
static_cast<int>(0x80410104) && *sceNetErrnoLoc() == 4);
Require(sceNetResolverStartNtoaMultipleRecordsEx(resolver, "localhost", records.data(), 5000000, 1, 0) ==
static_cast<int>(0x80410104) && *sceNetErrnoLoc() == 4 && records == resolved);
Require(sceNetResolverStartNtoa(resolver, "localhost", ipv4.data(), 5000000, 1, 0) == 0);
for (const int flags : {0x4, -1}) {
bool threw = false;
try { sceNetResolverAbort(resolver, flags); } catch (const std::runtime_error&) { threw = true; }
Require(threw);
}
Require(sceNetResolverStartNtoa(resolver, "localhost", ipv4.data(), 5000000, 1, 0) == 0);
std::array<std::uint8_t, 16> loopback6{};
loopback6[15] = 1;
std::memset(reverse, 0x5A, sizeof(reverse));
const int reverse6_result = sceNetResolverStartAton6(resolver, loopback6.data(), reverse, sizeof(reverse), 5000000, 1, 0);
Require(reverse6_result == 0 || reverse6_result == static_cast<int>(0x804101E1));
Require(sceNetResolverGetError(resolver, &resolver_error) == 0 && resolver_error == reverse6_result);
char host_reverse6[256]{};
sockaddr_in6 host_loopback6{};
host_loopback6.sin6_family = AF_INET6;
std::memcpy(&host_loopback6.sin6_addr, loopback6.data(), loopback6.size());
const bool host_named6 = ::getnameinfo(reinterpret_cast<const sockaddr*>(&host_loopback6), sizeof(host_loopback6),
host_reverse6, sizeof(host_reverse6), nullptr, 0, NI_NAMEREQD) == 0;
Require(host_named6 == (reverse6_result == 0));
if (reverse6_result == 0) {
Require(std::strcmp(reverse, host_reverse6) == 0);
const auto name_length = static_cast<int>(std::strlen(host_reverse6));
char exact[256];
std::memset(exact, 0x5A, sizeof(exact));
Require(sceNetResolverStartAton6(resolver, loopback6.data(), exact, name_length + 1, 5000000, 1, 0) == 0 &&
std::strcmp(exact, host_reverse6) == 0 && static_cast<unsigned char>(exact[name_length + 1]) == 0x5A);
std::memset(exact, 0x5A, sizeof(exact));
Require(sceNetResolverStartAton6(resolver, loopback6.data(), exact, name_length, 5000000, 1, 0) ==
static_cast<int>(0x804101E1) && static_cast<unsigned char>(exact[0]) == 0x5A);
} else {
Require(static_cast<unsigned char>(reverse[0]) == 0x5A);
}
std::memset(reverse, 0x5A, sizeof(reverse));
Require(sceNetResolverAbort(resolver, 2) == 0);
Require(sceNetResolverStartAton6(resolver, loopback6.data(), reverse, sizeof(reverse), 5000000, 1, 0) ==
static_cast<int>(0x80410104) && *sceNetErrnoLoc() == 4 && static_cast<unsigned char>(reverse[0]) == 0x5A);
Require(sceNetResolverGetError(resolver, &resolver_error) == 0 && resolver_error == static_cast<int>(0x80410104));
Require(sceNetResolverAbort(resolver, 1) == 0);
Require(sceNetResolverStartAton6(resolver, loopback6.data(), reverse, sizeof(reverse), 5000000, 1, 0) == reverse6_result);
Require(sceNetResolverStartNtoa(resolver, "localhost", ipv4.data(), 5000000, 1, 0) == static_cast<int>(0x80410104));
Require(sceNetResolverStartAton6(resolver, nullptr, reverse, sizeof(reverse), 5000000, 1, 0) == static_cast<int>(0x80410116) &&
*sceNetErrnoLoc() == 22);
Require(sceNetResolverStartAton6(resolver, loopback6.data(), nullptr, sizeof(reverse), 5000000, 1, 0) == static_cast<int>(0x80410116));
Require(sceNetResolverStartAton6(resolver, loopback6.data(), reverse, 0, 5000000, 1, 0) == static_cast<int>(0x80410116));
Require(sceNetResolverStartAton6(resolver + 1000, loopback6.data(), reverse, sizeof(reverse), 5000000, 1, 0) ==
static_cast<int>(0x80410109) && *sceNetErrnoLoc() == 9);
for (const int flags : {0x1, 0x10000, -1}) {
bool threw = false;
try { sceNetResolverStartAton6(resolver, loopback6.data(), reverse, sizeof(reverse), 5000000, 1, flags); }
catch (const std::runtime_error&) { threw = true; }
Require(threw);
}
const int other = sceNetResolverCreate("guest-sce-net-other", 0, 0);
Require(other >= 0 && other != resolver);
Require(sceNetResolverAbort(other, 3) == 0);
Require(sceNetResolverStartNtoaMultipleRecords(resolver, "localhost", plain.data(), 5000000, 1, 0) == 0);
Require(sceNetResolverDestroy(other) == 0);
Require(sceNetResolverAbort(other, 0) == static_cast<int>(0x80410109) && *sceNetErrnoLoc() == 9);
Require(sceNetResolverAbort(-1, 1) == static_cast<int>(0x80410109) && *sceNetErrnoLoc() == 9);
Require(sceNetResolverGetError(resolver, nullptr) == static_cast<int>(0x80410116) && *sceNetErrnoLoc() == 22);
Require(sceNetClearDnsCache(0) == 0);
ipv4.fill(0xA5);
Require(sceNetResolverStartNtoa(resolver, "localhost", ipv4.data(), 5000000, 1, 0) == 0 && ipv4[0] == 127);
Require(sceNetResolverGetError(resolver, &resolver_error) == 0 && resolver_error == 0);
for (const int flags : {0x1, -1}) {
bool threw = false;
try { sceNetClearDnsCache(flags); } catch (const std::runtime_error&) { threw = true; }
Require(threw);
}
Require(sceNetResolverDestroy(resolver) == 0);
resolver_error = -1;
Require(sceNetResolverGetError(resolver, &resolver_error) == static_cast<int>(0x80410109) &&
*sceNetErrnoLoc() == 9 && resolver_error == -1);
Require(sceNetResolverStartNtoaMultipleRecordsEx(resolver, "localhost", records.data(), 5000000, 1, 0) ==
static_cast<int>(0x80410109) && *sceNetErrnoLoc() == 9 && records == resolved);
Require(sceNetResolverStartNtoaMultipleRecords(resolver, "localhost", plain.data(), 5000000, 1, 0) ==
static_cast<int>(0x80410109) && *sceNetErrnoLoc() == 9 && plain == resolved);
Require(sceNetResolverAbort(resolver, 1) == static_cast<int>(0x80410109) && *sceNetErrnoLoc() == 9);
std::array<std::uint8_t, 16> ipv6{};
Require(sceNetInetPton(28, "::1", ipv6.data()) == 1);
char text[64]{};
Require(sceNetInetNtop(28, ipv6.data(), text, sizeof(text)) == text);
Require(std::strcmp(text, "::1") == 0);
CheckStreamSender(2);
if (!ipv6Unavailable) CheckStreamSender(28);
int state = -1;
Require(sceNetCtlGetState(&state) == 0);
Require(state == 0 || state == 3);
Require(sceNetCtlGetIfStat(nullptr) == static_cast<int>(0x80412107));
{
const char* counterPath = "guest_sce_net_byte_counter.txt";
std::FILE* counter = std::fopen(counterPath, "w");
Require(counter != nullptr);
Require(std::fputs("1099511627776", counter) >= 0);
Require(std::fclose(counter) == 0);
Require(NetCtlReadByteCounter_nid_no_patch(counterPath) == 1099511627776ull);
Require(std::remove(counterPath) == 0);
bool missing = false;
try {
NetCtlReadByteCounter_nid_no_patch(counterPath);
} catch (const std::runtime_error&) {
missing = true;
}
Require(missing);
}
{
std::array<std::uint32_t, 3> dns{};
dns.fill(0xA5A5A5A5u);
const int count = sceNetGetDnsInfo(dns.data(), 0);
Require(count >= 0 && count <= 2 && dns[2] == 0xA5A5A5A5u);
Require(count == 0 || state == 3);
for (int index = count; index < 2; ++index) Require(dns[index] == 0);
for (int index = 0; index < 2; ++index) {
NetCtlInfo info{};
const int result = sceNetCtlGetInfo(17 + index, &info);
if (result != 0) {
Require(result == static_cast<int>(0x80412108) && count == 0);
continue;
}
const auto* bytes = reinterpret_cast<const std::uint8_t*>(&dns[index]);
char text[16] = {};
std::snprintf(text, sizeof(text), "%u.%u.%u.%u", static_cast<unsigned>(bytes[0]), static_cast<unsigned>(bytes[1]), static_cast<unsigned>(bytes[2]), static_cast<unsigned>(bytes[3]));
Require(std::strcmp(text, reinterpret_cast<const char*>(info.opaque)) == 0);
}
for (const int flags : {0x1, -1}) {
bool threw = false;
try { sceNetGetDnsInfo(dns.data(), flags); } catch (const std::runtime_error&) { threw = true; }
Require(threw);
}
bool threw = false;
try { sceNetGetDnsInfo(nullptr, 0); } catch (const std::runtime_error&) { threw = true; }
Require(threw);
}
NetCtlIfStat first{};
first.device = 7;
first.reserved[7] = 7;
const int statResult = sceNetCtlGetIfStat(&first);
Require(statResult == (state == 3 ? 0 : static_cast<int>(0x80412108)));
if (statResult == 0) {
Require(first.device == 0 && first.reserved[7] == 0);
NetCtlIfStat second{};
Require(sceNetCtlGetIfStat(&second) == 0);
Require(second.tx_bytes >= first.tx_bytes && second.rx_bytes >= first.rx_bytes);
}
{
int cid = -1;
Require(sceNetCtlRegisterCallbackV6(nullptr, nullptr, &cid) == static_cast<int>(0x80412107));
Require(sceNetCtlRegisterCallbackV6(IgnoreNetCtlEvent, nullptr, nullptr) == static_cast<int>(0x80412107));
int cids[8] = {};
for (int i = 0; i < 8; ++i) {
Require(sceNetCtlRegisterCallbackV6(IgnoreNetCtlEvent, nullptr, &cids[i]) == 0);
Require(cids[i] == i);
}
Require(sceNetCtlRegisterCallbackV6(IgnoreNetCtlEvent, nullptr, &cid) == static_cast<int>(0x80412103));
Require(sceNetCtlUnregisterCallbackV6(-1) == static_cast<int>(0x80412105));
Require(sceNetCtlUnregisterCallbackV6(8) == static_cast<int>(0x80412105));
Require(sceNetCtlUnregisterCallbackV6(3) == 0);
Require(sceNetCtlRegisterCallbackV6(IgnoreNetCtlEvent, nullptr, &cid) == 0);
Require(cid == 3);
for (int i = 0; i < 8; ++i) {
Require(sceNetCtlUnregisterCallbackV6(i) == 0);
}
}
{
int cid = -1;
Require(sceNetCtlRegisterCallback(nullptr, nullptr, &cid) == static_cast<int>(0x80412107));
Require(sceNetCtlRegisterCallback(IgnoreNetCtlEvent, nullptr, nullptr) == static_cast<int>(0x80412107));
int cids[8] = {};
for (int i = 0; i < 8; ++i) {
Require(sceNetCtlRegisterCallback(IgnoreNetCtlEvent, nullptr, &cids[i]) == 0);
Require(cids[i] == i);
}
Require(sceNetCtlRegisterCallback(IgnoreNetCtlEvent, nullptr, &cid) == static_cast<int>(0x80412103));
Require(sceNetCtlRegisterCallbackV6(IgnoreNetCtlEvent, nullptr, &cid) == 0);
Require(sceNetCtlUnregisterCallback(-1) == static_cast<int>(0x80412105));
Require(sceNetCtlUnregisterCallback(8) == static_cast<int>(0x80412105));
Require(sceNetCtlUnregisterCallback(3) == 0);
Require(sceNetCtlRegisterCallback(IgnoreNetCtlEvent, nullptr, &cid) == 0);
Require(cid == 3);
for (int i = 0; i < 8; ++i) {
Require(sceNetCtlUnregisterCallback(i) == 0);
}
Require(sceNetCtlUnregisterCallbackV6(0) == 0);
}
if (ipv6Unavailable) {
std::puts("skipped the IPv6 socket checks, the host has no usable IPv6 loopback");
return 77;
}
}