Merge pull request #3530 from MarcZX100/codex/libkernel-inet-text-rules-3126
fix(libkernel): follow FreeBSD inet text rules
This commit is contained in:
commit
b2477d9804
|
|
@ -1,47 +1,217 @@
|
|||
#ifdef _WIN32
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
#else
|
||||
#include <arpa/inet.h>
|
||||
#endif
|
||||
#include "prx/libc/include/general/VabiMacros.hpp"
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
|
||||
extern "C" int* APS5_VABI __error_nid_postfix();
|
||||
|
||||
namespace {
|
||||
constexpr int kGuestInet = 2;
|
||||
constexpr int kGuestInet6 = 28;
|
||||
constexpr std::size_t kMaxText = 46;
|
||||
|
||||
bool ParseDecimalPart(const char* begin, const char* end, std::uint8_t& value) {
|
||||
const std::ptrdiff_t length = end - begin;
|
||||
if (length < 1 || length > 3) return false;
|
||||
if (length > 1 && *begin == '0') return false;
|
||||
unsigned total = 0;
|
||||
for (const char* cursor = begin; cursor != end; ++cursor) {
|
||||
if (*cursor < '0' || *cursor > '9') return false;
|
||||
total = total * 10 + static_cast<unsigned>(*cursor - '0');
|
||||
}
|
||||
if (total > 255) return false;
|
||||
value = static_cast<std::uint8_t>(total);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ParseQuad(const char* begin, const char* end, std::uint8_t (&bytes)[4]) {
|
||||
const char* partBegin = begin;
|
||||
int parts = 0;
|
||||
for (const char* cursor = begin;; ++cursor) {
|
||||
if (cursor != end && *cursor != '.') continue;
|
||||
if (parts == 4 || !ParseDecimalPart(partBegin, cursor, bytes[parts])) return false;
|
||||
++parts;
|
||||
if (cursor == end) break;
|
||||
partBegin = cursor + 1;
|
||||
}
|
||||
return parts == 4;
|
||||
}
|
||||
|
||||
int HexValue(char symbol) {
|
||||
if (symbol >= '0' && symbol <= '9') return symbol - '0';
|
||||
if (symbol >= 'a' && symbol <= 'f') return symbol - 'a' + 10;
|
||||
if (symbol >= 'A' && symbol <= 'F') return symbol - 'A' + 10;
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool ParseHexGroup(const char* begin, const char* end, std::uint16_t& value) {
|
||||
const std::ptrdiff_t length = end - begin;
|
||||
if (length < 1 || length > 4) return false;
|
||||
unsigned total = 0;
|
||||
for (const char* cursor = begin; cursor != end; ++cursor) {
|
||||
const int digit = HexValue(*cursor);
|
||||
if (digit < 0) return false;
|
||||
total = total * 16 + static_cast<unsigned>(digit);
|
||||
}
|
||||
value = static_cast<std::uint16_t>(total);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ParseGroupList(const char* begin, const char* end, bool mayEndWithQuad,
|
||||
std::uint16_t (&groups)[8], int& count) {
|
||||
count = 0;
|
||||
if (begin == end) return true;
|
||||
const char* groupBegin = begin;
|
||||
for (const char* cursor = begin;; ++cursor) {
|
||||
if (cursor != end && *cursor != ':') continue;
|
||||
const bool last = cursor == end;
|
||||
bool hasDot = false;
|
||||
for (const char* probe = groupBegin; probe != cursor; ++probe) hasDot = hasDot || *probe == '.';
|
||||
if (hasDot) {
|
||||
std::uint8_t quad[4];
|
||||
if (!last || !mayEndWithQuad || count > 6 || !ParseQuad(groupBegin, cursor, quad)) return false;
|
||||
groups[count++] = static_cast<std::uint16_t>(quad[0] << 8 | quad[1]);
|
||||
groups[count++] = static_cast<std::uint16_t>(quad[2] << 8 | quad[3]);
|
||||
return true;
|
||||
}
|
||||
if (count == 8 || !ParseHexGroup(groupBegin, cursor, groups[count])) return false;
|
||||
++count;
|
||||
if (last) return true;
|
||||
groupBegin = cursor + 1;
|
||||
}
|
||||
}
|
||||
|
||||
bool TextToV6(const char* text, std::uint8_t (&bytes)[16]) {
|
||||
const char* const end = text + std::strlen(text);
|
||||
const char* gap = std::strstr(text, "::");
|
||||
std::uint16_t head[8];
|
||||
std::uint16_t tail[8];
|
||||
int headCount = 0;
|
||||
int tailCount = 0;
|
||||
if (!gap) {
|
||||
if (!ParseGroupList(text, end, true, head, headCount) || headCount != 8) return false;
|
||||
} else {
|
||||
if (std::strstr(gap + 1, "::")) return false;
|
||||
if (!ParseGroupList(text, gap, false, head, headCount)) return false;
|
||||
if (!ParseGroupList(gap + 2, end, true, tail, tailCount)) return false;
|
||||
if (headCount + tailCount > 7) return false;
|
||||
}
|
||||
std::uint16_t words[8]{};
|
||||
for (int index = 0; index < headCount; ++index) words[index] = head[index];
|
||||
for (int index = 0; index < tailCount; ++index) words[8 - tailCount + index] = tail[index];
|
||||
for (int index = 0; index < 8; ++index) {
|
||||
bytes[index * 2] = static_cast<std::uint8_t>(words[index] >> 8);
|
||||
bytes[index * 2 + 1] = static_cast<std::uint8_t>(words[index]);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
std::size_t AppendDecimal(char* out, unsigned value) {
|
||||
char digits[3];
|
||||
std::size_t count = 0;
|
||||
do {
|
||||
digits[count++] = static_cast<char>('0' + value % 10);
|
||||
value /= 10;
|
||||
} while (value);
|
||||
for (std::size_t index = 0; index < count; ++index) out[index] = digits[count - 1 - index];
|
||||
return count;
|
||||
}
|
||||
|
||||
std::size_t FormatQuad(const std::uint8_t* bytes, char* out) {
|
||||
std::size_t length = 0;
|
||||
for (int index = 0; index < 4; ++index) {
|
||||
if (index) out[length++] = '.';
|
||||
length += AppendDecimal(out + length, bytes[index]);
|
||||
}
|
||||
return length;
|
||||
}
|
||||
|
||||
std::size_t AppendHex(char* out, unsigned value) {
|
||||
static const char symbols[] = "0123456789abcdef";
|
||||
char digits[4];
|
||||
std::size_t count = 0;
|
||||
do {
|
||||
digits[count++] = symbols[value & 15];
|
||||
value >>= 4;
|
||||
} while (value);
|
||||
for (std::size_t index = 0; index < count; ++index) out[index] = digits[count - 1 - index];
|
||||
return count;
|
||||
}
|
||||
|
||||
void FindLongestZeroRun(const std::uint16_t (&words)[8], int& start, int& length) {
|
||||
start = -1;
|
||||
length = 0;
|
||||
for (int from = 0; from < 8; ++from) {
|
||||
if (words[from] != 0) continue;
|
||||
int run = 0;
|
||||
while (from + run < 8 && words[from + run] == 0) ++run;
|
||||
if (run > length) {
|
||||
start = from;
|
||||
length = run;
|
||||
}
|
||||
from += run;
|
||||
}
|
||||
if (length < 2) {
|
||||
start = -1;
|
||||
length = 0;
|
||||
}
|
||||
}
|
||||
|
||||
std::size_t V6ToText(const std::uint8_t* bytes, char* out) {
|
||||
std::uint16_t words[8];
|
||||
for (int index = 0; index < 8; ++index)
|
||||
words[index] = static_cast<std::uint16_t>(bytes[index * 2] << 8 | bytes[index * 2 + 1]);
|
||||
int runStart;
|
||||
int runLength;
|
||||
FindLongestZeroRun(words, runStart, runLength);
|
||||
std::size_t length = 0;
|
||||
if (runStart == 0 && (runLength == 6 || (runLength == 5 && words[5] == 0xffff))) {
|
||||
const char* prefix = runLength == 6 ? "::" : "::ffff:";
|
||||
length = std::strlen(prefix);
|
||||
std::memcpy(out, prefix, length);
|
||||
return length + FormatQuad(bytes + 12, out + length);
|
||||
}
|
||||
for (int index = 0; index < 8;) {
|
||||
if (index == runStart) {
|
||||
out[length++] = ':';
|
||||
if (index + runLength == 8) out[length++] = ':';
|
||||
index += runLength;
|
||||
continue;
|
||||
}
|
||||
if (index) out[length++] = ':';
|
||||
length += AppendHex(out + length, words[index]);
|
||||
++index;
|
||||
}
|
||||
return length;
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
int APS5_VABI __inet_pton_nid_postfix(int family, const char* text, void* destination) {
|
||||
if (family != 2 && family != 28) { *__error_nid_postfix() = 47; return -1; }
|
||||
if (family != kGuestInet && family != kGuestInet6) { *__error_nid_postfix() = 47; return -1; }
|
||||
if (!text || !destination) { *__error_nid_postfix() = 14; return -1; }
|
||||
unsigned char address[16]{};
|
||||
const int nativeFamily = family == 2 ? AF_INET : AF_INET6;
|
||||
#ifdef _WIN32
|
||||
const int result = InetPtonA(nativeFamily, text, address);
|
||||
#else
|
||||
const int result = ::inet_pton(nativeFamily, text, address);
|
||||
#endif
|
||||
if (result == 1) std::memcpy(destination, address, family == 2 ? 4 : 16);
|
||||
else if (result == -1) *__error_nid_postfix() = 22;
|
||||
return result;
|
||||
if (family == kGuestInet) {
|
||||
std::uint8_t quad[4];
|
||||
if (!ParseQuad(text, text + std::strlen(text), quad)) return 0;
|
||||
std::memcpy(destination, quad, sizeof(quad));
|
||||
return 1;
|
||||
}
|
||||
std::uint8_t address[16];
|
||||
if (!TextToV6(text, address)) return 0;
|
||||
std::memcpy(destination, address, sizeof(address));
|
||||
return 1;
|
||||
}
|
||||
|
||||
const char* APS5_VABI __inet_ntop_nid_postfix(int family, const void* source,
|
||||
char* destination, std::uint32_t capacity) {
|
||||
if (family != 2 && family != 28) { *__error_nid_postfix() = 47; return nullptr; }
|
||||
if (family != kGuestInet && family != kGuestInet6) { *__error_nid_postfix() = 47; return nullptr; }
|
||||
if (!source || !destination) { *__error_nid_postfix() = 14; return nullptr; }
|
||||
char text[INET6_ADDRSTRLEN]{};
|
||||
const int nativeFamily = family == 2 ? AF_INET : AF_INET6;
|
||||
#ifdef _WIN32
|
||||
const auto* result = InetNtopA(nativeFamily, const_cast<void*>(source), text, sizeof(text));
|
||||
#else
|
||||
const auto* result = ::inet_ntop(nativeFamily, source, text, sizeof(text));
|
||||
#endif
|
||||
if (!result) { *__error_nid_postfix() = 22; return nullptr; }
|
||||
const auto size = std::strlen(text) + 1;
|
||||
if (size > capacity) { *__error_nid_postfix() = 28; return nullptr; }
|
||||
std::memcpy(destination, text, size);
|
||||
char text[kMaxText + 1];
|
||||
const auto* bytes = static_cast<const std::uint8_t*>(source);
|
||||
const std::size_t length = family == kGuestInet ? FormatQuad(bytes, text) : V6ToText(bytes, text);
|
||||
text[length] = '\0';
|
||||
if (length + 1 > capacity) { *__error_nid_postfix() = 28; return nullptr; }
|
||||
std::memcpy(destination, text, length + 1);
|
||||
return destination;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,4 +31,49 @@ int main() {
|
|||
Require(__inet_pton_nid_postfix(2, "256.1.2.3", address.data()) == 0);
|
||||
Require(__inet_pton_nid_postfix(28, "::ffff:192.0.2.17", address.data()) == 1);
|
||||
Require(address[10] == 255 && address[11] == 255 && address[12] == 192 && address[15] == 17);
|
||||
|
||||
for (const char* rejected : {"01.2.3.4", "1.2.3", "1.2.3.4.5", "1.2.3.4.", " 1.2.3.4", "0x1.2.3.4", "1..3.4", "1.2.3.-4"}) {
|
||||
address.fill(0xA5);
|
||||
Require(__inet_pton_nid_postfix(2, rejected, address.data()) == 0);
|
||||
Require(address[0] == 0xA5);
|
||||
}
|
||||
for (const char* rejected : {"::ffff:01.2.3.4", "::ffff:1.2.3.04", "1:2:3:4:5:6:7:8::", "::1:2:3:4:5:6:7:8", "1::2::3", ":::", ":1::", "1::2:", ":1:2:3:4:5:6:7",
|
||||
"1:2:3:4:5:6:7", "1:2:3:4:5:6:7:8:9", "12345::", "fe80::1%eth0", "1.2.3.4::", "::1.2.3.4:1", "::g", "1:2:3:4:5:6:1.2.3.4:7",
|
||||
"1:2:3:4:5:6:7:1.2.3.4", ""}) {
|
||||
address.fill(0xA5);
|
||||
Require(__inet_pton_nid_postfix(28, rejected, address.data()) == 0);
|
||||
Require(address[0] == 0xA5 && address[15] == 0xA5);
|
||||
}
|
||||
Require(__inet_pton_nid_postfix(28, "1:2:3:4:5:6:7::", address.data()) == 1);
|
||||
Require(address[13] == 7 && address[14] == 0 && address[1] == 1);
|
||||
Require(__inet_pton_nid_postfix(28, "ABCD:ef01::1", address.data()) == 1);
|
||||
Require(address[0] == 0xAB && address[1] == 0xCD && address[2] == 0xEF && address[3] == 0x01 && address[15] == 1);
|
||||
|
||||
struct Text { const char* bytesAsText; const char* expected; };
|
||||
const Text samples[] = {
|
||||
{"::ffff:0.0.0.1", "::ffff:0.0.0.1"}, {"::ffff:0:1:0", "::ffff:0:1:0"}, {"::1", "::1"}, {"::", "::"}, {"1::", "1::"},
|
||||
{"::2", "::2"}, {"::1.2.3.4", "::1.2.3.4"}, {"1:0:0:2:0:0:0:3", "1:0:0:2::3"}, {"1:0:2:0:3:0:4:0", "1:0:2:0:3:0:4:0"},
|
||||
{"0:0:1:0:0:2:0:0", "::1:0:0:2:0:0"}, {"::ffff:1.2.3.4", "::ffff:1.2.3.4"}, {"2001:DB8::17", "2001:db8::17"}};
|
||||
for (const Text& sample : samples) {
|
||||
Require(__inet_pton_nid_postfix(28, sample.bytesAsText, address.data()) == 1);
|
||||
char output[64]{};
|
||||
Require(__inet_ntop_nid_postfix(28, address.data(), output, sizeof(output)) == output);
|
||||
Require(std::strcmp(output, sample.expected) == 0);
|
||||
const auto length = std::strlen(sample.expected);
|
||||
std::memset(output, 'x', sizeof(output));
|
||||
Require(__inet_ntop_nid_postfix(28, address.data(), output, static_cast<std::uint32_t>(length + 1)) == output);
|
||||
Require(std::strcmp(output, sample.expected) == 0);
|
||||
std::memset(output, 'x', sizeof(output));
|
||||
*__error_nid_postfix() = 0;
|
||||
Require(__inet_ntop_nid_postfix(28, address.data(), output, static_cast<std::uint32_t>(length)) == nullptr);
|
||||
Require(*__error_nid_postfix() == 28 && output[0] == 'x');
|
||||
}
|
||||
address.fill(0xFF);
|
||||
char widest[64]{};
|
||||
Require(__inet_ntop_nid_postfix(28, address.data(), widest, sizeof(widest)) == widest);
|
||||
Require(std::strcmp(widest, "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff") == 0);
|
||||
const unsigned char fourth[4] = {255, 0, 7, 100};
|
||||
Require(__inet_ntop_nid_postfix(2, fourth, widest, 12) == widest && std::strcmp(widest, "255.0.7.100") == 0);
|
||||
Require(__inet_ntop_nid_postfix(2, fourth, widest, 11) == nullptr && *__error_nid_postfix() == 28);
|
||||
Require(__inet_ntop_nid_postfix(10, fourth, widest, sizeof(widest)) == nullptr && *__error_nid_postfix() == 47);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -341,6 +341,7 @@ Throughout the project, every function at every stage either **does exactly what
|
|||
- [sceVideoOutAddPreVblankStartEvent](../../core/libs/prx/libSceVideoOut/src/Event.cpp) (libSceVideoOut) - how long before the vblank the console signals pre-vblank start is unknown. The event is triggered on each emulated vblank tick immediately before the vblank event, with its own count as the event data
|
||||
- [sched_get_priority_max, sched_get_priority_min](../../core/libs/prx/libkernel/System/src/Process.cpp) (libkernel) - `SCHED_OTHER` (2) reports the same 256 to 767 range as `SCHED_FIFO` and `SCHED_RR`; the console's answer is unverified. FreeBSD accepts `SCHED_OTHER` with its own range, and shadPS4 rejects it with `EINVAL`. `pthread_setschedparam` ignores the policy, so a priority taken from this range applies
|
||||
|
||||
- [inet_pton, inet_ntop](../../core/libs/prx/libkernel/Socket/src/AddressConversion.cpp) (libkernel) - the text forms are assumed to be those of FreeBSD 11.4 and are unverified on a console: `inet_pton` accepts only four decimal parts without leading zeros for `AF_INET` and the RFC 4291 forms for `AF_INET6` (one `::` standing for at least one group, a dotted-quad tail only as the last 32 bits, no zone id), and `inet_ntop` prints lowercase hex with the first longest run of two or more zero groups compressed, and a dotted tail only for `::a.b.c.d` and `::ffff:a.b.c.d`. They no longer depend on the host resolver library
|
||||
- [gethostbyname](../../core/libs/prx/libScePosixForWebKit/Resolver.cpp) (libScePosixForWebKit) - numeric IPv4 forms, trailing whitespace and 64-bit accumulation follow FreeBSD 11.4 [inet_aton](https://github.com/freebsd/freebsd-src/blob/releng/11.4/lib/libc/inet/inet_addr.c); clearing the resolver error on numeric success follows [fakeaddr](https://github.com/freebsd/freebsd-src/blob/releng/11.4/lib/libc/net/gethostnamadr.c). Exact PS5 behavior is unverified; hostname lookup still uses the host resolver
|
||||
|
||||
- [sceAppContentGetAddcontDownloadProgress](../../core/libs/prx/libSceAppContent/Export.cpp) (libSceAppContent) - imported by PPSA08755, which fails to load without it; added without an implementation because its behavior is unverified. [SharpProspero](https://github.com/SvenGDK/SharpProspero) lists `(uint32_t serviceLabel, entitlementLabel, SceAppContentAddcontDownloadProgress*)`, but no PS5 reference gives the progress layout or the error codes
|
||||
|
|
|
|||
Loading…
Reference in New Issue