diff --git a/src/core/hle/service/http_c.cpp b/src/core/hle/service/http_c.cpp index f1c694114..221f3cc20 100644 --- a/src/core/hle/service/http_c.cpp +++ b/src/core/hle/service/http_c.cpp @@ -43,6 +43,10 @@ const ResultCode ERROR_TOO_MANY_CLIENT_CERTS = // 0xD8A0A0CB ErrorLevel::Permanent); const ResultCode ERROR_WRONG_CERT_ID = // 0xD8E0B839 ResultCode(57, ErrorModule::SSL, ErrorSummary::InvalidArgument, ErrorLevel::Permanent); +const ResultCode ERROR_WRONG_CERT_HANDLE = // 0xD8A0A0C9 + ResultCode(201, ErrorModule::HTTP, ErrorSummary::InvalidState, ErrorLevel::Permanent); +const ResultCode ERROR_CERT_ALREADY_SET = // 0xD8A0A03D + ResultCode(61, ErrorModule::HTTP, ErrorSummary::InvalidState, ErrorLevel::Permanent); void HTTP_C::Initialize(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp(ctx, 0x1, 1, 4); @@ -79,6 +83,8 @@ void HTTP_C::InitializeConnectionSession(Kernel::HLERequestContext& ctx) { const Context::Handle context_handle = rp.Pop(); u32 pid = rp.PopPID(); + LOG_DEBUG(Service_HTTP, "called, context_id={} pid={}", context_handle, pid); + auto* session_data = GetSessionData(ctx.Session()); ASSERT(session_data); @@ -105,7 +111,96 @@ void HTTP_C::InitializeConnectionSession(Kernel::HLERequestContext& ctx) { IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); rb.Push(RESULT_SUCCESS); - LOG_DEBUG(Service_HTTP, "called, context_id={} pid={}", context_handle, pid); +} + +void HTTP_C::BeginRequest(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx, 0x9, 1, 0); + const Context::Handle context_handle = rp.Pop(); + + LOG_WARNING(Service_HTTP, "(STUBBED) called, context_id={}", context_handle); + + auto* session_data = GetSessionData(ctx.Session()); + ASSERT(session_data); + + if (!session_data->initialized) { + LOG_ERROR(Service_HTTP, "Tried to make a request on an uninitialized session"); + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(ERROR_STATE_ERROR); + return; + } + + // This command can only be called with a bound context + if (!session_data->current_http_context) { + LOG_ERROR(Service_HTTP, "Tried to make a request without a bound context"); + + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(ResultCode(ErrorDescription::NotImplemented, ErrorModule::HTTP, + ErrorSummary::Internal, ErrorLevel::Permanent)); + return; + } + + if (session_data->current_http_context != context_handle) { + LOG_ERROR( + Service_HTTP, + "Tried to make a request on a mismatched session input context={} session context={}", + context_handle, *session_data->current_http_context); + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(ERROR_STATE_ERROR); + return; + } + + auto itr = contexts.find(context_handle); + ASSERT(itr != contexts.end()); + + // TODO(B3N30): Make the request + + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(RESULT_SUCCESS); +} + +void HTTP_C::BeginRequestAsync(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx, 0xA, 1, 0); + const Context::Handle context_handle = rp.Pop(); + + LOG_WARNING(Service_HTTP, "(STUBBED) called, context_id={}", context_handle); + + auto* session_data = GetSessionData(ctx.Session()); + ASSERT(session_data); + + if (!session_data->initialized) { + LOG_ERROR(Service_HTTP, "Tried to make a request on an uninitialized session"); + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(ERROR_STATE_ERROR); + return; + } + + // This command can only be called with a bound context + if (!session_data->current_http_context) { + LOG_ERROR(Service_HTTP, "Tried to make a request without a bound context"); + + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(ResultCode(ErrorDescription::NotImplemented, ErrorModule::HTTP, + ErrorSummary::Internal, ErrorLevel::Permanent)); + return; + } + + if (session_data->current_http_context != context_handle) { + LOG_ERROR( + Service_HTTP, + "Tried to make a request on a mismatched session input context={} session context={}", + context_handle, *session_data->current_http_context); + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(ERROR_STATE_ERROR); + return; + } + + auto itr = contexts.find(context_handle); + ASSERT(itr != contexts.end()); + + // TODO(B3N30): Make the request + + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(RESULT_SUCCESS); } void HTTP_C::CreateContext(Kernel::HLERequestContext& ctx) { @@ -238,6 +333,9 @@ void HTTP_C::AddRequestHeader(Kernel::HLERequestContext& ctx) { std::string value(value_size - 1, '\0'); value_buffer.Read(&value[0], 0, value_size - 1); + LOG_DEBUG(Service_HTTP, "called, name={}, value={}, context_handle={}", name, value, + context_handle); + auto* session_data = GetSessionData(ctx.Session()); ASSERT(session_data); @@ -294,9 +392,6 @@ void HTTP_C::AddRequestHeader(Kernel::HLERequestContext& ctx) { IPC::RequestBuilder rb = rp.MakeBuilder(1, 2); rb.Push(RESULT_SUCCESS); rb.PushMappedBuffer(value_buffer); - - LOG_DEBUG(Service_HTTP, "called, name={}, value={}, context_handle={}", name, value, - context_handle); } void HTTP_C::AddPostDataAscii(Kernel::HLERequestContext& ctx) { @@ -314,6 +409,9 @@ void HTTP_C::AddPostDataAscii(Kernel::HLERequestContext& ctx) { std::string value(value_size - 1, '\0'); value_buffer.Read(&value[0], 0, value_size - 1); + LOG_DEBUG(Service_HTTP, "called, name={}, value={}, context_handle={}", name, value, + context_handle); + auto* session_data = GetSessionData(ctx.Session()); ASSERT(session_data); @@ -369,9 +467,93 @@ void HTTP_C::AddPostDataAscii(Kernel::HLERequestContext& ctx) { IPC::RequestBuilder rb = rp.MakeBuilder(1, 2); rb.Push(RESULT_SUCCESS); rb.PushMappedBuffer(value_buffer); +} - LOG_DEBUG(Service_HTTP, "called, name={}, value={}, context_handle={}", name, value, - context_handle); +void HTTP_C::SetClientCertContext(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx, 0x29, 2, 0); + const u32 context_handle = rp.Pop(); + const u32 client_cert_handle = rp.Pop(); + + LOG_DEBUG(Service_HTTP, "called with context_handle={} client_cert_handle={}", context_handle, + client_cert_handle); + + auto* session_data = GetSessionData(ctx.Session()); + ASSERT(session_data); + + if (!session_data->initialized) { + LOG_ERROR(Service_HTTP, "Tried to set client cert on an uninitialized session"); + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(ERROR_STATE_ERROR); + return; + } + + // This command can only be called with a bound context + if (!session_data->current_http_context) { + LOG_ERROR(Service_HTTP, "Tried to set client cert without a bound context"); + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(ResultCode(ErrorDescription::NotImplemented, ErrorModule::HTTP, + ErrorSummary::Internal, ErrorLevel::Permanent)); + return; + } + + if (session_data->current_http_context != context_handle) { + LOG_ERROR(Service_HTTP, + "Tried to add set client cert on a mismatched session input context={} session " + "context={}", + context_handle, *session_data->current_http_context); + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(ERROR_STATE_ERROR); + return; + } + + auto http_context_itr = contexts.find(context_handle); + ASSERT(http_context_itr != contexts.end()); + + auto cert_context_itr = client_certs.find(client_cert_handle); + if (cert_context_itr == client_certs.end()) { + LOG_ERROR(Service_HTTP, "called with wrong client_cert_handle {}", client_cert_handle); + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(ERROR_WRONG_CERT_HANDLE); + return; + } + + if (http_context_itr->second.ssl_config.client_cert_ctx.lock()) { + LOG_ERROR(Service_HTTP, + "Tried to set a client cert to a context that already has a client cert"); + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(ERROR_CERT_ALREADY_SET); + return; + } + + if (http_context_itr->second.state != RequestState::NotStarted) { + LOG_ERROR(Service_HTTP, + "Tried to set a client cert on a context that has already been started."); + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(ResultCode(ErrCodes::InvalidRequestState, ErrorModule::HTTP, + ErrorSummary::InvalidState, ErrorLevel::Permanent)); + return; + } + + http_context_itr->second.ssl_config.client_cert_ctx = cert_context_itr->second; + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(RESULT_SUCCESS); +} + +void HTTP_C::GetSSLError(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx, 0x2a, 2, 0); + const u32 context_handle = rp.Pop(); + const u32 unk = rp.Pop(); + + LOG_WARNING(Service_HTTP, "(STUBBED) called, context_handle={}, unk={}", context_handle, unk); + + auto http_context_itr = contexts.find(context_handle); + ASSERT(http_context_itr != contexts.end()); + + IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); + rb.Push(RESULT_SUCCESS); + // Since we create the actual http/ssl context only when the request is submitted we can't check + // for SSL Errors here. Just submit no error. + rb.Push(0); } void HTTP_C::OpenClientCertContext(Kernel::HLERequestContext& ctx) { @@ -381,6 +563,8 @@ void HTTP_C::OpenClientCertContext(Kernel::HLERequestContext& ctx) { Kernel::MappedBuffer& cert_buffer = rp.PopMappedBuffer(); Kernel::MappedBuffer& key_buffer = rp.PopMappedBuffer(); + LOG_DEBUG(Service_HTTP, "called, cert_size {}, key_size {}", cert_size, key_size); + auto* session_data = GetSessionData(ctx.Session()); ASSERT(session_data); @@ -396,18 +580,17 @@ void HTTP_C::OpenClientCertContext(Kernel::HLERequestContext& ctx) { LOG_ERROR(Service_HTTP, "tried to load more then 2 client certs"); result = ERROR_TOO_MANY_CLIENT_CERTS; } else { - ++client_certs_counter; - client_certs[client_certs_counter].handle = client_certs_counter; - client_certs[client_certs_counter].certificate.resize(cert_size); - cert_buffer.Read(&client_certs[client_certs_counter].certificate[0], 0, cert_size); - client_certs[client_certs_counter].private_key.resize(key_size); - cert_buffer.Read(&client_certs[client_certs_counter].private_key[0], 0, key_size); - client_certs[client_certs_counter].session_id = session_data->session_id; + client_certs[++client_certs_counter] = std::make_shared(); + client_certs[client_certs_counter]->handle = client_certs_counter; + client_certs[client_certs_counter]->certificate.resize(cert_size); + cert_buffer.Read(&client_certs[client_certs_counter]->certificate[0], 0, cert_size); + client_certs[client_certs_counter]->private_key.resize(key_size); + cert_buffer.Read(&client_certs[client_certs_counter]->private_key[0], 0, key_size); + client_certs[client_certs_counter]->session_id = session_data->session_id; ++session_data->num_client_certs; } - LOG_DEBUG(Service_HTTP, "called, cert_size {}, key_size {}", cert_size, key_size); IPC::RequestBuilder rb = rp.MakeBuilder(1, 4); rb.Push(result); rb.PushMappedBuffer(cert_buffer); @@ -418,6 +601,8 @@ void HTTP_C::OpenDefaultClientCertContext(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp(ctx, 0x33, 1, 0); u8 cert_id = rp.Pop(); + LOG_DEBUG(Service_HTTP, "called, cert_id={} cert_handle={}", cert_id, client_certs_counter); + auto* session_data = GetSessionData(ctx.Session()); ASSERT(session_data); @@ -459,8 +644,8 @@ void HTTP_C::OpenDefaultClientCertContext(Kernel::HLERequestContext& ctx) { const auto& it = std::find_if(client_certs.begin(), client_certs.end(), [default_cert_id, &session_data](const auto& i) { - return default_cert_id == i.second.cert_id && - session_data->session_id == i.second.session_id; + return default_cert_id == i.second->cert_id && + session_data->session_id == i.second->session_id; }); if (it != client_certs.end()) { @@ -472,24 +657,24 @@ void HTTP_C::OpenDefaultClientCertContext(Kernel::HLERequestContext& ctx) { return; } - ++client_certs_counter; - client_certs[client_certs_counter].handle = client_certs_counter; - client_certs[client_certs_counter].certificate = ClCertA.certificate; - client_certs[client_certs_counter].private_key = ClCertA.private_key; - client_certs[client_certs_counter].session_id = session_data->session_id; + client_certs[++client_certs_counter] = std::make_shared(); + client_certs[client_certs_counter]->handle = client_certs_counter; + client_certs[client_certs_counter]->certificate = ClCertA.certificate; + client_certs[client_certs_counter]->private_key = ClCertA.private_key; + client_certs[client_certs_counter]->session_id = session_data->session_id; ++session_data->num_client_certs; IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); rb.Push(RESULT_SUCCESS); rb.Push(client_certs_counter); - - LOG_DEBUG(Service_HTTP, "called, cert_id={}", cert_id); } void HTTP_C::CloseClientCertContext(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp(ctx, 0x34, 1, 0); ClientCertContext::Handle cert_handle = rp.Pop(); + LOG_DEBUG(Service_HTTP, "called, cert_handle={}", cert_handle); + auto* session_data = GetSessionData(ctx.Session()); ASSERT(session_data); @@ -501,7 +686,7 @@ void HTTP_C::CloseClientCertContext(Kernel::HLERequestContext& ctx) { return; } - if (client_certs[cert_handle].session_id != session_data->session_id) { + if (client_certs[cert_handle]->session_id != session_data->session_id) { LOG_ERROR(Service_HTTP, "called from another main session"); IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); // This just return success without doing anything @@ -514,8 +699,6 @@ void HTTP_C::CloseClientCertContext(Kernel::HLERequestContext& ctx) { IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); rb.Push(RESULT_SUCCESS); - - LOG_DEBUG(Service_HTTP, "called, cert_handle={}", cert_handle); } void HTTP_C::Finalize(Kernel::HLERequestContext& ctx) { @@ -614,8 +797,8 @@ HTTP_C::HTTP_C() : ServiceFramework("http:C", 32) { {0x00060040, nullptr, "GetDownloadSizeState"}, {0x00070040, nullptr, "GetRequestError"}, {0x00080042, &HTTP_C::InitializeConnectionSession, "InitializeConnectionSession"}, - {0x00090040, nullptr, "BeginRequest"}, - {0x000A0040, nullptr, "BeginRequestAsync"}, + {0x00090040, &HTTP_C::BeginRequest, "BeginRequest"}, + {0x000A0040, &HTTP_C::BeginRequestAsync, "BeginRequestAsync"}, {0x000B0082, nullptr, "ReceiveData"}, {0x000C0102, nullptr, "ReceiveDataTimeout"}, {0x000D0146, nullptr, "SetProxy"}, @@ -645,8 +828,8 @@ HTTP_C::HTTP_C() : ServiceFramework("http:C", 32) { {0x00250080, nullptr, "AddDefaultCert"}, {0x00260080, nullptr, "SelectRootCertChain"}, {0x002700C4, nullptr, "SetClientCert"}, - {0x00290080, nullptr, "SetClientCertContext"}, - {0x002A0040, nullptr, "GetSSLError"}, + {0x00290080, &HTTP_C::SetClientCertContext, "SetClientCertContext"}, + {0x002A0040, &HTTP_C::GetSSLError, "GetSSLError"}, {0x002B0080, nullptr, "SetSSLOpt"}, {0x002C0080, nullptr, "SetSSLClearOpt"}, {0x002D0000, nullptr, "CreateRootCertChain"}, diff --git a/src/core/hle/service/http_c.h b/src/core/hle/service/http_c.h index 3ae540400..91a455820 100644 --- a/src/core/hle/service/http_c.h +++ b/src/core/hle/service/http_c.h @@ -193,6 +193,24 @@ private: */ void InitializeConnectionSession(Kernel::HLERequestContext& ctx); + /** + * HTTP_C::BeginRequest service function + * Inputs: + * 1 : Context handle + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + */ + void BeginRequest(Kernel::HLERequestContext& ctx); + + /** + * HTTP_C::BeginRequestAsync service function + * Inputs: + * 1 : Context handle + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + */ + void BeginRequestAsync(Kernel::HLERequestContext& ctx); + /** * HTTP_C::AddRequestHeader service function * Inputs: @@ -223,6 +241,27 @@ private: */ void AddPostDataAscii(Kernel::HLERequestContext& ctx); + /** + * HTTP_C::SetClientCertContext service function + * Inputs: + * 1 : Context handle + * 2 : Cert context handle + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + */ + void SetClientCertContext(Kernel::HLERequestContext& ctx); + + /** + * HTTP_C::GetSSLError service function + * Inputs: + * 1 : Context handle + * 2 : Unknown + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + * 2 : SSL Error code + */ + void GetSSLError(Kernel::HLERequestContext& ctx); + /** * HTTP_C::OpenClientCertContext service function * Inputs: @@ -280,7 +319,7 @@ private: std::unordered_map contexts; /// Global list of ClientCert contexts currently opened. - std::unordered_map client_certs; + std::unordered_map> client_certs; struct { std::vector certificate;