// Copyright 2012 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.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "base/check_op.h" #include "base/compiler_specific.h" #include "base/containers/circular_deque.h" #include "base/containers/contains.h" #include "base/containers/flat_set.h" #include "base/containers/linked_list.h" #include "base/debug/debugger.h" #include "base/feature_list.h" #include "base/functional/bind.h" #include "base/functional/callback.h" #include "base/functional/callback_helpers.h" #include "base/location.h" #include "base/logging.h" #include "base/memory/ptr_util.h" #include "base/memory/raw_ptr.h" #include "base/memory/safe_ref.h" #include "base/memory/weak_ptr.h" #include "base/metrics/field_trial.h" #include "base/metrics/field_trial_params.h" #include "base/metrics/histogram_functions.h" #include "base/metrics/histogram_macros.h" #include "base/no_destructor.h" #include "base/numerics/safe_conversions.h" #include "base/observer_list.h" #include "base/ranges/algorithm.h" #include "base/sequence_checker.h" #include "base/strings/strcat.h" #include "base/strings/string_number_conversions.h" #include "base/strings/string_split.h" #include "base/strings/string_util.h" #include "base/strings/stringprintf.h" #include "base/strings/utf_string_conversions.h" #include "base/task/sequenced_task_runner.h" #include "base/task/single_thread_task_runner.h" #include "base/task/thread_pool.h" #include "base/threading/scoped_blocking_call.h" #include "base/time/default_tick_clock.h" #include "base/time/time.h" #include "base/types/optional_util.h" #include "base/values.h" #include "build/build_config.h" #include "net/base/address_family.h" #include "net/base/address_list.h" #include "net/base/completion_once_callback.h" #include "net/base/features.h" #include "net/base/host_port_pair.h" #include "net/base/ip_address.h" #include "net/base/ip_endpoint.h" #include "net/base/net_errors.h" #include "net/base/network_anonymization_key.h" #include "net/base/network_change_notifier.h" #include "net/base/network_interfaces.h" #include "net/base/prioritized_dispatcher.h" #include "net/base/request_priority.h" #include "net/base/trace_constants.h" #include "net/base/tracing.h" #include "net/base/url_util.h" #include "net/dns/dns_alias_utility.h" #include "net/dns/dns_client.h" #include "net/dns/dns_names_util.h" #include "net/dns/dns_response.h" #include "net/dns/dns_response_result_extractor.h" #include "net/dns/dns_transaction.h" #include "net/dns/dns_util.h" #include "net/dns/host_cache.h" #include "net/dns/host_resolver_dns_task.h" #include "net/dns/host_resolver_internal_result.h" #include "net/dns/host_resolver_manager_job.h" #include "net/dns/host_resolver_manager_request_impl.h" #include "net/dns/host_resolver_manager_service_endpoint_request_impl.h" #include "net/dns/host_resolver_mdns_listener_impl.h" #include "net/dns/host_resolver_mdns_task.h" #include "net/dns/host_resolver_nat64_task.h" #include "net/dns/host_resolver_proc.h" #include "net/dns/host_resolver_system_task.h" #include "net/dns/httpssvc_metrics.h" #include "net/dns/loopback_only.h" #include "net/dns/mdns_client.h" #include "net/dns/public/dns_protocol.h" #include "net/dns/public/dns_query_type.h" #include "net/dns/public/host_resolver_results.h" #include "net/dns/public/resolve_error_info.h" #include "net/dns/public/secure_dns_mode.h" #include "net/dns/public/secure_dns_policy.h" #include "net/dns/public/util.h" #include "net/dns/record_parsed.h" #include "net/dns/resolve_context.h" #include "net/dns/test_dns_config_service.h" #include "net/http/http_network_session.h" #include "net/log/net_log.h" #include "net/log/net_log_capture_mode.h" #include "net/log/net_log_event_type.h" #include "net/log/net_log_source.h" #include "net/log/net_log_source_type.h" #include "net/log/net_log_with_source.h" #include "net/socket/client_socket_factory.h" #include "net/url_request/url_request_context.h" #include "third_party/abseil-cpp/absl/types/variant.h" #include "url/scheme_host_port.h" #include "url/url_constants.h" #if BUILDFLAG(ENABLE_MDNS) #include "net/dns/mdns_client_impl.h" #endif #if BUILDFLAG(IS_WIN) #include #include "net/base/winsock_init.h" #endif #if BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) #include #include "net/base/sys_addrinfo.h" #if BUILDFLAG(IS_ANDROID) #include "base/android/build_info.h" #else // !BUILDFLAG(IS_ANDROID) #include #endif // BUILDFLAG(IS_ANDROID) #endif // BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) namespace net { namespace { // Limit the size of hostnames that will be resolved to combat issues in // some platform's resolvers. const size_t kMaxHostLength = 4096; // Time between IPv6 probes, i.e. for how long results of each IPv6 probe are // cached. const int kIPv6ProbePeriodMs = 1000; // Google DNS address used for IPv6 probes. const uint8_t kIPv6ProbeAddress[] = {0x20, 0x01, 0x48, 0x60, 0x48, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0x88}; // True if |hostname| ends with either ".local" or ".local.". bool ResemblesMulticastDNSName(std::string_view hostname) { return hostname.ends_with(".local") || hostname.ends_with(".local."); } bool ConfigureAsyncDnsNoFallbackFieldTrial() { const bool kDefault = false; // Configure the AsyncDns field trial as follows: // groups AsyncDnsNoFallbackA and AsyncDnsNoFallbackB: return true, // groups AsyncDnsA and AsyncDnsB: return false, // groups SystemDnsA and SystemDnsB: return false, // otherwise (trial absent): return default. std::string group_name = base::FieldTrialList::FindFullName("AsyncDns"); if (!group_name.empty()) { return base::StartsWith(group_name, "AsyncDnsNoFallback", base::CompareCase::INSENSITIVE_ASCII); } return kDefault; } base::Value::Dict NetLogIPv6AvailableParams(bool ipv6_available, bool cached) { base::Value::Dict dict; dict.Set("ipv6_available", ipv6_available); dict.Set("cached", cached); return dict; } // Maximum of 64 concurrent resolver calls (excluding retries). // Between 2010 and 2020, the limit was set to 6 because of a report of a broken // home router that would fail in the presence of more simultaneous queries. // In 2020, we conducted an experiment to see if this kind of router was still // present on the Internet, and found no evidence of any remaining issues, so // we increased the limit to 64 at that time. const size_t kDefaultMaxSystemTasks = 64u; PrioritizedDispatcher::Limits GetDispatcherLimits( const HostResolver::ManagerOptions& options) { PrioritizedDispatcher::Limits limits(NUM_PRIORITIES, options.max_concurrent_resolves); // If not using default, do not use the field trial. if (limits.total_jobs != HostResolver::ManagerOptions::kDefaultParallelism) return limits; // Default, without trial is no reserved slots. limits.total_jobs = kDefaultMaxSystemTasks; // Parallelism is determined by the field trial. std::string group = base::FieldTrialList::FindFullName("HostResolverDispatch"); if (group.empty()) return limits; // The format of the group name is a list of non-negative integers separated // by ':'. Each of the elements in the list corresponds to an element in // |reserved_slots|, except the last one which is the |total_jobs|. std::vector group_parts = base::SplitStringPiece( group, ":", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL); if (group_parts.size() != NUM_PRIORITIES + 1) { NOTREACHED(); return limits; } std::vector parsed(group_parts.size()); for (size_t i = 0; i < group_parts.size(); ++i) { if (!base::StringToSizeT(group_parts[i], &parsed[i])) { NOTREACHED(); return limits; } } const size_t total_jobs = parsed.back(); parsed.pop_back(); const size_t total_reserved_slots = std::accumulate(parsed.begin(), parsed.end(), 0u); // There must be some unreserved slots available for the all priorities. if (total_reserved_slots > total_jobs || (total_reserved_slots == total_jobs && parsed[MINIMUM_PRIORITY] == 0)) { NOTREACHED(); return limits; } limits.total_jobs = total_jobs; limits.reserved_slots = parsed; return limits; } base::Value::Dict NetLogResults(const HostCache::Entry& results) { base::Value::Dict dict; dict.Set("results", results.NetLogParams()); return dict; } std::vector FilterAddresses(std::vector addresses, DnsQueryTypeSet query_types) { DCHECK(!query_types.Has(DnsQueryType::UNSPECIFIED)); DCHECK(!query_types.empty()); const AddressFamily want_family = HostResolver::DnsQueryTypeSetToAddressFamily(query_types); if (want_family == ADDRESS_FAMILY_UNSPECIFIED) return addresses; // Keep only the endpoints that match `want_family`. addresses.erase( base::ranges::remove_if( addresses, [want_family](AddressFamily family) { return family != want_family; }, &IPEndPoint::GetFamily), addresses.end()); return addresses; } int GetPortForGloballyReachableCheck() { if (!base::FeatureList::IsEnabled( features::kUseAlternativePortForGloballyReachableCheck)) { return 443; } return features::kAlternativePortForGloballyReachableCheck.Get(); } } // namespace //----------------------------------------------------------------------------- bool ResolveLocalHostname(std::string_view host, std::vector* address_list) { address_list->clear(); if (!IsLocalHostname(host)) return false; address_list->emplace_back(IPAddress::IPv6Localhost(), 0); address_list->emplace_back(IPAddress::IPv4Localhost(), 0); return true; } class HostResolverManager::ProbeRequestImpl : public HostResolver::ProbeRequest, public ResolveContext::DohStatusObserver { public: ProbeRequestImpl(base::WeakPtr context, base::WeakPtr resolver) : context_(std::move(context)), resolver_(std::move(resolver)) {} ProbeRequestImpl(const ProbeRequestImpl&) = delete; ProbeRequestImpl& operator=(const ProbeRequestImpl&) = delete; ~ProbeRequestImpl() override { // Ensure that observers are deregistered to avoid wasting memory. if (context_) context_->UnregisterDohStatusObserver(this); } int Start() override { DCHECK(resolver_); DCHECK(!runner_); if (!context_) return ERR_CONTEXT_SHUT_DOWN; context_->RegisterDohStatusObserver(this); StartRunner(false /* network_change */); return ERR_IO_PENDING; } // ResolveContext::DohStatusObserver void OnSessionChanged() override { CancelRunner(); } void OnDohServerUnavailable(bool network_change) override { // Start the runner asynchronously, as this may trigger reentrant calls into // HostResolverManager, which are not allowed during notification handling. base::SequencedTaskRunner::GetCurrentDefault()->PostTask( FROM_HERE, base::BindOnce(&ProbeRequestImpl::StartRunner, weak_ptr_factory_.GetWeakPtr(), network_change)); } private: void StartRunner(bool network_change) { DCHECK(resolver_); DCHECK(!resolver_->invalidation_in_progress_); if (!context_) return; // Reachable if the context ends before a posted task runs. if (!runner_) runner_ = resolver_->CreateDohProbeRunner(context_.get()); if (runner_) runner_->Start(network_change); } void CancelRunner() { runner_.reset(); // Cancel any asynchronous StartRunner() calls. weak_ptr_factory_.InvalidateWeakPtrs(); } base::WeakPtr context_; std::unique_ptr runner_; base::WeakPtr resolver_; base::WeakPtrFactory weak_ptr_factory_{this}; }; //----------------------------------------------------------------------------- HostResolverManager::HostResolverManager( const HostResolver::ManagerOptions& options, SystemDnsConfigChangeNotifier* system_dns_config_notifier, NetLog* net_log) : HostResolverManager(PassKey(), options, system_dns_config_notifier, handles::kInvalidNetworkHandle, net_log) {} HostResolverManager::HostResolverManager( base::PassKey, const HostResolver::ManagerOptions& options, SystemDnsConfigChangeNotifier* system_dns_config_notifier, handles::NetworkHandle target_network, NetLog* net_log) : host_resolver_system_params_(nullptr, options.max_system_retry_attempts), net_log_(net_log), system_dns_config_notifier_(system_dns_config_notifier), target_network_(target_network), check_ipv6_on_wifi_(options.check_ipv6_on_wifi), ipv6_reachability_override_(base::FeatureList::IsEnabled( features::kEnableIPv6ReachabilityOverride)), tick_clock_(base::DefaultTickClock::GetInstance()), https_svcb_options_( options.https_svcb_options ? *options.https_svcb_options : HostResolver::HttpsSvcbOptions::FromFeatures()) { PrioritizedDispatcher::Limits job_limits = GetDispatcherLimits(options); dispatcher_ = std::make_unique(job_limits); max_queued_jobs_ = job_limits.total_jobs * 100u; DCHECK_GE(dispatcher_->num_priorities(), static_cast(NUM_PRIORITIES)); #if BUILDFLAG(IS_WIN) EnsureWinsockInit(); #endif #if (BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_APPLE) && !BUILDFLAG(IS_ANDROID)) || \ BUILDFLAG(IS_FUCHSIA) RunLoopbackProbeJob(); #endif // Network-bound HostResolverManagers don't need to act on network changes. if (!IsBoundToNetwork()) { NetworkChangeNotifier::AddIPAddressObserver(this); NetworkChangeNotifier::AddConnectionTypeObserver(this); } if (system_dns_config_notifier_) system_dns_config_notifier_->AddObserver(this); EnsureSystemHostResolverCallReady(); auto connection_type = IsBoundToNetwork() ? NetworkChangeNotifier::GetNetworkConnectionType(target_network) : NetworkChangeNotifier::GetConnectionType(); UpdateConnectionType(connection_type); #if defined(ENABLE_BUILT_IN_DNS) dns_client_ = DnsClient::CreateClient(net_log_); dns_client_->SetInsecureEnabled( options.insecure_dns_client_enabled, options.additional_types_via_insecure_dns_enabled); dns_client_->SetConfigOverrides(options.dns_config_overrides); #else DCHECK(options.dns_config_overrides == DnsConfigOverrides()); #endif allow_fallback_to_systemtask_ = !ConfigureAsyncDnsNoFallbackFieldTrial(); } HostResolverManager::~HostResolverManager() { DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); // Prevent the dispatcher from starting new jobs. dispatcher_->SetLimitsToZero(); // It's now safe for Jobs to call KillDnsTask on destruction, because // OnJobComplete will not start any new jobs. jobs_.clear(); if (target_network_ == handles::kInvalidNetworkHandle) { NetworkChangeNotifier::RemoveIPAddressObserver(this); NetworkChangeNotifier::RemoveConnectionTypeObserver(this); } if (system_dns_config_notifier_) system_dns_config_notifier_->RemoveObserver(this); } // static std::unique_ptr HostResolverManager::CreateNetworkBoundHostResolverManager( const HostResolver::ManagerOptions& options, handles::NetworkHandle target_network, NetLog* net_log) { #if BUILDFLAG(IS_ANDROID) DCHECK(NetworkChangeNotifier::AreNetworkHandlesSupported()); return std::make_unique( PassKey(), options, nullptr /* system_dns_config_notifier */, target_network, net_log); #else // !BUILDFLAG(IS_ANDROID) NOTIMPLEMENTED(); return nullptr; #endif // BUILDFLAG(IS_ANDROID) } std::unique_ptr HostResolverManager::CreateRequest( absl::variant host, NetworkAnonymizationKey network_anonymization_key, NetLogWithSource net_log, std::optional optional_parameters, ResolveContext* resolve_context) { return CreateRequest(HostResolver::Host(std::move(host)), std::move(network_anonymization_key), std::move(net_log), std::move(optional_parameters), resolve_context); } std::unique_ptr HostResolverManager::CreateRequest( HostResolver::Host host, NetworkAnonymizationKey network_anonymization_key, NetLogWithSource net_log, std::optional optional_parameters, ResolveContext* resolve_context) { DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); DCHECK(!invalidation_in_progress_); DCHECK_EQ(resolve_context->GetTargetNetwork(), target_network_); // ResolveContexts must register (via RegisterResolveContext()) before use to // ensure cached data is invalidated on network and configuration changes. DCHECK(registered_contexts_.HasObserver(resolve_context)); return std::make_unique( std::move(net_log), std::move(host), std::move(network_anonymization_key), std::move(optional_parameters), resolve_context->GetWeakPtr(), weak_ptr_factory_.GetWeakPtr(), tick_clock_); } std::unique_ptr HostResolverManager::CreateDohProbeRequest(ResolveContext* context) { DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); return std::make_unique(context->GetWeakPtr(), weak_ptr_factory_.GetWeakPtr()); } std::unique_ptr HostResolverManager::CreateMdnsListener(const HostPortPair& host, DnsQueryType query_type) { DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); DCHECK_NE(DnsQueryType::UNSPECIFIED, query_type); auto listener = std::make_unique(host, query_type); MDnsClient* client; int rv = GetOrCreateMdnsClient(&client); if (rv == OK) { std::unique_ptr inner_listener = client->CreateListener( DnsQueryTypeToQtype(query_type), host.host(), listener.get()); listener->set_inner_listener(std::move(inner_listener)); } else { listener->set_initialization_error(rv); } return listener; } std::unique_ptr HostResolverManager::CreateServiceEndpointRequest( url::SchemeHostPort scheme_host_port, NetworkAnonymizationKey network_anonymization_key, NetLogWithSource net_log, ResolveHostParameters parameters, ResolveContext* resolve_context) { DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); DCHECK(!invalidation_in_progress_); DCHECK_EQ(resolve_context->GetTargetNetwork(), target_network_); if (resolve_context) { DCHECK(registered_contexts_.HasObserver(resolve_context)); } return std::make_unique( std::move(scheme_host_port), std::move(network_anonymization_key), std::move(net_log), std::move(parameters), resolve_context ? resolve_context->GetWeakPtr() : nullptr, weak_ptr_factory_.GetWeakPtr(), tick_clock_); } void HostResolverManager::SetInsecureDnsClientEnabled( bool enabled, bool additional_dns_types_enabled) { DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); if (!dns_client_) return; bool enabled_before = dns_client_->CanUseInsecureDnsTransactions(); bool additional_types_before = enabled_before && dns_client_->CanQueryAdditionalTypesViaInsecureDns(); dns_client_->SetInsecureEnabled(enabled, additional_dns_types_enabled); // Abort current tasks if `CanUseInsecureDnsTransactions()` changes or if // insecure transactions are enabled and // `CanQueryAdditionalTypesViaInsecureDns()` changes. Changes to allowing // additional types don't matter if insecure transactions are completely // disabled. if (dns_client_->CanUseInsecureDnsTransactions() != enabled_before || (dns_client_->CanUseInsecureDnsTransactions() && dns_client_->CanQueryAdditionalTypesViaInsecureDns() != additional_types_before)) { AbortInsecureDnsTasks(ERR_NETWORK_CHANGED, false /* fallback_only */); } } base::Value::Dict HostResolverManager::GetDnsConfigAsValue() const { DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); return dns_client_ ? dns_client_->GetDnsConfigAsValueForNetLog() : base::Value::Dict(); } void HostResolverManager::SetDnsConfigOverrides(DnsConfigOverrides overrides) { DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); if (!dns_client_ && overrides == DnsConfigOverrides()) return; // Not allowed to set overrides if compiled without DnsClient. DCHECK(dns_client_); bool transactions_allowed_before = dns_client_->CanUseSecureDnsTransactions() || dns_client_->CanUseInsecureDnsTransactions(); bool changed = dns_client_->SetConfigOverrides(std::move(overrides)); if (changed) { NetworkChangeNotifier::TriggerNonSystemDnsChange(); // Only invalidate cache if new overrides have resulted in a config change. InvalidateCaches(); // Need to update jobs iff transactions were previously allowed because // in-progress jobs may be running using a now-invalid configuration. if (transactions_allowed_before) { UpdateJobsForChangedConfig(); } } } void HostResolverManager::RegisterResolveContext(ResolveContext* context) { registered_contexts_.AddObserver(context); context->InvalidateCachesAndPerSessionData( dns_client_ ? dns_client_->GetCurrentSession() : nullptr, false /* network_change */); } void HostResolverManager::DeregisterResolveContext( const ResolveContext* context) { registered_contexts_.RemoveObserver(context); // Destroy Jobs when their context is closed. RemoveAllJobs(context); } void HostResolverManager::SetTickClockForTesting( const base::TickClock* tick_clock) { tick_clock_ = tick_clock; } void HostResolverManager::SetIPv6ReachabilityOverride( bool reachability_override) { ipv6_reachability_override_ = reachability_override; } void HostResolverManager::SetMaxQueuedJobsForTesting(size_t value) { DCHECK_EQ(0u, dispatcher_->num_queued_jobs()); DCHECK_GE(value, 0u); max_queued_jobs_ = value; } void HostResolverManager::SetHaveOnlyLoopbackAddresses(bool result) { if (result) { additional_resolver_flags_ |= HOST_RESOLVER_LOOPBACK_ONLY; } else { additional_resolver_flags_ &= ~HOST_RESOLVER_LOOPBACK_ONLY; } } void HostResolverManager::SetMdnsSocketFactoryForTesting( std::unique_ptr socket_factory) { DCHECK(!mdns_client_); mdns_socket_factory_ = std::move(socket_factory); } void HostResolverManager::SetMdnsClientForTesting( std::unique_ptr client) { mdns_client_ = std::move(client); } void HostResolverManager::SetDnsClientForTesting( std::unique_ptr dns_client) { DCHECK(dns_client); if (dns_client_) { if (!dns_client->GetSystemConfigForTesting()) dns_client->SetSystemConfig(dns_client_->GetSystemConfigForTesting()); dns_client->SetConfigOverrides(dns_client_->GetConfigOverridesForTesting()); } dns_client_ = std::move(dns_client); // Inform `registered_contexts_` of the new `DnsClient`. InvalidateCaches(); } void HostResolverManager::SetLastIPv6ProbeResultForTesting( bool last_ipv6_probe_result) { SetLastIPv6ProbeResult(last_ipv6_probe_result); } // static bool HostResolverManager::IsLocalTask(TaskType task) { switch (task) { case TaskType::SECURE_CACHE_LOOKUP: case TaskType::INSECURE_CACHE_LOOKUP: case TaskType::CACHE_LOOKUP: case TaskType::CONFIG_PRESET: case TaskType::HOSTS: return true; default: return false; } } void HostResolverManager::InitializeJobKeyAndIPAddress( const NetworkAnonymizationKey& network_anonymization_key, const ResolveHostParameters& parameters, const NetLogWithSource& source_net_log, JobKey& out_job_key, IPAddress& out_ip_address) { out_job_key.network_anonymization_key = network_anonymization_key; out_job_key.source = parameters.source; const bool is_ip = out_ip_address.AssignFromIPLiteral( out_job_key.host.GetHostnameWithoutBrackets()); out_job_key.secure_dns_mode = GetEffectiveSecureDnsMode(parameters.secure_dns_policy); out_job_key.flags = HostResolver::ParametersToHostResolverFlags(parameters) | additional_resolver_flags_; if (parameters.dns_query_type != DnsQueryType::UNSPECIFIED) { out_job_key.query_types = {parameters.dns_query_type}; return; } DnsQueryTypeSet effective_types = {DnsQueryType::A, DnsQueryType::AAAA}; // Disable AAAA queries when we cannot do anything with the results. bool use_local_ipv6 = true; if (dns_client_) { const DnsConfig* config = dns_client_->GetEffectiveConfig(); if (config) { use_local_ipv6 = config->use_local_ipv6; } } // When resolving IPv4 literals, there's no need to probe for IPv6. When // resolving IPv6 literals, there's no benefit to artificially limiting our // resolution based on a probe. Prior logic ensures that this is an automatic // query, so the code requesting the resolution should be amenable to // receiving an IPv6 resolution. if (!use_local_ipv6 && !is_ip && !last_ipv6_probe_result_ && !ipv6_reachability_override_) { out_job_key.flags |= HOST_RESOLVER_DEFAULT_FAMILY_SET_DUE_TO_NO_IPV6; effective_types.Remove(DnsQueryType::AAAA); } // Optimistically enable feature-controlled queries. These queries may be // skipped at a later point. // `https_svcb_options_.enable` has precedence, so if enabled, ignore any // other related features. if (https_svcb_options_.enable && out_job_key.host.HasScheme()) { static const char* const kSchemesForHttpsQuery[] = { url::kHttpScheme, url::kHttpsScheme, url::kWsScheme, url::kWssScheme}; if (base::Contains(kSchemesForHttpsQuery, out_job_key.host.GetScheme())) { effective_types.Put(DnsQueryType::HTTPS); } } out_job_key.query_types = effective_types; } HostCache::Entry HostResolverManager::ResolveLocally( bool only_ipv6_reachable, const JobKey& job_key, const IPAddress& ip_address, ResolveHostParameters::CacheUsage cache_usage, SecureDnsPolicy secure_dns_policy, HostResolverSource source, const NetLogWithSource& source_net_log, HostCache* cache, std::deque* out_tasks, std::optional* out_stale_info) { DCHECK(out_stale_info); *out_stale_info = std::nullopt; CreateTaskSequence(job_key, cache_usage, secure_dns_policy, out_tasks); if (!ip_address.IsValid()) { // Check that the caller supplied a valid hostname to resolve. For // MULTICAST_DNS, we are less restrictive. // TODO(ericorth): Control validation based on an explicit flag rather // than implicitly based on |source|. const bool is_valid_hostname = job_key.source == HostResolverSource::MULTICAST_DNS ? dns_names_util::IsValidDnsName(job_key.host.GetHostname()) : IsCanonicalizedHostCompliant(job_key.host.GetHostname()); if (!is_valid_hostname) { return HostCache::Entry(ERR_NAME_NOT_RESOLVED, HostCache::Entry::SOURCE_UNKNOWN); } } bool resolve_canonname = job_key.flags & HOST_RESOLVER_CANONNAME; bool default_family_due_to_no_ipv6 = job_key.flags & HOST_RESOLVER_DEFAULT_FAMILY_SET_DUE_TO_NO_IPV6; // The result of |getaddrinfo| for empty hosts is inconsistent across systems. // On Windows it gives the default interface's address, whereas on Linux it // gives an error. We will make it fail on all platforms for consistency. if (job_key.host.GetHostname().empty() || job_key.host.GetHostname().size() > kMaxHostLength) { return HostCache::Entry(ERR_NAME_NOT_RESOLVED, HostCache::Entry::SOURCE_UNKNOWN); } if (ip_address.IsValid()) { // Use NAT64Task for IPv4 literal when the network is IPv6 only. if (HostResolver::MayUseNAT64ForIPv4Literal(job_key.flags, source, ip_address) && only_ipv6_reachable) { out_tasks->push_front(TaskType::NAT64); return HostCache::Entry(ERR_DNS_CACHE_MISS, HostCache::Entry::SOURCE_UNKNOWN); } return ResolveAsIP(job_key.query_types, resolve_canonname, ip_address); } // Special-case localhost names, as per the recommendations in // https://tools.ietf.org/html/draft-west-let-localhost-be-localhost. std::optional resolved = ServeLocalhost(job_key.host.GetHostname(), job_key.query_types, default_family_due_to_no_ipv6); if (resolved) return resolved.value(); // Do initial cache lookups. while (!out_tasks->empty() && IsLocalTask(out_tasks->front())) { TaskType task = out_tasks->front(); out_tasks->pop_front(); if (task == TaskType::SECURE_CACHE_LOOKUP || task == TaskType::INSECURE_CACHE_LOOKUP || task == TaskType::CACHE_LOOKUP) { bool secure = task == TaskType::SECURE_CACHE_LOOKUP; HostCache::Key key = job_key.ToCacheKey(secure); bool ignore_secure = task == TaskType::CACHE_LOOKUP; resolved = MaybeServeFromCache(cache, key, cache_usage, ignore_secure, source_net_log, out_stale_info); if (resolved) { // |MaybeServeFromCache()| will update |*out_stale_info| as needed. DCHECK(out_stale_info->has_value()); source_net_log.AddEvent( NetLogEventType::HOST_RESOLVER_MANAGER_CACHE_HIT, [&] { return NetLogResults(resolved.value()); }); // TODO(crbug.com/1200908): Call StartBootstrapFollowup() if the Secure // DNS Policy is kBootstrap and the result is not secure. Note: A naive // implementation could cause an infinite loop if |resolved| always // expires or is evicted before the followup runs. return resolved.value(); } DCHECK(!out_stale_info->has_value()); } else if (task == TaskType::CONFIG_PRESET) { resolved = MaybeReadFromConfig(job_key); if (resolved) { source_net_log.AddEvent( NetLogEventType::HOST_RESOLVER_MANAGER_CONFIG_PRESET_MATCH, [&] { return NetLogResults(resolved.value()); }); StartBootstrapFollowup(job_key, cache, source_net_log); return resolved.value(); } } else if (task == TaskType::HOSTS) { resolved = ServeFromHosts(job_key.host.GetHostname(), job_key.query_types, default_family_due_to_no_ipv6, *out_tasks); if (resolved) { source_net_log.AddEvent( NetLogEventType::HOST_RESOLVER_MANAGER_HOSTS_HIT, [&] { return NetLogResults(resolved.value()); }); return resolved.value(); } } else { NOTREACHED(); } } return HostCache::Entry(ERR_DNS_CACHE_MISS, HostCache::Entry::SOURCE_UNKNOWN); } void HostResolverManager::CreateAndStartJob(JobKey key, std::deque tasks, RequestImpl* request) { DCHECK(!tasks.empty()); auto jobit = jobs_.find(key); Job* job; if (jobit == jobs_.end()) { job = AddJobWithoutRequest(key, request->parameters().cache_usage, request->host_cache(), std::move(tasks), request->priority(), request->source_net_log()); job->AddRequest(request); job->RunNextTask(); } else { job = jobit->second.get(); job->AddRequest(request); } } HostResolverManager::Job* HostResolverManager::AddJobWithoutRequest( JobKey key, ResolveHostParameters::CacheUsage cache_usage, HostCache* host_cache, std::deque tasks, RequestPriority priority, const NetLogWithSource& source_net_log) { auto new_job = std::make_unique(weak_ptr_factory_.GetWeakPtr(), key, cache_usage, host_cache, std::move(tasks), priority, source_net_log, tick_clock_, https_svcb_options_); auto insert_result = jobs_.emplace(std::move(key), std::move(new_job)); auto& iterator = insert_result.first; bool is_new = insert_result.second; DCHECK(is_new); auto& job = iterator->second; job->OnAddedToJobMap(iterator); return job.get(); } void HostResolverManager::CreateAndStartJobForServiceEndpointRequest( JobKey key, std::deque tasks, ServiceEndpointRequestImpl* request) { CHECK(!tasks.empty()); auto jobit = jobs_.find(key); if (jobit == jobs_.end()) { Job* job = AddJobWithoutRequest(key, request->parameters().cache_usage, request->host_cache(), std::move(tasks), request->priority(), request->net_log()); job->AddServiceEndpointRequest(request); job->RunNextTask(); } else { jobit->second->AddServiceEndpointRequest(request); } } HostCache::Entry HostResolverManager::ResolveAsIP(DnsQueryTypeSet query_types, bool resolve_canonname, const IPAddress& ip_address) { DCHECK(ip_address.IsValid()); DCHECK(!query_types.Has(DnsQueryType::UNSPECIFIED)); // IP literals cannot resolve unless the query type is an address query that // allows addresses with the same address family as the literal. E.g., don't // return IPv6 addresses for IPv4 queries or anything for a non-address query. AddressFamily family = GetAddressFamily(ip_address); if (!query_types.Has(AddressFamilyToDnsQueryType(family))) { return HostCache::Entry(ERR_NAME_NOT_RESOLVED, HostCache::Entry::SOURCE_UNKNOWN); } std::set aliases; if (resolve_canonname) { aliases = {ip_address.ToString()}; } return HostCache::Entry(OK, {IPEndPoint(ip_address, 0)}, std::move(aliases), HostCache::Entry::SOURCE_UNKNOWN); } std::optional HostResolverManager::MaybeServeFromCache( HostCache* cache, const HostCache::Key& key, ResolveHostParameters::CacheUsage cache_usage, bool ignore_secure, const NetLogWithSource& source_net_log, std::optional* out_stale_info) { DCHECK(out_stale_info); *out_stale_info = std::nullopt; if (!cache) return std::nullopt; if (cache_usage == ResolveHostParameters::CacheUsage::DISALLOWED) return std::nullopt; // Local-only requests search the cache for non-local-only results. HostCache::Key effective_key = key; if (effective_key.host_resolver_source == HostResolverSource::LOCAL_ONLY) effective_key.host_resolver_source = HostResolverSource::ANY; const std::pair* cache_result; HostCache::EntryStaleness staleness; if (cache_usage == ResolveHostParameters::CacheUsage::STALE_ALLOWED) { cache_result = cache->LookupStale(effective_key, tick_clock_->NowTicks(), &staleness, ignore_secure); } else { DCHECK(cache_usage == ResolveHostParameters::CacheUsage::ALLOWED); cache_result = cache->Lookup(effective_key, tick_clock_->NowTicks(), ignore_secure); staleness = HostCache::kNotStale; } if (cache_result) { *out_stale_info = std::move(staleness); source_net_log.AddEvent( NetLogEventType::HOST_RESOLVER_MANAGER_CACHE_HIT, [&] { return NetLogResults(cache_result->second); }); return cache_result->second; } return std::nullopt; } std::optional HostResolverManager::MaybeReadFromConfig( const JobKey& key) { DCHECK(HasAddressType(key.query_types)); if (!key.host.HasScheme()) { return std::nullopt; } std::optional> preset_addrs = dns_client_->GetPresetAddrs(key.host.AsSchemeHostPort()); if (!preset_addrs) return std::nullopt; std::vector filtered_addresses = FilterAddresses(std::move(*preset_addrs), key.query_types); if (filtered_addresses.empty()) return std::nullopt; return HostCache::Entry(OK, std::move(filtered_addresses), /*aliases=*/{}, HostCache::Entry::SOURCE_CONFIG); } void HostResolverManager::StartBootstrapFollowup( JobKey key, HostCache* host_cache, const NetLogWithSource& source_net_log) { DCHECK_EQ(SecureDnsMode::kOff, key.secure_dns_mode); DCHECK(host_cache); key.secure_dns_mode = SecureDnsMode::kSecure; if (jobs_.count(key) != 0) return; Job* job = AddJobWithoutRequest( key, ResolveHostParameters::CacheUsage::ALLOWED, host_cache, {TaskType::SECURE_DNS}, RequestPriority::LOW, source_net_log); job->RunNextTask(); } std::optional HostResolverManager::ServeFromHosts( std::string_view hostname, DnsQueryTypeSet query_types, bool default_family_due_to_no_ipv6, const std::deque& tasks) { DCHECK(!query_types.Has(DnsQueryType::UNSPECIFIED)); // Don't attempt a HOSTS lookup if there is no DnsConfig or the HOSTS lookup // is going to be done next as part of a system lookup. if (!dns_client_ || !HasAddressType(query_types) || (!tasks.empty() && tasks.front() == TaskType::SYSTEM)) return std::nullopt; const DnsHosts* hosts = dns_client_->GetHosts(); if (!hosts || hosts->empty()) return std::nullopt; // HOSTS lookups are case-insensitive. std::string effective_hostname = base::ToLowerASCII(hostname); // If |address_family| is ADDRESS_FAMILY_UNSPECIFIED other implementations // (glibc and c-ares) return the first matching line. We have more // flexibility, but lose implicit ordering. // We prefer IPv6 because "happy eyeballs" will fall back to IPv4 if // necessary. std::vector addresses; if (query_types.Has(DnsQueryType::AAAA)) { auto it = hosts->find(DnsHostsKey(effective_hostname, ADDRESS_FAMILY_IPV6)); if (it != hosts->end()) { addresses.emplace_back(it->second, 0); } } if (query_types.Has(DnsQueryType::A)) { auto it = hosts->find(DnsHostsKey(effective_hostname, ADDRESS_FAMILY_IPV4)); if (it != hosts->end()) { addresses.emplace_back(it->second, 0); } } // If got only loopback addresses and the family was restricted, resolve // again, without restrictions. See SystemHostResolverCall for rationale. if (default_family_due_to_no_ipv6 && base::ranges::all_of(addresses, &IPAddress::IsIPv4, &IPEndPoint::address) && base::ranges::all_of(addresses, &IPAddress::IsLoopback, &IPEndPoint::address)) { query_types.Put(DnsQueryType::AAAA); return ServeFromHosts(hostname, query_types, false, tasks); } if (addresses.empty()) return std::nullopt; return HostCache::Entry(OK, std::move(addresses), /*aliases=*/{}, HostCache::Entry::SOURCE_HOSTS); } std::optional HostResolverManager::ServeLocalhost( std::string_view hostname, DnsQueryTypeSet query_types, bool default_family_due_to_no_ipv6) { DCHECK(!query_types.Has(DnsQueryType::UNSPECIFIED)); std::vector resolved_addresses; if (!HasAddressType(query_types) || !ResolveLocalHostname(hostname, &resolved_addresses)) { return std::nullopt; } if (default_family_due_to_no_ipv6 && query_types.Has(DnsQueryType::A) && !query_types.Has(DnsQueryType::AAAA)) { // The caller disabled the AAAA query due to lack of detected IPv6 support. // (See SystemHostResolverCall for rationale). query_types.Put(DnsQueryType::AAAA); } std::vector filtered_addresses = FilterAddresses(std::move(resolved_addresses), query_types); return HostCache::Entry(OK, std::move(filtered_addresses), /*aliases=*/{}, HostCache::Entry::SOURCE_UNKNOWN); } void HostResolverManager::CacheResult(HostCache* cache, const HostCache::Key& key, const HostCache::Entry& entry, base::TimeDelta ttl) { // Don't cache an error unless it has a positive TTL. if (cache && (entry.error() == OK || ttl.is_positive())) cache->Set(key, entry, tick_clock_->NowTicks(), ttl); } std::unique_ptr HostResolverManager::RemoveJob( JobMap::iterator job_it) { DCHECK(job_it != jobs_.end()); DCHECK(job_it->second); DCHECK_EQ(1u, jobs_.count(job_it->first)); std::unique_ptr job; job_it->second.swap(job); jobs_.erase(job_it); job->OnRemovedFromJobMap(); return job; } SecureDnsMode HostResolverManager::GetEffectiveSecureDnsMode( SecureDnsPolicy secure_dns_policy) { // Use switch() instead of if() to ensure that all policies are handled. switch (secure_dns_policy) { case SecureDnsPolicy::kDisable: case SecureDnsPolicy::kBootstrap: return SecureDnsMode::kOff; case SecureDnsPolicy::kAllow: break; } const DnsConfig* config = dns_client_ ? dns_client_->GetEffectiveConfig() : nullptr; SecureDnsMode secure_dns_mode = SecureDnsMode::kOff; if (config) { secure_dns_mode = config->secure_dns_mode; } return secure_dns_mode; } bool HostResolverManager::ShouldForceSystemResolverDueToTestOverride() const { // If tests have provided a catch-all DNS block and then disabled it, check // that we are not at risk of sending queries beyond the local network. if (HostResolverProc::GetDefault() && system_resolver_disabled_for_testing_) { DCHECK(dns_client_); DCHECK(dns_client_->GetEffectiveConfig()); DCHECK(base::ranges::none_of(dns_client_->GetEffectiveConfig()->nameservers, &IPAddress::IsPubliclyRoutable, &IPEndPoint::address)) << "Test could query a publicly-routable address."; } return !host_resolver_system_params_.resolver_proc && HostResolverProc::GetDefault() && !system_resolver_disabled_for_testing_; } void HostResolverManager::PushDnsTasks(bool system_task_allowed, SecureDnsMode secure_dns_mode, bool insecure_tasks_allowed, bool allow_cache, bool prioritize_local_lookups, ResolveContext* resolve_context, std::deque* out_tasks) { DCHECK(dns_client_); DCHECK(dns_client_->GetEffectiveConfig()); // If a catch-all DNS block has been set for unit tests, we shouldn't send // DnsTasks. It is still necessary to call this method, however, so that the // correct cache tasks for the secure dns mode are added. const bool dns_tasks_allowed = !ShouldForceSystemResolverDueToTestOverride(); // Upgrade the insecure DnsTask depending on the secure dns mode. switch (secure_dns_mode) { case SecureDnsMode::kSecure: DCHECK(!allow_cache || out_tasks->front() == TaskType::SECURE_CACHE_LOOKUP); // Policy misconfiguration can put us in secure DNS mode without any DoH // servers to query. See https://crbug.com/1326526. if (dns_tasks_allowed && dns_client_->CanUseSecureDnsTransactions()) out_tasks->push_back(TaskType::SECURE_DNS); break; case SecureDnsMode::kAutomatic: DCHECK(!allow_cache || out_tasks->front() == TaskType::CACHE_LOOKUP); if (dns_client_->FallbackFromSecureTransactionPreferred( resolve_context)) { // Don't run a secure DnsTask if there are no available DoH servers. if (dns_tasks_allowed && insecure_tasks_allowed) out_tasks->push_back(TaskType::DNS); } else if (prioritize_local_lookups) { // If local lookups are prioritized, the cache should be checked for // both secure and insecure results prior to running a secure DnsTask. // The task sequence should already contain the appropriate cache task. if (dns_tasks_allowed) { out_tasks->push_back(TaskType::SECURE_DNS); if (insecure_tasks_allowed) out_tasks->push_back(TaskType::DNS); } } else { if (allow_cache) { // Remove the initial cache lookup task so that the secure and // insecure lookups can be separated. out_tasks->pop_front(); out_tasks->push_back(TaskType::SECURE_CACHE_LOOKUP); } if (dns_tasks_allowed) out_tasks->push_back(TaskType::SECURE_DNS); if (allow_cache) out_tasks->push_back(TaskType::INSECURE_CACHE_LOOKUP); if (dns_tasks_allowed && insecure_tasks_allowed) out_tasks->push_back(TaskType::DNS); } break; case SecureDnsMode::kOff: DCHECK(!allow_cache || IsLocalTask(out_tasks->front())); if (dns_tasks_allowed && insecure_tasks_allowed) out_tasks->push_back(TaskType::DNS); break; default: NOTREACHED(); break; } constexpr TaskType kWantTasks[] = {TaskType::DNS, TaskType::SECURE_DNS}; const bool no_dns_or_secure_tasks = base::ranges::find_first_of(*out_tasks, kWantTasks) == out_tasks->end(); // The system resolver can be used as a fallback for a non-existent or // failing DnsTask if allowed by the request parameters. if (system_task_allowed && (no_dns_or_secure_tasks || allow_fallback_to_systemtask_)) out_tasks->push_back(TaskType::SYSTEM); } void HostResolverManager::CreateTaskSequence( const JobKey& job_key, ResolveHostParameters::CacheUsage cache_usage, SecureDnsPolicy secure_dns_policy, std::deque* out_tasks) { DCHECK(out_tasks->empty()); // A cache lookup should generally be performed first. For jobs involving a // DnsTask, this task may be replaced. bool allow_cache = cache_usage != ResolveHostParameters::CacheUsage::DISALLOWED; if (secure_dns_policy == SecureDnsPolicy::kBootstrap) { DCHECK_EQ(SecureDnsMode::kOff, job_key.secure_dns_mode); if (allow_cache) out_tasks->push_front(TaskType::INSECURE_CACHE_LOOKUP); out_tasks->push_front(TaskType::CONFIG_PRESET); if (allow_cache) out_tasks->push_front(TaskType::SECURE_CACHE_LOOKUP); } else if (allow_cache) { if (job_key.secure_dns_mode == SecureDnsMode::kSecure) { out_tasks->push_front(TaskType::SECURE_CACHE_LOOKUP); } else { out_tasks->push_front(TaskType::CACHE_LOOKUP); } } out_tasks->push_back(TaskType::HOSTS); // Determine what type of task a future Job should start. bool prioritize_local_lookups = cache_usage == HostResolver::ResolveHostParameters::CacheUsage::STALE_ALLOWED; const bool has_address_type = HasAddressType(job_key.query_types); switch (job_key.source) { case HostResolverSource::ANY: // Force address queries with canonname to use HostResolverSystemTask to // counter poor CNAME support in DnsTask. See https://crbug.com/872665 // // Otherwise, default to DnsTask (with allowed fallback to // HostResolverSystemTask for address queries). But if hostname appears to // be an MDNS name (ends in *.local), go with HostResolverSystemTask for // address queries and MdnsTask for non- address queries. if ((job_key.flags & HOST_RESOLVER_CANONNAME) && has_address_type) { out_tasks->push_back(TaskType::SYSTEM); } else if (!ResemblesMulticastDNSName(job_key.host.GetHostname())) { bool system_task_allowed = has_address_type && job_key.secure_dns_mode != SecureDnsMode::kSecure; if (dns_client_ && dns_client_->GetEffectiveConfig()) { bool insecure_allowed = dns_client_->CanUseInsecureDnsTransactions() && !dns_client_->FallbackFromInsecureTransactionPreferred() && (has_address_type || dns_client_->CanQueryAdditionalTypesViaInsecureDns()); PushDnsTasks(system_task_allowed, job_key.secure_dns_mode, insecure_allowed, allow_cache, prioritize_local_lookups, &*job_key.resolve_context, out_tasks); } else if (system_task_allowed) { out_tasks->push_back(TaskType::SYSTEM); } } else if (has_address_type) { // For *.local address queries, try the system resolver even if the // secure dns mode is SECURE. Public recursive resolvers aren't expected // to handle these queries. out_tasks->push_back(TaskType::SYSTEM); } else { out_tasks->push_back(TaskType::MDNS); } break; case HostResolverSource::SYSTEM: out_tasks->push_back(TaskType::SYSTEM); break; case HostResolverSource::DNS: if (dns_client_ && dns_client_->GetEffectiveConfig()) { bool insecure_allowed = dns_client_->CanUseInsecureDnsTransactions() && (has_address_type || dns_client_->CanQueryAdditionalTypesViaInsecureDns()); PushDnsTasks(false /* system_task_allowed */, job_key.secure_dns_mode, insecure_allowed, allow_cache, prioritize_local_lookups, &*job_key.resolve_context, out_tasks); } break; case HostResolverSource::MULTICAST_DNS: out_tasks->push_back(TaskType::MDNS); break; case HostResolverSource::LOCAL_ONLY: // If no external source allowed, a job should not be created or started break; } // `HOST_RESOLVER_CANONNAME` is only supported through system resolution. if (job_key.flags & HOST_RESOLVER_CANONNAME) { DCHECK(base::ranges::find(*out_tasks, TaskType::DNS) == out_tasks->end()); DCHECK(base::ranges::find(*out_tasks, TaskType::MDNS) == out_tasks->end()); } } namespace { bool RequestWillUseWiFi(handles::NetworkHandle network) { NetworkChangeNotifier::ConnectionType connection_type; if (network == handles::kInvalidNetworkHandle) connection_type = NetworkChangeNotifier::GetConnectionType(); else connection_type = NetworkChangeNotifier::GetNetworkConnectionType(network); return connection_type == NetworkChangeNotifier::CONNECTION_WIFI; } } // namespace void HostResolverManager::FinishIPv6ReachabilityCheck( CompletionOnceCallback callback, int rv) { SetLastIPv6ProbeResult((rv == OK) ? true : false); std::move(callback).Run(OK); if (!ipv6_request_callbacks_.empty()) { std::vector tmp_request_callbacks; ipv6_request_callbacks_.swap(tmp_request_callbacks); for (auto& request_callback : tmp_request_callbacks) { std::move(request_callback).Run(OK); } } } int HostResolverManager::StartIPv6ReachabilityCheck( const NetLogWithSource& net_log, ClientSocketFactory* client_socket_factory, CompletionOnceCallback callback) { // Don't bother checking if the request will use WiFi and IPv6 is assumed to // not work on WiFi. if (!check_ipv6_on_wifi_ && RequestWillUseWiFi(target_network_)) { probing_ipv6_ = false; last_ipv6_probe_result_ = false; last_ipv6_probe_time_ = base::TimeTicks(); return OK; } if (probing_ipv6_) { ipv6_request_callbacks_.push_back(std::move(callback)); return ERR_IO_PENDING; } // Cache the result for kIPv6ProbePeriodMs (measured from after // StartGloballyReachableCheck() completes). int rv = OK; bool cached = true; if (last_ipv6_probe_time_.is_null() || (tick_clock_->NowTicks() - last_ipv6_probe_time_).InMilliseconds() > kIPv6ProbePeriodMs) { probing_ipv6_ = true; rv = StartGloballyReachableCheck( IPAddress(kIPv6ProbeAddress), net_log, client_socket_factory, base::BindOnce(&HostResolverManager::FinishIPv6ReachabilityCheck, weak_ptr_factory_.GetWeakPtr(), std::move(callback))); if (rv != ERR_IO_PENDING) { SetLastIPv6ProbeResult((rv == OK) ? true : false); rv = OK; } cached = false; } net_log.AddEvent( NetLogEventType::HOST_RESOLVER_MANAGER_IPV6_REACHABILITY_CHECK, [&] { return NetLogIPv6AvailableParams(last_ipv6_probe_result_, cached); }); return rv; } void HostResolverManager::SetLastIPv6ProbeResult(bool last_ipv6_probe_result) { probing_ipv6_ = false; last_ipv6_probe_result_ = last_ipv6_probe_result; last_ipv6_probe_time_ = tick_clock_->NowTicks(); } int HostResolverManager::StartGloballyReachableCheck( const IPAddress& dest, const NetLogWithSource& net_log, ClientSocketFactory* client_socket_factory, CompletionOnceCallback callback) { std::unique_ptr probing_socket = client_socket_factory->CreateDatagramClientSocket( DatagramSocket::DEFAULT_BIND, net_log.net_log(), net_log.source()); DatagramClientSocket* probing_socket_ptr = probing_socket.get(); auto refcounted_socket = base::MakeRefCounted< base::RefCountedData>>( std::move(probing_socket)); int rv = probing_socket_ptr->ConnectAsync( IPEndPoint(dest, GetPortForGloballyReachableCheck()), base::BindOnce(&HostResolverManager::RunFinishGloballyReachableCheck, weak_ptr_factory_.GetWeakPtr(), refcounted_socket, std::move(callback))); if (rv != ERR_IO_PENDING) { rv = FinishGloballyReachableCheck(probing_socket_ptr, rv) ? OK : ERR_FAILED; } return rv; } bool HostResolverManager::FinishGloballyReachableCheck( DatagramClientSocket* socket, int rv) { if (rv != OK) { return false; } IPEndPoint endpoint; rv = socket->GetLocalAddress(&endpoint); if (rv != OK) { return false; } const IPAddress& address = endpoint.address(); if (address.IsLinkLocal()) { return false; } if (address.IsIPv6()) { const uint8_t kTeredoPrefix[] = {0x20, 0x01, 0, 0}; if (IPAddressStartsWith(address, kTeredoPrefix)) { return false; } } return true; } void HostResolverManager::RunFinishGloballyReachableCheck( scoped_refptr>> socket, CompletionOnceCallback callback, int rv) { bool is_reachable = FinishGloballyReachableCheck(socket->data.get(), rv); std::move(callback).Run(is_reachable ? OK : ERR_FAILED); } void HostResolverManager::RunLoopbackProbeJob() { RunHaveOnlyLoopbackAddressesJob( base::BindOnce(&HostResolverManager::SetHaveOnlyLoopbackAddresses, weak_ptr_factory_.GetWeakPtr())); } void HostResolverManager::RemoveAllJobs(const ResolveContext* context) { for (auto it = jobs_.begin(); it != jobs_.end();) { const JobKey& key = it->first; if (&*key.resolve_context == context) { RemoveJob(it++); } else { ++it; } } } void HostResolverManager::AbortJobsWithoutTargetNetwork(bool in_progress_only) { // In Abort, a Request callback could spawn new Jobs with matching keys, so // first collect and remove all running jobs from `jobs_`. std::vector> jobs_to_abort; for (auto it = jobs_.begin(); it != jobs_.end();) { Job* job = it->second.get(); if (!job->HasTargetNetwork() && (!in_progress_only || job->is_running())) { jobs_to_abort.push_back(RemoveJob(it++)); } else { ++it; } } // Pause the dispatcher so it won't start any new dispatcher jobs while // aborting the old ones. This is needed so that it won't start the second // DnsTransaction for a job in `jobs_to_abort` if the DnsConfig just became // invalid. PrioritizedDispatcher::Limits limits = dispatcher_->GetLimits(); dispatcher_->SetLimits( PrioritizedDispatcher::Limits(limits.reserved_slots.size(), 0)); // Life check to bail once `this` is deleted. base::WeakPtr self = weak_ptr_factory_.GetWeakPtr(); // Then Abort them. for (size_t i = 0; self.get() && i < jobs_to_abort.size(); ++i) { jobs_to_abort[i]->Abort(); } if (self) dispatcher_->SetLimits(limits); } void HostResolverManager::AbortInsecureDnsTasks(int error, bool fallback_only) { // Aborting jobs potentially modifies |jobs_| and may even delete some jobs. // Create safe closures of all current jobs. std::vector job_abort_closures; for (auto& job : jobs_) { job_abort_closures.push_back( job.second->GetAbortInsecureDnsTaskClosure(error, fallback_only)); } // Pause the dispatcher so it won't start any new dispatcher jobs while // aborting the old ones. This is needed so that it won't start the second // DnsTransaction for a job if the DnsConfig just changed. PrioritizedDispatcher::Limits limits = dispatcher_->GetLimits(); dispatcher_->SetLimits( PrioritizedDispatcher::Limits(limits.reserved_slots.size(), 0)); for (base::OnceClosure& closure : job_abort_closures) std::move(closure).Run(); dispatcher_->SetLimits(limits); } // TODO(crbug.com/995984): Consider removing this and its usage. void HostResolverManager::TryServingAllJobsFromHosts() { if (!dns_client_ || !dns_client_->GetEffectiveConfig()) return; // TODO(szym): Do not do this if nsswitch.conf instructs not to. // http://crbug.com/117655 // Life check to bail once |this| is deleted. base::WeakPtr self = weak_ptr_factory_.GetWeakPtr(); for (auto it = jobs_.begin(); self.get() && it != jobs_.end();) { Job* job = it->second.get(); ++it; // This could remove |job| from |jobs_|, but iterator will remain valid. job->ServeFromHosts(); } } void HostResolverManager::OnIPAddressChanged() { DCHECK(!IsBoundToNetwork()); last_ipv6_probe_time_ = base::TimeTicks(); // Abandon all ProbeJobs. probe_weak_ptr_factory_.InvalidateWeakPtrs(); InvalidateCaches(); #if (BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_APPLE) && !BUILDFLAG(IS_ANDROID)) || \ BUILDFLAG(IS_FUCHSIA) RunLoopbackProbeJob(); #endif AbortJobsWithoutTargetNetwork(true /* in_progress_only */); // `this` may be deleted inside AbortJobsWithoutTargetNetwork(). } void HostResolverManager::OnConnectionTypeChanged( NetworkChangeNotifier::ConnectionType type) { DCHECK(!IsBoundToNetwork()); UpdateConnectionType(type); } void HostResolverManager::OnSystemDnsConfigChanged( std::optional config) { DCHECK(!IsBoundToNetwork()); // If tests have provided a catch-all DNS block and then disabled it, check // that we are not at risk of sending queries beyond the local network. if (HostResolverProc::GetDefault() && system_resolver_disabled_for_testing_ && config.has_value()) { DCHECK(base::ranges::none_of(config->nameservers, &IPAddress::IsPubliclyRoutable, &IPEndPoint::address)) << "Test could query a publicly-routable address."; } bool changed = false; bool transactions_allowed_before = false; if (dns_client_) { transactions_allowed_before = dns_client_->CanUseSecureDnsTransactions() || dns_client_->CanUseInsecureDnsTransactions(); changed = dns_client_->SetSystemConfig(std::move(config)); } // Always invalidate cache, even if no change is seen. InvalidateCaches(); if (changed) { // Need to update jobs iff transactions were previously allowed because // in-progress jobs may be running using a now-invalid configuration. if (transactions_allowed_before) UpdateJobsForChangedConfig(); } } void HostResolverManager::UpdateJobsForChangedConfig() { // Life check to bail once `this` is deleted. base::WeakPtr self = weak_ptr_factory_.GetWeakPtr(); // Existing jobs that were set up using the nameservers and secure dns mode // from the original config need to be aborted (does not apply to jobs // targeting a specific network). AbortJobsWithoutTargetNetwork(false /* in_progress_only */); // `this` may be deleted inside AbortJobsWithoutTargetNetwork(). if (self.get()) TryServingAllJobsFromHosts(); } void HostResolverManager::OnFallbackResolve(int dns_task_error) { DCHECK(dns_client_); DCHECK_NE(OK, dns_task_error); // Nothing to do if DnsTask is already not preferred. if (dns_client_->FallbackFromInsecureTransactionPreferred()) return; dns_client_->IncrementInsecureFallbackFailures(); // If DnsClient became not preferred, fallback all fallback-allowed insecure // DnsTasks to HostResolverSystemTasks. if (dns_client_->FallbackFromInsecureTransactionPreferred()) AbortInsecureDnsTasks(ERR_FAILED, true /* fallback_only */); } int HostResolverManager::GetOrCreateMdnsClient(MDnsClient** out_client) { #if BUILDFLAG(ENABLE_MDNS) if (!mdns_client_) { if (!mdns_socket_factory_) mdns_socket_factory_ = std::make_unique(net_log_); mdns_client_ = MDnsClient::CreateDefault(); } int rv = OK; if (!mdns_client_->IsListening()) rv = mdns_client_->StartListening(mdns_socket_factory_.get()); DCHECK_NE(ERR_IO_PENDING, rv); DCHECK(rv != OK || mdns_client_->IsListening()); if (rv == OK) *out_client = mdns_client_.get(); return rv; #else // Should not request MDNS resoltuion unless MDNS is enabled. NOTREACHED(); return ERR_UNEXPECTED; #endif } void HostResolverManager::InvalidateCaches(bool network_change) { DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); DCHECK(!invalidation_in_progress_); #if DCHECK_IS_ON() base::WeakPtr self_ptr = weak_ptr_factory_.GetWeakPtr(); size_t num_jobs = jobs_.size(); #endif invalidation_in_progress_ = true; for (auto& context : registered_contexts_) { context.InvalidateCachesAndPerSessionData( dns_client_ ? dns_client_->GetCurrentSession() : nullptr, network_change); } invalidation_in_progress_ = false; #if DCHECK_IS_ON() // Sanity checks that invalidation does not have reentrancy issues. DCHECK(self_ptr); DCHECK_EQ(num_jobs, jobs_.size()); #endif } void HostResolverManager::UpdateConnectionType( NetworkChangeNotifier::ConnectionType type) { host_resolver_system_params_.unresponsive_delay = GetTimeDeltaForConnectionTypeFromFieldTrialOrDefault( "DnsUnresponsiveDelayMsByConnectionType", HostResolverSystemTask::Params::kDnsDefaultUnresponsiveDelay, type); // Note that NetworkChangeNotifier always sends a CONNECTION_NONE notification // before non-NONE notifications. This check therefore just ensures each // connection change notification is handled once and has nothing to do with // whether the change is to offline or online. if (type == NetworkChangeNotifier::CONNECTION_NONE && dns_client_) { dns_client_->ReplaceCurrentSession(); InvalidateCaches(true /* network_change */); } } std::unique_ptr HostResolverManager::CreateDohProbeRunner( ResolveContext* resolve_context) { DCHECK(resolve_context); DCHECK(registered_contexts_.HasObserver(resolve_context)); if (!dns_client_ || !dns_client_->CanUseSecureDnsTransactions()) { return nullptr; } return dns_client_->GetTransactionFactory()->CreateDohProbeRunner( resolve_context); } } // namespace net