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

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

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

我經常與虛擬機集群打交道( 文1 、 文2 、 文3 、 文4 、 文5 、 文6 ),因此最終花費了大量時間試圖掌握 DNS 查詢 的工作原理。遇到問題時,我只是不求甚解的使用 StackOverflow 上的“解決方案”,而不知道它們為什麼有時工作,有時不工作。

最終我對此感到了厭倦,決定一併找出所有問題的原因。我沒有在網上找到完整的指南,我問過一些同事,他們不知所以然(或許是問題太具體了)。

既然如此,我開始自己寫這樣的手冊。

結果發現,“Linux 執行一次 DNS 查詢”這句話的背後有相當多的工作。

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

“究竟有多難呢?”

本系列文章試圖將 Linux 主機上程序獲取(域名對應的) IP 地址的過程及期間涉及的組件進行分塊剖析。如果不理解這些塊的協同工作方式,調試解決 dnsmasq、vagrant landrush 和 resolvconf 等相關的問題會讓人感到眼花繚亂。

同時這也是一份有價值的說明,指出原本很簡單的東西是如何隨著時間的推移變得相當複雜。在弄清楚 DNS 查詢的原理的過程中,我瞭解了大量不同的技術及其發展歷程。

我甚至編寫了一些 自動化腳本 ,可以讓我在虛擬機中進行實驗。歡迎讀者參與貢獻或勘誤。

請注意,本系列主題並不是“DNS 工作原理”,而是與查詢 Linux 主機配置的真實 DNS 服務器(這裡假設查詢了一臺 DNS 服務器,但後面你會看到有時並不需要)相關的內容,以及如何確定使用哪個查詢結果,或者如何使用其它方式確定 IP 地址。

1) 其實並沒有名為“DNS 查詢”的系統調用

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

工作方式並非如此

首先要了解的一點是,Linux 上並沒有一個單獨的方法可以完成 DNS 查詢工作;沒有一個有這樣的明確接口的核心 系統調用(system call)。

不過,有一個標準 C 庫函數調用 getaddrinfo ,不少程序使用了該調用;但不是所有程序或應用都使用該調用!

讓我們看一下兩個簡單的標準程序:ping 和 host:

root@linuxdns1:~# ping -c1 bbc.co.uk | head -1 PING bbc.co.uk (151.101.192.81) 56(84) bytes of data.

root@linuxdns1:~# host bbc.co.uk | head -1 bbc.co.uk has address 151.101.192.81

對於同一個域名,兩個程序得到的 IP 地址是相同的;那麼它們是使用同樣的方法得到結果的吧?

事實並非如此。

下面文件給出了我主機上 ping 對應的 DNS 相關的系統調用:

root@linuxdns1:~# strace -e trace=open -f ping -c1 google.com open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3 open("/lib/x86_64-linux-gnu/libcap.so.2", O_RDONLY|O_CLOEXEC) = 3 open("/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3 open("/etc/resolv.conf", O_RDONLY|O_CLOEXEC) = 4 open("/etc/resolv.conf", O_RDONLY|O_CLOEXEC) = 4 open("/etc/nsswitch.conf", O_RDONLY|O_CLOEXEC) = 4 open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 4 open("/lib/x86_64-linux-gnu/libnss_files.so.2", O_RDONLY|O_CLOEXEC) = 4 open("/etc/host.conf", O_RDONLY|O_CLOEXEC) = 4 open("/etc/hosts", O_RDONLY|O_CLOEXEC) = 4 open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 4 open("/lib/x86_64-linux-gnu/libnss_dns.so.2", O_RDONLY|O_CLOEXEC) = 4 open("/lib/x86_64-linux-gnu/libresolv.so.2", O_RDONLY|O_CLOEXEC) = 4 PING google.com (216.58.204.46) 56(84) bytes of data. open("/etc/hosts", O_RDONLY|O_CLOEXEC) = 4 64 bytes from lhr25s12-in-f14.1e100.net (216.58.204.46): icmp_seq=1 ttl=63 time=13.0 ms [...]

下面是 host 對應的系統調用:

