From 951128389d3b87a95fdfccf033795486985c7bd5 Mon Sep 17 00:00:00 2001 From: georgemoralis Date: Sun, 2 Mar 2025 23:09:38 +0200 Subject: [PATCH] [Lib] libsceHttp (#2576) * implemented sceHttpUriParse * argg clang * improved in case of file://// (probably) * rewrote httpuriparse to support file://// * fixed uriparse --- CMakeLists.txt | 1 + src/core/libraries/network/http.cpp | 275 +++++++++++++++++++++++- src/core/libraries/network/http.h | 20 +- src/core/libraries/network/http_error.h | 66 ++++++ 4 files changed, 352 insertions(+), 10 deletions(-) create mode 100644 src/core/libraries/network/http_error.h diff --git a/CMakeLists.txt b/CMakeLists.txt index b05a175bb..88d874af0 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -298,6 +298,7 @@ set(KERNEL_LIB src/core/libraries/kernel/sync/mutex.cpp set(NETWORK_LIBS src/core/libraries/network/http.cpp src/core/libraries/network/http.h + src/core/libraries/network/http_error.h src/core/libraries/network/http2.cpp src/core/libraries/network/http2.h src/core/libraries/network/net.cpp diff --git a/src/core/libraries/network/http.cpp b/src/core/libraries/network/http.cpp index 8e06c76fa..dbb5b096a 100644 --- a/src/core/libraries/network/http.cpp +++ b/src/core/libraries/network/http.cpp @@ -5,6 +5,7 @@ #include "core/libraries/error_codes.h" #include "core/libraries/libs.h" #include "core/libraries/network/http.h" +#include "http_error.h" namespace Libraries::Http { @@ -566,17 +567,277 @@ int PS4_SYSV_ABI sceHttpUriMerge() { return ORBIS_OK; } -int PS4_SYSV_ABI sceHttpUriParse() { +int PS4_SYSV_ABI sceHttpUriParse(OrbisHttpUriElement* out, const char* srcUri, void* pool, + size_t* require, size_t prepare) { + LOG_INFO(Lib_Http, "srcUri = {}", std::string(srcUri)); + if (!srcUri) { + LOG_ERROR(Lib_Http, "invalid url"); + return ORBIS_HTTP_ERROR_INVALID_URL; + } + if (!out && !pool && !require) { + LOG_ERROR(Lib_Http, "invalid values"); + return ORBIS_HTTP_ERROR_INVALID_VALUE; + } + + if (out && pool) { + memset(out, 0, sizeof(OrbisHttpUriElement)); + out->scheme = (char*)pool; + } + + // Track the total required buffer size + size_t requiredSize = 0; + + // Parse the scheme (e.g., "http:", "https:", "file:") + size_t schemeLength = 0; + while (srcUri[schemeLength] && srcUri[schemeLength] != ':') { + if (!isalnum(srcUri[schemeLength])) { + LOG_ERROR(Lib_Http, "invalid url"); + return ORBIS_HTTP_ERROR_INVALID_URL; + } + schemeLength++; + } + + if (pool && prepare < schemeLength + 1) { + LOG_ERROR(Lib_Http, "out of memory"); + return ORBIS_HTTP_ERROR_OUT_OF_MEMORY; + } + + if (out && pool) { + memcpy(out->scheme, srcUri, schemeLength); + out->scheme[schemeLength] = '\0'; + } + + requiredSize += schemeLength + 1; + + // Move past the scheme and ':' character + size_t offset = schemeLength + 1; + + // Check if "//" appears after the scheme + if (strncmp(srcUri + offset, "//", 2) == 0) { + // "//" is present + if (out) { + out->opaque = false; + } + offset += 2; // Move past "//" + } else { + // "//" is not present + if (out) { + out->opaque = true; + } + } + + // Handle "file" scheme + if (strncmp(srcUri, "file", 4) == 0) { + // File URIs typically start with "file://" + if (out && !out->opaque) { + // Skip additional slashes (e.g., "////") + while (srcUri[offset] == '/') { + offset++; + } + + // Parse the path (everything after the slashes) + char* pathStart = (char*)srcUri + offset; + size_t pathLength = 0; + while (pathStart[pathLength] && pathStart[pathLength] != '?' && + pathStart[pathLength] != '#') { + pathLength++; + } + + // Ensure the path starts with '/' + if (pathLength > 0 && pathStart[0] != '/') { + // Prepend '/' to the path + requiredSize += pathLength + 2; // Include '/' and null terminator + + if (pool && prepare < requiredSize) { + LOG_ERROR(Lib_Http, "out of memory"); + return ORBIS_HTTP_ERROR_OUT_OF_MEMORY; + } + + if (out && pool) { + out->path = (char*)pool + (requiredSize - pathLength - 2); + out->path[0] = '/'; // Add leading '/' + memcpy(out->path + 1, pathStart, pathLength); + out->path[pathLength + 1] = '\0'; + } + } else { + // Path already starts with '/' + requiredSize += pathLength + 1; + + if (pool && prepare < requiredSize) { + LOG_ERROR(Lib_Http, "out of memory"); + return ORBIS_HTTP_ERROR_OUT_OF_MEMORY; + } + + if (out && pool) { + memcpy((char*)pool + (requiredSize - pathLength - 1), pathStart, pathLength); + out->path = (char*)pool + (requiredSize - pathLength - 1); + out->path[pathLength] = '\0'; + } + } + + // Move past the path + offset += pathLength; + } + } + + // Handle non-file schemes (e.g., "http", "https") + else { + // Parse the host and port + char* hostStart = (char*)srcUri + offset; + while (*hostStart == '/') { + hostStart++; + } + + size_t hostLength = 0; + while (hostStart[hostLength] && hostStart[hostLength] != '/' && + hostStart[hostLength] != '?' && hostStart[hostLength] != ':') { + hostLength++; + } + + requiredSize += hostLength + 1; + + if (pool && prepare < requiredSize) { + LOG_ERROR(Lib_Http, "out of memory"); + return ORBIS_HTTP_ERROR_OUT_OF_MEMORY; + } + + if (out && pool) { + memcpy((char*)pool + (requiredSize - hostLength - 1), hostStart, hostLength); + out->hostname = (char*)pool + (requiredSize - hostLength - 1); + out->hostname[hostLength] = '\0'; + } + + // Move past the host + offset += hostLength; + + // Parse the port (if present) + if (hostStart[hostLength] == ':') { + char* portStart = hostStart + hostLength + 1; + size_t portLength = 0; + while (portStart[portLength] && isdigit(portStart[portLength])) { + portLength++; + } + + requiredSize += portLength + 1; + + if (pool && prepare < requiredSize) { + LOG_ERROR(Lib_Http, "out of memory"); + return ORBIS_HTTP_ERROR_OUT_OF_MEMORY; + } + + // Convert the port string to a uint16_t + char portStr[6]; // Max length for a port number (65535) + if (portLength > 5) { + LOG_ERROR(Lib_Http, "invalid url"); + return ORBIS_HTTP_ERROR_INVALID_URL; + } + memcpy(portStr, portStart, portLength); + portStr[portLength] = '\0'; + + uint16_t port = (uint16_t)atoi(portStr); + if (port == 0 && portStr[0] != '0') { + LOG_ERROR(Lib_Http, "invalid url"); + return ORBIS_HTTP_ERROR_INVALID_URL; + } + + // Set the port in the output structure + if (out) { + out->port = port; + } + + // Move past the port + offset += portLength + 1; + } + } + + // Parse the path (if present) + if (srcUri[offset] == '/') { + char* pathStart = (char*)srcUri + offset; + size_t pathLength = 0; + while (pathStart[pathLength] && pathStart[pathLength] != '?' && + pathStart[pathLength] != '#') { + pathLength++; + } + + requiredSize += pathLength + 1; + + if (pool && prepare < requiredSize) { + LOG_ERROR(Lib_Http, "out of memory"); + return ORBIS_HTTP_ERROR_OUT_OF_MEMORY; + } + + if (out && pool) { + memcpy((char*)pool + (requiredSize - pathLength - 1), pathStart, pathLength); + out->path = (char*)pool + (requiredSize - pathLength - 1); + out->path[pathLength] = '\0'; + } + + // Move past the path + offset += pathLength; + } + + // Parse the query (if present) + if (srcUri[offset] == '?') { + char* queryStart = (char*)srcUri + offset + 1; + size_t queryLength = 0; + while (queryStart[queryLength] && queryStart[queryLength] != '#') { + queryLength++; + } + + requiredSize += queryLength + 1; + + if (pool && prepare < requiredSize) { + LOG_ERROR(Lib_Http, "out of memory"); + return ORBIS_HTTP_ERROR_OUT_OF_MEMORY; + } + + if (out && pool) { + memcpy((char*)pool + (requiredSize - queryLength - 1), queryStart, queryLength); + out->query = (char*)pool + (requiredSize - queryLength - 1); + out->query[queryLength] = '\0'; + } + + // Move past the query + offset += queryLength + 1; + } + + // Parse the fragment (if present) + if (srcUri[offset] == '#') { + char* fragmentStart = (char*)srcUri + offset + 1; + size_t fragmentLength = 0; + while (fragmentStart[fragmentLength]) { + fragmentLength++; + } + + requiredSize += fragmentLength + 1; + + if (pool && prepare < requiredSize) { + LOG_ERROR(Lib_Http, "out of memory"); + return ORBIS_HTTP_ERROR_OUT_OF_MEMORY; + } + + if (out && pool) { + memcpy((char*)pool + (requiredSize - fragmentLength - 1), fragmentStart, + fragmentLength); + out->fragment = (char*)pool + (requiredSize - fragmentLength - 1); + out->fragment[fragmentLength] = '\0'; + } + } + + // Calculate the total required buffer size + if (require) { + *require = requiredSize; // Update with actual required size + } + + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceHttpUriSweepPath(char* dst, const char* src, size_t srcSize) { LOG_ERROR(Lib_Http, "(STUBBED) called"); return ORBIS_OK; } -int PS4_SYSV_ABI sceHttpUriSweepPath() { - LOG_ERROR(Lib_Http, "(STUBBED) called"); - return ORBIS_OK; -} - -int PS4_SYSV_ABI sceHttpUriUnescape() { +int PS4_SYSV_ABI sceHttpUriUnescape(char* out, size_t* require, size_t prepare, const char* in) { LOG_ERROR(Lib_Http, "(STUBBED) called"); return ORBIS_OK; } diff --git a/src/core/libraries/network/http.h b/src/core/libraries/network/http.h index 24bc83020..c687c60c4 100644 --- a/src/core/libraries/network/http.h +++ b/src/core/libraries/network/http.h @@ -11,6 +11,19 @@ class SymbolsResolver; namespace Libraries::Http { +struct OrbisHttpUriElement { + bool opaque; + char* scheme; + char* username; + char* password; + char* hostname; + char* path; + char* query; + char* fragment; + u16 port; + u8 reserved[10]; +}; + int PS4_SYSV_ABI sceHttpAbortRequest(); int PS4_SYSV_ABI sceHttpAbortRequestForce(); int PS4_SYSV_ABI sceHttpAbortWaitRequest(); @@ -122,9 +135,10 @@ int PS4_SYSV_ABI sceHttpUriBuild(); int PS4_SYSV_ABI sceHttpUriCopy(); int PS4_SYSV_ABI sceHttpUriEscape(); int PS4_SYSV_ABI sceHttpUriMerge(); -int PS4_SYSV_ABI sceHttpUriParse(); -int PS4_SYSV_ABI sceHttpUriSweepPath(); -int PS4_SYSV_ABI sceHttpUriUnescape(); +int PS4_SYSV_ABI sceHttpUriParse(OrbisHttpUriElement* out, const char* srcUri, void* pool, + size_t* require, size_t prepare); +int PS4_SYSV_ABI sceHttpUriSweepPath(char* dst, const char* src, size_t srcSize); +int PS4_SYSV_ABI sceHttpUriUnescape(char* out, size_t* require, size_t prepare, const char* in); int PS4_SYSV_ABI sceHttpWaitRequest(); void RegisterlibSceHttp(Core::Loader::SymbolsResolver* sym); diff --git a/src/core/libraries/network/http_error.h b/src/core/libraries/network/http_error.h new file mode 100644 index 000000000..49cc89766 --- /dev/null +++ b/src/core/libraries/network/http_error.h @@ -0,0 +1,66 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/libraries/error_codes.h" + +constexpr int ORBIS_HTTP_ERROR_BEFORE_INIT = 0x80431001; +constexpr int ORBIS_HTTP_ERROR_ALREADY_INITED = 0x80431020; +constexpr int ORBIS_HTTP_ERROR_BUSY = 0x80431021; +constexpr int ORBIS_HTTP_ERROR_OUT_OF_MEMORY = 0x80431022; +constexpr int ORBIS_HTTP_ERROR_NOT_FOUND = 0x80431025; +constexpr int ORBIS_HTTP_ERROR_INVALID_VERSION = 0x8043106a; +constexpr int ORBIS_HTTP_ERROR_INVALID_ID = 0x80431100; +constexpr int ORBIS_HTTP_ERROR_OUT_OF_SIZE = 0x80431104; +constexpr int ORBIS_HTTP_ERROR_INVALID_VALUE = 0x804311fe; + +constexpr int ORBIS_HTTP_ERROR_INVALID_URL = 0x80433060; +constexpr int ORBIS_HTTP_ERROR_UNKNOWN_SCHEME = 0x80431061; +constexpr int ORBIS_HTTP_ERROR_NETWORK = 0x80431063; +constexpr int ORBIS_HTTP_ERROR_BAD_RESPONSE = 0x80431064; +constexpr int ORBIS_HTTP_ERROR_BEFORE_SEND = 0x80431065; +constexpr int ORBIS_HTTP_ERROR_AFTER_SEND = 0x80431066; +constexpr int ORBIS_HTTP_ERROR_TIMEOUT = 0x80431068; +constexpr int ORBIS_HTTP_ERROR_UNKNOWN_AUTH_TYPE = 0x80431069; +constexpr int ORBIS_HTTP_ERROR_UNKNOWN_METHOD = 0x8043106b; +constexpr int ORBIS_HTTP_ERROR_READ_BY_HEAD_METHOD = 0x8043106f; +constexpr int ORBIS_HTTP_ERROR_NOT_IN_COM = 0x80431070; +constexpr int ORBIS_HTTP_ERROR_NO_CONTENT_LENGTH = 0x80431071; +constexpr int ORBIS_HTTP_ERROR_CHUNK_ENC = 0x80431072; +constexpr int ORBIS_HTTP_ERROR_TOO_LARGE_RESPONSE_HEADER = 0x80431073; +constexpr int ORBIS_HTTP_ERROR_SSL = 0x80431075; +constexpr int ORBIS_HTTP_ERROR_INSUFFICIENT_STACKSIZE = 0x80431076; +constexpr int ORBIS_HTTP_ERROR_ABORTED = 0x80431080; +constexpr int ORBIS_HTTP_ERROR_UNKNOWN = 0x80431081; +constexpr int ORBIS_HTTP_ERROR_EAGAIN = 0x80431082; +constexpr int ORBIS_HTTP_ERROR_PROXY = 0x80431084; +constexpr int ORBIS_HTTP_ERROR_BROKEN = 0x80431085; + +constexpr int ORBIS_HTTP_ERROR_PARSE_HTTP_NOT_FOUND = 0x80432025; +constexpr int ORBIS_HTTP_ERROR_PARSE_HTTP_INVALID_RESPONSE = 0x80432060; +constexpr int ORBIS_HTTP_ERROR_PARSE_HTTP_INVALID_VALUE = 0x804321fe; + +constexpr int ORBIS_HTTP_ERROR_RESOLVER_EPACKET = 0x80436001; +constexpr int ORBIS_HTTP_ERROR_RESOLVER_ENODNS = 0x80436002; +constexpr int ORBIS_HTTP_ERROR_RESOLVER_ETIMEDOUT = 0x80436003; +constexpr int ORBIS_HTTP_ERROR_RESOLVER_ENOSUPPORT = 0x80436004; +constexpr int ORBIS_HTTP_ERROR_RESOLVER_EFORMAT = 0x80436005; +constexpr int ORBIS_HTTP_ERROR_RESOLVER_ESERVERFAILURE = 0x80436006; +constexpr int ORBIS_HTTP_ERROR_RESOLVER_ENOHOST = 0x80436007; +constexpr int ORBIS_HTTP_ERROR_RESOLVER_ENOTIMPLEMENTED = 0x80436008; +constexpr int ORBIS_HTTP_ERROR_RESOLVER_ESERVERREFUSED = 0x80436009; +constexpr int ORBIS_HTTP_ERROR_RESOLVER_ENORECORD = 0x8043600a; + +constexpr int ORBIS_HTTPS_ERROR_CERT = 0x80435060; +constexpr int ORBIS_HTTPS_ERROR_HANDSHAKE = 0x80435061; +constexpr int ORBIS_HTTPS_ERROR_IO = 0x80435062; +constexpr int ORBIS_HTTPS_ERROR_INTERNAL = 0x80435063; +constexpr int ORBIS_HTTPS_ERROR_PROXY = 0x80435064; + +constexpr int ORBIS_HTTPS_ERROR_SSL_INTERNAL = 0x01; +constexpr int ORBIS_HTTPS_ERROR_SSL_INVALID_CERT = 0x02; +constexpr int ORBIS_HTTPS_ERROR_SSL_CN_CHECK = 0x04; +constexpr int ORBIS_HTTPS_ERROR_SSL_NOT_AFTER_CHECK = 0x08; +constexpr int ORBIS_HTTPS_ERROR_SSL_NOT_BEFORE_CHECK = 0x10; +constexpr int ORBIS_HTTPS_ERROR_SSL_UNKNOWN_CA = 0x20;