#include "SceTypes.hpp" #include "prx/libc/include/general/VabiMacros.hpp" #include #include #include #include #include #include #include struct GuestTimeval { std::int64_t seconds; std::int64_t microseconds; }; struct GuestDescriptorSet { std::array words{}; void Add(int descriptor) { words[descriptor / 64] |= (std::uint64_t{1} << (descriptor % 64)); } bool Has(int descriptor) const { return ((words[descriptor / 64] >> (descriptor % 64)) & 1u) != 0; } }; struct PollDescriptor { int descriptor; short events; short revents; }; extern "C" { int APS5_VABI socket_nid_postfix(int, int, int); int APS5_VABI bind_nid_postfix(int, const void*, std::uint32_t); int APS5_VABI getsockname_nid_postfix(int, void*, std::uint32_t*); int APS5_VABI connect_nid_postfix(int, const void*, std::uint32_t); std::int64_t APS5_VABI send_nid_postfix(int, const void*, std::uint64_t, int); std::int64_t APS5_VABI recv_nid_postfix(int, void*, std::uint64_t, int); int APS5_VABI close_nid_postfix(int); int APS5_VABI sceKernelClose(int); std::int64_t APS5_VABI fstat_nid_disambig1_nid_postfix(int, FileStat*); int APS5_VABI dup_nid_postfix(int); int APS5_VABI open_nid_postfix(const char*, int, int); std::int64_t APS5_VABI read_nid_postfix(int, void*, std::uint64_t); int APS5_VABI select_nid_postfix(int, void*, void*, void*, const void*); int APS5_VABI poll_nid_postfix(PollDescriptor*, std::uint32_t, int); int* APS5_VABI __error_nid_postfix(); } static void Check(bool value, int line) { if (!value) { std::fprintf(stderr, "socket descriptor check failed at line %d, errno %d\n", line, *__error_nid_postfix()); std::abort(); } } #define Require(value) Check((value), __LINE__) constexpr int DescriptorSetSize = 1024; constexpr int Inet = 2; constexpr int Datagram = 2; constexpr short In = 0x1; constexpr int KernelBadDescriptor = static_cast(0x80020009u); template static bool Throws(TCall call) { try { call(); } catch (const std::runtime_error&) { return true; } return false; } static int Socket() { const int descriptor = socket_nid_postfix(Inet, Datagram, 0); Require(descriptor >= 0 && descriptor < DescriptorSetSize); return descriptor; } static int BoundSocket(std::array& address) { const int descriptor = Socket(); address = {16, 2, 0, 0, 127, 0, 0, 1}; Require(bind_nid_postfix(descriptor, address.data(), address.size()) == 0); std::uint32_t size = address.size(); Require(getsockname_nid_postfix(descriptor, address.data(), &size) == 0); return descriptor; } static int ConnectedSocket(const std::array& address) { const int descriptor = Socket(); Require(connect_nid_postfix(descriptor, address.data(), address.size()) == 0); return descriptor; } static void WaitReadable(int descriptor) { GuestDescriptorSet read{}; read.Add(descriptor); GuestTimeval limit{2, 0}; Require(select_nid_postfix(descriptor + 1, read.words.data(), nullptr, nullptr, &limit) == 1); Require(read.Has(descriptor)); } static void SelectSocketAndFile() { std::array address{}; const int receiver = BoundSocket(address); const int sender = ConnectedSocket(address); const int file = open_nid_postfix("/dev/urandom", 0, 0); Require(file >= 0 && file < DescriptorSetSize && file != receiver && file != sender); const int highest = std::max({receiver, sender, file}); { GuestDescriptorSet read{}; read.Add(receiver); GuestTimeval none{0, 0}; Require(select_nid_postfix(receiver + 1, read.words.data(), nullptr, nullptr, &none) == 0); Require(!read.Has(receiver)); } { GuestDescriptorSet read{}; read.Add(receiver); read.Add(file); GuestTimeval none{0, 0}; Require(select_nid_postfix(highest + 1, read.words.data(), nullptr, nullptr, &none) == 1); Require(read.Has(file) && !read.Has(receiver)); } const char byte = 'd'; Require(send_nid_postfix(sender, &byte, 1, 0) == 1); WaitReadable(receiver); { GuestDescriptorSet read{}; GuestDescriptorSet write{}; read.Add(receiver); read.Add(file); write.Add(sender); GuestTimeval none{0, 0}; Require(select_nid_postfix(highest + 1, read.words.data(), write.words.data(), nullptr, &none) == 3); Require(read.Has(receiver) && read.Has(file) && write.Has(sender)); } char received = 0; Require(recv_nid_postfix(receiver, &received, 1, 0) == 1 && received == byte); for (const int descriptor : {receiver, sender, file}) Require(close_nid_postfix(descriptor) == 0); } static void CloseReleasesNumber() { const int socket = Socket(); Require(close_nid_postfix(socket) == 0); FileStat status{}; Require(fstat_nid_disambig1_nid_postfix(socket, &status) == -1 && *__error_nid_postfix() == 9); std::vector files; while (files.size() < 8 && (files.empty() || files.back() != socket)) { const int file = open_nid_postfix("/dev/urandom", 0, 0); Require(file >= 0); files.push_back(file); } Require(files.back() == socket); unsigned char bytes[4]{}; Require(read_nid_postfix(socket, bytes, sizeof(bytes)) == sizeof(bytes)); PollDescriptor entry{socket, In, 0}; Require(Throws([&] { poll_nid_postfix(&entry, 1, 0); })); for (const int file : files) Require(close_nid_postfix(file) == 0); const int kernelClosed = Socket(); Require(sceKernelClose(kernelClosed) == 0); Require(sceKernelClose(kernelClosed) == KernelBadDescriptor); entry = {kernelClosed, In, 0}; Require(poll_nid_postfix(&entry, 1, 0) == 1 && entry.revents == 0x20); } static void DuplicateKeepsSocket() { std::array address{}; const int receiver = BoundSocket(address); const int sender = ConnectedSocket(address); const int duplicate = dup_nid_postfix(receiver); Require(duplicate >= 0 && duplicate < DescriptorSetSize && duplicate != receiver && duplicate != sender); Require(close_nid_postfix(receiver) == 0); const char byte = 'u'; Require(send_nid_postfix(sender, &byte, 1, 0) == 1); WaitReadable(duplicate); char received = 0; Require(recv_nid_postfix(duplicate, &received, 1, 0) == 1 && received == byte); Require(close_nid_postfix(duplicate) == 0); Require(close_nid_postfix(sender) == 0); } int main() { SelectSocketAndFile(); CloseReleasesNumber(); DuplicateKeepsSocket(); return 0; }