Chrome Host Resolution

Implementation and support of host resolution for the Chrome network stack. Includes client implementations of host resolution protocols (DNS and mDNS), host resolution caching, support for dealing with system host resolution (including reading HOSTS files and tracking system network settings related to host resolution), and various related utilities.

Note: “DNS” in this directory (including the directory name) is often used as shorthand for all host resolution, not just that using the Domain Name System. This document attempts to use “DNS” only to refer to the actual Domain Name System, except when referring to strings or paths that contain other usage of “DNS”.

Usage

From outside the network service

Most interaction with host resolution should be through the network service network::HostResolver, retrieved from network::NetworkContext using network::NetworkContext::CreateHostResolver().

Host resolution is requested using network::HostResolver::ResolveHost(). There is also a shortcut using network::NetworkContext::ResolveHost() when a separate passable object is not needed.

Some general utilities are also available in /net/dns/public/ that are intended for use by any code, inside or outside the network service. Otherwise, code outside the network service should never interact directly with the code in /net/dns/.

From inside the network service

Inside the network service or inside the Chrome networking stack, host resolution goes through net::HostResolver, retrieved from net::URLRequestContext.

Stand-alone tools

Tests and stand-alone tools not part of the browser may interact with host resolution directly by creating their own HostResolvers using net::HostResolver::CreateStandaloneResolver().

Test support

MockHostResolver

net::MockHostResolver

Tests with the ability to inject and replace the used net::HostResolver should replace it with a net::MockHostResolver, allowing rule-based results. net::MockCachingHostResolver is the same except it includes basic support for the caching functionality normally done by prod net::HostResolvers.

Some tests may also find net::HangingHostResolver useful. It will begin host resolution requests, but never complete them until cancellation.

TestHostResolver

content::TestHostResolver

Used by most browser tests (via content::BrowserTestBase), content::TestHostResolver installs itself on creation globally into all host resolvers in the process. By default, only allows resolution of the local host and returns net::ERR_NAME_NOT_RESOLVED for other hostnames. Allows setting rules for other results using a net::RuleBasedHostResolverProc.

Warning: content::TestHostResolver only replaces host address resolution to the system and then artificially uses such system resolution for many requests that would normally be handled differently (e.g. using the built-in DNS client). This means a significant amount of normal prod host resolution logic will be bypassed in tests using content::TestHostResolver.

Request remapping

Most prod logic for creating HostResolvers will check if any global remappings have been requested. In the browser, this is requested using the “host-resolver-rules” commandline flag.

See net::HostMappingRules for details on the format of the rules string. Allows remapping any request hostname to another hostname, an IP address, or a NOTFOUND error.

Implementation

HostResolver

net::HostResolver

The main interface for requesting host resolution within the network stack or network service. In prod, generally owned, and retrieved as-needed from net::URLRequestContexts. Created using net::HostResolver::CreateResolver() or net::HostResolver::CreateStandaloneResolver().

Various implementations are used in prod.

ContextHostResolver

net::ContextHostResolver

The main prod implementation of net::HostResolver. Expected to be owned 1:1 by a single net::URLRequestContext, the net::ContextHostResolver owns or keeps references to per-URLRequestContext properties used for host resolution, including an owned net::HostCache.

On resolution, calls into an underlying net::HostResolverManager with the per- context properties.

On destruction, silently cancels all host resolution requests made through the net::ContextHostResolver instance. This prevents the underlying net::HostResolverManager from continuing to reference the per-context properties that may be destroyed on destruction of the net::URLRequestContext or net::ContextHostResolver.

MappedHostResolver

net::MappedHostResolver

A wrapping implementation around another net::HostResolver. Maintains request remapping rules to remap request hostnames to other hostnames or IP addresses.

Used to implement the “host-resolver-rules” commandline flag.

StaleHostResolver

cronet::StaleHostResolver

A wrapping implementation around another net::HostResolver. Returns stale (expired or invalidated by network changes) data from the net::HostCache when non-stale results take longer than a configurable timeout. Reduces host resolution latency at the expense of accuracy.

Only used and created by Cronet.

HostResolverManager

net::HostResolverManager

Scheduler and controller of host resolution requests. Contains the logic for immediate host resolution from fast local sources (e.g. querying net::HostCaches, IP address literals, etc). Throttles, schedules, and merges asynchronous jobs for resolution from slower network sources.

On destruction, silently cancels all in-progress host resolution requests.

In prod, a single shared net::HostResolverManager is generally used for the entire browser. The shared manager is owned and configured by the network::NetworkService.

Request

