blob: 97b64898354a538737fba9aa9e72a7c157233784 [file] [log] [blame]
// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "net/dns/host_resolver_manager_service_endpoint_request_impl.h"
#include "base/memory/safe_ref.h"
#include "base/no_destructor.h"
#include "base/notreached.h"
#include "base/types/optional_util.h"
#include "net/base/net_errors.h"
#include "net/dns/dns_alias_utility.h"
#include "net/dns/dns_task_results_manager.h"
#include "net/dns/host_resolver.h"
#include "net/dns/host_resolver_manager.h"
#include "url/scheme_host_port.h"
namespace net {
HostResolverManager::ServiceEndpointRequestImpl::FinalizedResult::
FinalizedResult(std::vector<ServiceEndpoint> endpoints,
std::set<std::string> dns_aliases)
: endpoints(std::move(endpoints)), dns_aliases(std::move(dns_aliases)) {}
HostResolverManager::ServiceEndpointRequestImpl::FinalizedResult::
~FinalizedResult() = default;
HostResolverManager::ServiceEndpointRequestImpl::FinalizedResult::
FinalizedResult(FinalizedResult&&) = default;
HostResolverManager::ServiceEndpointRequestImpl::FinalizedResult&
HostResolverManager::ServiceEndpointRequestImpl::FinalizedResult::operator=(
FinalizedResult&&) = default;
HostResolverManager::ServiceEndpointRequestImpl::ServiceEndpointRequestImpl(
url::SchemeHostPort scheme_host_port,
NetworkAnonymizationKey network_anonymization_key,
NetLogWithSource net_log,
ResolveHostParameters parameters,
base::WeakPtr<ResolveContext> resolve_context,
base::WeakPtr<HostResolverManager> manager,
const base::TickClock* tick_clock)
: host_(std::move(scheme_host_port)),
network_anonymization_key_(
NetworkAnonymizationKey::IsPartitioningEnabled()
? std::move(network_anonymization_key)
: NetworkAnonymizationKey()),
net_log_(std::move(net_log)),
parameters_(std::move(parameters)),
resolve_context_(std::move(resolve_context)),
manager_(std::move(manager)),
tick_clock_(tick_clock),
priority_(parameters_.initial_priority) {}
HostResolverManager::ServiceEndpointRequestImpl::~ServiceEndpointRequestImpl() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (!job_.has_value()) {
return;
}
LogCancelRequest();
// Clear the delegate to avoid calling delegate's callback after destruction.
// The following CancelServiceEndpointRequest() could result in calling
// OnJobCancelled() synchronously.
delegate_ = nullptr;
job_.value()->CancelServiceEndpointRequest(this);
}
int HostResolverManager::ServiceEndpointRequestImpl::Start(Delegate* delegate) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
CHECK(!delegate_);
CHECK(manager_);
if (!resolve_context_) {
return ERR_CONTEXT_SHUT_DOWN;
}
delegate_ = delegate;
JobKey job_key(host_, resolve_context_.get());
IPAddress ip_address;
manager_->InitializeJobKeyAndIPAddress(
network_anonymization_key_, parameters_, net_log_, job_key, ip_address);
// Try to resolve locally first.
std::optional<HostCache::EntryStaleness> stale_info;
std::deque<TaskType> tasks;
HostCache::Entry results = manager_->ResolveLocally(
/*only_ipv6_reachable=*/false, job_key, ip_address,
parameters_.cache_usage, parameters_.secure_dns_policy,
parameters_.source, net_log_, host_cache(), &tasks, &stale_info);
if (results.error() != ERR_DNS_CACHE_MISS ||
parameters_.source == HostResolverSource::LOCAL_ONLY || tasks.empty()) {
SetFinalizedResultFromLegacyResults(results);
return results.error();
}
manager_->CreateAndStartJobForServiceEndpointRequest(std::move(job_key),
std::move(tasks), this);
return ERR_IO_PENDING;
}
const std::vector<ServiceEndpoint>&
HostResolverManager::ServiceEndpointRequestImpl::GetEndpointResults() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (finalized_result_.has_value()) {
return finalized_result_->endpoints;
}
if (job_) {
CHECK(job_.value()->dns_task_results_manager());
return job_.value()->dns_task_results_manager()->GetCurrentEndpoints();
}
NOTREACHED_NORETURN();
}
const std::set<std::string>&
HostResolverManager::ServiceEndpointRequestImpl::GetDnsAliasResults() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (finalized_result_.has_value()) {
return finalized_result_->dns_aliases;
}
if (job_) {
CHECK(job_.value()->dns_task_results_manager());
// TODO(crbug.com/41493696): Call dns_alias_utility::FixUpDnsAliases().
return job_.value()->dns_task_results_manager()->GetAliases();
}
NOTREACHED_NORETURN();
}
bool HostResolverManager::ServiceEndpointRequestImpl::EndpointsCryptoReady() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (finalized_result_.has_value()) {
return true;
}
if (job_) {
CHECK(job_.value()->dns_task_results_manager());
return job_.value()->dns_task_results_manager()->IsMetadataReady();
}
NOTREACHED_NORETURN();
}
void HostResolverManager::ServiceEndpointRequestImpl::AssignJob(
base::SafeRef<Job> job) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
CHECK(!job_.has_value());
job_ = job;
}
void HostResolverManager::ServiceEndpointRequestImpl::OnJobCompleted(
const HostCache::Entry& results,
bool obtained_securely) {
CHECK(job_);
CHECK(delegate_);
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
job_.reset();
SetFinalizedResultFromLegacyResults(results);
const bool is_secure_network_error =
obtained_securely && results.error() != OK;
error_info_ = ResolveErrorInfo(results.error(), is_secure_network_error);
delegate_->OnServiceEndpointRequestFinished(results.error());
// Do not add code below. `this` may be deleted at this point.
}
void HostResolverManager::ServiceEndpointRequestImpl::OnJobCancelled() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
CHECK(job_);
job_.reset();
// The owner of `this` has already destroyed `this`.
if (!delegate_) {
return;
}
LogCancelRequest();
finalized_result_ = FinalizedResult(/*endpoints=*/{}, /*dns_aliases=*/{});
error_info_ = ResolveErrorInfo(ERR_DNS_REQUEST_CANCELLED,
/*is_secure_network_error=*/false);
delegate_->OnServiceEndpointRequestFinished(ERR_DNS_REQUEST_CANCELLED);
// Do not add code below. `this` may be deleted at this point.
}
void HostResolverManager::ServiceEndpointRequestImpl::
OnServiceEndpointsChanged() {
// This method is called asynchronously via a posted task. `job_` could
// be completed or cancelled before executing the task.
if (finalized_result_.has_value()) {
return;
}
CHECK(job_);
CHECK(job_.value()->dns_task_results_manager());
CHECK(delegate_);
delegate_->OnServiceEndpointsUpdated();
// Do not add code below. `this` may be deleted at this point.
}
base::WeakPtr<HostResolverManager::ServiceEndpointRequestImpl>
HostResolverManager::ServiceEndpointRequestImpl::GetWeakPtr() {
return weak_ptr_factory_.GetWeakPtr();
}
void HostResolverManager::ServiceEndpointRequestImpl::
SetFinalizedResultFromLegacyResults(const HostCache::Entry& results) {
CHECK(!finalized_result_);
if (results.error() == OK && !parameters_.is_speculative) {
std::vector<IPEndPoint> ipv4_endpoints;
std::vector<IPEndPoint> ipv6_endpoints;
for (const auto& ip_endpoint : results.ip_endpoints()) {
std::vector<IPEndPoint>& ip_endpoints =
ip_endpoint.address().IsIPv6() ? ipv6_endpoints : ipv4_endpoints;
if (ip_endpoint.port() == 0) {
ip_endpoints.emplace_back(ip_endpoint.address(), host_.GetPort());
} else {
ip_endpoints.emplace_back(ip_endpoint);
}
}
// See HostCache::Entry::GetEndpoints.
std::vector<ServiceEndpoint> endpoints;
if (!ipv4_endpoints.empty() || !ipv6_endpoints.empty()) {
for (const auto& metadata : results.GetMetadatas()) {
if (!base::Contains(results.canonical_names(), metadata.target_name)) {
continue;
}
ServiceEndpoint endpoint;
endpoint.ipv4_endpoints = ipv4_endpoints;
endpoint.ipv6_endpoints = ipv6_endpoints;
endpoint.metadata = metadata;
endpoints.emplace_back(std::move(endpoint));
}
// Append Non-SVCB endpoints at the end for fallback.
// TODO(crbug.com/41493696): Revisit how to handle non-SVCB endpoints once
// the connection layer starts using this API. Adding non-SVCB endpoints
// here might be inconsistent with intermediate results generated by
// DnsTaskResultsManager, which doesn't append non-SVCB endpoints.
ServiceEndpoint non_alternative_endpoint;
non_alternative_endpoint.ipv4_endpoints = ipv4_endpoints;
non_alternative_endpoint.ipv6_endpoints = ipv6_endpoints;
endpoints.emplace_back(std::move(non_alternative_endpoint));
}
finalized_result_ =
FinalizedResult(std::move(endpoints),
dns_alias_utility::FixUpDnsAliases(results.aliases()));
} else {
finalized_result_ = FinalizedResult(/*endpoints=*/{}, /*dns_aliases=*/{});
}
}
void HostResolverManager::ServiceEndpointRequestImpl::LogCancelRequest() {
net_log_.AddEvent(NetLogEventType::CANCELLED);
net_log_.EndEvent(NetLogEventType::HOST_RESOLVER_MANAGER_REQUEST);
}
} // namespace net