Linux DNS 查询剖析(第一部分)

[复制链接]
wxb3315431 发表于 2019-4-13 01:57:36 | 显示全部楼层 |阅读模式
来源:https://www.toutiao.com/group/6593282678538109444/
若内容不全,可点击上述链接查看来源网页,在网页中点击红色双层向下的箭头阅读全文


编译自:https://zwischenzugs.com/2018/06/08/anatomy-of-a-linux-dns-lookup-part-i/

作者:Dmatech

译者:AndySong

我经常与虚拟机集群打交道(文1、文2、文3、文4、文5、文6),因此最终花费了大量时间试图掌握DNS查询的工作原理。遇到问题时,我只是不求甚解的使用StackOverflow上的“解决方案”,而不知道它们为什么有时工作,有时不工作。

最终我对此感到了厌倦,决定一并找出所有问题的原因。我没有在网上找到完整的指南,我问过一些同事,他们不知所以然(或许是问题太具体了)。

既然如此,我开始自己写这样的手册。

结果发现,“Linux执行一次DNS查询”这句话的背后有相当多的工作。

“究竟有多难呢?”

本系列文章试图将Linux主机上程序获取(域名对应的)IP地址的过程及期间涉及的组件进行分块剖析。如果不理解这些块的协同工作方式,调试解决dnsmasq、vagrantlandrush和resolvconf等相关的问题会让人感到眼花缭乱。

同时这也是一份有价值的说明,指出原本很简单的东西是如何随着时间的推移变得相当复杂。在弄清楚DNS查询的原理的过程中,我了解了大量不同的技术及其发展历程。

我甚至编写了一些自动化脚本,可以让我在虚拟机中进行实验。欢迎读者参与贡献或勘误。

请注意,本系列主题并不是“DNS工作原理”,而是与查询Linux主机配置的真实DNS服务器(这里假设查询了一台DNS服务器,但后面你会看到有时并不需要)相关的内容,以及如何确定使用哪个查询结果,或者如何使用其它方式确定IP地址。

1)其实并没有名为“DNS查询”的系统调用

工作方式并非如此

首先要了解的一点是,Linux上并没有一个单独的方法可以完成DNS查询工作;没有一个有这样的明确接口的核心系统调用(systemcall)。

不过,有一个标准C库函数调用getaddrinfo,不少程序使用了该调用;但不是所有程序或应用都使用该调用!

让我们看一下两个简单的标准程序:ping和host:

root@linuxdns1:~#ping-c1bbc.co.uk|head-1PINGbbc.co.uk(151.101.192.81)56(84)bytesofdata.

root@linuxdns1:~#hostbbc.co.uk|head-1bbc.co.ukhasaddress151.101.192.81

对于同一个域名,两个程序得到的IP地址是相同的;那么它们是使用同样的方法得到结果的吧?

事实并非如此。

下面文件给出了我主机上ping对应的DNS相关的系统调用:

root@linuxdns1:~#strace-etrace=open-fping-c1google.comopen("/etc/ld.so.cache",O_RDONLY|O_CLOEXEC)=3open("/lib/x86_64-linux-gnu/libcap.so.2",O_RDONLY|O_CLOEXEC)=3open("/lib/x86_64-linux-gnu/libc.so.6",O_RDONLY|O_CLOEXEC)=3open("/etc/resolv.conf",O_RDONLY|O_CLOEXEC)=4open("/etc/resolv.conf",O_RDONLY|O_CLOEXEC)=4open("/etc/nsswitch.conf",O_RDONLY|O_CLOEXEC)=4open("/etc/ld.so.cache",O_RDONLY|O_CLOEXEC)=4open("/lib/x86_64-linux-gnu/libnss_files.so.2",O_RDONLY|O_CLOEXEC)=4open("/etc/host.conf",O_RDONLY|O_CLOEXEC)=4open("/etc/hosts",O_RDONLY|O_CLOEXEC)=4open("/etc/ld.so.cache",O_RDONLY|O_CLOEXEC)=4open("/lib/x86_64-linux-gnu/libnss_dns.so.2",O_RDONLY|O_CLOEXEC)=4open("/lib/x86_64-linux-gnu/libresolv.so.2",O_RDONLY|O_CLOEXEC)=4PINGgoogle.com(216.58.204.46)56(84)bytesofdata.open("/etc/hosts",O_RDONLY|O_CLOEXEC)=464bytesfromlhr25s12-in-f14.1e100.net(216.58.204.46):icmp_seq=1ttl=63time=13.0ms[...]

下面是host对应的系统调用:

$strace-etrace=open-fhostgoogle.com[...][pid9869]open("/usr/share/locale/en_US.UTF-8/LC_MESSAGES/libdst.cat",O_RDONLY)=-1ENOENT(Nosuchfileordirectory)[pid9869]open("/usr/share/locale/en/libdst.cat",O_RDONLY)=-1ENOENT(Nosuchfileordirectory)[pid9869]open("/usr/share/locale/en/LC_MESSAGES/libdst.cat",O_RDONLY)=-1ENOENT(Nosuchfileordirectory)[pid9869]open("/usr/lib/ssl/openssl.cnf",O_RDONLY)=6[pid9869]open("/usr/lib/x86_64-linux-gnu/openssl-1.0.0/engines/libgost.so",O_RDONLY|O_CLOEXEC)=6[pid9869]open("/etc/resolv.conf",O_RDONLY)=6google.comhasaddress216.58.204.46[...]