$ strace -e trace=open -f host google.com [...] [pid 9869] open("/usr/share/locale/en_US.UTF-8/LC_MESSAGES/libdst.cat", O_RDONLY) = -1 ENOENT (No such file or directory) [pid 9869] open("/usr/share/locale/en/libdst.cat", O_RDONLY) = -1 ENOENT (No such file or directory) [pid 9869] open("/usr/share/locale/en/LC_MESSAGES/libdst.cat", O_RDONLY) = -1 ENOENT (No such file or directory) [pid 9869] open("/usr/lib/ssl/openssl.cnf", O_RDONLY) = 6 [pid 9869] open("/usr/lib/x86_64-linux-gnu/openssl-1.0.0/engines/libgost.so", O_RDONLY|O_CLOEXEC) = 6[pid 9869] open("/etc/resolv.conf", O_RDONLY) = 6 google.com has address 216.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 最初是 Solaris OS 的一部分,可以讓應用無需硬編碼查詢所需的文件或服務,而是在其它集中式的、無需應用開發人員管理的配置文件中找到。

下面是我的 nsswitch.conf:

passwd: compat group: compat shadow: compat gshadow: files hosts: files dns myhostname networks: files protocols: db files services: db files ethers: db files rpc: db files netgroup: nis

我們需要關注的是 hosts 行。我們知道 ping 用到 nsswitch.conf 文件,那麼我們修改這個文件(的 hosts 行),看看能夠如何影響 ping。

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

如果你修改 nsswitch.conf,將 hosts 行僅保留 files:

hosts: files

此時, ping 無法獲取 google.com 對應的 IP 地址:

$ ping -c1 google.com ping: unknown host google.com

但 localhost 的解析不受影響:

$ ping -c1 localhost PING localhost (127.0.0.1) 56(84) bytes of data. 64 bytes from localhost (127.0.0.1): icmp_seq=1 ttl=64 time=0.039 ms

此外,host 命令正常返回:

$ host google.com google.com has address 216.58.206.110

畢竟如我們之前看到的那樣,host 不受 nsswitch.conf 影響。

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

如果你修改 nsswitch.conf,將 hosts 行僅保留 dns:

hosts: dns

此時,google.com 的解析恢復正常:

$ ping -c1 google.com PING google.com (216.58.198.174) 56(84) bytes of data. 64 bytes from lhr25s10-in-f174.1e100.net (216.58.198.174): icmp_seq=1 ttl=63 time=8.01 ms

但 localhost 無法解析:

$ ping -c1 localhost ping: unknown host localhost

下圖給出默認 NSSwitch 中 hosts 行對應的查詢邏輯:

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

我的 hosts: 配置是 nsswitch.conf 給出的默認值

3) /etc/resolv.conf

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

下面給出我主機的 /etc/resolv.conf 文件內容:

# Dynamic resolv.conf(5) file for glibc resolver(3) generated by resolvconf(8) # DO NOT EDIT THIS FILE BY HAND -- YOUR CHANGES WILL BE OVERWRITTEN nameserver 10.0.2.3

先忽略前兩行,後面我們會回過頭來看這部分(它們很重要,但你還需要一些知識儲備)。

其中 nameserver 行指定了查詢用到的 DNS 服務器。

如果將該行註釋掉:

#nameserver 10.0.2.3

再次運行:

$ ping -c1 google.com ping: unknown host google.com

解析失敗了,這是因為沒有可用的名字服務器 2 。

該文件中還可以使用其它選項。例如,你可以在 resolv.conf 文件中增加如下行:

search com

然後執行 ping google (不寫 .com)

$ ping google PING google.com (216.58.204.14) 56(84) bytes of data.

程序會自動為你嘗試 .com 域。

第一部分總結

第一部分到此結束,下一部分我們會了解 resolv.conf 文件是如何創建和更新的。

下面總結第一部分涵蓋的內容:

  • 操作系統中並不存在“DNS 查詢”這個系統調用
  • 不同程序可能採用不同的策略獲取名字對應的 IP 地址
  • 例如, ping 使用 nsswitch,後者進而使用(或可以使用) /etc/hosts、/etc/resolv.conf 以及主機名得到解析結果
  • /etc/resolv.conf 用於決定:
  • 查詢什麼地址(LCTT 譯註:這裡可能指 search 帶來的影響)
  • 使用什麼 DNS 服務器執行查詢

如果你曾認為 DNS 查詢很複雜,請跟隨這個系列學習吧。


  1. ping 實現的變種之多令人驚歎。我 不 希望在這裡討論過多。 ↩
  2. 另一個需要注意的地方: host 在沒有指定 nameserver 的情況下會嘗試 127.0.0.1:53。 ↩

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

本文由 LCTT 原創編譯, Linux中國 榮譽推出


分享到:


相關文章: