从Chrome源码看DNS解析过程
作者:媒体转发 时间:2018-02-04 01:18

DNS解析的作用是把域名解析成相应的IP地址,因为在广域网上路由器需要知道IP地址才知道把报文发给谁。DNS是Domain Name System域名系统的缩写,它是一个协议,在RFC 1035具体描述了这个协议。具体过程如下图所示:

这个过程看似简单,但是有几个问题:
(1)浏览器是怎么知道DNS解析服务器,如上图的8.8.8.8这台?
(2)一个域名可以解析成多个IP地址吗,如果只有一个IP地址,在并发量很大的情况下,那台服务器可能会爆?
(3)把域名绑了host之后,是不是就不用域名解析了直接用的本地host指定的IP地址?
(4)域名解析的有效时间为多长,即过了多久后同一个域名需要再次进行解析?
(5)什么是域名解析的A记录、AAAA记录、CNAME记录?
其实域名解析和Chrome没有直接关系,即使是最简单的curl命令也需要进行域名解析,但是我们可以通过Chrome源码来看一下这个过程是怎么样的,并且回答上面的问题。
首先第一个问题,浏览器是怎么知道DNS解析服务器的,在本机的网络设置里面可以看到当前的DNS服务器IP,如我电脑的:

这两个DNS Server是我家接的某正宽带提供的:

一般宽带服务商都会提供DNS服务器,谷歌还为公众提供了两个免费的DNS服务,分别为8.8.8.8和8.8.4.4,取这两个IP地址是为了容易记住,当你的DNS服务不好用的时候,可以尝试改成这两个。
入网的设备是怎么获取到这些IP地址的呢?是通过动态主机配置协议(DHCP),当一台设备连到路由器之后,路由器通过DHCP给它分配一个IP地址,并告诉它DNS服务器,如下路由器的DHCP设置:

通过wireshark抓包可以观察到这个过程:

当我的电脑连上wifi的时候,会发一个DHCP Request的广播,路由器收到这个广播后就会向我的电脑分配一个IP地址并告知DNS服务器。
这个时候系统就有DNS服务器了,Chrome是调res_ninit这个系统函数(Linux)去获取系统的DNS服务器,这个函数是通过读取/etc/resolver.conf这个文件获取DNS:
#
# Mac OS X Notice
#
# This file is not used by the host name and address resolution
# or the DNS query routing mechanisms used by most processes on
# this Mac OS X system.
#
# This file is automatically generated.
#
search DHCP HOST
nameserver 59.108.61.61
nameserver 219.232.48.61
search选项的作用是当一个域名不可解析时,就会尝试在后面添加相应的后缀,如ping hello,无法解析就会分别ping hello.DHCP/hello.HOST,结果最后都无法解析。
Chrome在启动的时候根据不同的操作系统去获取DNS服务器配置,然后把它放到DNSConfig的nameservers:
// List of name server addresses.
std::vector<IPEndPoint> nameservers;
Chrome还会监听网络变化同步改变配置。
然后用这个nameservers列表去初始化一个socket pool即套接字池,套接字是用来发请求的。在需要做域名解析的时候会从套接字池里面取出一个socket,并传递想要用的server_index,初始化的时候是0,即取第一个DNS服务IP地址,一旦解析请求两次都失败了,则server_index + 1使用下一个DNS服务。
unsigned server_index =
(first_server_index_ + attempt_number) % config.nameservers.size();
// Skip over known failed servers.
// 最大attempts数为2,在构造DnsConfig设定的
server_index = session_->NextGoodServerIndex(server_index);
如果所有的nameserver都失败了,那么它会取最早失败的nameserver.
Chrome在启动的时候除了会读取DNS server之外,还会去取读取和解析hosts文件,放到DNSConfig的hosts属性里面,它是一个哈希map:
// Parsed results of a Hosts file.
//




