#include "common/abi.h" #include "loader/symbolDatabase.h" #include #include #include #include #include namespace Libs::LibHttp { void InitNet_1_Http(Loader::SymbolDatabase *symbols); } namespace { struct SceHttpUriElement { int opaque = 0; char *scheme = nullptr; char *username = nullptr; char *password = nullptr; char *hostname = nullptr; char *path = nullptr; char *query = nullptr; char *fragment = nullptr; uint16_t port = 0; uint8_t reserved[10]{}; }; int failures = 0; #define CHECK(condition) \ do { \ if (!(condition)) { \ std::fprintf(stderr, "HttpUriParseTests:%d: %s\n", __LINE__, \ #condition); \ failures++; \ } \ } while (false) using HttpUriParse = int(KYTY_SYSV_ABI *)(SceHttpUriElement *, const char *, void *, size_t *, size_t); HttpUriParse GetHttpUriParse() { Loader::SymbolDatabase symbols; Libs::LibHttp::InitNet_1_Http(&symbols); const auto *record = symbols.FindByNid("IWalAn-guFs", Loader::SymbolType::Func); CHECK(record != nullptr); return record != nullptr ? reinterpret_cast(record->vaddr) : nullptr; } template bool PointsIntoPool(const char *ptr, const std::array &pool, size_t used) { if (ptr == nullptr) { return false; } const auto address = reinterpret_cast(ptr); const auto begin = reinterpret_cast(pool.data()); return address >= begin && address < begin + used; } void TestAbsentQuery(HttpUriParse parse) { constexpr char url[] = "http://example.com/path"; size_t required = 0; CHECK(parse(nullptr, url, nullptr, &required, 0) == 0); const size_t expected_required = sizeof("http") + sizeof("example.com") + sizeof("/path") + 1; CHECK(required == expected_required); std::array pool{}; SceHttpUriElement out{}; size_t parsed_required = 0; CHECK(parse(&out, url, pool.data(), &parsed_required, required) == 0); CHECK(parsed_required == required); CHECK(out.query != nullptr); CHECK(PointsIntoPool(out.query, pool, required)); if (out.query != nullptr) { CHECK(out.query[0] == '\0'); } } void TestEmptyUri(HttpUriParse parse) { constexpr char url[] = ""; size_t required = 0; CHECK(parse(nullptr, url, nullptr, &required, 0) == 0); CHECK(required == 4); std::array pool{}; SceHttpUriElement out{}; size_t parsed_required = 0; CHECK(parse(&out, url, pool.data(), &parsed_required, required) == 0); CHECK(parsed_required == required); CHECK(out.query != nullptr); CHECK(PointsIntoPool(out.query, pool, required)); if (out.query != nullptr) { CHECK(out.query[0] == '\0'); } } void TestPresentQuery(HttpUriParse parse) { constexpr char url[] = "http://example.com/path?foo=bar"; size_t required = 0; CHECK(parse(nullptr, url, nullptr, &required, 0) == 0); std::array pool{}; SceHttpUriElement out{}; size_t parsed_required = 0; CHECK(parse(&out, url, pool.data(), &parsed_required, required) == 0); CHECK(parsed_required == required); CHECK(out.query != nullptr); CHECK(PointsIntoPool(out.query, pool, required)); if (out.query != nullptr) { CHECK(std::strcmp(out.query, "?foo=bar") == 0); } } } // namespace int main() { const auto parse = GetHttpUriParse(); if (parse == nullptr) { return 1; } TestAbsentQuery(parse); TestEmptyUri(parse); TestPresentQuery(parse); return failures == 0 ? 0 : 1; }