392 lines
17 KiB
Markdown
392 lines
17 KiB
Markdown
|
|
# 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.
|
||
|
|
|
||
|
|
*** promo
|
||
|
|
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".
|
||
|
|
***
|
||
|
|
|
||
|
|
[TOC]
|
||
|
|
|
||
|
|
## Usage
|
||
|
|
|
||
|
|
### From outside the network service
|
||
|
|
|
||
|
|
Most interaction with host resolution should be through the [network service](/services/network/README.md)
|
||
|
|
[`network::HostResolver`](/services/network/public/mojom/host_resolver.mojom),
|
||
|
|
retrieved from [`network::NetworkContext`](/services/network/public/mojom/network_context.mojom)
|
||
|
|
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/`](/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/`](/net/dns/).
|
||
|
|
|
||
|
|
### From inside the network service
|
||
|
|
|
||
|
|
Inside the network service or inside the Chrome networking stack, host
|
||
|
|
resolution goes through [`net::HostResolver`](/net/dns/host_resolver.h),
|
||
|
|
retrieved from [`net::URLRequestContext`](/net/url_request/url_request_context.h).
|
||
|
|
|
||
|
|
### 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`](/net/dns/mock_host_resolver.h)
|
||
|
|
|
||
|
|
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::HostResolver`s.
|
||
|
|
|
||
|
|
Some tests may also find `net::HangingHostResolver` useful. It will begin host
|
||
|
|
resolution requests, but never complete them until cancellation.
|
||
|
|
|
||
|
|
### TestHostResolver
|
||
|
|
|
||
|
|
[`content::TestHostResolver`](/content/public/test/test_host_resolver.h)
|
||
|
|
|
||
|
|
Used by most browser tests (via [`content::BrowserTestBase`](/content/public/test/browser_test_base.h)),
|
||
|
|
`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](/net/dns/mock_host_resolver.h).
|
||
|
|
|
||
|
|
*** note
|
||
|
|
**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"](/services/network/public/cpp/network_switches.h)
|
||
|
|
commandline flag.
|
||
|
|
|
||
|
|
See [`net::HostMappingRules`](/net/base/host_mapping_rules.h) 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`](/net/dns/host_resolver.h)
|
||
|
|
|
||
|
|
The main interface for requesting host resolution within the network stack or
|
||
|
|
network service. In prod, generally owned, and retrieved as-needed from
|
||
|
|
[`net::URLRequestContext`](/net/url_request/url_request_context.h)s. Created
|
||
|
|
using `net::HostResolver::CreateResolver()` or
|
||
|
|
`net::HostResolver::CreateStandaloneResolver()`.
|
||
|
|
|
||
|
|
Various implementations are used in prod.
|
||
|
|
|
||
|
|
#### ContextHostResolver
|
||
|
|
|
||
|
|
[`net::ContextHostResolver`](/net/dns/context_host_resolver.h)
|
||
|
|
|
||
|
|
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`](/net/dns/host_cache.h).
|
||
|
|
|
||
|
|
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`](/net/dns/mapped_host_resolver.h)
|
||
|
|
|
||
|
|
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"](/services/network/public/cpp/network_switches.h)
|
||
|
|
commandline flag.
|
||
|
|
|
||
|
|
#### StaleHostResolver
|
||
|
|
|
||
|
|
[`cronet::StaleHostResolver`](/components/cronet/stale_host_resolver.h)
|
||
|
|
|
||
|
|
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](/components/cronet/README.md).
|
||
|
|
|
||
|
|
### HostResolverManager
|
||
|
|
|
||
|
|
[`net::HostResolverManager`](/net/dns/host_resolver_manager.h)
|
||
|
|
|
||
|
|
Scheduler and controller of host resolution requests. Contains the logic for
|
||
|
|
immediate host resolution from fast local sources (e.g. querying
|
||
|
|
`net::HostCache`s, 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`](/services/network/network_service.h).
|
||
|
|
|
||
|
|
#### Request
|
||
|
|
|
||
|
|
`net::HostResolverManager::RequestImpl`
|
||
|
|
|
||
|
|
Implementation of [`net::HostResolver::ResolveHostRequest`](/net/dns/host_resolver.h)
|
||
|
|
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::HostCache`s 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::Job`s using a [`net::PrioritizedDispatcher`](/net/base/prioritized_dispatcher.h).
|
||
|
|
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`](/net/dns/dns_config.h) 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`](/net/dns/host_resolver_source.h).
|
||
|
|
|
||
|
|
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`](/base/task/thread_pool.h) 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`](/net/dns/address_info.h) helper. In tests, the blocking
|
||
|
|
task runner may use a test implementation of
|
||
|
|
[`net::HostResolverProc`](/net/dns/host_resolver_proc.h), 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`](/net/dns/dns_client.h), 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`](/net/dns/dns_transaction.h) for each DNS name/type pair
|
||
|
|
to be queried. The task will then process successful results from the returned
|
||
|
|
[`net::DnsResponse`](/net/dns/dns_response.h).
|
||
|
|
|
||
|
|
When a request requires both A and AAAA results, they are handled via two
|
||
|
|
separate `net::DnsTransaction`s 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::DnsAttempt`s, each
|
||
|
|
representing an individual DNS request. A single `net::DnsTransaction` can run
|
||
|
|
many `net::DnsAttempt`s 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`](/net/dns/host_resolver_mdns_task.h)
|
||
|
|
|
||
|
|
Results are queried from mDNS using [`net::MDnsClient`](/net/dns/mdns_client.h).
|
||
|
|
|
||
|
|
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`](/net/dns/host_resolver_mdns_task.h),
|
||
|
|
which will create and run a [`net::MDnsTransaction`](/net/dns/mdns_client.h) 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`](/net/dns/host_cache.h) owned by the
|
||
|
|
[`net::ContextHostResolver`](/net/dns/context_host_resolver.h). 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.
|