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