diff --git a/src/core/hle/service/http/http_c.cpp b/src/core/hle/service/http/http_c.cpp index 3451ed7d7b..b57a061e45 100644 --- a/src/core/hle/service/http/http_c.cpp +++ b/src/core/hle/service/http/http_c.cpp @@ -65,13 +65,6 @@ const ResultCode ERROR_WRONG_CERT_HANDLE = // 0xD8A0A0C9 const ResultCode ERROR_CERT_ALREADY_SET = // 0xD8A0A03D ResultCode(61, ErrorModule::HTTP, ErrorSummary::InvalidState, ErrorLevel::Permanent); -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 static URLInfo SplitUrl(const std::string& url) { @@ -166,7 +159,6 @@ void Context::MakeRequest() { URLInfo url_info = SplitUrl(url); httplib::Request request; - httplib::Error error{-1}; std::vector pending_headers; request.method = request_method_strings.at(method); request.path = url_info.path; @@ -178,11 +170,6 @@ void Context::MakeRequest() { return true; }; - auto header_writter = [&pending_headers](httplib::Stream& strm, - httplib::Headers& httplib_headers) { - return HandleHeaderWrite(pending_headers, strm, httplib_headers); - }; - for (const auto& header : headers) { pending_headers.push_back(header); } @@ -200,59 +187,67 @@ void Context::MakeRequest() { state = RequestState::InProgress; - // Sadly, we have to duplicate code, the class hierarchy in httplib is not very useful... if (url_info.is_https) { - X509* cert = nullptr; - EVP_PKEY* key = nullptr; - { - std::unique_ptr client; - if (uses_default_client_cert) { - const unsigned char* tmpCertPtr = clcert_data->certificate.data(); - const unsigned char* tmpKeyPtr = clcert_data->private_key.data(); - cert = d2i_X509(nullptr, &tmpCertPtr, (long)clcert_data->certificate.size()); - key = d2i_PrivateKey(EVP_PKEY_RSA, nullptr, &tmpKeyPtr, - (long)clcert_data->private_key.size()); - client = - std::make_unique(url_info.host, url_info.port, cert, key); - } else if (auto client_cert = ssl_config.client_cert_ctx.lock()) { - const unsigned char* tmpCertPtr = client_cert->certificate.data(); - const unsigned char* tmpKeyPtr = client_cert->private_key.data(); - cert = d2i_X509(nullptr, &tmpCertPtr, (long)client_cert->certificate.size()); - key = d2i_PrivateKey(EVP_PKEY_RSA, nullptr, &tmpKeyPtr, - (long)client_cert->private_key.size()); - client = - std::make_unique(url_info.host, url_info.port, cert, key); - } else { - client = std::make_unique(url_info.host, url_info.port); - } - - // TODO(B3N30): Check for SSLOptions-Bits and set the verify method accordingly - // https://www.3dbrew.org/wiki/SSL_Services#SSLOpt - // Hack: Since for now RootCerts are not implemented we set the VerifyMode to None. - client->enable_server_certificate_verification(false); - - client->set_header_writer(header_writter); - - if (!client->send(request, response, error)) { - LOG_ERROR(Service_HTTP, "Request failed: {}: {}", error, httplib::to_string(error)); - state = RequestState::TimedOut; - } else { - LOG_DEBUG(Service_HTTP, "Request successful"); - // TODO(B3N30): Verify this state on HW - state = RequestState::ReadyToDownloadContent; - } - } - if (cert) { - X509_free(cert); - } - if (key) { - EVP_PKEY_free(key); - } + MakeRequestSSL(request, url_info, pending_headers); } else { - std::unique_ptr client = - std::make_unique(url_info.host, url_info.port); + MakeRequestNonSSL(request, url_info, pending_headers); + } +} - client->set_header_writer(header_writter); +void Context::MakeRequestNonSSL(httplib::Request& request, const URLInfo& url_info, + std::vector& pending_headers) { + httplib::Error error{-1}; + std::unique_ptr client = + std::make_unique(url_info.host, url_info.port); + + client->set_header_writer( + [&pending_headers](httplib::Stream& strm, httplib::Headers& httplib_headers) { + return HandleHeaderWrite(pending_headers, strm, httplib_headers); + }); + + if (!client->send(request, response, error)) { + LOG_ERROR(Service_HTTP, "Request failed: {}: {}", error, httplib::to_string(error)); + state = RequestState::TimedOut; + } else { + LOG_DEBUG(Service_HTTP, "Request successful"); + // TODO(B3N30): Verify this state on HW + state = RequestState::ReadyToDownloadContent; + } +} +void Context::MakeRequestSSL(httplib::Request& request, const URLInfo& url_info, + std::vector& pending_headers) { + httplib::Error error{-1}; + X509* cert = nullptr; + EVP_PKEY* key = nullptr; + { + std::unique_ptr client; + if (uses_default_client_cert) { + const unsigned char* tmpCertPtr = clcert_data->certificate.data(); + const unsigned char* tmpKeyPtr = clcert_data->private_key.data(); + cert = d2i_X509(nullptr, &tmpCertPtr, (long)clcert_data->certificate.size()); + key = d2i_PrivateKey(EVP_PKEY_RSA, nullptr, &tmpKeyPtr, + (long)clcert_data->private_key.size()); + client = std::make_unique(url_info.host, url_info.port, cert, key); + } else if (auto client_cert = ssl_config.client_cert_ctx.lock()) { + const unsigned char* tmpCertPtr = client_cert->certificate.data(); + const unsigned char* tmpKeyPtr = client_cert->private_key.data(); + cert = d2i_X509(nullptr, &tmpCertPtr, (long)client_cert->certificate.size()); + key = d2i_PrivateKey(EVP_PKEY_RSA, nullptr, &tmpKeyPtr, + (long)client_cert->private_key.size()); + client = std::make_unique(url_info.host, url_info.port, cert, key); + } else { + client = std::make_unique(url_info.host, url_info.port); + } + + // TODO(B3N30): Check for SSLOptions-Bits and set the verify method accordingly + // https://www.3dbrew.org/wiki/SSL_Services#SSLOpt + // Hack: Since for now RootCerts are not implemented we set the VerifyMode to None. + client->enable_server_certificate_verification(false); + + client->set_header_writer( + [&pending_headers](httplib::Stream& strm, httplib::Headers& httplib_headers) { + return HandleHeaderWrite(pending_headers, strm, httplib_headers); + }); if (!client->send(request, response, error)) { LOG_ERROR(Service_HTTP, "Request failed: {}: {}", error, httplib::to_string(error)); @@ -263,6 +258,12 @@ void Context::MakeRequest() { state = RequestState::ReadyToDownloadContent; } } + if (cert) { + X509_free(cert); + } + if (key) { + EVP_PKEY_free(key); + } } void HTTP_C::Initialize(Kernel::HLERequestContext& ctx) { diff --git a/src/core/hle/service/http/http_c.h b/src/core/hle/service/http/http_c.h index 370889ee0f..07547d05ea 100644 --- a/src/core/hle/service/http/http_c.h +++ b/src/core/hle/service/http/http_c.h @@ -61,6 +61,13 @@ 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. @@ -134,8 +141,6 @@ public: Context(const Context&) = delete; Context& operator=(const Context&) = delete; - void MakeRequest(); - struct Proxy { std::string url; std::string username; @@ -215,6 +220,12 @@ public: size_t current_copied_data; bool uses_default_client_cert{}; httplib::Response response; + + void MakeRequest(); + void MakeRequestNonSSL(httplib::Request& request, const URLInfo& url_info, + std::vector& pending_headers); + void MakeRequestSSL(httplib::Request& request, const URLInfo& url_info, + std::vector& pending_headers); }; struct SessionData : public Kernel::SessionRequestHandler::SessionDataBase {