https://zwischenzugs.com/2018/06/08/anatomy-of-a-linux-dns-lookup-part-i/
作者 | Dmatech
譯者 | Andy Song (pinewall) ????共計翻譯:31 篇 貢獻時間:149 天
我經常與虛擬機器叢集打交道(文1[1]、文2[2]、文3[3]、文4[4]、文5[5]、文6[6]),因此最終花費了大量時間試圖掌握 DNS 查詢[7]的工作原理。遇到問題時,我只是不求甚解的使用 StackOverflow 上的“解決方案”,而不知道它們為什麼有時工作,有時不工作。
最終我對此感到了厭倦,決定一併找出所有問題的原因。我沒有在網上找到完整的指南,我問過一些同事,他們不知所以然(或許是問題太具體了)。
既然如此,我開始自己寫這樣的手冊。
結果發現,“Linux 執行一次 DNS 查詢”這句話的背後有相當多的工作。
“究竟有多難呢?”
本系列文章試圖將 Linux 主機上程式獲取(域名對應的) IP 地址的過程及期間涉及的元件進行分塊剖析。如果不理解這些塊的協同工作方式,除錯解決 dnsmasq
、vagrant landrush
和 resolvconf
等相關的問題會讓人感到眼花繚亂。
同時這也是一份有價值的說明,指出原本很簡單的東西是如何隨著時間的推移變得相當複雜。在弄清楚 DNS 查詢的原理的過程中,我瞭解了大量不同的技術及其發展歷程。
我甚至編寫了一些自動化指令碼[8],可以讓我在虛擬機器中進行實驗。歡迎讀者參與貢獻或勘誤。
請註意,本系列主題並不是“DNS 工作原理”,而是與查詢 Linux 主機配置的真實 DNS 伺服器(這裡假設查詢了一臺 DNS 伺服器,但後面你會看到有時並不需要)相關的內容,以及如何確定使用哪個查詢結果,或者如何使用其它方式確定 IP 地址。
1) 其實並沒有名為“DNS 查詢”的系統呼叫
工作方式並非如此
首先要瞭解的一點是,Linux 上並沒有一個單獨的方法可以完成 DNS 查詢工作;沒有一個有這樣的明確介面的核心系統呼叫。
不過,有一個標準 C 庫函式呼叫 getaddrinfo
[9],不少程式使用了該呼叫;但不是所有程式或應用都使用該呼叫!
讓我們看一下兩個簡單的標準程式: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
行對應的查詢邏輯:
我的 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
檔案是如何建立和更新的。
下麵總結第一部分涵蓋的內容:
ping
使用 nsswitch
,後者進而使用(或可以使用) /etc/hosts
、/etc/resolv.conf
以及主機名得到解析結果/etc/resolv.conf
用於決定:
search
帶來的影響)如果你曾認為 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[11] 譯者:pinewall 校對:wxy
本文由 LCTT 原創編譯,Linux中國 榮譽推出