mirror of
https://github.com/boykopovar/AnyPS5.git
synced 2026-10-22 00:22:57 -05:00
FreeBSD's in_getsockaddr and in6_getsockaddr succeed for a socket that has no local address and return the wildcard address with port 0. Winsock fails getsockname with WSAEINVAL until the socket is bound, which the guest saw as EINVAL. Return the zero address of the socket's family for that error.
211 lines
10 KiB
C++
211 lines
10 KiB
C++
#include "prx/libc/include/general/VabiMacros.hpp"
|
|
#include <algorithm>
|
|
#include <array>
|
|
#include <chrono>
|
|
#include <cstdint>
|
|
#include <cstdio>
|
|
#include <cstdlib>
|
|
#include <cstring>
|
|
#include <thread>
|
|
|
|
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 listen_nid_postfix(int, int);
|
|
int APS5_VABI getsockname_nid_postfix(int, void*, std::uint32_t*);
|
|
int APS5_VABI connect_nid_postfix(int, const void*, std::uint32_t);
|
|
int APS5_VABI accept_nid_postfix(int, 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 getpeername_nid_postfix(int, void*, std::uint32_t*);
|
|
int APS5_VABI shutdown_nid_postfix(int, int);
|
|
std::int64_t APS5_VABI recvfrom_nid_postfix(int, void*, std::uint64_t, int, void*, std::uint32_t*);
|
|
int APS5_VABI fcntl_nid_postfix(int, int, ...);
|
|
int APS5_VABI close_nid_postfix(int);
|
|
int* APS5_VABI __error_nid_postfix();
|
|
}
|
|
|
|
static void Require(bool condition) {
|
|
if (!condition) std::abort();
|
|
}
|
|
|
|
static void StreamReceiveReportsNoSender() {
|
|
const int listener = socket_nid_postfix(2, 1, 0);
|
|
Require(listener >= 0);
|
|
std::array<std::uint8_t, 16> address{16, 2, 0, 0, 127, 0, 0, 1};
|
|
Require(bind_nid_postfix(listener, address.data(), address.size()) == 0 && listen_nid_postfix(listener, 4) == 0);
|
|
std::uint32_t size = address.size();
|
|
Require(getsockname_nid_postfix(listener, address.data(), &size) == 0);
|
|
const int client = socket_nid_postfix(2, 1, 0);
|
|
Require(connect_nid_postfix(client, address.data(), address.size()) == 0);
|
|
const int accepted = accept_nid_postfix(listener, nullptr, nullptr);
|
|
Require(accepted >= 0);
|
|
const char text[] = "stream";
|
|
Require(send_nid_postfix(client, text, sizeof(text), 0) == sizeof(text));
|
|
std::array<std::uint8_t, 28> sender;
|
|
sender.fill(0xaa);
|
|
char received[sizeof(text)]{};
|
|
std::uint32_t senderSize = sender.size();
|
|
Require(recvfrom_nid_postfix(accepted, received, sizeof(received), 0, sender.data(), &senderSize) == sizeof(text));
|
|
Require(std::strcmp(text, received) == 0);
|
|
Require(senderSize == 0 && sender[0] == 0xaa && sender[1] == 0xaa);
|
|
for (const int descriptor : {client, accepted, listener}) Require(close_nid_postfix(descriptor) == 0);
|
|
}
|
|
|
|
static void ListenersShareAPortAcrossFamilies() {
|
|
const int inet = socket_nid_postfix(2, 1, 0);
|
|
Require(inet >= 0);
|
|
std::array<std::uint8_t, 16> any{16, 2};
|
|
Require(bind_nid_postfix(inet, any.data(), any.size()) == 0 && listen_nid_postfix(inet, 4) == 0);
|
|
std::uint32_t size = any.size();
|
|
Require(getsockname_nid_postfix(inet, any.data(), &size) == 0 && size == any.size());
|
|
const int inet6 = socket_nid_postfix(28, 1, 0);
|
|
if (inet6 < 0) {
|
|
Require(*__error_nid_postfix() == 47);
|
|
Require(close_nid_postfix(inet) == 0);
|
|
return;
|
|
}
|
|
std::array<std::uint8_t, 28> any6{28, 28, any[2], any[3]};
|
|
if (bind_nid_postfix(inet6, any6.data(), any6.size()) != 0) {
|
|
if (*__error_nid_postfix() != 49) {
|
|
std::fprintf(stderr, "bind of [::]:%d failed with errno %d while an IPv4 listener holds 0.0.0.0:%d\n",
|
|
(any[2] << 8) | any[3], *__error_nid_postfix(), (any[2] << 8) | any[3]);
|
|
std::abort();
|
|
}
|
|
} else {
|
|
Require(listen_nid_postfix(inet6, 4) == 0);
|
|
}
|
|
Require(close_nid_postfix(inet6) == 0);
|
|
Require(close_nid_postfix(inet) == 0);
|
|
}
|
|
|
|
constexpr int GetFlags = 3;
|
|
constexpr int SetFlags = 4;
|
|
constexpr int NonBlockingFlag = 4;
|
|
|
|
static int AcceptWithin(int listener) {
|
|
for (int attempt = 0; attempt < 200; ++attempt) {
|
|
const int accepted = accept_nid_postfix(listener, nullptr, nullptr);
|
|
if (accepted >= 0) return accepted;
|
|
Require(*__error_nid_postfix() == 35);
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
static void AcceptedSocketsFollowListenerBlocking() {
|
|
const int listener = socket_nid_postfix(2, 1, 0);
|
|
Require(listener >= 0);
|
|
std::array<std::uint8_t, 16> address{16, 2, 0, 0, 127, 0, 0, 1};
|
|
Require(bind_nid_postfix(listener, address.data(), address.size()) == 0 && listen_nid_postfix(listener, 4) == 0);
|
|
std::uint32_t size = address.size();
|
|
Require(getsockname_nid_postfix(listener, address.data(), &size) == 0);
|
|
|
|
const int firstClient = socket_nid_postfix(2, 1, 0);
|
|
Require(connect_nid_postfix(firstClient, address.data(), address.size()) == 0);
|
|
const int blocking = accept_nid_postfix(listener, nullptr, nullptr);
|
|
Require(blocking >= 0 && (fcntl_nid_postfix(blocking, GetFlags) & NonBlockingFlag) == 0);
|
|
|
|
Require(fcntl_nid_postfix(listener, SetFlags, NonBlockingFlag) == 0);
|
|
const int secondClient = socket_nid_postfix(2, 1, 0);
|
|
Require(connect_nid_postfix(secondClient, address.data(), address.size()) == 0);
|
|
const int inherited = AcceptWithin(listener);
|
|
Require(inherited >= 0 && (fcntl_nid_postfix(inherited, GetFlags) & NonBlockingFlag) != 0);
|
|
char byte;
|
|
Require(recv_nid_postfix(inherited, &byte, 1, 0) == -1 && *__error_nid_postfix() == 35);
|
|
Require(fcntl_nid_postfix(inherited, SetFlags, 0) == 0 && (fcntl_nid_postfix(inherited, GetFlags) & NonBlockingFlag) == 0);
|
|
Require((fcntl_nid_postfix(listener, GetFlags) & NonBlockingFlag) != 0);
|
|
|
|
for (const int descriptor : {firstClient, secondClient, blocking, inherited, listener}) Require(close_nid_postfix(descriptor) == 0);
|
|
}
|
|
|
|
static void UnboundSocketNameIsZero() {
|
|
for (const int family : {2, 28}) {
|
|
for (const int type : {1, 2}) {
|
|
const int descriptor = socket_nid_postfix(family, type, 0);
|
|
Require(descriptor >= 0);
|
|
const std::uint32_t expected = family == 2 ? 16 : 28;
|
|
std::array<std::uint8_t, 32> name;
|
|
name.fill(0xaa);
|
|
std::uint32_t size = name.size();
|
|
Require(getsockname_nid_postfix(descriptor, name.data(), &size) == 0);
|
|
Require(size == expected && name[0] == expected && name[1] == family);
|
|
for (std::uint32_t index = 2; index < expected; ++index) Require(name[index] == 0);
|
|
Require(name[expected] == 0xaa);
|
|
Require(close_nid_postfix(descriptor) == 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
int main() {
|
|
StreamReceiveReportsNoSender();
|
|
ListenersShareAPortAcrossFamilies();
|
|
AcceptedSocketsFollowListenerBlocking();
|
|
UnboundSocketNameIsZero();
|
|
const int listener = socket_nid_postfix(2, 1, 6);
|
|
Require(listener >= 0);
|
|
std::array<std::uint8_t, 16> address{16, 2, 0, 0, 127, 0, 0, 1};
|
|
Require(bind_nid_postfix(listener, address.data(), address.size()) == 0);
|
|
Require(bind_nid_postfix(listener, address.data(), address.size()) == -1 && *__error_nid_postfix() == 22);
|
|
Require(listen_nid_postfix(listener, 4) == 0);
|
|
std::uint32_t address_size = address.size();
|
|
Require(getsockname_nid_postfix(listener, address.data(), &address_size) == 0);
|
|
Require(address_size == 16 && (address[2] != 0 || address[3] != 0));
|
|
|
|
const int client = socket_nid_postfix(2, 1, 0);
|
|
Require(client >= 0);
|
|
Require(accept_nid_postfix(client, nullptr, nullptr) == -1 && *__error_nid_postfix() == 22);
|
|
Require(connect_nid_postfix(client, address.data(), address.size()) == 0);
|
|
std::array<std::uint8_t, 16> peer{};
|
|
address_size = peer.size();
|
|
const int accepted = accept_nid_postfix(listener, peer.data(), &address_size);
|
|
Require(accepted >= 0 && address_size == 16 && peer[1] == 2);
|
|
|
|
std::array<std::uint8_t, 16> connected_peer{};
|
|
address_size = connected_peer.size();
|
|
Require(getpeername_nid_postfix(accepted, connected_peer.data(), &address_size) == 0);
|
|
Require(address_size == 16 && connected_peer[1] == 2);
|
|
|
|
for (auto query : {getsockname_nid_postfix, getpeername_nid_postfix}) {
|
|
std::array<std::uint8_t, 16> expected{};
|
|
address_size = expected.size();
|
|
Require(query(accepted, expected.data(), &address_size) == 0 && address_size == expected.size());
|
|
for (std::uint32_t capacity : {0u, 1u, 2u, 15u, 16u, 28u, UINT32_MAX}) {
|
|
std::array<std::uint8_t, 28> actual;
|
|
actual.fill(0xa5);
|
|
address_size = capacity;
|
|
const auto copied = std::min(capacity, std::uint32_t{16});
|
|
Require(query(accepted, actual.data(), &address_size) == 0 && address_size == copied);
|
|
Require(std::memcmp(actual.data(), expected.data(), copied) == 0);
|
|
Require(std::all_of(actual.begin() + copied, actual.end(), [](auto byte) { return byte == 0xa5; }));
|
|
}
|
|
}
|
|
const int shortClient = socket_nid_postfix(2, 1, 0);
|
|
Require(shortClient >= 0 && connect_nid_postfix(shortClient, address.data(), address.size()) == 0);
|
|
peer.fill(0xa5);
|
|
address_size = 1;
|
|
const int shortAccepted = accept_nid_postfix(listener, peer.data(), &address_size);
|
|
Require(shortAccepted >= 0 && address_size == 1 && peer[0] == 16);
|
|
Require(std::all_of(peer.begin() + 1, peer.end(), [](auto byte) { return byte == 0xa5; }));
|
|
Require(close_nid_postfix(shortAccepted) == 0 && close_nid_postfix(shortClient) == 0);
|
|
|
|
const char request[] = "guest TCP loopback";
|
|
char received[sizeof(request)]{};
|
|
Require(send_nid_postfix(client, request, sizeof(request), 0x1) == -1 && *__error_nid_postfix() == 45);
|
|
Require(send_nid_postfix(client, request, sizeof(request), 0x20000) == sizeof(request));
|
|
Require(recv_nid_postfix(accepted, received, sizeof(received), 0) == sizeof(received));
|
|
Require(std::strcmp(request, received) == 0);
|
|
Require(shutdown_nid_postfix(client, 1) == 0);
|
|
Require(send_nid_postfix(client, request, sizeof(request), 0x20000) == -1 && *__error_nid_postfix() == 32);
|
|
|
|
Require(close_nid_postfix(accepted) == 0);
|
|
#ifndef _WIN32
|
|
std::int64_t sent = 0;
|
|
for (int i = 0; i < 100 && sent >= 0; ++i) sent = send_nid_postfix(client, request, sizeof(request), 0x20000);
|
|
Require(sent == -1 && *__error_nid_postfix() == 32);
|
|
#endif
|
|
Require(close_nid_postfix(client) == 0);
|
|
Require(close_nid_postfix(listener) == 0);
|
|
Require(close_nid_postfix(listener) == -1 && *__error_nid_postfix() == 9);
|
|
}
|