mirror of
https://github.com/boykopovar/AnyPS5.git
synced 2026-11-04 13:07:32 +00:00
A routing table built by hand with an unscoped default route, a host route, a host route to 0.0.0.0, a rejecting default route, an interface-scoped default route, one that is down, one on an interface that does not exist and a truncated last message must give the unscoped route first and the scoped one after it, and must count every match while writing only as many as fit.
209 lines
6.9 KiB
C++
209 lines
6.9 KiB
C++
#ifdef _WIN32
|
|
#define WIN32_LEAN_AND_MEAN
|
|
#include <winsock2.h>
|
|
#include <ws2tcpip.h>
|
|
#else
|
|
#include <arpa/inet.h>
|
|
#include <netinet/in.h>
|
|
#include <sys/socket.h>
|
|
#include <unistd.h>
|
|
#endif
|
|
#ifdef __APPLE__
|
|
#include <net/if.h>
|
|
#include <net/route.h>
|
|
#include <vector>
|
|
#endif
|
|
#include "prx/libc/include/general/VabiMacros.hpp"
|
|
#include "SceTypes.hpp"
|
|
#include <cstddef>
|
|
#include <cstdint>
|
|
#include <cstdio>
|
|
#include <cstdlib>
|
|
#include <cstring>
|
|
#include <initializer_list>
|
|
#include <stdexcept>
|
|
|
|
#ifdef __APPLE__
|
|
struct NetCtlDefaultRoute {
|
|
char name[IF_NAMESIZE];
|
|
in_addr gateway;
|
|
std::uint32_t metric;
|
|
};
|
|
#endif
|
|
|
|
extern "C" {
|
|
int APS5_VABI sceNetCtlGetInfo(int, NetCtlInfo*);
|
|
int APS5_VABI sceNetCtlGetState(int*);
|
|
#ifdef __APPLE__
|
|
std::size_t NetCtlDefaultRoutes_nid_no_patch(const char*, std::size_t, NetCtlDefaultRoute*, std::size_t);
|
|
#endif
|
|
}
|
|
|
|
namespace {
|
|
|
|
void Require(bool value) { if (!value) std::abort(); }
|
|
|
|
constexpr int kErrInvalidCode = static_cast<int>(0x80412106);
|
|
constexpr int kErrInvalidAddr = static_cast<int>(0x80412107);
|
|
constexpr int kErrNotConnected = static_cast<int>(0x80412108);
|
|
constexpr int kStateIpObtained = 3;
|
|
constexpr std::uint8_t kFill = 0xa5;
|
|
constexpr std::size_t kAddressLength = 16;
|
|
|
|
NetCtlInfo Query(int code) {
|
|
NetCtlInfo info;
|
|
std::memset(&info, kFill, sizeof(info));
|
|
Require(sceNetCtlGetInfo(code, &info) == 0);
|
|
return info;
|
|
}
|
|
|
|
void RequireUntouchedFrom(const NetCtlInfo& info, std::size_t offset) {
|
|
for (std::size_t i = offset; i < sizeof(info.opaque); ++i) Require(info.opaque[i] == kFill);
|
|
}
|
|
|
|
std::uint32_t QueryWord(int code) {
|
|
const auto info = Query(code);
|
|
std::uint32_t value = 0;
|
|
std::memcpy(&value, info.opaque, sizeof(value));
|
|
RequireUntouchedFrom(info, sizeof(value));
|
|
return value;
|
|
}
|
|
|
|
in_addr QueryAddress(int code) {
|
|
const auto info = Query(code);
|
|
char text[kAddressLength];
|
|
std::memcpy(text, info.opaque, sizeof(text));
|
|
Require(std::memchr(text, '\0', sizeof(text)) != nullptr);
|
|
RequireUntouchedFrom(info, kAddressLength);
|
|
in_addr address{};
|
|
Require(inet_pton(AF_INET, text, &address) == 1);
|
|
return address;
|
|
}
|
|
|
|
bool Binds(in_addr address) {
|
|
sockaddr_in local{};
|
|
local.sin_family = AF_INET;
|
|
local.sin_addr = address;
|
|
#ifdef _WIN32
|
|
const SOCKET descriptor = socket(AF_INET, SOCK_DGRAM, 0);
|
|
Require(descriptor != INVALID_SOCKET);
|
|
const bool bound = bind(descriptor, reinterpret_cast<const sockaddr*>(&local), sizeof(local)) == 0;
|
|
closesocket(descriptor);
|
|
#else
|
|
const int descriptor = socket(AF_INET, SOCK_DGRAM, 0);
|
|
Require(descriptor >= 0);
|
|
const bool bound = bind(descriptor, reinterpret_cast<const sockaddr*>(&local), sizeof(local)) == 0;
|
|
close(descriptor);
|
|
#endif
|
|
return bound;
|
|
}
|
|
|
|
}
|
|
|
|
#ifdef __APPLE__
|
|
void AppendRoute(std::vector<char>& table, std::uint32_t destination, std::uint32_t gateway, int flags, unsigned short index) {
|
|
rt_msghdr header{};
|
|
header.rtm_msglen = static_cast<unsigned short>(sizeof(header) + 2 * sizeof(sockaddr_in) + sizeof(std::uint32_t));
|
|
header.rtm_version = RTM_VERSION;
|
|
header.rtm_type = RTM_GET;
|
|
header.rtm_index = index;
|
|
header.rtm_flags = flags;
|
|
header.rtm_addrs = RTA_DST | RTA_GATEWAY | RTA_NETMASK;
|
|
sockaddr_in addresses[2]{};
|
|
for (auto& address : addresses) {
|
|
address.sin_len = sizeof(address);
|
|
address.sin_family = AF_INET;
|
|
}
|
|
addresses[0].sin_addr.s_addr = htonl(destination);
|
|
addresses[1].sin_addr.s_addr = htonl(gateway);
|
|
const std::uint32_t emptyMask = 0;
|
|
const auto* bytes = reinterpret_cast<const char*>(&header);
|
|
table.insert(table.end(), bytes, bytes + sizeof(header));
|
|
bytes = reinterpret_cast<const char*>(addresses);
|
|
table.insert(table.end(), bytes, bytes + sizeof(addresses));
|
|
bytes = reinterpret_cast<const char*>(&emptyMask);
|
|
table.insert(table.end(), bytes, bytes + sizeof(emptyMask));
|
|
}
|
|
|
|
void CheckDefaultRoutes() {
|
|
const auto loopback = static_cast<unsigned short>(if_nametoindex("lo0"));
|
|
Require(loopback != 0);
|
|
constexpr int up = RTF_UP | RTF_GATEWAY | RTF_STATIC;
|
|
std::vector<char> table;
|
|
AppendRoute(table, 0, 0x0A000001, up, loopback);
|
|
AppendRoute(table, 0x01020304, 0x0A000009, up | RTF_HOST, loopback);
|
|
AppendRoute(table, 0, 0x0A000009, up | RTF_HOST, loopback);
|
|
AppendRoute(table, 0, 0x0A000009, up | RTF_REJECT, loopback);
|
|
AppendRoute(table, 0, 0x0A000002, up | RTF_IFSCOPE, loopback);
|
|
AppendRoute(table, 0, 0x0A000009, RTF_GATEWAY | RTF_STATIC, loopback);
|
|
AppendRoute(table, 0, 0x0A000009, up, 0xFFFF);
|
|
const std::size_t complete = table.size();
|
|
AppendRoute(table, 0, 0x0A000009, up, loopback);
|
|
table.resize(table.size() - sizeof(std::uint32_t));
|
|
|
|
NetCtlDefaultRoute routes[4]{};
|
|
Require(NetCtlDefaultRoutes_nid_no_patch(table.data(), table.size(), routes, 4) == 2);
|
|
Require(std::strcmp(routes[0].name, "lo0") == 0 && ntohl(routes[0].gateway.s_addr) == 0x0A000001 && routes[0].metric == 0);
|
|
Require(std::strcmp(routes[1].name, "lo0") == 0 && ntohl(routes[1].gateway.s_addr) == 0x0A000002 && routes[1].metric == 1);
|
|
|
|
NetCtlDefaultRoute first[1]{};
|
|
Require(NetCtlDefaultRoutes_nid_no_patch(table.data(), complete, first, 1) == 2);
|
|
Require(ntohl(first[0].gateway.s_addr) == 0x0A000001);
|
|
Require(NetCtlDefaultRoutes_nid_no_patch(table.data(), 0, nullptr, 0) == 0);
|
|
}
|
|
#endif
|
|
|
|
int main() {
|
|
#ifdef _WIN32
|
|
WSADATA data{};
|
|
Require(WSAStartup(MAKEWORD(2, 2), &data) == 0);
|
|
#endif
|
|
#ifdef __APPLE__
|
|
CheckDefaultRoutes();
|
|
#endif
|
|
NetCtlInfo info{};
|
|
Require(sceNetCtlGetInfo(0, &info) == kErrInvalidCode);
|
|
Require(sceNetCtlGetInfo(22, &info) == kErrInvalidCode);
|
|
Require(sceNetCtlGetInfo(14, nullptr) == kErrInvalidAddr);
|
|
|
|
const int result = sceNetCtlGetInfo(14, &info);
|
|
if (result == kErrNotConnected) {
|
|
std::puts("skipped, the host has no IPv4 address");
|
|
return 77;
|
|
}
|
|
Require(result == 0);
|
|
int state = -1;
|
|
Require(sceNetCtlGetState(&state) == 0 && state == kStateIpObtained);
|
|
|
|
Require(QueryWord(1) == 0);
|
|
const auto ether = Query(2);
|
|
const std::uint8_t mac[6] = {0x02, 0x50, 0x53, 0x35, 0x00, 0x01};
|
|
Require(std::memcmp(ether.opaque, mac, sizeof(mac)) == 0);
|
|
RequireUntouchedFrom(ether, sizeof(mac));
|
|
const auto mtu = QueryWord(3);
|
|
Require(mtu >= 68 && mtu <= 65535);
|
|
Require(QueryWord(4) == 1);
|
|
Require(QueryWord(11) == 0);
|
|
Require(QueryWord(19) == 0);
|
|
|
|
const auto address = QueryAddress(14);
|
|
const auto host = ntohl(address.s_addr);
|
|
Require(host != 0 && (host >> 24) != 127);
|
|
Require(Binds(address));
|
|
const auto mask = ntohl(QueryAddress(15).s_addr);
|
|
Require(mask != 0 && (~mask & (~mask + 1)) == 0);
|
|
QueryAddress(16);
|
|
QueryAddress(17);
|
|
QueryAddress(18);
|
|
|
|
for (const int code : {5, 6, 7, 8, 9, 10, 12, 13, 20, 21}) {
|
|
bool threw = false;
|
|
try {
|
|
sceNetCtlGetInfo(code, &info);
|
|
} catch (const std::runtime_error&) {
|
|
threw = true;
|
|
}
|
|
Require(threw);
|
|
}
|
|
}
|