在使用consul做docker容器服務化的過程中,使用到了dnsmasq做DNS請求轉發,于是研究了下DNS協議的一些內容,如有錯漏希望大家指正。
1 DNS基礎
DNS是一種基于TCP/IP應用的分布式數據庫,提供了主機名字和IP地址的轉換以及有關電子郵件的選路信息等。在應用看來,對DNS的訪問時通過一個地址解析器(resolver)來完成的,在Unix/Linux中,這個解析器主要通過兩個函數gethostbyname
和gethostbyaddr
來訪問的。
DNS的名字空間和Unix文件系統很相似,具有層次結構,如下圖所示。每個節點有個最多63個字符的標識。其中頂層域有arpa(地用作地址到名字轉換的特殊域),com,edu,gov,int,mil,net,org(com到org這7個稱為普通域或者組織域),cn, ae,us...(這些為國家代碼,稱為國家域)。許多國家將二級域組織成類似普通域的結構,比如.edu.cn則是我國的教育機構的二級域名,比如大學的官網一般都是這種域名,如 www.hust.edu.cn
是華中科大的域名。
沒有機構管理域名層次結構中的每個標識,而只有一個機構NIC負責分配頂級域和委派其他指定區域的授權機構。一個獨立管理DNS子樹的就稱為一個區域,一個常見的區域是二級域,比如hust.edu,二級域通常將它們的區域劃分為更小的區域,比如大學一般會根據院系劃分區域,比如 sse.hust.edu.cn是華中科大的軟件學院域名,而cs.hust.edu.cn是華中科大的計算機學院域名。
一旦一個區域的授權機構被委派后,它就要負責給該區域提供多個名字服務器(Linux中的/etc/resolv.conf中的nameserver就是這個)并負責域名的管理。當名字服務器中沒有域名信息時,則需要與其他名字服務器聯系,當然不是所有的名字服務器都知道如何同其他名字服務器聯系,但是他們都知道同根的名字服務器聯系。主名字服務器必須知道根服務器的IP地址(注意,不是域名,必須是IP地址),這個可以預先配置好。根服務器則知道所有二級域中的每個授權名字服務器的名字和IP地址,這是一個反復的過程,也就是根服務器告訴了處理請求的名字服務器與另一個名字服務器聯系從而最終得到對應信息。
DNS的一個基本特性是使用高速緩存,當一個名字服務器收到映射信息后,會將信息存儲在高速緩存中,加速后面相同請求的查找效率。
2 DNS請求流程(遞歸查詢和迭代查詢)
先說明一下Linux系統的DNS解析的流程,其他系統除了一些細節不同,大體類似:
-
我們的系統里面會有一個
/etc/resolv.conf
文件,這里面的nameserver那些行就是指定的DNS服務器的地址。比如我的電腦里面的就是10.0.1.13
,而這里的options參數是用來設置nameserver請求超時時間以及嘗試請求次數的。# /etc/resolv.conf.head can replace this line nameserver 10.0.1.13 options timeout:2 options attempts:3
當我們請求域名解析的時候,會先向resolv.conf中配置的一個nameserver發送DNS請求,請求順序基于/etc/resolv.conf里面指定的順序,如果得到了解析結果,則返回。如果第一個nameserver沒有解析結果,則馬上向第二個nameserver發送解析請求。當然,如果第一個nameserver不是解析不了域名,而是自身的DNS服務已經掛掉或者不是DNS服務器,則會等待timeout(默認為5秒)后再向第二個nameserver發送請求,一共嘗試attempts(默認為2)次。(當然如果你的電腦里面的/etc/hosts文件里面有指定域名和ip記錄,而且
/etc/nsswitch.conf
里面設置了hosts:files dns
的話,則會先從/etc/hosts
里面解析)。要注意的是,resolv.conf中的options設置只對使用了glibc的resolver的才會有效,比如對getent,ping等工具是有效的,而像dig,nslookup是不會用到resolver的,所以它們用的是自身設置的超時和嘗試次數。另外,不同的工具對resolv.conf中的attempts選項的解析也不一樣,在debian8.8中使用getent發現在所有的nameserver的DNS請求失敗情況下嘗試次數為attempts*4
,而使用ping嘗試次數卻是attempts*2
次,也就是說如果你設置attempts為2,最終getent會對每個nameserver分別請求8次,ping則是對每個nameserver分別請求4次。通常的我們的resolv.conf中配置的是本地DNS服務器(非權威DNS服務器)地址,客戶端到本地DNS服務器通常是用的遞歸查詢。也就是說,在終端輸入
dig www.163.com
的時候,會先向本地DNS服務器 10.0.1.3發送DNS請求,然后等待10.0.1.3返回解析結果。而本地DNS服務器10.0.1.3會先檢查自己的緩存,如果緩存中有域名解析記錄,則直接返回應答。如果沒有記錄,如果本地DNS服務器只是一個作為一個DNS代理,比如dnsmasq這種,則它需要向它的上游DNS服務器發起遞歸查詢。而如果本地DNS服務器不是作為代理使用,則它需要發起迭代查詢,即先從配置獲取根服務器列表(根服務器有13個IP),然后本地DNS服務器作為客戶端選擇一個根服務器發送查詢請求,根域名服務器不支持遞歸查詢,它只是返回頂級域名服務器列表(com的頂級DNS服務器列表,如a.gtld-servers.net
等);然后本地DNS服務器選擇一個頂級域名服務器列表發送查詢請求,頂級域名服務器也不支持遞歸查詢,它只是返回授權服務器列表(ourglb0.com
的授權服務器列表,如dns1.ourglb0.org
);最后本地DNS服務器選擇一個授權服務器發送查詢請求,獲取域名的A記錄。如果域名本身做了CNAME的,則還需要對CNAME后的域名做額外查詢。整個過程可以通過dig +trace www.163.com
查看。遞歸查詢和迭代查詢的一個示例如下圖所示,其中cis.poly.edu到dns.poly.edu的為遞歸查詢,而dns.poly.edu向其他DNS服務器發出的查詢則為迭代查詢。
- 根DNS服務器現在有13個域名編號,從a.root-servers.net到m.root-servers.net,當然這不是說根DNS服務器就只有13臺,根據維基百科的數據,到2014年10月,13個編號根服務器一共是504臺,分布在全球各地,大部分通過任播(Anycast)技術,編號相同的根服務器使用同一個IP,504臺根服務器總共只使用13個IP。
3 DNS報文分析
DNS查詢和響應的報文格式如下,它由12字節長的首部和4個可變長度字段組成。格式如下:
頭部 | |
---|---|
標識 | 標志 |
問題數 | 資源記錄數 |
授權資源記錄數 | 額外資源記錄數 |
內容字段 |
---|
查詢問題 |
回答(資源記錄數可變) |
授權(資源記錄數可變) |
額外信息(資源記錄數可變) |
先看一個例子,后面再針對例子說明各個字段的含義:
先打開wireshark,監聽dns協議,然后命令行運行 dig www.163.com
,此時返回的信息如下:
ssj@ssj-mbp ~/Prog $ dig www.163.com
; <<>> DiG 9.9.7-P3 <<>> www.163.com
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 60537
;; flags: qr rd ra; QUERY: 1, ANSWER: 5, AUTHORITY: 5, ADDITIONAL: 3
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;www.163.com. IN A
;; ANSWER SECTION:
www.163.com. 535 IN CNAME www.163.com.lxdns.com.
www.163.com.lxdns.com. 536 IN CNAME 163.xdwscache.ourglb0.com.
163.xdwscache.ourglb0.com. 38 IN A 122.13.74.252
163.xdwscache.ourglb0.com. 38 IN A 157.255.20.6
163.xdwscache.ourglb0.com. 38 IN A 112.90.246.87
;; AUTHORITY SECTION:
ourglb0.com. 2143 IN NS dns4.ourglb0.info.
ourglb0.com. 2143 IN NS dns3.ourglb0.org.
ourglb0.com. 2143 IN NS dns1.ourglb0.org.
ourglb0.com. 2143 IN NS dns2.ourglb0.info.
ourglb0.com. 2143 IN NS dns5.ourglb0.org.
;; ADDITIONAL SECTION:
dns1.ourglb0.org. 2493 IN A 150.138.173.201
dns5.ourglb0.org. 10 IN A 221.202.204.225
;; Query time: 78 msec
;; SERVER: 10.0.1.13#53(10.0.1.13)
;; WHEN: Sat Aug 05 16:52:20 CST 2017
;; MSG SIZE rcvd: 306
可以看到DNS請求用的UDP協議封裝的,忽略前面的以太網,IP,UDP頭部,我們只關注DNS協議的具體數據包內容。DNS協議首部分為標識和標志,問題數,資源記錄數,授權資源記錄數,額外資源記錄數。下面一一分析:
3.1 DNS協議頭部
標識:
標識由客戶端程序設定并由服務器返回結果,客戶端程序通過它來確定響應與查詢是否匹配,示例中標識字段為0xb182
。
標志:
而16bit的標志字段則分為若干子字段如下:
1 | 4 | 1 | 1 | 1 | 1 | 3 | 4 |
---|---|---|---|---|---|---|---|
QR | opcode | AA | TC | RD | RA | Z AD CD | rcode |
- QR:1bit,0表示查詢報文,1表示響應報文。
- opcode:4bit,通常為0(標準查詢),其他值有1(反向查詢),2(服務器狀態查詢)。示例中請求報文這個值為0。
- AA:1bit,表示授權回答。在請求報文中不用設置該字段,響應報文中該字段如果為1,表示該名字服務器是授權于這個域的,如果為0,則不是。示例中為0,表示10.0.1.13不是這個域名的授權服務器。
- TC:1bit,表示可以截斷的。使用UDP時,它表示當應答長度超過512字節時,只返回前512字節。示例中為0,表示沒有截斷。
- RD:1bit,表示“期望遞歸”。這個bit可以在查詢中設置,并在響應中返回。該標志告知名字服務器必須處理這個查詢,也稱之為遞歸查詢。如果該位為0,且被請求名字服務器沒有一個授權回答,則返回一個能解答該查詢的其他名字服務器列表,這就是迭代查詢。示例中為1,表示期望遞歸查詢。
- RA:1bit,表示“可用遞歸”,響應中使用。如果名字服務器支持遞歸查詢,則在響應中將該位設置為1,示例中就是設置的1,表示該名字服務器支持遞歸查詢。除了某些根服務器之外,大部分名字服務器都提供遞歸查詢。
- Z, AD, CD:3bit,Z位和CD位通常為0。AD位在響應中設置(示例中的請求中AD位為1原是dig設置的,通常情況為0),在響應中只有所有返回的數據都是認證過的或者符合名字服務器安全策略的才能置位AD位,否則必須是0。CD(check disabled)為0,表示resolver會對名字服務器返回的數據進行檢查,名字服務器只能返回認證過的數據。
- rcode:4bit,響應中才有的字段,表示返回碼。通常為0(沒有出錯)和3(名字錯誤)。名字錯誤只能從一個授權服務器返回,它表示在查詢中指定的域名不存在。示例中返回0,表示沒有出錯。
問題數: 請求包為1,響應包為1.
資源記錄數: 請求包為0,響應包為5.
授權資源記錄數: 請求包為0,響應包為5.
額外資源記錄數: 請求包為0,響應包為1.
3.2 DNS協議內容字段
3.2.1 查詢內容字段
查詢問題
查詢問題的每個問題格式如下:查詢名,查詢類型和查詢類。
查詢名是 www.163.com 存儲格式是 3(計數) w w w 3(計數) 1 6 3 3(計數) c o m
查詢類型:在這里是00 01,一個字節,對應類型A。常用的查詢類型對應的數值和描述如下:
查詢類型 | 數值 | 描述 |
---|---|---|
A | 1 | IP地址 |
NS | 2 | 名字服務器 |
CNAME | 5 | 規范名稱 |
PTR | 12 | 指針記錄 |
HINPO | 13 | 主機信息 |
MX | 15 | 郵件交換記錄 |
AXFR | 252 | 對區域轉換的請求 |
*/ANY | 255 | 對所有記錄的請求 |
查詢類:這里是00 01,即對應IN。指互聯網地址,通常這個值為1.
額外信息(Additional Data)
額外信息這里有EDNS(擴展DNS)的內容,EDNS允許請求方通知名字服務器返回大的數據包。這種新的資源記錄叫OPT(即偽資源記錄),OPT不能被緩存和轉發,只存儲在DNS請求和響應的額外信息段中。注意,用dig的時候在請求中通常會有額外信息這一項,一般的DNS請求沒有該項,如果不想在請求中加EDNS,則可以使用dig +noedns xxx.com
。EDNS的記錄格式如下:
- Name:名字,1字節,通常為0,也就是Root。
- Type:類型,2字節,為0x0029,即41,表示為OPT。
- UDP payload size:請求方的UDP payload大小,2字節,這里為0x1000,即4096字節。
- RCODE: 返回狀態碼,1字節。加上之前的頭部的4位返回碼,一共可以有12位返回碼,表示更多的返回類型。
- VERSION: EDNS版本,1字節。通常為0.
- Z:保留字段,2字節,通常為0.
- RDATA LEN: 可變消息長度,這里為0.
- RDATA: 可變消息的數據,這里為空。
3.2.2 響應內容字段
查詢問題
由響應包可知,查詢問題部分與查詢請求基本一致。
答案
接下來是答案部分,這里一共有5條記錄。答案的格式如下:
域名,類型,類,TTL,資源數據長度,資源數據。
;; ANSWER SECTION:
www.163.com. 535 IN CNAME www.163.com.lxdns.com.
www.163.com.lxdns.com. 536 IN CNAME 163.xdwscache.ourglb0.com.
163.xdwscache.ourglb0.com. 38 IN A 122.13.74.252
163.xdwscache.ourglb0.com. 38 IN A 157.255.20.6
163.xdwscache.ourglb0.com. 38 IN A 112.90.246.87
第1條記錄為CNAME類型記錄。首先是16個字節0xc00c表示域名,這里用的壓縮的方式,轉換成二進制是0x11000000 00001100即16個字節的前兩位為11,這表示它是一個16bit的指針而不是8bit的計數字節,后面的14位說明域名針對標識的偏移位置為12,即為查詢問題字段的域名開始位置。接下來的1個字節為記錄類型0x05,也就是CNAME類型,然后2個字節是0x0001,即類為IN。接著4字節0x00000111為生存時間TTL為273,接下來2個字節0x0014是資源數據長度為20,后面的20個字節為規范名字www.163.com.lxdns.com,注意最后的com是域名結尾部分,也是用的壓縮的方式,即用的前2位標識,后面14位指定偏移位置為20.
第2條記錄也是CNAME的。www.163.com.lxdns.com CNAME到163.xdwscache.ourglb0.com。
第3條記錄為A記錄,163.xdwscache.ourglb0.com的一個IP地址為122.90.246.87,TTL為38,數據長度為4字節,數據內容為IP地址。后面的2條記錄也全都為163.xdwscache.ourglb0.com域名的A記錄。
也就是說 www.163.com的別名是 www.163.com.lxdns.com,而www.163.com.lxdns.com的別名又是 163.xdwscache.ourglb0.com。最終解析163.xdwscache.ourglb0.com域名有5個對應的IP地址。
授權
接著是五條授權服務器記錄,如下:
;; AUTHORITY SECTION:
ourglb0.com. 2143 IN NS dns4.ourglb0.info.
ourglb0.com. 2143 IN NS dns3.ourglb0.org.
ourglb0.com. 2143 IN NS dns1.ourglb0.org.
ourglb0.com. 2143 IN NS dns2.ourglb0.info.
ourglb0.com. 2143 IN NS dns5.ourglb0.org.
分別對應5條授權服務器記錄,第1條如下,其他4條類似:
- 域名: ourglb0.com
- TTL: 2143
- 類: IN,即internet地址
- 類型: NS,即名字服務器。
- 名字服務器:dns2.ourglb0.info
授權服務器知道后,如果我們直接向授權服務器的IP發送dns查詢請求,可以看到響應中的AA字段會設置為1,表示該名字服務器是授權于該域的。運行 dig @150.138.173.201 163.xdwscache.ourglb0.com
可以看到AA設置為了1.
額外信息
額外信息記錄有2條,如下所示,A記錄的格式,記錄的是授權服務器的ip地址。
;; ADDITIONAL SECTION:
dns1.ourglb0.org. 2493 IN A 150.138.173.201
dns5.ourglb0.org. 10 IN A 221.202.204.225
4 指針查詢
前面說到的都是正向查詢,即根據域名查詢IP地址,還有一種查詢是指針查詢,與域名查詢IP相反,由IP地址查詢對應的域名。注意到頂級域arpa就是用來做這個的,如果沒有arpa頂級域,那么要查詢IP地址對應的域名是很麻煩的。使用 dig -x xxx.xxx.xxx.xxx
可以發送指針查詢來查詢一個IP對應的域名。
5 使用dnsmasq搭建DNS代理
在項目中使用到consul做服務發現,而consul本身搭載了一個DNS服務器,我們需要將consul的服務域名如 redis.service.consul
的解析指向consul的DNS,而將其他域名解析使用系統本身的DNS。
使用apt-get install dnsmasq
安裝好dnsmasq后,配置文件/etc/dnsmasq.conf
如下設置,另外添加/etc/dnsmasq.d/consul.conf
文件,內容為server=/consul/172.17.42.1#8600
(其中 172.17.42.1:8600 是consul的DNS服務器監聽地址),而/etc/resolv.dnsmasq.conf
文件內容跟/etc/resolv.conf
一致,里面存儲上游DNS服務器列表地址。
###/etc/dnsmasq.conf內容###
resolv-file=/etc/resolv.dnsmasq.conf
interface=eth0
cache-size=10000 #設置緩存大小
conf-dir=/etc/dnsmasq.d/,*.conf
max-cache-ttl=600 #設置最大緩存時間
這樣的效果就是,針對consul的域名全部轉發到consul的DNS服務器處理,而其他的域名則由resolv.dnsmasq.conf
文件中的DNS服務器進行解析。需要注意的一點是,對于轉發到consul的域名解析請求,dnsmasq不會做緩存,而通過resolv-file中的DNS服務器解析的請求,則會緩存起來,下次請求只要緩存沒有過期就會從緩存中返回。
6 參考資料
- 《TCP/IP協議詳解》第14章-DNS:域名系統
- 《計算機網絡-自頂向下的分析方法》
- https://zh.wikipedia.org/wiki/%E6%A0%B9%E7%B6%B2%E5%9F%9F%E5%90%8D%E7%A8%B1%E4%BC%BA%E6%9C%8D%E5%99%A8