mirror of
https://github.com/boykopovar/AnyPS5.git
synced 2026-10-21 16:12:47 -05:00
Windows answers a SYN to a closed port with a RST and then retransmits the SYN, so connect to a closed loopback port takes about 2 s before ECONNREFUSED; FreeBSD fails at once. PS5VR (PPSA99177) makes about 33 such connects to 127.0.0.1:17600 and reaches its home screen 45 s late. When no socket listens on the destination port (GetExtendedTcpTable), a loopback connect now sets SIO_TCP_INITIAL_RTO with no SYN retransmissions and fails at once. A port with a listener keeps the retransmissions: Windows also answers a SYN to a full accept backlog with a RST, and the retransmission connects once the server accepts, as the console's does after FreeBSD drops the SYN. guest_refused_connect requires ECONNREFUSED in under a second for a closed port through libkernel connect and sceNetConnect, and a second connect to a listener with backlog 1 to succeed once the server accepts the first, through both. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
109 lines
4.6 KiB
C++
109 lines
4.6 KiB
C++
#include "prx/libc/include/general/VabiMacros.hpp"
|
|
#include <array>
|
|
#include <chrono>
|
|
#include <cstdint>
|
|
#include <cstdlib>
|
|
#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 connect_nid_postfix(int, const void*, std::uint32_t);
|
|
int APS5_VABI listen_nid_postfix(int, int);
|
|
int APS5_VABI accept_nid_postfix(int, void*, std::uint32_t*);
|
|
int APS5_VABI getsockname_nid_postfix(int, void*, std::uint32_t*);
|
|
int APS5_VABI close_nid_postfix(int);
|
|
int* APS5_VABI __error_nid_postfix();
|
|
int APS5_VABI sceNetInit_nid_postfix();
|
|
int APS5_VABI sceNetSocket(const char*, int, int, int);
|
|
int APS5_VABI sceNetConnect(int, const void*, std::uint32_t);
|
|
int APS5_VABI sceNetSocketClose(int);
|
|
int* APS5_VABI sceNetErrnoLoc();
|
|
}
|
|
|
|
namespace {
|
|
|
|
constexpr int GuestInet = 2;
|
|
constexpr int GuestStream = 1;
|
|
constexpr int GuestConnectionRefused = 61;
|
|
constexpr auto RefusalLimit = std::chrono::milliseconds(1000);
|
|
|
|
void Require(bool value) { if (!value) std::abort(); }
|
|
|
|
std::array<std::uint8_t, 16> ClosedLoopbackPort() {
|
|
std::array<std::uint8_t, 16> address{16, 2, 0, 0, 127, 0, 0, 1};
|
|
const int descriptor = socket_nid_postfix(GuestInet, GuestStream, 0);
|
|
Require(descriptor >= 0);
|
|
Require(bind_nid_postfix(descriptor, address.data(), address.size()) == 0);
|
|
std::uint32_t length = address.size();
|
|
Require(getsockname_nid_postfix(descriptor, address.data(), &length) == 0);
|
|
Require(close_nid_postfix(descriptor) == 0);
|
|
Require(address[2] != 0 || address[3] != 0);
|
|
return address;
|
|
}
|
|
|
|
template<typename TConnect>
|
|
bool ConnectsOnceBacklogDrains(TConnect connect) {
|
|
std::array<std::uint8_t, 16> address{16, 2, 0, 0, 127, 0, 0, 1};
|
|
const int listener = socket_nid_postfix(GuestInet, GuestStream, 0);
|
|
Require(listener >= 0);
|
|
Require(bind_nid_postfix(listener, address.data(), address.size()) == 0);
|
|
std::uint32_t length = address.size();
|
|
Require(getsockname_nid_postfix(listener, address.data(), &length) == 0);
|
|
Require(listen_nid_postfix(listener, 1) == 0);
|
|
const int queued = socket_nid_postfix(GuestInet, GuestStream, 0);
|
|
Require(queued >= 0 && connect_nid_postfix(queued, address.data(), address.size()) == 0);
|
|
std::thread acceptor([listener] {
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
|
const int accepted = accept_nid_postfix(listener, nullptr, nullptr);
|
|
Require(accepted >= 0 && close_nid_postfix(accepted) == 0);
|
|
});
|
|
const bool connected = connect(address);
|
|
acceptor.join();
|
|
Require(close_nid_postfix(queued) == 0 && close_nid_postfix(listener) == 0);
|
|
return connected;
|
|
}
|
|
|
|
template<typename TConnect>
|
|
bool RefusedAtOnce(TConnect connect) {
|
|
const auto start = std::chrono::steady_clock::now();
|
|
const bool refused = connect();
|
|
return refused && std::chrono::steady_clock::now() - start < RefusalLimit;
|
|
}
|
|
|
|
}
|
|
|
|
int main() {
|
|
Require(sceNetInit_nid_postfix() == 0);
|
|
const auto address = ClosedLoopbackPort();
|
|
Require(RefusedAtOnce([&] {
|
|
const int descriptor = socket_nid_postfix(GuestInet, GuestStream, 0);
|
|
Require(descriptor >= 0);
|
|
const bool refused = connect_nid_postfix(descriptor, address.data(), address.size()) == -1 && *__error_nid_postfix() == GuestConnectionRefused;
|
|
Require(close_nid_postfix(descriptor) == 0);
|
|
return refused;
|
|
}));
|
|
Require(RefusedAtOnce([&] {
|
|
const int descriptor = sceNetSocket("refused", GuestInet, GuestStream, 0);
|
|
Require(descriptor >= 0);
|
|
const bool refused = sceNetConnect(descriptor, address.data(), address.size()) == static_cast<int>(0x80410100u | GuestConnectionRefused)
|
|
&& *sceNetErrnoLoc() == GuestConnectionRefused;
|
|
Require(sceNetSocketClose(descriptor) == 0);
|
|
return refused;
|
|
}));
|
|
Require(ConnectsOnceBacklogDrains([](const std::array<std::uint8_t, 16>& listening) {
|
|
const int descriptor = socket_nid_postfix(GuestInet, GuestStream, 0);
|
|
Require(descriptor >= 0);
|
|
const bool connected = connect_nid_postfix(descriptor, listening.data(), listening.size()) == 0;
|
|
Require(close_nid_postfix(descriptor) == 0);
|
|
return connected;
|
|
}));
|
|
Require(ConnectsOnceBacklogDrains([](const std::array<std::uint8_t, 16>& listening) {
|
|
const int descriptor = sceNetSocket("backlog", GuestInet, GuestStream, 0);
|
|
Require(descriptor >= 0);
|
|
const bool connected = sceNetConnect(descriptor, listening.data(), listening.size()) == 0;
|
|
Require(sceNetSocketClose(descriptor) == 0);
|
|
return connected;
|
|
}));
|
|
}
|