Chromium 网络栈之HostResolver
发布于 2021-11-16
HostResolver 是用来解析 DNS 的接口。此外可以可以通过它拿到 HostCache、DnsConfig等对象。
DNS 配置
系统 DNS 配置
实现 NetworkChangeNotifier::DNSObserver 接口,就可以在加载到系统 DNS 信息或者系统 DNS 信息发生变化时,被通知到。
class SystemDNSMonitor : public net::NetworkChangeNotifier::DNSObserver {
public:
SystemDNSMonitor() { net::NetworkChangeNotifier::AddDNSObserver(this); }
~SystemDNSMonitor() override {
net::NetworkChangeNotifier::RemoveDNSObserver(this);
}
private:
// DNSObserver
void OnInitialDNSConfigRead() override { PrintSystemDnsConfig(); }
void OnDNSChanged() override { PrintSystemDnsConfig(); }
void PrintSystemDnsConfig() {
net::DnsConfig dns_config;
net::NetworkChangeNotifier::GetDnsConfig(&dns_config);
std::string result;
JSONStringValueSerializer s(&result);
s.set_pretty_print(true);
s.Serialize(*dns_config.ToValue().get());
printf("%s\n", result.c_str());
}
DISALLOW_COPY_AND_ASSIGN(SystemDNSMonitor);
};
输出结果如下:
{
"append_to_multi_label_name": false,
"attempts": 2,
"doh_servers": [],
"hosts": [
{
"address_family": 2,
"addresss": "::1",
"host": "localhost"
},
{
"address_family": 1,
"addresss": "0.0.0.0",
"host": "www.xmind.net"
},
{
"address_family": 1,
"addresss": "127.0.0.1",
"host": "localhost"
},
{
"address_family": 1,
"addresss": "10.113.200.103",
"host": "qq"
}
],
"nameservers": [
"172.16.202.3:53",
"172.16.202.2:53",
"172.16.100.97:53",
"172.16.100.100:53"
],
"ndots": 1,
"rotate": false,
"search": [
"qq.com"
],
"timeout": 1,
"unhandled_options": false,
"use_local_ipv6": false
}
DnsConfig 中:
- nameservers,系统 DNS 服务器的列表
- hosts,本地的域名与IP映射信息
- dns_over_https_servers,DOH 服务器的列表
DnsConfigService
系统 DnsConfig 信息是被 DnsConfigService 监控加载的,具体的平台有不同的实现,Windows 平台是 DnsConfigServiceWin。它通过监控注册表、读取hosts文件内容和调用一些Windows API 来获取这些信息。
网络上下文 DNS 配置
网络上下文 DNS 配置默认是使用系统 DNS 配置,我们也可以通过 DnsConfigOverrides 去自定义网络上下文 DNS 配置。
如下是设置网络上下文 DNS 配置的 DNS 服务器地址:
net::HostResolver* host_resolver =
g_network_manager->url_request_context()->host_resolver();
net::DnsConfigOverrides dns_config_override =
net::DnsConfigOverrides::CreateOverridingEverythingWithDefaults();
base::Optional<std::vector<net::IPEndPoint>> nameservers(
base::in_place, std::vector<net::IPEndPoint>());
nameservers->push_back(
net::IPEndPoint(net::IPAddress(114, 114, 114, 114), 53));
dns_config_override.nameservers = nameservers;
host_resolver->SetDnsConfigOverrides(dns_config_override);
打印出当前网络上下文 DNS 配置:
net::HostResolver* host_resolver =
g_network_manager->url_request_context()->host_resolver();
std::unique_ptr<base::Value> value(host_resolver->GetDnsConfigAsValue());
std::string result;
JSONStringValueSerializer s(&result);
s.set_pretty_print(true);
s.Serialize(*value.get());
printf("%s\n", result.c_str());
DNS 缓存
HostCache 是用来维护 DNS 解析结果的缓存。比如我们访问了 https://www.baidu.com/
、https://www.qq.com/
,再看看 HostCache :
net::HostResolver* host_resolver =
g_network_manager->url_request_context()->host_resolver();
net::HostCache* host_cache = host_resolver->GetHostCache();
base::ListValue entry_list;
host_cache->GetAsListValue(&entry_list, true);
printf("%s\n", ValueToString(entry_list).c_str());
输出结果如下:
[
{
"addresses": [
"46.82.174.69"
],
"dns_query_type": 1,
"expiration": "112514750",
"flags": 4,
"host_resolver_source": 2,
"hostname": "google.com",
"network_changes": 1,
"secure": false,
"ttl": 189000
},
{
"addresses": [
"23.194.210.56"
],
"dns_query_type": 1,
"expiration": "112385767",
"flags": 4,
"host_resolver_source": 2,
"hostname": "ipp.corel.com",
"network_changes": 1,
"secure": false,
"ttl": 32000
},
{
"addresses": [
"203.205.254.157",
"183.3.226.35",
"61.129.7.47",
"123.151.137.18"
],
"dns_query_type": 1,
"expiration": "112385730",
"flags": 4,
"host_resolver_source": 2,
"hostname": "qq.com",
"network_changes": 1,
"secure": false,
"ttl": 35000
},
{
"addresses": [
"172.217.160.110"
],
"dns_query_type": 1,
"expiration": "112385733",
"flags": 12,
"host_resolver_source": 1,
"hostname": "google.com",
"network_changes": 1,
"secure": false,
"ttl": -1000
},
{
"addresses": [
"127.0.0.1"
],
"dns_query_type": 1,
"expiration": "112385751",
"flags": 12,
"host_resolver_source": 1,
"hostname": "ipp.corel.com",
"network_changes": 1,
"secure": false,
"ttl": -1000
},
{
"addresses": [
"112.53.26.232",
"111.30.144.71"
],
"dns_query_type": 1,
"expiration": "112385713",
"flags": 12,
"host_resolver_source": 1,
"hostname": "qq.com",
"network_changes": 1,
"secure": false,
"ttl": -1000
}
]
dns_query_type 指的就是 DnsQueryType:
// https://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml#dns-parameters-4
enum class DnsQueryType { UNSPECIFIED, A, AAAA, TXT, PTR, SRV, MAX = SRV };
host_resolver_source 指的就是 HostResolverSource,解析结果的来源方式:
- ANY,任意的来源都可以
- SYSTEM,系统的方式获取,比如调用
getaddrinfo()
API - DNS,通过网络栈发送网络请求获取
- MULTICAST_DNS,通过 Multicast DNS 的方式获取
- LOCAL_ONLY,只通过 hosts 文件或者 cache 的内容获取
DNS 解析
解析 DNS 也比较简单,通过 HostResolver 去创建一个 HostResolver::ResolveHostRequest 去获得数据。
void SyncResolveHost::StartOnNetworkThread() {
net_log = net::NetLogWithSource::Make(url_request_context_->net_log(),
net::NetLogSourceType::HTTP_STREAM_JOB);
net::HostResolver* host_resolver = url_request_context_->host_resolver();
net::HostResolver::ResolveHostParameters parameters;
parameters.dns_query_type = dns_query_type_;
parameters.source = source_;
request_ = host_resolver->CreateRequest(net::HostPortPair(host_, 443),
net_log, parameters);
int result = request_->Start(
base::BindOnce(&SyncResolveHost::OnResult, base::Unretained(this)));
if (result == net::OK) {
OnResult(result);
}
}
void SyncResolveHost::OnResult(int result) {
printf("ResolveHost %s type=%s source=%s\n", host_.c_str(),
TypeToString(dns_query_type_).c_str(),
SourceToString(source_).c_str());
PrintAddressList(request_.get());
request_.reset();
wait_event_->Signal();
}
void ResolveHost(const std::string& host,
net::DnsQueryType dns_query_type,
net::HostResolverSource source) {
SyncResolveHost r(g_network_manager->task_runner(),
g_network_manager->url_request_context());
r.Resolve(host, dns_query_type, source);
r.Wait();
}
ResolveHost("qq.com", net::DnsQueryType::UNSPECIFIED,
net::HostResolverSource::SYSTEM);
ResolveHost("qq.com", net::DnsQueryType::UNSPECIFIED,
net::HostResolverSource::DNS);
ResolveHost("qq.com", net::DnsQueryType::UNSPECIFIED,
net::HostResolverSource::ANY);
ResolveHost("qq.com", net::DnsQueryType::UNSPECIFIED,
net::HostResolverSource::MULTICAST_DNS);
ResolveHost("qq.com", net::DnsQueryType::UNSPECIFIED,
net::HostResolverSource::LOCAL_ONLY);
输出结果如下:
ResolveHost qq.com type=UNSPECIFIED source=SYSTEM
addresss:112.53.26.232:443
addresss:111.30.144.71:443
ResolveHost qq.com type=UNSPECIFIED source=DNS
addresss:203.205.254.157:443
addresss:183.3.226.35:443
addresss:61.129.7.47:443
addresss:123.151.137.18:443
ResolveHost qq.com type=UNSPECIFIED source=ANY
addresss:183.3.226.35:443
addresss:61.129.7.47:443
addresss:123.151.137.18:443
addresss:203.205.254.157:443
ResolveHost qq.com type=UNSPECIFIED source=MULTICAST_DNS
ResolveHost qq.com type=UNSPECIFIED source=LOCAL_ONLY
addresss:183.3.226.35:443
addresss:61.129.7.47:443
addresss:123.151.137.18:443
addresss:203.205.254.157:443
其中 LOCAL_ONLY 是从本地 hosts 文件或者 cache 缓存里同步返回数据。并且它的优先级最高。如果在 HostResolver 为某个域名设置了对应的 hosts数据,则所有的类型都使用这个,效果如下:
net::DnsHosts hosts;
hosts[std::make_pair<std::string, net::AddressFamily>(
"qq.com", net::AddressFamily::ADDRESS_FAMILY_IPV4)] =
net::IPAddress(114, 114, 114, 114);
dns_config_override.hosts =
base::Optional<net::DnsHosts>(base::in_place, hosts);
host_resolver->SetDnsConfigOverrides(dns_config_override);
ResolveHost("qq.com", net::DnsQueryType::UNSPECIFIED,
net::HostResolverSource::SYSTEM);
ResolveHost("qq.com", net::DnsQueryType::UNSPECIFIED,
net::HostResolverSource::DNS);
ResolveHost("qq.com", net::DnsQueryType::UNSPECIFIED,
net::HostResolverSource::ANY);
ResolveHost("qq.com", net::DnsQueryType::UNSPECIFIED,
net::HostResolverSource::MULTICAST_DNS);
ResolveHost("qq.com", net::DnsQueryType::UNSPECIFIED,
net::HostResolverSource::LOCAL_ONLY);
输出结果如下:
ResolveHost qq.com type=UNSPECIFIED source=SYSTEM
addresss:114.114.114.114:443
ResolveHost qq.com type=UNSPECIFIED source=DNS
addresss:114.114.114.114:443
ResolveHost qq.com type=UNSPECIFIED source=ANY
addresss:114.114.114.114:443
ResolveHost qq.com type=UNSPECIFIED source=MULTICAST_DNS
addresss:114.114.114.114:443
ResolveHost qq.com type=UNSPECIFIED source=LOCAL_ONLY
addresss:114.114.114.114:443
针对每个 DNS 解析请求,HostResolverManager 会根据参数信息把请求调度给 HostResolverManager::Job,然后 HostResolverManager::Job 再创建对应的 Task。
- HostResolverSource::SYSTEM,创建 ProcTask
- HostResolverSource::DNS,创建 DnsTask
- HostResolverSource::MULTICAST_DNS,创建 HostResolverMdnsTask