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..b27161d53 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,244 @@ int PS4_SYSV_ABI sceHttpUriMerge() { return ORBIS_OK; } -int PS4_SYSV_ABI sceHttpUriParse() { +// Helper function to calculate the length of a URI component +static size_t calculateComponentLength(const char* start, const char* end) { + return (end > start) ? (size_t)(end - start) : 0; +} + +// Helper function to parse the port from the URI +static int parsePort(const char* uri, int* offset) { + int port = 0; + int digits = 0; + + while (uri[*offset] >= '0' && uri[*offset] <= '9' && digits < 5) { + port = port * 10 + (uri[*offset] - '0'); + (*offset)++; + digits++; + } + + if (digits == 0 || port > 65535) { + return 0; // Invalid port + } + return port; +} + +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 values"); + return ORBIS_HTTP_ERROR_INVALID_URL; + } + + // Initialize the output structure if provided + if (out) { + memset(out, 0, sizeof(OrbisHttpUriElement)); + } + + // Calculate the required buffer size + size_t requiredBufferSize = 0; + char* currentPos = (char*)srcUri; + + // Scheme (e.g., "http:") + char* schemeEnd = strchr(currentPos, ':'); + if (!schemeEnd) { + LOG_ERROR(Lib_Http, "invalid url"); + return ORBIS_HTTP_ERROR_INVALID_URL; + } + requiredBufferSize += + calculateComponentLength(currentPos, schemeEnd) + 1; // Include null terminator + currentPos = schemeEnd + 1; + + // Check if the URI is opaque or hierarchical + bool isOpaque = true; // Assume opaque by default + if (strncmp(currentPos, "//", 2) == 0) { + isOpaque = false; // Hierarchical if "//" is present + currentPos += 2; // Skip "//" + } + + //in case it starts with file:///// + if (strncmp(currentPos, "//", 2) == 0) { + currentPos += 2; // Skip "//" + } + + // Host and port (e.g., "example.com:8080") + char* hostEnd = strchr(currentPos, '/'); + if (!hostEnd) { + hostEnd = currentPos + strlen(currentPos); + } + + // Check for credentials (username:password@host) + char* atSymbol = strchr(currentPos, '@'); + if (atSymbol && atSymbol < hostEnd) { + requiredBufferSize += + calculateComponentLength(currentPos, atSymbol) + 1; // Include null terminator + currentPos = atSymbol + 1; + } + + // Check for port (host:port) + char* colon = strchr(currentPos, ':'); + if (colon && colon < hostEnd) { + requiredBufferSize += + calculateComponentLength(currentPos, colon) + 1; // Include null terminator + currentPos = colon + 1; + } + + // Host + requiredBufferSize += + calculateComponentLength(currentPos, hostEnd) + 1; // Include null terminator + currentPos = hostEnd; + + // Path (e.g., "/path") + char* pathEnd = strchr(currentPos, '?'); + if (!pathEnd) { + pathEnd = strchr(currentPos, '#'); + if (!pathEnd) { + pathEnd = currentPos + strlen(currentPos); + } + } + requiredBufferSize += + calculateComponentLength(currentPos, pathEnd) + 1; // Include null terminator + currentPos = pathEnd; + + // Query (e.g., "?query=value") + if (*currentPos == '?') { + currentPos++; + char* queryEnd = strchr(currentPos, '#'); + if (!queryEnd) { + queryEnd = currentPos + strlen(currentPos); + } + requiredBufferSize += + calculateComponentLength(currentPos, queryEnd) + 1; // Include null terminator + currentPos = queryEnd; + } + + // Fragment (e.g., "#fragment") + if (*currentPos == '#') { + currentPos++; + requiredBufferSize += + calculateComponentLength(currentPos, currentPos + strlen(currentPos)) + + 1; // Include null terminator + } + + // Return the required buffer size + if (require) { + *require = requiredBufferSize; + } + + // If pool is NULL, we're done + if (!pool) { + return ORBIS_OK; + } + + // Check if the provided buffer is large enough + if (prepare < requiredBufferSize) { + LOG_ERROR(Lib_Http, "out of memory"); + return ORBIS_HTTP_ERROR_OUT_OF_MEMORY; + } + + // Copy the URI components to the buffer + char* buffer = (char*)pool; + strncpy(buffer, srcUri, prepare); + buffer[prepare - 1] = '\0'; // Ensure null termination + + // Parse and assign the components to the output structure if provided + if (out) { + // Scheme + schemeEnd = strchr(buffer, ':'); + *schemeEnd = '\0'; + out->scheme = buffer; + buffer = schemeEnd + 1; + + // Opaque flag + out->opaque = isOpaque; + + // Host and port + if (!isOpaque) { + buffer += 2; // Skip "//" + } + + hostEnd = strchr(buffer, '/'); + if (!hostEnd) { + hostEnd = buffer + strlen(buffer); + } + + // Credentials (username:password@host) + atSymbol = strchr(buffer, '@'); + if (atSymbol && atSymbol < hostEnd) { + *atSymbol = '\0'; + colon = strchr(buffer, ':'); + if (colon) { + *colon = '\0'; + out->username = buffer; + out->password = colon + 1; + } else { + out->username = buffer; + } + buffer = atSymbol + 1; + } + + // Port (host:port) + colon = strchr(buffer, ':'); + if (colon && colon < hostEnd) { + *colon = '\0'; + int offset = colon + 1 - buffer; + out->port = parsePort(buffer, &offset); + if (out->port == 0) { + LOG_ERROR(Lib_Http, "invalid url"); + return ORBIS_HTTP_ERROR_INVALID_URL; + } + } + + // Host + *hostEnd = '\0'; + out->hostname = buffer; + buffer = hostEnd + 1; + + // Path + pathEnd = strchr(buffer, '?'); + if (!pathEnd) { + pathEnd = strchr(buffer, '#'); + if (!pathEnd) { + pathEnd = buffer + strlen(buffer); + } + } + *pathEnd = '\0'; + out->path = buffer; + buffer = pathEnd + 1; + + // Query + if (*buffer == '?') { + buffer++; + char* queryEnd = strchr(buffer, '#'); + if (!queryEnd) { + queryEnd = buffer + strlen(buffer); + } + *queryEnd = '\0'; + out->query = buffer; + buffer = queryEnd + 1; + } + + // Fragment + if (*buffer == '#') { + buffer++; + out->fragment = buffer; + } + + // Initialize the reserved field to zero + memset(out->reserved, 0, sizeof(out->reserved)); // RE indicates some works here but haven't + // been added not sure if it neccesary + } + + 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;