
NOTREACHED() and NOTREACHED_IN_MIGRATION() are both CHECK-fatal now. The former is [[noreturn]] so this CL also performs dead-code removal after the NOTREACHED(). This CL does not attempt to do additional rewrites of any surrounding code, like: if (!foo) { NOTREACHED(); } to CHECK(foo); Those transforms take a non-trivial amount of time (and there are thousands of instances). Cleanup can be left as an exercise for the reader. Bug: 40580068 Change-Id: Ib4c548ea8515f93270129e0701697df7ae62cf34 Cq-Include-Trybots: luci.chromium.try:linux-dcheck-off-rel Low-Coverage-Reason: OTHER Should-be-unreachable code Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5970055 Commit-Queue: Peter Boström <pbos@chromium.org> Reviewed-by: mmenke <mmenke@chromium.org> Cr-Commit-Position: refs/heads/main@{#1376027}
448 lines
14 KiB
C++
448 lines
14 KiB
C++
// 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_request_impl.h"
|
|
|
|
#include <deque>
|
|
#include <optional>
|
|
#include <set>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
#include "base/containers/linked_list.h"
|
|
#include "base/memory/raw_ptr.h"
|
|
#include "base/memory/safe_ref.h"
|
|
#include "base/memory/weak_ptr.h"
|
|
#include "base/metrics/histogram_macros.h"
|
|
#include "base/sequence_checker.h"
|
|
#include "base/time/tick_clock.h"
|
|
#include "net/base/address_list.h"
|
|
#include "net/base/completion_once_callback.h"
|
|
#include "net/base/network_anonymization_key.h"
|
|
#include "net/base/request_priority.h"
|
|
#include "net/dns/dns_alias_utility.h"
|
|
#include "net/dns/host_cache.h"
|
|
#include "net/dns/host_resolver.h"
|
|
#include "net/dns/host_resolver_manager.h"
|
|
#include "net/dns/host_resolver_manager_job.h"
|
|
#include "net/dns/public/host_resolver_results.h"
|
|
#include "net/dns/public/resolve_error_info.h"
|
|
#include "net/http/http_network_session.h"
|
|
#include "net/log/net_log_with_source.h"
|
|
#include "net/socket/client_socket_factory.h"
|
|
#include "net/url_request/url_request_context.h"
|
|
|
|
namespace net {
|
|
|
|
HostResolverManager::RequestImpl::RequestImpl(
|
|
NetLogWithSource source_net_log,
|
|
HostResolver::Host request_host,
|
|
NetworkAnonymizationKey network_anonymization_key,
|
|
std::optional<ResolveHostParameters> optional_parameters,
|
|
base::WeakPtr<ResolveContext> resolve_context,
|
|
base::WeakPtr<HostResolverManager> resolver,
|
|
const base::TickClock* tick_clock)
|
|
: source_net_log_(std::move(source_net_log)),
|
|
request_host_(std::move(request_host)),
|
|
network_anonymization_key_(
|
|
NetworkAnonymizationKey::IsPartitioningEnabled()
|
|
? std::move(network_anonymization_key)
|
|
: NetworkAnonymizationKey()),
|
|
parameters_(optional_parameters ? std::move(optional_parameters).value()
|
|
: ResolveHostParameters()),
|
|
resolve_context_(std::move(resolve_context)),
|
|
priority_(parameters_.initial_priority),
|
|
job_key_(request_host_, resolve_context_.get()),
|
|
resolver_(std::move(resolver)),
|
|
tick_clock_(tick_clock) {}
|
|
|
|
HostResolverManager::RequestImpl::~RequestImpl() {
|
|
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
|
|
if (!job_.has_value()) {
|
|
return;
|
|
}
|
|
|
|
job_.value()->CancelRequest(this);
|
|
LogCancelRequest();
|
|
}
|
|
|
|
int HostResolverManager::RequestImpl::Start(CompletionOnceCallback callback) {
|
|
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
|
|
DCHECK(callback);
|
|
// Start() may only be called once per request.
|
|
CHECK(!job_.has_value());
|
|
DCHECK(!complete_);
|
|
DCHECK(!callback_);
|
|
// Parent HostResolver must still be alive to call Start().
|
|
DCHECK(resolver_);
|
|
|
|
if (!resolve_context_) {
|
|
complete_ = true;
|
|
resolver_.reset();
|
|
set_error_info(ERR_CONTEXT_SHUT_DOWN, false);
|
|
return ERR_NAME_NOT_RESOLVED;
|
|
}
|
|
|
|
LogStartRequest();
|
|
|
|
next_state_ = STATE_IPV6_REACHABILITY;
|
|
callback_ = std::move(callback);
|
|
|
|
int rv = OK;
|
|
rv = DoLoop(rv);
|
|
return rv;
|
|
}
|
|
|
|
const AddressList* HostResolverManager::RequestImpl::GetAddressResults() const {
|
|
DCHECK(complete_);
|
|
return base::OptionalToPtr(legacy_address_results_);
|
|
}
|
|
|
|
const std::vector<HostResolverEndpointResult>*
|
|
HostResolverManager::RequestImpl::GetEndpointResults() const {
|
|
DCHECK(complete_);
|
|
return base::OptionalToPtr(endpoint_results_);
|
|
}
|
|
|
|
const std::vector<std::string>*
|
|
HostResolverManager::RequestImpl::GetTextResults() const {
|
|
DCHECK(complete_);
|
|
return results_ ? &results_.value().text_records() : nullptr;
|
|
}
|
|
|
|
const std::vector<HostPortPair>*
|
|
HostResolverManager::RequestImpl::GetHostnameResults() const {
|
|
DCHECK(complete_);
|
|
return results_ ? &results_.value().hostnames() : nullptr;
|
|
}
|
|
|
|
const std::set<std::string>*
|
|
HostResolverManager::RequestImpl::GetDnsAliasResults() const {
|
|
DCHECK(complete_);
|
|
|
|
// If `include_canonical_name` param was true, should only ever have at most
|
|
// a single alias, representing the expected "canonical name".
|
|
#if DCHECK_IS_ON()
|
|
if (parameters().include_canonical_name && fixed_up_dns_alias_results_) {
|
|
DCHECK_LE(fixed_up_dns_alias_results_->size(), 1u);
|
|
if (GetAddressResults()) {
|
|
std::set<std::string> address_list_aliases_set(
|
|
GetAddressResults()->dns_aliases().begin(),
|
|
GetAddressResults()->dns_aliases().end());
|
|
DCHECK(address_list_aliases_set == fixed_up_dns_alias_results_.value());
|
|
}
|
|
}
|
|
#endif // DCHECK_IS_ON()
|
|
|
|
return base::OptionalToPtr(fixed_up_dns_alias_results_);
|
|
}
|
|
|
|
const std::vector<bool>*
|
|
HostResolverManager::RequestImpl::GetExperimentalResultsForTesting() const {
|
|
DCHECK(complete_);
|
|
return results_ ? &results_.value().https_record_compatibility() : nullptr;
|
|
}
|
|
|
|
net::ResolveErrorInfo HostResolverManager::RequestImpl::GetResolveErrorInfo()
|
|
const {
|
|
DCHECK(complete_);
|
|
return error_info_;
|
|
}
|
|
|
|
const std::optional<HostCache::EntryStaleness>&
|
|
HostResolverManager::RequestImpl::GetStaleInfo() const {
|
|
DCHECK(complete_);
|
|
return stale_info_;
|
|
}
|
|
|
|
void HostResolverManager::RequestImpl::ChangeRequestPriority(
|
|
RequestPriority priority) {
|
|
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
|
|
if (!job_.has_value()) {
|
|
priority_ = priority;
|
|
return;
|
|
}
|
|
job_.value()->ChangeRequestPriority(this, priority);
|
|
}
|
|
|
|
void HostResolverManager::RequestImpl::set_results(HostCache::Entry results) {
|
|
// Should only be called at most once and before request is marked
|
|
// completed.
|
|
DCHECK(!complete_);
|
|
DCHECK(!results_);
|
|
DCHECK(!parameters_.is_speculative);
|
|
|
|
results_ = std::move(results);
|
|
FixUpEndpointAndAliasResults();
|
|
}
|
|
|
|
void HostResolverManager::RequestImpl::set_error_info(
|
|
int error,
|
|
bool is_secure_network_error) {
|
|
error_info_ = ResolveErrorInfo(error, is_secure_network_error);
|
|
}
|
|
|
|
void HostResolverManager::RequestImpl::set_stale_info(
|
|
HostCache::EntryStaleness stale_info) {
|
|
// Should only be called at most once and before request is marked
|
|
// completed.
|
|
DCHECK(!complete_);
|
|
DCHECK(!stale_info_);
|
|
DCHECK(!parameters_.is_speculative);
|
|
|
|
stale_info_ = std::move(stale_info);
|
|
}
|
|
|
|
void HostResolverManager::RequestImpl::AssignJob(base::SafeRef<Job> job) {
|
|
CHECK(!job_.has_value());
|
|
job_ = std::move(job);
|
|
}
|
|
|
|
const HostResolverManager::JobKey& HostResolverManager::RequestImpl::GetJobKey()
|
|
const {
|
|
CHECK(job_.has_value());
|
|
return job_.value()->key();
|
|
}
|
|
|
|
void HostResolverManager::RequestImpl::OnJobCancelled(const JobKey& job_key) {
|
|
CHECK(job_.has_value());
|
|
CHECK(job_key == job_.value()->key());
|
|
job_.reset();
|
|
DCHECK(!complete_);
|
|
DCHECK(callback_);
|
|
callback_.Reset();
|
|
|
|
// No results should be set.
|
|
DCHECK(!results_);
|
|
|
|
LogCancelRequest();
|
|
}
|
|
|
|
void HostResolverManager::RequestImpl::OnJobCompleted(
|
|
const JobKey& job_key,
|
|
int error,
|
|
bool is_secure_network_error) {
|
|
set_error_info(error, is_secure_network_error);
|
|
|
|
CHECK(job_.has_value());
|
|
CHECK(job_key == job_.value()->key());
|
|
job_.reset();
|
|
|
|
DCHECK(!complete_);
|
|
complete_ = true;
|
|
|
|
LogFinishRequest(error, true /* async_completion */);
|
|
|
|
DCHECK(callback_);
|
|
std::move(callback_).Run(HostResolver::SquashErrorCode(error));
|
|
}
|
|
|
|
int HostResolverManager::RequestImpl::DoLoop(int rv) {
|
|
do {
|
|
ResolveState state = next_state_;
|
|
next_state_ = STATE_NONE;
|
|
switch (state) {
|
|
case STATE_IPV6_REACHABILITY:
|
|
rv = DoIPv6Reachability();
|
|
break;
|
|
case STATE_GET_PARAMETERS:
|
|
DCHECK_EQ(OK, rv);
|
|
rv = DoGetParameters();
|
|
break;
|
|
case STATE_GET_PARAMETERS_COMPLETE:
|
|
rv = DoGetParametersComplete(rv);
|
|
break;
|
|
case STATE_RESOLVE_LOCALLY:
|
|
rv = DoResolveLocally();
|
|
break;
|
|
case STATE_START_JOB:
|
|
rv = DoStartJob();
|
|
break;
|
|
case STATE_FINISH_REQUEST:
|
|
rv = DoFinishRequest(rv);
|
|
break;
|
|
default:
|
|
NOTREACHED() << "next_state_: " << next_state_;
|
|
}
|
|
} while (next_state_ != STATE_NONE && rv != ERR_IO_PENDING);
|
|
|
|
return rv;
|
|
}
|
|
|
|
void HostResolverManager::RequestImpl::OnIOComplete(int rv) {
|
|
rv = DoLoop(rv);
|
|
if (rv != ERR_IO_PENDING && !callback_.is_null()) {
|
|
std::move(callback_).Run(rv);
|
|
}
|
|
}
|
|
|
|
int HostResolverManager::RequestImpl::DoIPv6Reachability() {
|
|
next_state_ = STATE_GET_PARAMETERS;
|
|
// If a single reachability probe has not been completed, and the latest
|
|
// probe will return asynchronously, return ERR_NAME_NOT_RESOLVED when the
|
|
// request source is LOCAL_ONLY. This is due to LOCAL_ONLY requiring a
|
|
// synchronous response, so it cannot wait on an async probe result and
|
|
// cannot make assumptions about reachability.
|
|
if (parameters_.source == HostResolverSource::LOCAL_ONLY) {
|
|
int rv = resolver_->StartIPv6ReachabilityCheck(
|
|
source_net_log_, GetClientSocketFactory(),
|
|
base::DoNothingAs<void(int)>());
|
|
if (rv == ERR_IO_PENDING) {
|
|
next_state_ = STATE_FINISH_REQUEST;
|
|
return ERR_NAME_NOT_RESOLVED;
|
|
}
|
|
return OK;
|
|
}
|
|
return resolver_->StartIPv6ReachabilityCheck(
|
|
source_net_log_, GetClientSocketFactory(),
|
|
base::BindOnce(&RequestImpl::OnIOComplete,
|
|
weak_ptr_factory_.GetWeakPtr()));
|
|
}
|
|
|
|
int HostResolverManager::RequestImpl::DoGetParameters() {
|
|
resolver_->InitializeJobKeyAndIPAddress(network_anonymization_key_,
|
|
parameters_, source_net_log_,
|
|
job_key_, ip_address_);
|
|
|
|
// A reachability probe to determine if the network is only reachable on
|
|
// IPv6 will be scheduled if the parameters are met for using NAT64 in place
|
|
// of an IPv4 address.
|
|
if (HostResolver::MayUseNAT64ForIPv4Literal(
|
|
job_key_.flags, parameters_.source, ip_address_) &&
|
|
resolver_->last_ipv6_probe_result_) {
|
|
next_state_ = STATE_GET_PARAMETERS_COMPLETE;
|
|
return resolver_->StartGloballyReachableCheck(
|
|
ip_address_, source_net_log_, GetClientSocketFactory(),
|
|
base::BindOnce(&RequestImpl::OnIOComplete,
|
|
weak_ptr_factory_.GetWeakPtr()));
|
|
}
|
|
next_state_ = STATE_RESOLVE_LOCALLY;
|
|
return OK;
|
|
}
|
|
|
|
int HostResolverManager::RequestImpl::DoGetParametersComplete(int rv) {
|
|
next_state_ = STATE_RESOLVE_LOCALLY;
|
|
only_ipv6_reachable_ = (rv == ERR_FAILED) ? true : false;
|
|
return OK;
|
|
}
|
|
|
|
int HostResolverManager::RequestImpl::DoResolveLocally() {
|
|
std::optional<HostCache::EntryStaleness> stale_info;
|
|
HostCache::Entry results = resolver_->ResolveLocally(
|
|
only_ipv6_reachable_, job_key_, ip_address_, parameters_.cache_usage,
|
|
parameters_.secure_dns_policy, parameters_.source, source_net_log_,
|
|
host_cache(), &tasks_, &stale_info);
|
|
if (results.error() != ERR_DNS_CACHE_MISS ||
|
|
parameters_.source == HostResolverSource::LOCAL_ONLY || tasks_.empty()) {
|
|
if (results.error() == OK && !parameters_.is_speculative) {
|
|
set_results(results.CopyWithDefaultPort(request_host_.GetPort()));
|
|
}
|
|
if (stale_info && !parameters_.is_speculative) {
|
|
set_stale_info(std::move(stale_info).value());
|
|
}
|
|
next_state_ = STATE_FINISH_REQUEST;
|
|
return results.error();
|
|
}
|
|
next_state_ = STATE_START_JOB;
|
|
return OK;
|
|
}
|
|
|
|
int HostResolverManager::RequestImpl::DoStartJob() {
|
|
resolver_->CreateAndStartJob(std::move(job_key_), std::move(tasks_), this);
|
|
DCHECK(!complete_);
|
|
resolver_.reset();
|
|
return ERR_IO_PENDING;
|
|
}
|
|
|
|
int HostResolverManager::RequestImpl::DoFinishRequest(int rv) {
|
|
CHECK(!job_.has_value());
|
|
complete_ = true;
|
|
set_error_info(rv, /*is_secure_network_error=*/false);
|
|
rv = HostResolver::SquashErrorCode(rv);
|
|
LogFinishRequest(rv, /*async_completion=*/false);
|
|
return rv;
|
|
}
|
|
|
|
void HostResolverManager::RequestImpl::FixUpEndpointAndAliasResults() {
|
|
DCHECK(results_.has_value());
|
|
DCHECK(!legacy_address_results_.has_value());
|
|
DCHECK(!endpoint_results_.has_value());
|
|
DCHECK(!fixed_up_dns_alias_results_.has_value());
|
|
|
|
endpoint_results_ = results_.value().GetEndpoints();
|
|
if (endpoint_results_.has_value()) {
|
|
fixed_up_dns_alias_results_ = results_.value().aliases();
|
|
|
|
// Skip fixups for `include_canonical_name` requests. Just use the
|
|
// canonical name exactly as it was received from the system resolver.
|
|
if (parameters().include_canonical_name) {
|
|
DCHECK_LE(fixed_up_dns_alias_results_.value().size(), 1u);
|
|
} else {
|
|
fixed_up_dns_alias_results_ = dns_alias_utility::FixUpDnsAliases(
|
|
fixed_up_dns_alias_results_.value());
|
|
}
|
|
|
|
legacy_address_results_ = HostResolver::EndpointResultToAddressList(
|
|
endpoint_results_.value(), fixed_up_dns_alias_results_.value());
|
|
}
|
|
}
|
|
|
|
void HostResolverManager::RequestImpl::LogStartRequest() {
|
|
DCHECK(request_time_.is_null());
|
|
request_time_ = tick_clock_->NowTicks();
|
|
|
|
source_net_log_.BeginEvent(
|
|
NetLogEventType::HOST_RESOLVER_MANAGER_REQUEST, [this] {
|
|
base::Value::Dict dict;
|
|
dict.Set("host", request_host_.ToString());
|
|
dict.Set("dns_query_type",
|
|
kDnsQueryTypes.at(parameters_.dns_query_type));
|
|
dict.Set("allow_cached_response",
|
|
parameters_.cache_usage !=
|
|
ResolveHostParameters::CacheUsage::DISALLOWED);
|
|
dict.Set("is_speculative", parameters_.is_speculative);
|
|
dict.Set("network_anonymization_key",
|
|
network_anonymization_key_.ToDebugString());
|
|
dict.Set("secure_dns_policy",
|
|
base::strict_cast<int>(parameters_.secure_dns_policy));
|
|
return dict;
|
|
});
|
|
}
|
|
|
|
void HostResolverManager::RequestImpl::LogFinishRequest(int net_error,
|
|
bool async_completion) {
|
|
source_net_log_.EndEventWithNetErrorCode(
|
|
NetLogEventType::HOST_RESOLVER_MANAGER_REQUEST, net_error);
|
|
|
|
if (!parameters_.is_speculative) {
|
|
DCHECK(!request_time_.is_null());
|
|
base::TimeDelta duration = tick_clock_->NowTicks() - request_time_;
|
|
|
|
DEPRECATED_UMA_HISTOGRAM_MEDIUM_TIMES("Net.DNS.Request.TotalTime",
|
|
duration);
|
|
if (async_completion) {
|
|
DEPRECATED_UMA_HISTOGRAM_MEDIUM_TIMES("Net.DNS.Request.TotalTimeAsync",
|
|
duration);
|
|
}
|
|
}
|
|
}
|
|
|
|
void HostResolverManager::RequestImpl::LogCancelRequest() {
|
|
source_net_log_.AddEvent(NetLogEventType::CANCELLED);
|
|
source_net_log_.EndEvent(NetLogEventType::HOST_RESOLVER_MANAGER_REQUEST);
|
|
}
|
|
|
|
ClientSocketFactory*
|
|
HostResolverManager::RequestImpl::GetClientSocketFactory() {
|
|
if (resolve_context_->url_request_context()) {
|
|
return resolve_context_->url_request_context()
|
|
->GetNetworkSessionContext()
|
|
->client_socket_factory;
|
|
} else {
|
|
return ClientSocketFactory::GetDefaultFactory();
|
|
}
|
|
}
|
|
|
|
} // namespace net
|