net::HostResolverManager::RequestImpl

Implementation of net::HostResolver::ResolveHostRequest and overall representation of a single request for resolution from a net::HostResolverManager. The RequestImpl object itself primarily acts only as a container of parameters and results for the request, leaving the actual logic to the net::HostResolverManager itself.

Data collected at this layer:

  • “Net.DNS.Request.TotalTime” (recommended for experiments)
  • “Net.DNS.Request.TotalTimeAsync”

Job

net::HostResolverManager::Job

Representation of an asynchronous job for resolution from slower network sources. Contains the logic to determine and query the appropriate source for host resolution results with retry and fallback support to other sources. On completion adds results to relevant net::HostCaches and invokes request callbacks.

Multiple requests can be merged into a single Job if compatible. This includes merging newly-started Jobs with already-running Jobs.

net::HostResolverManager schedules and throttles running net::HostResolverManager::Jobs using a net::PrioritizedDispatcher. The throttling is important to avoid overworking network sources, especially poorly designed home routers that may crash on only a small number of concurrent DNS resolves.

Data collected at this layer:

  • “Net.DNS.ResolveSuccessTime”
  • “Net.DNS.ResolveFailureTime”
  • “Net.DNS.ResolveCategory”
  • “Net.DNS.ResolveError.Fast”
  • “Net.DNS.ResolveError.Slow”
  • “Net.DNS.SecureDnsMode.[Off/Automatic/Secure].ResolveTime”

Host resolution sources

Various sources are used to query host resolution. The sources to be used by a net::HostResolverManager::Job are determined in advance of running the Job by net::HostResolverManager::CreateTaskSequence(), which outputs a list of net::HostResolverManager::TaskType specifying the order of sources to attempt. By default, this will use internal logic to decide the source to use and will often allow fallback to additional sources.

The sources chosen by default are also affected by the Secure DNS mode, by default determined from net::DnsConfig::secure_dns_mode but overridable for individual requests using net::HostResolver::ResolveHostParameters::secure_dns_mode_override.

Specific sources for a request can be specified using net::HostResolver::ResolveHostParameters::source and net::HostResolverSource.

The Job will then use *Task objects that implement the behavior specific to the particular resolution sources.

SYSTEM

net::HostResolverSource::SYSTEM net::HostResolverManager::TaskType::SYSTEM

Implemented by: net::HostResolverSystemTask

Usually called the “system resolver” or sometimes the “proc resolver” (because it was historically always implemented using net::HostResolverProc). Results are queried from the system or OS using the getaddrinfo() OS API call. This source is only capable of address (A and AAAA) resolves but will also query for canonname info if the request includes the HOST_RESOLVER_CANONNAME flag. The system will query from its own internal cache, HOSTS files, DNS, and sometimes mDNS, depending on the capabilities of the system.

When host resolution requests do not specify a source, the system resolver will always be used for address resolves when any of the following are true:

  • Requests with the HOST_RESOLVER_CANONNAME flag
  • For hostnames ending in “.local”
  • When the Secure DNS mode is net::SecureDnsMode::OFF and net::HostResolverSource::DNS is not enabled via net::HostResolverManager::SetInsecureDnsClientEnabled(true)
  • When a system DNS configuration could not be determined

Secure DNS requests cannot be made using the system resolver.

net::HostResolverSystemTask's behavior can be overridden by an asynchronous global override (e.g. in case resolution needs to be brokered out of the current process for sandboxing reasons). Otherwise, it posts a blocking task to a base::ThreadPool to make blocking resolution requests in-process. On a timeout, additional attempts are made, but previous attempts are not cancelled as there is no cancellation mechanism for getaddrinfo(). The first attempt to complete is used and any other attempt completions are ignored.

In prod, the blocking task runner always calls SystemHostResolverCall(), which makes the actual call to getaddrinfo() using the net::AddressInfo helper. In tests, the blocking task runner may use a test implementation of net::HostResolverProc, which itself can be chained.

Data collected specifically for this source:

  • “Net.DNS.SystemTask.SuccessTime”
  • “Net.DNS.SystemTask.FailureTime”

DNS

net::HostResolverSource::DNS net::HostResolverManager::TaskType::DNS net::HostResolverManager::TaskType::SECURE_DNS

Implemented by: net::HostResolverManager::DnsTask

Usually called the “built-in resolver” or the “async resolver”. Results are queried from DNS using net::DnsClient, a Chrome network stack implementation of a DNS “stub resolver” or “DNS client”.

