Chromium 网络栈之HostResolver

发布于 2021-11-16


HostResolver 是用来解析 DNS 的接口。此外可以可以通过它拿到 HostCache、DnsConfig等对象。

chromium net host resolver

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