小菜學(xué)網(wǎng)絡(luò)之DNS報(bào)文格式
本文轉(zhuǎn)載自微信公眾號(hào)「小菜學(xué)編程」,作者fasionchan 。轉(zhuǎn)載本文請(qǐng)聯(lián)系小菜學(xué)編程公眾號(hào)。
經(jīng)過(guò)前面學(xué)習(xí),我們知道查詢一個(gè)域名,需要與 DNS 服務(wù)器進(jìn)行通信。那么,DNS 通信過(guò)程大概是怎樣的呢?
DNS 是一個(gè)典型的 Client-Server 應(yīng)用,客戶端發(fā)起域名查詢請(qǐng)求,服務(wù)端對(duì)請(qǐng)求進(jìn)行應(yīng)答:
DNS 一般采用 UDP 作為傳輸層協(xié)議( TCP 亦可),端口號(hào)是 53 。請(qǐng)求報(bào)文和應(yīng)答報(bào)文均作為數(shù)據(jù),搭載在 UDP 數(shù)據(jù)報(bào)中進(jìn)行傳輸:
很顯然,DNS 請(qǐng)求報(bào)文和應(yīng)答報(bào)文均需要滿足一定的格式,才能被通信雙方所理解。這就是 DNS 協(xié)議負(fù)責(zé)的范疇,它位于傳輸層之上,屬于 應(yīng)用層 協(xié)議。
報(bào)文格式
DNS 報(bào)文分為 請(qǐng)求 和 應(yīng)答 兩種,結(jié)構(gòu)是類似的,大致分為五部分:
- 頭部( header ),描述報(bào)文類型,以及其下 4 個(gè)小節(jié)的情況;
- 問(wèn)題節(jié)( question ),保存查詢問(wèn)題;
- 答案節(jié)( answer ),保存問(wèn)題答案,也就是查詢結(jié)果;
- 授權(quán)信息節(jié)( authority ),保存授權(quán)信息;
- 附加信息節(jié)( additional ),保存附加信息;
也有不少文獻(xiàn)將 DNS 請(qǐng)求稱為 DNS 查詢( query ),兩者是一個(gè)意思。
其中,頭部是固定的,共 12 字節(jié);其他節(jié)不固定,記錄數(shù)可多可少,數(shù)目保存在頭部中。頭部分為 6 個(gè)字段:
- 標(biāo)識(shí)( identifier ),一個(gè) 16 位的 ID ,在應(yīng)答中原樣返回,以此匹配請(qǐng)求和應(yīng)答;
- 標(biāo)志( flags ),一些標(biāo)志位,共 16 位;
- 問(wèn)題記錄數(shù)( question count ),一個(gè) 16 位整數(shù),表示問(wèn)題節(jié)中的記錄個(gè)數(shù);
- 答案記錄數(shù)( answer count ),一個(gè) 16 位整數(shù),表示答案節(jié)中的記錄個(gè)數(shù);
- 授權(quán)信息記錄數(shù)( authority record count ),一個(gè) 16 位整數(shù),表示授權(quán)信息節(jié)中的記錄個(gè)數(shù);
- 附加信息記錄數(shù)( additional record count ),一個(gè) 16 位整數(shù),表示附加信息節(jié)中的記錄個(gè)數(shù);
最后,我們來(lái)解釋一下標(biāo)志字段中的各個(gè)標(biāo)志位:
- QR 位標(biāo)記報(bào)文是一個(gè)查詢請(qǐng)求,還是查詢應(yīng)答;
- 0 表示查詢請(qǐng)求;
- 1 表示查詢應(yīng)答;
- 操作碼( opcode )占 4 位,表示操作類型:
- 0 代表標(biāo)準(zhǔn)查詢;
- 1 代表反向查詢;
- 2 代表服務(wù)器狀態(tài)請(qǐng)求;
- AA 位表示 權(quán)威回答( authoritative answer ),意味著當(dāng)前查詢結(jié)果是由域名的權(quán)威服務(wù)器給出的;
- TC 位表示 截短( truncated ),使用 UDP 時(shí),如果應(yīng)答超過(guò) 512 字節(jié),只返回前 512 個(gè)字節(jié);
- RD 位表示 期望遞歸 ( recursion desired ),在請(qǐng)求中設(shè)置,并在應(yīng)答中返回;
- 該位為 1 時(shí),服務(wù)器必須處理這個(gè)請(qǐng)求:如果服務(wù)器沒(méi)有授權(quán)回答,它必須替客戶端請(qǐng)求其他 DNS 服務(wù)器,這也是所謂的 遞歸查詢 ;
- 該位為 0 時(shí),如果服務(wù)器沒(méi)有授權(quán)回答,它就返回一個(gè)能夠處理該查詢的服務(wù)器列表給客戶端,由客戶端自己進(jìn)行 迭代查詢 ;
- RA 位表示可遞歸( recursion available ),如果服務(wù)器支持遞歸查詢,就會(huì)在應(yīng)答中設(shè)置該位,以告知客戶端;
- 保留位,這 3 位目前未用,留作未來(lái)擴(kuò)展;
- 響應(yīng)碼( response code )占 4 位,表示請(qǐng)求結(jié)果,常見(jiàn)的值包括:
- 0 表示沒(méi)有差錯(cuò);
- 3 表示名字差錯(cuò),該差錯(cuò)由權(quán)威服務(wù)器返回,表示待查詢的域名不存在;
問(wèn)題記錄
客戶端查詢域名時(shí),需要向服務(wù)端發(fā)送請(qǐng)求報(bào)文;待查詢域名作為問(wèn)題記錄,保存在問(wèn)題節(jié)中。
問(wèn)題節(jié)支持保存多條問(wèn)題記錄,記錄條數(shù)則保存在 DNS 頭部中的問(wèn)題記錄數(shù)字段。這意味著,DNS 協(xié)議單個(gè)請(qǐng)求能夠同時(shí)查詢多個(gè)域名,雖然通常只查詢一個(gè)。
一個(gè)問(wèn)題記錄由 3 個(gè)字段組成:
- 待查詢域名( Name ),這個(gè)字段長(zhǎng)度不固定,由具體域名決定;
- 查詢類型( Type ),域名除了關(guān)聯(lián) IP 地址,還可以關(guān)聯(lián)其他信息,常見(jiàn)類型包括(下節(jié)詳細(xì)介紹):
- 1 表示 A 記錄,即 IP 地址;
- 28 表示 AAAA 記錄,即 IPv6 地址;
- etc
- 類 ( Class )通常為 1 ,表示 TCP/IP 互聯(lián)網(wǎng)地址;
最后,我們回過(guò)頭來(lái)考察域名字段,它的長(zhǎng)度是不固定的。域名按 . 切分成若干部分,再依次保存。每個(gè)部分由一個(gè)前導(dǎo)計(jì)數(shù)字節(jié)開(kāi)頭,記錄當(dāng)前部分的字符數(shù)。
以域名 fasionchan.com. 為例,以 . 切分成 3 個(gè)部分,fasionchan 、com 以及空字符串 。請(qǐng)注意,空字符串 代表根域。因此,待查詢域名字段依次為:
- 一個(gè)前導(dǎo)字節(jié)保存整數(shù) 10 ,然后 10 個(gè)字節(jié)保存 fasionchan 部分(二級(jí)域);
- 一個(gè)前導(dǎo)字節(jié)保存整數(shù) 3 ,然后 3 個(gè)字節(jié)保存 com 部分(一級(jí)域);
- 一個(gè)前導(dǎo)字節(jié)保存整數(shù) 0 ,然后 0 個(gè)字節(jié)保存 部分(根域);
由此可見(jiàn),每一級(jí)域名的長(zhǎng)度理論上可以支持多達(dá) 255 個(gè)字符。
查詢類型 | 名稱代碼 | 描述 |
---|---|---|
1 | A | IPv4地址 |
2 | NS | 名稱服務(wù)器 |
5 | CNAME | 規(guī)范名稱 |
15 | MX | 電子郵件交互 |
16 | TXT | 文本信息 |
28 | AAAA | IPv6地址 |
查詢類型這里先不展開(kāi),下一小節(jié)會(huì)詳細(xì)介紹。
資源記錄
服務(wù)端處理查詢請(qǐng)求后,需要向客戶端發(fā)送應(yīng)答報(bào)文;域名查詢結(jié)果作為資源記錄,保存在答案以及其后兩節(jié)中。
答案節(jié)、授權(quán)信息節(jié)和附加信息節(jié)均由一條或多條資源記錄組成,記錄數(shù)目保存在頭部中的對(duì)應(yīng)字段,不再贅述。
資源記錄結(jié)構(gòu)和問(wèn)題記錄非常相似,它總共有 6 個(gè)字段,前 3 個(gè)和問(wèn)題記錄完全一樣:
- 被查詢域名( Name ),與問(wèn)題記錄相同;
- 查詢類型( Type ),與問(wèn)題記錄相同;
- 類 ( Class ),與問(wèn)題記錄相同;
- 有效期( TTL ),域名記錄一般不會(huì)頻繁改動(dòng),所以在有效期內(nèi)可以將結(jié)果緩存起來(lái),降低請(qǐng)求頻率;
- 數(shù)據(jù)長(zhǎng)度( Resource Data Length ),即查詢結(jié)果的長(zhǎng)度;
- 數(shù)據(jù)( Resource Data ),即查詢結(jié)果;
如果查詢類型是 A 記錄,那查詢結(jié)果就是一個(gè) IP 地址,保存于資源記錄中的數(shù)據(jù)字段;而數(shù)據(jù)長(zhǎng)度字段值為 4 ,因?yàn)?IP 地址的長(zhǎng)度為 32 位,折合 4 字節(jié)。
報(bào)文實(shí)例
我們以 test.fasionchan.com 這個(gè)域名為例,來(lái)講解 DNS 查詢請(qǐng)求報(bào)文和應(yīng)答報(bào)文。
執(zhí)行 dig 命令即可查詢?cè)撚蛎?/p>
- dig test.fasionchan.com
我們對(duì)查詢 test.fasionchan.com 的一次通信過(guò)程進(jìn)行抓包,結(jié)果保存在 Github 上,以供參考。童鞋們可以將抓包結(jié)果下載到本地,并用 WireShark 打開(kāi),并結(jié)合講解進(jìn)行分析。
請(qǐng)求報(bào)文
抓包結(jié)果請(qǐng)求報(bào)文只有頭部、問(wèn)題節(jié)和附加節(jié),圖解假設(shè)沒(méi)有附加節(jié)。
先看頭部,問(wèn)題記錄數(shù)為 1 ,其他記錄數(shù)都是 0 。這意味著,請(qǐng)求報(bào)文只有問(wèn)題節(jié),而且問(wèn)題節(jié)中只有一條問(wèn)題記錄,只查詢一個(gè)域名。頭部中的標(biāo)志位分別如下:
- QR=0 ,表示該報(bào)文是一個(gè)請(qǐng)求報(bào)文;
- 操作碼為 0 ,表示這個(gè) DNS 請(qǐng)求是一個(gè)標(biāo)準(zhǔn)請(qǐng)求;
- TC=0 ,表示請(qǐng)求報(bào)文沒(méi)有被截短;
- RD=1 ,表示客戶端希望服務(wù)器可以執(zhí)行遞歸查詢;
問(wèn)題記錄我們已經(jīng)很熟悉了,不再贅述:
- Type=1 ,表示客戶端希望查詢 A 記錄,即與域名關(guān)聯(lián)的 IP 地址;
- Class=1 ,代表 TCP/IP 互聯(lián)網(wǎng);
應(yīng)答報(bào)文
抓包結(jié)果應(yīng)答報(bào)文只有頭部、問(wèn)題節(jié)和答案節(jié)。其中,問(wèn)題節(jié)中的問(wèn)題記錄與請(qǐng)求報(bào)文一樣,圖中就不展開(kāi)了。
先看頭部,問(wèn)題記錄數(shù)為 1 ,答案記錄數(shù)也是 1 ,其他記錄數(shù)都是 0 。這意味著,應(yīng)答報(bào)文只有問(wèn)題節(jié)和答案節(jié),而且它們各自只有一條記錄。頭部中的標(biāo)志位分別如下:
- QR=1 ,表示該報(bào)文是一個(gè)應(yīng)答報(bào)文;
- 操作碼為 0 ,表示這個(gè) DNS 請(qǐng)求是一個(gè)標(biāo)準(zhǔn)請(qǐng)求;
- AA=0 ,表示結(jié)果不是由域名的權(quán)威服務(wù)器返回的,因?yàn)椴樵儗?duì)象是本地的 DNS 緩存服務(wù)器(如果是向權(quán)威服務(wù)器發(fā)起查詢,它返回的應(yīng)答報(bào)文 AA=1 );
- TC=0 ,表示應(yīng)答報(bào)文沒(méi)有被截短;
- RD=1 ,與請(qǐng)求報(bào)文保持一致,略;
- RA=1 ,表示服務(wù)端支持遞歸查詢;
- 響應(yīng)碼為 0 ,表示查詢成功,沒(méi)有出錯(cuò);
答案節(jié)中的資源記錄就是查詢結(jié)果,前 3 個(gè)字段與問(wèn)題記錄一樣,不再贅述。
TTL 字段是一個(gè)整數(shù),表示有效期,單位是秒。例子中的查詢結(jié)果,有效期是752秒,即 12 分 32 秒。也就是說(shuō),查詢結(jié)果從現(xiàn)在開(kāi)始算,12分32秒內(nèi)均有效,無(wú)須重新請(qǐng)求。
查詢結(jié)果是一個(gè) IP 地址,長(zhǎng)度為 4 個(gè)字節(jié),保存在資源數(shù)據(jù)字段中。
域名壓縮
我們注意到,應(yīng)答報(bào)文中,會(huì)將請(qǐng)求報(bào)文中的問(wèn)題記錄原樣返回。由于問(wèn)題記錄和資源記錄都會(huì)保存域名,這意味著域名會(huì)被重復(fù)保存,而報(bào)文尺寸是有限的!
為了節(jié)約報(bào)文空間,有必要解決域名重復(fù)保存問(wèn)題,這也是所謂的信息壓縮。具體做法如下:
域名在報(bào)文中第二次出現(xiàn)時(shí),只用兩個(gè)字節(jié)來(lái)保存。第一個(gè)字節(jié)最高兩位都是 1 ,余下部分和第二個(gè)字節(jié)組合在一起,表示域名第一次出現(xiàn)時(shí)在報(bào)文中的偏移量。通過(guò)這個(gè)偏移量,就可以找到對(duì)應(yīng)的域名。
由此一來(lái),原來(lái)需要 21 個(gè)字節(jié)來(lái)保存的域名,現(xiàn)在只需區(qū)區(qū)兩個(gè)字節(jié)即可搞定,數(shù)據(jù)量大大降低!
實(shí)際上,域名壓縮機(jī)制還可以針對(duì)域名的某個(gè)部分進(jìn)行。舉個(gè)例子,假設(shè)一個(gè)請(qǐng)求報(bào)文同時(shí)查詢兩個(gè)域名:
- fasionchan.com
- test.fasionchan.com
請(qǐng)求報(bào)文中包含兩個(gè)問(wèn)題記錄,分別對(duì)應(yīng)域名 fasionchan.com 和 test.fasionchan.com 。這兩個(gè)域名都有一個(gè)公共后綴 fasionchan.com ,無(wú)須重復(fù)保存。
如上圖,第二個(gè)域名只需保存 test 部分,然后接兩個(gè)字節(jié)特殊的壓縮字節(jié),指向第一個(gè)問(wèn)題記錄中的 fasionchan.com 。如果兩條問(wèn)題記錄順序顛倒,結(jié)果也是類似的,留待童鞋們自行思考。