diff --git a/core/libs/prx/libSceLibcInternal/Inet.cpp b/core/libs/prx/libSceLibcInternal/Inet.cpp index 1dd8ad853d..df1147f799 100644 --- a/core/libs/prx/libSceLibcInternal/Inet.cpp +++ b/core/libs/prx/libSceLibcInternal/Inet.cpp @@ -1,3 +1,32 @@ +/*- + * Copyright (c) 1983, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +/* Adapted from FreeBSD release/11.4.0 lib/libc/inet/inet_network.c: https://github.com/freebsd/freebsd-src/blob/8c639945a44588f6cfdddfb057bb5aad88735da0/lib/libc/inet/inet_network.c */ #include "prx/libkernel/include/Api.hpp" #include "prx/libSceLibcInternal/include/Api.hpp" #include "prx/libc/include/general/VabiMacros.hpp" @@ -13,6 +42,7 @@ char g_ntoaBuffer[18]; bool IsDigit(unsigned char c) { return c >= '0' && c <= '9'; } bool IsHexDigit(unsigned char c) { return IsDigit(c) || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'); } bool IsSpace(unsigned char c) { return c == ' ' || (c >= '\t' && c <= '\r'); } +bool IsLower(unsigned char c) { return c >= 'a' && c <= 'z'; } } @@ -107,4 +137,111 @@ char* APS5_VABI inet_ntoa_nid_postfix(std::uint32_t address) { return g_ntoaBuffer; } +std::uint32_t APS5_VABI inet_netof_nid_postfix(std::uint32_t address) { + const auto* bytes = reinterpret_cast(&address); + const std::uint32_t host = (static_cast(bytes[0]) << 24) | (static_cast(bytes[1]) << 16) | + (static_cast(bytes[2]) << 8) | static_cast(bytes[3]); + if ((host & 0x80000000u) == 0) return (host & 0xff000000u) >> 24; + if ((host & 0xc0000000u) == 0x80000000u) return (host & 0xffff0000u) >> 16; + return (host & 0xffffff00u) >> 8; +} + +std::uint32_t APS5_VABI __inet_netof_nid_postfix(std::uint32_t address) { + return inet_netof_nid_postfix(address); +} + +std::uint32_t APS5_VABI inet_lnaof_nid_postfix(std::uint32_t address) { + const auto* bytes = reinterpret_cast(&address); + const std::uint32_t host = (static_cast(bytes[0]) << 24) | (static_cast(bytes[1]) << 16) | + (static_cast(bytes[2]) << 8) | static_cast(bytes[3]); + if ((host & 0x80000000u) == 0) return host & 0x00ffffffu; + if ((host & 0xc0000000u) == 0x80000000u) return host & 0x0000ffffu; + return host & 0x000000ffu; +} + +std::uint32_t APS5_VABI __inet_lnaof_nid_postfix(std::uint32_t address) { + return inet_lnaof_nid_postfix(address); +} + +std::uint32_t APS5_VABI inet_makeaddr_nid_postfix(std::uint32_t net, std::uint32_t host) { + std::uint32_t value = 0; + if (net < 128) value = (net << 24) | (host & 0x00ffffffu); + else if (net < 65536) value = (net << 16) | (host & 0x0000ffffu); + else if (net < 16777216) value = (net << 8) | (host & 0x000000ffu); + else value = net | host; + std::uint32_t address = 0; + const std::uint8_t network[4] = {static_cast(value >> 24), static_cast(value >> 16), + static_cast(value >> 8), static_cast(value)}; + std::memcpy(&address, network, sizeof(network)); + return address; +} + +std::uint32_t APS5_VABI __inet_makeaddr_nid_postfix(std::uint32_t net, std::uint32_t host) { + return inet_makeaddr_nid_postfix(net, host); +} + +std::uint32_t APS5_VABI inet_network_nid_postfix(const char* text) { + if (text == nullptr) throw std::invalid_argument("inet_network: null text"); + std::uint32_t val = 0; + std::uint32_t base = 10; + std::uint32_t parts[4] = {}; + std::uint32_t* pp = parts; + int digit = 0; + +again: + val = 0; + base = 10; + digit = 0; + if (*text == '0') { + digit = 1; + base = 8; + text++; + } + if (*text == 'x' || *text == 'X') { + base = 16; + text++; + } + char c = *text; + while (c != '\0') { + const auto uc = static_cast(c); + if (IsDigit(uc)) { + if (base == 8 && (c == '8' || c == '9')) return 0xffffffffu; + val = (val * base) + static_cast(c - '0'); + text++; + digit = 1; + c = *text; + continue; + } + if (base == 16 && IsHexDigit(uc)) { + val = (val << 4) + static_cast(uc + 10 - (IsLower(uc) ? 'a' : 'A')); + text++; + digit = 1; + c = *text; + continue; + } + break; + } + if (!digit) return 0xffffffffu; + if (pp >= parts + 4 || val > 0xffu) return 0xffffffffu; + if (*text == '.') { + *pp++ = val; + text++; + goto again; + } + if (*text != '\0' && !IsSpace(static_cast(*text))) return 0xffffffffu; + *pp++ = val; + const std::ptrdiff_t n = pp - parts; + if (n > 4) return 0xffffffffu; + val = 0; + for (std::ptrdiff_t i = 0; i < n; i++) { + val <<= 8; + val |= parts[i] & 0xffu; + } + return val; +} + +std::uint32_t APS5_VABI __inet_network_nid_postfix(const char* text) { + return inet_network_nid_postfix(text); +} + } diff --git a/core/libs/prx/libSceLibcInternal/include/Api.hpp b/core/libs/prx/libSceLibcInternal/include/Api.hpp index 1d8dc1eacc..36706ac7a6 100644 --- a/core/libs/prx/libSceLibcInternal/include/Api.hpp +++ b/core/libs/prx/libSceLibcInternal/include/Api.hpp @@ -53,6 +53,14 @@ std::uint32_t APS5_VABI inet_addr_nid_postfix(const char* text); char* APS5_VABI inet_ntoa_r_nid_postfix(std::uint32_t address, char* buffer, std::uint32_t size); char* APS5_VABI __inet_ntoa_r_nid_postfix(std::uint32_t address, char* buffer, std::uint32_t size); char* APS5_VABI inet_ntoa_nid_postfix(std::uint32_t address); +std::uint32_t APS5_VABI inet_netof_nid_postfix(std::uint32_t address); +std::uint32_t APS5_VABI __inet_netof_nid_postfix(std::uint32_t address); +std::uint32_t APS5_VABI inet_lnaof_nid_postfix(std::uint32_t address); +std::uint32_t APS5_VABI __inet_lnaof_nid_postfix(std::uint32_t address); +std::uint32_t APS5_VABI inet_makeaddr_nid_postfix(std::uint32_t net, std::uint32_t host); +std::uint32_t APS5_VABI __inet_makeaddr_nid_postfix(std::uint32_t net, std::uint32_t host); +std::uint32_t APS5_VABI inet_network_nid_postfix(const char* text); +std::uint32_t APS5_VABI __inet_network_nid_postfix(const char* text); int APS5_VABI statvfs_nid_postfix(const char* path, LibcStatvfs* output); void APS5_VABI psignal_nid_postfix(unsigned int signal, const char* prefix); int APS5_VABI posix_spawnattr_init_nid_postfix(PosixSpawnAttributes** attributes); diff --git a/core/libs/tests/GuestInetAddress.cpp b/core/libs/tests/GuestInetAddress.cpp index 444bfb5b21..db5aefb711 100644 --- a/core/libs/tests/GuestInetAddress.cpp +++ b/core/libs/tests/GuestInetAddress.cpp @@ -68,5 +68,74 @@ int main() { *__error_nid_postfix() = 0; Require(__inet_ntoa_r_nid_postfix(Network(192, 168, 0, 255), oneShort, sizeof(oneShort)) == oneShort && *__error_nid_postfix() == 28, "__inet_ntoa_r small buffer"); Require(std::string(oneShort, sizeof(oneShort)) == std::string(sizeof(oneShort), 'z') && std::string(text) == "255.255.255.255", "__inet_ntoa_r leaves the buffers"); + + const std::uint32_t classA = Network(10, 1, 2, 3); + Require(inet_netof_nid_postfix(classA) == 10, "inet_netof class A"); + Require(__inet_netof_nid_postfix(classA) == 10, "__inet_netof class A"); + Require(inet_lnaof_nid_postfix(classA) == 0x010203, "inet_lnaof class A"); + Require(__inet_lnaof_nid_postfix(classA) == 0x010203, "__inet_lnaof class A"); + Require(inet_makeaddr_nid_postfix(10, 0x010203) == classA, "inet_makeaddr class A"); + Require(__inet_makeaddr_nid_postfix(10, 0x010203) == classA, "__inet_makeaddr class A"); + + const std::uint32_t classB = Network(172, 16, 5, 6); + Require(inet_netof_nid_postfix(classB) == 0xac10, "inet_netof class B"); + Require(__inet_netof_nid_postfix(classB) == 0xac10, "__inet_netof class B"); + Require(inet_lnaof_nid_postfix(classB) == 0x0506, "inet_lnaof class B"); + Require(__inet_lnaof_nid_postfix(classB) == 0x0506, "__inet_lnaof class B"); + Require(inet_makeaddr_nid_postfix(0xac10, 0x0506) == classB, "inet_makeaddr class B"); + Require(__inet_makeaddr_nid_postfix(0xac10, 0x0506) == classB, "__inet_makeaddr class B"); + + const std::uint32_t classC = Network(192, 168, 1, 100); + Require(inet_netof_nid_postfix(classC) == 0xc0a801, "inet_netof class C"); + Require(__inet_netof_nid_postfix(classC) == 0xc0a801, "__inet_netof class C"); + Require(inet_lnaof_nid_postfix(classC) == 100, "inet_lnaof class C"); + Require(__inet_lnaof_nid_postfix(classC) == 100, "__inet_lnaof class C"); + Require(inet_makeaddr_nid_postfix(0xc0a801, 100) == classC, "inet_makeaddr class C"); + Require(__inet_makeaddr_nid_postfix(0xc0a801, 100) == classC, "__inet_makeaddr class C"); + + const std::uint32_t classD = Network(224, 1, 2, 3); + Require(inet_netof_nid_postfix(classD) == 0xe00102u, "inet_netof class D"); + Require(inet_lnaof_nid_postfix(classD) == 0x03u, "inet_lnaof class D"); + + const std::uint32_t classE = Network(240, 5, 6, 7); + Require(inet_netof_nid_postfix(classE) == 0xf00506u, "inet_netof class E"); + Require(inet_lnaof_nid_postfix(classE) == 0x07u, "inet_lnaof class E"); + + Require(inet_makeaddr_nid_postfix(127, 0x123456) == Network(127, 0x12, 0x34, 0x56), "inet_makeaddr threshold < 128"); + Require(inet_makeaddr_nid_postfix(128, 0x1234) == Network(0, 128, 0x12, 0x34), "inet_makeaddr threshold == 128"); + Require(inet_makeaddr_nid_postfix(65535, 0x5678) == Network(255, 255, 0x56, 0x78), "inet_makeaddr threshold 65535"); + Require(inet_makeaddr_nid_postfix(65536, 0x9a) == Network(1, 0, 0, 0x9a), "inet_makeaddr threshold == 65536"); + Require(inet_makeaddr_nid_postfix(16777215, 0xbc) == Network(255, 255, 255, 0xbc), "inet_makeaddr threshold 16777215"); + Require(inet_makeaddr_nid_postfix(16777216, 0xde) == Network(1, 0, 0, 0xde), "inet_makeaddr threshold >= 16777216"); + + Require(inet_network_nid_postfix("127.0.0.1") == 0x7f000001u, "inet_network dotted quad"); + Require(__inet_network_nid_postfix("127.0.0.1") == 0x7f000001u, "__inet_network dotted quad"); + Require(inet_network_nid_postfix("10.1") == 0x0a01u, "inet_network two parts"); + Require(inet_network_nid_postfix("192.168.1") == 0xc0a801u, "inet_network three parts"); + Require(inet_network_nid_postfix("0xff") == 0xffu, "inet_network hex"); + Require(inet_network_nid_postfix("0x1234") == 0xffffffffu && inet_network_nid_postfix("300") == 0xffffffffu, "inet_network component > 0xff"); + Require(inet_network_nid_postfix("0177.0.0.1") == 0x7f000001u, "inet_network octal dotted quad"); + Require(inet_network_nid_postfix("012") == 10u, "inet_network octal single"); + Require(inet_network_nid_postfix("08") == 0xffffffffu && inet_network_nid_postfix("09") == 0xffffffffu, "inet_network invalid octal digit"); + Require(inet_network_nid_postfix("1.2.3.4.5") == 0xffffffffu, "inet_network more than four parts"); + Require(inet_network_nid_postfix("0x") == 0u, "inet_network bare 0x"); + Require(inet_network_nid_postfix("x1f") == 0x1fu, "inet_network x prefix"); + Require(inet_network_nid_postfix("1 x") == 1u, "inet_network space before char"); + Require(inet_network_nid_postfix("1x") == 0xffffffffu, "inet_network junk immediately after"); + Require(inet_network_nid_postfix("bad") == 0xffffffffu, "inet_network bad"); + Require(inet_network_nid_postfix("256.1.1.1") == 0xffffffffu, "inet_network out of range"); + Require(inet_network_nid_postfix("0XAB") == 0xabu && inet_network_nid_postfix("0xAb.0XcD") == 0xabcdu && inet_network_nid_postfix("xF") == 0xfu, "inet_network upper-case hex"); + Require(inet_network_nid_postfix("x") == 0xffffffffu && inet_network_nid_postfix("X") == 0xffffffffu, "inet_network x without digits"); + Require(inet_network_nid_postfix("1..2") == 0xffffffffu && inet_network_nid_postfix(".1") == 0xffffffffu && inet_network_nid_postfix("1.") == 0xffffffffu, "inet_network empty part"); + Require(inet_network_nid_postfix("1\t") == 1u && inet_network_nid_postfix("1\n") == 1u && inet_network_nid_postfix("1\v") == 1u, "inet_network trailing whitespace"); + Require(inet_network_nid_postfix("1-") == 0xffffffffu, "inet_network trailing junk"); + + bool threw = false; + try { inet_network_nid_postfix(nullptr); } catch (const std::invalid_argument&) { threw = true; } + Require(threw, "inet_network null throws"); + threw = false; + try { __inet_network_nid_postfix(nullptr); } catch (const std::invalid_argument&) { threw = true; } + Require(threw, "__inet_network null throws"); + return 0; } diff --git a/docs/dev/debt/libSceLibcInternal.md b/docs/dev/debt/libSceLibcInternal.md index 704ce78dd0..d989eab350 100644 --- a/docs/dev/debt/libSceLibcInternal.md +++ b/docs/dev/debt/libSceLibcInternal.md @@ -20,6 +20,7 @@ - [_err, _warn](../../../core/libs/prx/libSceLibcInternal/Err.cpp) and [_getprogname](../../../core/libs/prx/libSceLibcInternal/Export.cpp) (libSceLibcInternal) are assumed to be the same functions as `err`, `warn` and `getprogname` and run their code; the console's module was not read. FreeBSD 11.4 defines the underscore names and makes the public names weak references to them ([`_err`](https://github.com/freebsd/freebsd-src/blob/8c639945a44588f6cfdddfb057bb5aad88735da0/lib/libc/gen/err.c#L70-L79), [`_warn`](https://github.com/freebsd/freebsd-src/blob/8c639945a44588f6cfdddfb057bb5aad88735da0/lib/libc/gen/err.c#L135-L144), [`_getprogname`](https://github.com/freebsd/freebsd-src/blob/8c639945a44588f6cfdddfb057bb5aad88735da0/lib/libc/gen/getprogname.c#L10-L17)) - [inet_aton, inet_addr, inet_ntoa and inet_ntoa_r](../../../core/libs/prx/libSceLibcInternal/Inet.cpp) (libSceLibcInternal) follow the FreeBSD code in the console's `libSceLibcInternal.sprx` (SDK 4.5x build of `J03950001`, 0x8ADE0 to 0x8B3D0) and matched it on a PS5 (firmware 13.60) for 20 `inet_aton` inputs; a null text, which the console dereferences, throws. `inet_ntoa_r` formats through libkernel's `__inet_ntop`, so a null buffer with room for the text throws and a null buffer that is too small sets `ENOSPC`, as `__inet_ntop` does for a null destination; the console's `inet_ntoa_r` was not measured with a null buffer - [__inet_ntoa_r](../../../core/libs/prx/libSceLibcInternal/Inet.cpp) (libSceLibcInternal) was not measured on the console: it runs `inet_ntoa_r`, as FreeBSD 11.4 names the function `__inet_ntoa_r` ([arpa/inet.h](https://github.com/freebsd/freebsd-src/blob/8c639945a44588f6cfdddfb057bb5aad88735da0/include/arpa/inet.h#L129)) and makes `inet_ntoa_r` a weak alias of it ([inet_ntoa.c](https://github.com/freebsd/freebsd-src/blob/8c639945a44588f6cfdddfb057bb5aad88735da0/lib/libc/inet/inet_ntoa.c#L62-L76)) +- [inet_netof, inet_lnaof, inet_makeaddr and inet_network](../../../core/libs/prx/libSceLibcInternal/Inet.cpp) (libSceLibcInternal, NIDs `UXEIr+tUsM4`, `4y7OKpbXUDs`, `5YaIoLorY8A`, `-Qdf3rmtqRA`, and `__` aliases `3R15U5X00nY`, `bD39ou3sn7w`, `9cf7QE7cmto`, `xDCABmswhcM`) follow FreeBSD 11.4 [inet_netof.c](https://github.com/freebsd/freebsd-src/blob/8c639945a44588f6cfdddfb057bb5aad88735da0/lib/libc/inet/inet_netof.c), [inet_lnaof.c](https://github.com/freebsd/freebsd-src/blob/8c639945a44588f6cfdddfb057bb5aad88735da0/lib/libc/inet/inet_lnaof.c), [inet_makeaddr.c](https://github.com/freebsd/freebsd-src/blob/8c639945a44588f6cfdddfb057bb5aad88735da0/lib/libc/inet/inet_makeaddr.c) and [inet_network.c](https://github.com/freebsd/freebsd-src/blob/8c639945a44588f6cfdddfb057bb5aad88735da0/lib/libc/inet/inet_network.c) (commit `8c639945a4`). `inet_network` components are bounded to 0xff with a 32-bit accumulator. A null text, which the reference dereferences, throws std::invalid_argument. Console unverified. ### Additional PS5 3.20 imports (xdCloudy #9498)