每當我們在浏覽器上敲入任何一個域名訪問某個網站的時候,我們都要使用Dns協議進行一次”域名:IP”的查詢;作為命令行使用者,與dns有關用的最多的就是Nslookup 命令吧;作為程序員,以c#程序員為例,要得到一個域名的ip大概也是這麼一行“System.Net.Dns.GetHostByName(string UriHostName)”。
在這簡單使用的背面,很少人會真了解其協議的規則,這也許就是高度封裝給程序員帶來的一點麻煩吧。下面來了解一下dns協議的內容。
整個dns分為5個部分,分別為Header、Question、Answer、Authority、Additional。
其中頭部的大小是固定的為12字節。這5個部分不是全部都是必須的,在向服務器發送查詢請求的時候,只需要前2個。回復的時候也不一定包含5個(按查詢的內容和返回的信息而定)。
header頭部分是必須的,無論發送查詢或者返回結果都需要該部分,且長度一定,為12字節。結果如下圖
ID:長度為16位,是一個用戶發送查詢的時候定義的隨機數,當服務器返回結果的時候,返回包的ID與用戶發送的一致。
QR:長度1位,值0是請求,1是應答。
Opcode:長度4位,值0是標准查詢,1是反向查詢,2死服務器狀態查詢。
AA:長度1位,授權應答(Authoritative Answer) - 這個比特位在應答的時候才有意義,指出給出應答的服務器是查詢域名的授權解析服務器。
TC:長度1位,截斷(TrunCation) - 用來指出報文比允許的長度還要長,導致被截斷。
RD:長度1位,期望遞歸(Recursion Desired) - 這個比特位被請求設置,應答的時候使用的相同的值返回。如果設置了RD,就建議域名服務器進行遞歸解析,遞歸查詢的支持是可選的。
RA:長度1位,支持遞歸(Recursion Available) - 這個比特位在應答中設置或取消,用來代表服務器是否支持遞歸查詢。
Z:長度3位,保留值,值為0.
RCode:長度4位,應答碼,類似http的stateCode一樣,值0沒有錯誤、1格式錯誤、2服務器錯誤、3名字錯誤、4服務器不支持、5拒絕。
QDCount:長度16位,報文請求段中的問題記錄數。
ANCount:長度16位,報文回答段中的回答記錄數。
NSCOUNT :長度16位,報文授權段中的授權記錄數。
ARCOUNT :長度16位,報文附加段中的附加記錄數。
這部分的內容是你要查詢的內容。也是必須的。
QName:是你要查詢的域名,屬於不定長字段。他的格式是“長度(1字節)+N字節內容(N由前面的長度定義)+~~~+長度0。以一個長度單位N為開始,然後連續的N字節為其內容,然後又是一個N2長度的一字節,然後後面又是N2個字節內容,直到遇到長度為0的長度標記。
QType:長度16位,表示查詢類型。取值大概如下:
enum QueryType //查詢的資源記錄類型。
{
A=0x01, //指定計算機 IP 地址。
NS=0x02, //指定用於命名區域的 DNS 名稱服務器。
MD=0x03, //指定郵件接收站(此類型已經過時了,使用MX代替)
MF=0x04, //指定郵件中轉站(此類型已經過時了,使用MX代替)
CNAME=0x05, //指定用於別名的規范名稱。
SOA=0x06, //指定用於 DNS 區域的“起始授權機構”。
MB=0x07, //指定郵箱域名。
MG=0x08, //指定郵件組成員。
MR=0x09, //指定郵件重命名域名。
NULL=0x0A, //指定空的資源記錄
WKS=0x0B, //描述已知服務。
PTR=0x0C, //如果查詢是 IP 地址,則指定計算機名;否則指定指向其它信息的指針。
HINFO=0x0D, //指定計算機 CPU 以及操作系統類型。
MINFO=0x0E, //指定郵箱或郵件列表信息。
MX=0x0F, //指定郵件交換器。
TXT=0x10, //指定文本信息。
UINFO=0x64, //指定用戶信息。
UID=0x65, //指定用戶標識符。
GID=0x66, //指定組名的組標識符。
ANY=0xFF //指定所有數據類型。
};
QClass:長度為16位,表示分類。
enum QueryClass //指定信息的協議組。
{
IN=0x01, //指定 Internet 類別。
CSNET=0x02, //指定 CSNET 類別。(已過時)
CHAOS=0x03, //指定 Chaos 類別。
HESIOD=0x04,//指定 MIT Athena Hesiod 類別。
ANY=0xFF //指定任何以前列出的通配符。
};
接下來的3個結構,格式可以說相同。都是如下圖的結構和字段。
Name:回復查詢的域名,不定長。 這裡的名字和Question結構的名字是一樣的,在這裡詳細說一下。
假設name字段的內容如下
05 6c 69 78 69 6e 02 6d 65 0
第一個字節是長度:5,那麼接下來的5個字節都是內容6c 69 78 69 6e ,ascii碼轉過來是“lixin”。然後又是長度2,後面2個字節的內容6d 65 字母為me,然後是長度0,表示結束了。最後還要把兩段文字組合起來中間加點號成lixin.me。
但是,在question結構是這樣,在之後的資源結構中,如果name字段的內容前面有出現了,那麼他就不會再浪費空間去重復記錄,而是指向某個前面出現了name的位置。如:
在question結構中的name字段的內容為lixin.me,即“05 6c 69 78 69 6e 02 6d 65 0”。然後在第3個結構中的answer中,第一個字段name的內容也是lixin.me,那麼他會指向question中的name地址,讓我們去那個地址讀name內容。所以此時answer結構的name字段的內容為:
C0 0C
C0:這時不是表示接下來的內容有多長,而是接下來的內容在偏移量中,
0C:十進制是12的意思,就是偏移12個字節。從頭開始12位,因為Header結構是固定的12字節,所以偏移0C就是到了Question的Name字段,即上面的“05 6c 69 78 69 6e 02 6d 65 0”。
Type:同上QType。
Class:同上QClass。
TTL:生存時間。4字節,指示RDATA中的資源記錄在緩存的生存時間。
RDLength:資源的長度。
RDdata:資源的內容。
在下一篇將講講具體開發一個dns查詢器的方法。先預報一下,程序還沒弄全好,使用的是c#,目前完成了大概70%。
轉載請注明:來自李鑫