diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index 4b6e74c49..86ecae0fd 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -59,6 +59,8 @@ add_library(citra_common STATIC microprofile.cpp microprofile.h microprofileui.h + network.cpp + network.h param_package.cpp param_package.h play_time_manager.cpp diff --git a/src/common/network.cpp b/src/common/network.cpp new file mode 100644 index 000000000..d00c8202f --- /dev/null +++ b/src/common/network.cpp @@ -0,0 +1,69 @@ +// Copyright 2020 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "common/network.h" +#include + +namespace Common { + +URLInfo SplitUrl(const std::string& url) { + const std::string prefix = "://"; + constexpr int default_http_port = 80; + constexpr int default_https_port = 443; + + std::string host; + int port = -1; + std::string path; + + const auto scheme_end = url.find(prefix); + const auto prefix_end = scheme_end == std::string::npos ? 0 : scheme_end + prefix.length(); + bool is_https = scheme_end != std::string::npos && url.starts_with("https"); + const auto path_index = url.find("/", prefix_end); + + if (path_index == std::string::npos) { + // If no path is specified after the host, set it to "/" + host = url.substr(prefix_end); + path = "/"; + } else { + host = url.substr(prefix_end, path_index - prefix_end); + path = url.substr(path_index); + } + + const auto port_start = host.find(":"); + if (port_start != std::string::npos) { + std::string port_str = host.substr(port_start + 1); + host = host.substr(0, port_start); + char* p_end = nullptr; + port = std::strtol(port_str.c_str(), &p_end, 10); + if (*p_end) { + port = -1; + } + } + + if (port == -1) { + port = is_https ? default_https_port : default_http_port; + } + return URLInfo{ + .is_https = is_https, + .host = host, + .port = port, + .path = path, + }; +} + +static std::optional get_proxy(const char* specific) { + // Try scheme-specific proxy first, then generic proxy + const char* proxy_url = std::getenv(specific); + if (!proxy_url) proxy_url = std::getenv("all_proxy"); + // No proxy in use + if (!proxy_url) return std::nullopt; + + URLInfo proxy_info = SplitUrl(proxy_url); + return proxy_info; +} + +std::optional http_proxy = get_proxy("http_proxy"); +std::optional https_proxy = get_proxy("https_proxy"); + +} // namespace Common \ No newline at end of file diff --git a/src/common/network.h b/src/common/network.h new file mode 100644 index 000000000..b0b598d27 --- /dev/null +++ b/src/common/network.h @@ -0,0 +1,25 @@ +// Copyright Citra Emulator Project / Azahar Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include +#include + +namespace Common { +struct URLInfo { + bool is_https; + std::string host; + int port; + std::string path; +}; + +// Splits URL into its components. Example: https://citra-emu.org:443/index.html +// is_https: true; host: citra-emu.org; port: 443; path: /index.html +URLInfo SplitUrl(const std::string& url); + +extern std::optional http_proxy; +extern std::optional https_proxy; + +} \ No newline at end of file diff --git a/src/core/hle/service/ac/ac.cpp b/src/core/hle/service/ac/ac.cpp index dc892ead7..582b288d2 100644 --- a/src/core/hle/service/ac/ac.cpp +++ b/src/core/hle/service/ac/ac.cpp @@ -6,6 +6,7 @@ #include "common/archives.h" #include "common/common_types.h" #include "common/logging/log.h" +#include "common/network.h" #include "common/settings.h" #include "core/core.h" #include "core/hle/ipc.h" @@ -186,12 +187,64 @@ void Module::Interface::RegisterDisconnectEvent(Kernel::HLERequestContext& ctx) void Module::Interface::GetConnectingProxyEnable(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp(ctx); - constexpr bool proxy_enabled = false; + const bool proxy_enabled = Common::https_proxy.has_value(); IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); rb.Push(ResultSuccess); rb.Push(proxy_enabled); +} +void Module::Interface::GetConnectingProxyAuthType(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx); + const int proxy_auth = 0; + + IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); + rb.Push(ResultSuccess); + rb.Push(proxy_auth); + LOG_WARNING(Service_AC, "(STUBBED) called"); +} + +void Module::Interface::GetConnectingProxyPort(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx); + const int proxy_port = Common::https_proxy->port; + + IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); + rb.Push(ResultSuccess); + rb.Push(proxy_port); +} + +void Module::Interface::GetConnectingProxyHost(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx); + const std::string& proxy_host = Common::https_proxy->host; + + std::vector buffer(proxy_host.begin(), proxy_host.end()); + + IPC::RequestBuilder rb = rp.MakeBuilder(1, 2); + rb.Push(ResultSuccess); + rb.PushStaticBuffer(std::move(buffer), 0); +} + +void Module::Interface::GetConnectingProxyUserName(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx); + const std::string proxy_user = ""; + + std::vector buffer(proxy_user.begin(), proxy_user.end()); + + IPC::RequestBuilder rb = rp.MakeBuilder(1, 2); + rb.Push(ResultSuccess); + rb.PushStaticBuffer(std::move(buffer), 0); + LOG_WARNING(Service_AC, "(STUBBED) called"); +} + +void Module::Interface::GetConnectingProxyPassword(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx); + const std::string proxy_pass = ""; + + std::vector buffer(proxy_pass.begin(), proxy_pass.end()); + + IPC::RequestBuilder rb = rp.MakeBuilder(1, 2); + rb.Push(ResultSuccess); + rb.PushStaticBuffer(std::move(buffer), 0); LOG_WARNING(Service_AC, "(STUBBED) called"); } diff --git a/src/core/hle/service/ac/ac.h b/src/core/hle/service/ac/ac.h index afa5b846b..4ea1ef3ac 100644 --- a/src/core/hle/service/ac/ac.h +++ b/src/core/hle/service/ac/ac.h @@ -160,6 +160,52 @@ public: */ void GetConnectingProxyEnable(Kernel::HLERequestContext& ctx); + /** + * AC::GetConnectingProxyAuthType service function + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + * 2 : int, proxy auth type enum + */ + void GetConnectingProxyAuthType(Kernel::HLERequestContext& ctx); + + /** + * AC::GetConnectingProxyPort service function + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + * 2 : short, proxy port number + */ + void GetConnectingProxyPort(Kernel::HLERequestContext& ctx); + + /** + * AC::GetConnectingProxyHost service function + * Inputs: + * 64 : Buffer size << 14 | 2 + * 65 : Output pointer to buffer + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + */ + void GetConnectingProxyHost(Kernel::HLERequestContext& ctx); + + /** + * AC::GetConnectingProxyUserName service function + * Inputs: + * 64 : Buffer size << 14 | 2 + * 65 : Output pointer to buffer + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + */ + void GetConnectingProxyUserName(Kernel::HLERequestContext& ctx); + + /** + * AC::GetConnectingProxyPassword service function + * Inputs: + * 64 : Buffer size << 14 | 2 + * 65 : Output pointer to buffer + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + */ + void GetConnectingProxyPassword(Kernel::HLERequestContext& ctx); + /** * AC::IsConnected service function * Outputs: diff --git a/src/core/hle/service/ac/ac_i.cpp b/src/core/hle/service/ac/ac_i.cpp index b6917df97..9a973c866 100644 --- a/src/core/hle/service/ac/ac_i.cpp +++ b/src/core/hle/service/ac/ac_i.cpp @@ -29,6 +29,11 @@ AC_I::AC_I(std::shared_ptr ac) : Module::Interface(std::move(ac), "ac:i" {0x002F, &AC_I::GetNZoneBeaconNotFoundEvent, "GetNZoneBeaconNotFoundEvent"}, {0x0030, &AC_I::RegisterDisconnectEvent, "RegisterDisconnectEvent"}, {0x0036, &AC_I::GetConnectingProxyEnable, "GetConnectingProxyEnable"}, + {0x0037, &AC_I::GetConnectingProxyAuthType, "GetConnectingProxyAuthType"}, + {0x0038, &AC_I::GetConnectingProxyPort, "GetConnectingProxyPort"}, + {0x0039, &AC_I::GetConnectingProxyHost, "GetConnectingProxyHost"}, + {0x003A, &AC_I::GetConnectingProxyUserName, "GetConnectingProxyUserName"}, + {0x003B, &AC_I::GetConnectingProxyPassword, "GetConnectingProxyPassword"}, {0x003C, nullptr, "GetAPSSIDList"}, {0x003E, &AC_I::IsConnected, "IsConnected"}, {0x0040, &AC_I::SetClientVersion, "SetClientVersion"}, diff --git a/src/core/hle/service/ac/ac_u.cpp b/src/core/hle/service/ac/ac_u.cpp index d340ad3b1..c24e70444 100644 --- a/src/core/hle/service/ac/ac_u.cpp +++ b/src/core/hle/service/ac/ac_u.cpp @@ -29,6 +29,11 @@ AC_U::AC_U(std::shared_ptr ac) : Module::Interface(std::move(ac), "ac:u" {0x002F, &AC_U::GetNZoneBeaconNotFoundEvent, "GetNZoneBeaconNotFoundEvent"}, {0x0030, &AC_U::RegisterDisconnectEvent, "RegisterDisconnectEvent"}, {0x0036, &AC_U::GetConnectingProxyEnable, "GetConnectingProxyEnable"}, + {0x0037, &AC_U::GetConnectingProxyAuthType, "GetConnectingProxyAuthType"}, + {0x0038, &AC_U::GetConnectingProxyPort, "GetConnectingProxyPort"}, + {0x0039, &AC_U::GetConnectingProxyHost, "GetConnectingProxyHost"}, + {0x003A, &AC_U::GetConnectingProxyUserName, "GetConnectingProxyUserName"}, + {0x003B, &AC_U::GetConnectingProxyPassword, "GetConnectingProxyPassword"}, {0x003C, nullptr, "GetAPSSIDList"}, {0x003E, &AC_U::IsConnected, "IsConnected"}, {0x0040, &AC_U::SetClientVersion, "SetClientVersion"}, diff --git a/src/core/hle/service/http/http_c.cpp b/src/core/hle/service/http/http_c.cpp index b1f47373b..eeaff74fb 100644 --- a/src/core/hle/service/http/http_c.cpp +++ b/src/core/hle/service/http/http_c.cpp @@ -11,6 +11,7 @@ #include #include "common/archives.h" #include "common/assert.h" +#include "common/network.h" #include "common/scope_exit.h" #include "common/string_util.h" #include "core/core.h" @@ -111,53 +112,6 @@ constexpr Result ErrorIncompatibleSendPostData = // 0xD8A0A036 Result(ErrCodes::IncompatibleSendPostData, ErrorModule::HTTP, ErrorSummary::InvalidState, ErrorLevel::Permanent); -// Splits URL into its components. Example: https://citra-emu.org:443/index.html -// is_https: true; host: citra-emu.org; port: 443; path: /index.html -static URLInfo SplitUrl(const std::string& url) { - const std::string prefix = "://"; - constexpr int default_http_port = 80; - constexpr int default_https_port = 443; - - std::string host; - int port = -1; - std::string path; - - const auto scheme_end = url.find(prefix); - const auto prefix_end = scheme_end == std::string::npos ? 0 : scheme_end + prefix.length(); - bool is_https = scheme_end != std::string::npos && url.starts_with("https"); - const auto path_index = url.find("/", prefix_end); - - if (path_index == std::string::npos) { - // If no path is specified after the host, set it to "/" - host = url.substr(prefix_end); - path = "/"; - } else { - host = url.substr(prefix_end, path_index - prefix_end); - path = url.substr(path_index); - } - - const auto port_start = host.find(":"); - if (port_start != std::string::npos) { - std::string port_str = host.substr(port_start + 1); - host = host.substr(0, port_start); - char* p_end = nullptr; - port = std::strtol(port_str.c_str(), &p_end, 10); - if (*p_end) { - port = -1; - } - } - - if (port == -1) { - port = is_https ? default_https_port : default_http_port; - } - return URLInfo{ - .is_https = is_https, - .host = host, - .port = port, - .path = path, - }; -} - static std::size_t WriteHeaders(httplib::Stream& stream, std::span headers) { std::size_t write_len = 0; @@ -290,7 +244,7 @@ void Context::MakeRequest() { {RequestMethod::PutEmpty, "PUT"}, }; - URLInfo url_info = SplitUrl(url); + Common::URLInfo url_info = Common::SplitUrl(url); httplib::Request request; std::vector pending_headers; @@ -371,7 +325,7 @@ void Context::MakeRequest() { } } -void Context::MakeRequestNonSSL(httplib::Request& request, const URLInfo& url_info, +void Context::MakeRequestNonSSL(httplib::Request& request, const Common::URLInfo& url_info, std::vector& pending_headers) { httplib::Error error{-1}; std::unique_ptr client = @@ -382,6 +336,10 @@ void Context::MakeRequestNonSSL(httplib::Request& request, const URLInfo& url_in return HandleHeaderWrite(pending_headers, strm, httplib_headers); }); + if (Common::http_proxy) { + client->set_proxy(Common::http_proxy->host, Common::http_proxy->port); + } + if (!client->send(request, response, error)) { LOG_ERROR(Service_HTTP, "Request failed: {}: {}", error, httplib::to_string(error)); state = RequestState::Completed; @@ -391,7 +349,7 @@ void Context::MakeRequestNonSSL(httplib::Request& request, const URLInfo& url_in } } -void Context::MakeRequestSSL(httplib::Request& request, const URLInfo& url_info, +void Context::MakeRequestSSL(httplib::Request& request, const Common::URLInfo& url_info, std::vector& pending_headers) { httplib::Error error{-1}; X509* cert = nullptr; @@ -440,6 +398,10 @@ void Context::MakeRequestSSL(httplib::Request& request, const URLInfo& url_info, return HandleHeaderWrite(pending_headers, strm, httplib_headers); }); + if (Common::https_proxy) { + client->set_proxy(Common::https_proxy->host, Common::https_proxy->port); + } + if (!client->send(request, response, error)) { LOG_ERROR(Service_HTTP, "Request failed: {}: {}", error, httplib::to_string(error)); state = RequestState::Completed; diff --git a/src/core/hle/service/http/http_c.h b/src/core/hle/service/http/http_c.h index 28f7c4795..80c7c0336 100644 --- a/src/core/hle/service/http/http_c.h +++ b/src/core/hle/service/http/http_c.h @@ -18,6 +18,7 @@ #include #include #include +#include "common/network.h" #include "common/thread.h" #include "core/hle/ipc_helpers.h" #include "core/hle/kernel/shared_memory.h" @@ -89,13 +90,6 @@ enum class ClientCertID : u32 { Default = 0x40, // Default client cert }; -struct URLInfo { - bool is_https; - std::string host; - int port; - std::string path; -}; - /// Represents a client certificate along with its private key, stored as a byte array of DER data. /// There can only be at most one client certificate context attached to an HTTP context at any /// given time. @@ -296,9 +290,9 @@ public: void ParseAsciiPostData(); std::string ParseMultipartFormData(); void MakeRequest(); - void MakeRequestNonSSL(httplib::Request& request, const URLInfo& url_info, + void MakeRequestNonSSL(httplib::Request& request, const Common::URLInfo& url_info, std::vector& pending_headers); - void MakeRequestSSL(httplib::Request& request, const URLInfo& url_info, + void MakeRequestSSL(httplib::Request& request, const Common::URLInfo& url_info, std::vector& pending_headers); bool ContentProvider(size_t offset, size_t length, httplib::DataSink& sink); bool ChunkedContentProvider(size_t offset, httplib::DataSink& sink);