可以看出ping打开了nsswitch.conf文件,但host没有;但两个程序都打开了/etc/resolv.conf文件。

下面我们依次查看这两个.conf扩展名的文件。

2)NSSwitch与/etc/nsswitch.conf

我们已经确认应用可以自主决定选用哪个DNS服务器。很多应用(例如ping)通过配置文件/etc/nsswitch.conf(根据具体实现1)参考NSSwitch完成选择。

NSSwitch不仅用于DNS查询,例如,还用于密码与用户信息查询。

NSSwitch最初是SolarisOS的一部分,可以让应用无需硬编码查询所需的文件或服务,而是在其它集中式的、无需应用开发人员管理的配置文件中找到。

下面是我的nsswitch.conf:

passwd:compatgroup:compatshadow:compatgshadow:fileshosts:filesdnsmyhostnamenetworks:filesprotocols:dbfilesservices:dbfilesethers:dbfilesrpc:dbfilesnetgroup:nis

我们需要关注的是hosts行。我们知道ping用到nsswitch.conf文件,那么我们修改这个文件(的hosts行),看看能够如何影响ping。

修改nsswitch.conf,hosts行仅保留files

如果你修改nsswitch.conf,将hosts行仅保留files:

hosts:files

此时,ping无法获取google.com对应的IP地址:

$ping-c1google.comping:unknownhostgoogle.com

但localhost的解析不受影响:

$ping-c1localhostPINGlocalhost(127.0.0.1)56(84)bytesofdata.64bytesfromlocalhost(127.0.0.1):icmp_seq=1ttl=64time=0.039ms

此外,host命令正常返回:

$hostgoogle.comgoogle.comhasaddress216.58.206.110

毕竟如我们之前看到的那样,host不受nsswitch.conf影响。

修改nsswitch.conf,hosts行仅保留dns

如果你修改nsswitch.conf,将hosts行仅保留dns:

hosts:dns

此时,google.com的解析恢复正常:

$ping-c1google.comPINGgoogle.com(216.58.198.174)56(84)bytesofdata.64bytesfromlhr25s10-in-f174.1e100.net(216.58.198.174):icmp_seq=1ttl=63time=8.01ms

但localhost无法解析:

$ping-c1localhostping:unknownhostlocalhost

下图给出默认NSSwitch中hosts行对应的查询逻辑:

我的hosts:配置是nsswitch.conf给出的默认值

3)/etc/resolv.conf

我们已经知道host和ping都使用/etc/resolv.conf文件。

下面给出我主机的/etc/resolv.conf文件内容:

#Dynamicresolv.conf(5)fileforglibcresolver(3)generatedbyresolvconf(8)#DONOTEDITTHISFILEBYHAND--YOURCHANGESWILLBEOVERWRITTENnameserver10.0.2.3

先忽略前两行,后面我们会回过头来看这部分(它们很重要,但你还需要一些知识储备)。

其中nameserver行指定了查询用到的DNS服务器。

如果将该行注释掉:

#nameserver10.0.2.3

再次运行:

$ping-c1google.comping:unknownhostgoogle.com

解析失败了,这是因为没有可用的名字服务器2。

该文件中还可以使用其它选项。例如,你可以在resolv.conf文件中增加如下行:

searchcom

然后执行pinggoogle(不写.com)

$pinggooglePINGgoogle.com(216.58.204.14)56(84)bytesofdata.

程序会自动为你尝试.com域。

第一部分总结

第一部分到此结束,下一部分我们会了解resolv.conf文件是如何创建和更新的。

下面总结第一部分涵盖的内容:

  • 操作系统中并不存在“DNS查询”这个系统调用
  • 不同程序可能采用不同的策略获取名字对应的IP地址
  • 例如,ping使用nsswitch,后者进而使用(或可以使用)/etc/hosts、/etc/resolv.conf以及主机名得到解析结果
  • /etc/resolv.conf用于决定:
  • 查询什么地址(LCTT译注:这里可能指search带来的影响)
  • 使用什么DNS服务器执行查询

如果你曾认为DNS查询很复杂,请跟随这个系列学习吧。

  • ping实现的变种之多令人惊叹。我不希望在这里讨论过多。↩
  • 另一个需要注意的地方:host在没有指定nameserver的情况下会尝试127.0.0.1:53。↩

via:https://zwischenzugs.com/2018/06/08/anatomy-of-a-linux-dns-lookup-part-i/

作者:dmatech译者:pinewall校对:wxy

本文由LCTT原创编译,Linux中国荣誉推出

点击“了解更多”可访问文内链接