This commit is contained in:
Ash 2026-03-29 18:15:32 -04:00 committed by GitHub
commit 2aa9f4c424
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 221 additions and 60 deletions

View File

@ -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

69
src/common/network.cpp Normal file
View File

@ -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 <cstdlib>
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<URLInfo> 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<URLInfo> http_proxy = get_proxy("http_proxy");
std::optional<URLInfo> https_proxy = get_proxy("https_proxy");
} // namespace Common

25
src/common/network.h Normal file
View File

@ -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 <string>
#include <optional>
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<URLInfo> http_proxy;
extern std::optional<URLInfo> https_proxy;
}

View File

@ -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<u8> 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<u8> 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<u8> 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");
}

View File

@ -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:

View File

@ -29,6 +29,11 @@ AC_I::AC_I(std::shared_ptr<Module> 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"},

View File

@ -29,6 +29,11 @@ AC_U::AC_U(std::shared_ptr<Module> 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"},

View File

@ -11,6 +11,7 @@
#include <fmt/format.h>
#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<const Context::RequestHeader> 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<Context::RequestHeader> 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<Context::RequestHeader>& pending_headers) {
httplib::Error error{-1};
std::unique_ptr<httplib::Client> 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<Context::RequestHeader>& 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;

View File

@ -18,6 +18,7 @@
#include <boost/serialization/vector.hpp>
#include <boost/serialization/weak_ptr.hpp>
#include <httplib.h>
#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<Context::RequestHeader>& pending_headers);
void MakeRequestSSL(httplib::Request& request, const URLInfo& url_info,
void MakeRequestSSL(httplib::Request& request, const Common::URLInfo& url_info,
std::vector<Context::RequestHeader>& pending_headers);
bool ContentProvider(size_t offset, size_t length, httplib::DataSink& sink);
bool ChunkedContentProvider(size_t offset, httplib::DataSink& sink);