When host resolution requests do not specify a source, the built-in resolver will be used when all of the following are true:

  • DnsClient is enabled for insecure requests enabled via net::HostResolverManager::SetInsecureDnsClientEnabled(true) or the Secure DNS mode is not net::SecureDnsMode::OFF.
  • The system DNS configuration could be determined successfully
  • The request hostname does not end in “.local”
  • The request is not an address query with the HOST_RESOLVER_CANONNAME flag

The net::HostResolverManager::DnsTask will create and run a net::DnsTransaction for each DNS name/type pair to be queried. The task will then process successful results from the returned net::DnsResponse.

When a request requires both A and AAAA results, they are handled via two separate net::DnsTransactions and the net::HostResolverManager::DnsTask will request a second slots from the net::PrioritizedDispatcher used by net::HostResolverManager. The A transaction is started immediately on starting the net::HostResolverManager::DnsTask, and the AAAA transaction is started once a second dispatcher slot can be obtained.

Each net::DnsTransaction internally makes a series of net::DnsAttempts, each representing an individual DNS request. A single net::DnsTransaction can run many net::DnsAttempts due to retry logic, fallback between multiple configured DNS servers, and name permutation due to configured search suffixes.

Data collected specifically for this source (more internally to net::DnsTransaction implementation not listed here):

  • “Net.DNS.DnsTask.SuccessTime”
  • “Net.DNS.InsecureDnsTask.FailureTime”
  • “Net.DNS.JobQueueTime.PerTransaction”
  • “Net.DNS.JobQueueTime.Failure”
  • “Net.DNS.JobQueueTime.Success”
  • “Net.DNS.SecureDnsTask.DnsModeSecure.FailureTime”
  • “Net.DNS.SecureDnsTask.DnsModeAutomatic.FailureTime”

MULTICAST_DNS

net::HostResolverSource::MULTICAST_DNS net::HostResolverManager::TaskType::MDNS

Implemented by net::HostResolverMdnsTask

Results are queried from mDNS using net::MDnsClient.

When host resolution requests do not specify a source, mDNS is only used for non-address requests when the request hostname ends in “.local”.

mDNS requests start with net::HostResolverMdnsTask, which will create and run a net::MDnsTransaction for each query type needed.

Unlike net::HostResolverManager::DnsTask, each net::HostResolverMdnsTask will only ever use a single dispatcher slot, even when both A and AAAA types are queried concurrently.

net::MDnsClient maintains its own cache, separate from the main net::HostCache owned by the net::ContextHostResolver. As such, mDNS results are never cached in the net::HostCache.

IPv6 and connectivity

Some poorly written DNS servers, especially on home routers, are unaware of the existence of IPv6 and will result in bad performance or even crash when sent AAAA DNS queries.

To avoid such issues, net::HostResolverManager heuristically detects IPv4-only networks by attempting a UDP connection to 2001:4860:4860::8888 (the IPv6 address for Google Public DNS). If the connection fails, Chrome will convert host resolution requests for net::DnsQueryType::UNSPECIFIED to net::DnsQueryType::A. This generally results in disallowing AAAA requests.

Exceptions when AAAA requests are always allowed despite a failed connectivity check:

  • The host resolution request explicitly requests net::DnsQueryType::AAAA
  • IP address literal resolution including when a hostname request has been rewritten to an IP address literal using net::MappedHostResolver
  • Results read from HOSTS files where there is no non-loopback IPv4 result. Note that this exception only applies when Chrome does the read from HOSTS. When Chrome's built-in DNS client is not used, HOSTS is only read by the system where Chrome would only request A results to avoid the system making AAAA DNS queries.

The heuristic for detecting IPv4-only networks is not perfect. E.g., it fails and disallows AAAA requests in private (no global internet access including to Google Public DNS) IPv6-only networks, which could then break most Chrome usage on the network because, being an IPv6-only network, AAAA results are necessary.

Workarounds to allow Chrome to attempt to load IPv6 endpoints when the connectivity check fails:

  • Starting Chrome with --host-resolver-rules="MAP the.hostname.com [dead::beef]" where the.hostname.com is the hostname to allow resolving and dead::beef is the IPv6 address to resolve it to. net::MappedHostResolver acts at a level before IPv6 connectivity checks, and if a hostname is remapped to an IP literal, connectivity checks do not apply.
  • Add entries for the hostnames to resolve to the HOSTS file with just IPv6 results. Only works with the built-in DNS client is used.
  • Add a network route to 2001:4860:4860::8888. Doesn't have to actually be functional (could just drop requests to it). As long as Chrome can connect a UDP socket to the address, it will pass the heuristic checking IPv6-connectivity.