上一篇文章《DIY一個DNS查詢器:了解DNS協議》中講了DNS查詢協議的原理和數據結構。經過兩個星期的開發,完成了該查詢器的編寫。期間也遇到了一些問題,如:
1資源記錄(Resource Record)中的RDData內容的格式。
2關於壓縮編碼的指針問題。
3代碼冗余結構不清晰。
尤其是壓縮編碼的問題,困擾了我很久,找了很多中文資料,都說到當長度的值為“192”的時候為指針,下一字節的內容即偏移的位置,但是在過程中卻發現存在該值為“193”的情況,一直不解了好久。這裡我給解釋下:
假設第13字節內容為:05-6c-69-78-69-6e-02-6d-6e
翻譯為“5-l-i-x-i-n-2-m-e”
而其後的某個地方,同樣出現了lixin.me的字符串,那麼就會使用指針編碼:"c0-0c”。
c0十進制是192,代表下面的內容是一個指針,0c是個地址,指向了data[12]的位置。
而為什麼會出現193呢,也好理解,因為用一個字節的內容來表示偏移,頂多也只能偏移256個字節,那麼假設udp包有500個字節長,而要指向第300字節就無能為力了。因此實際上的偏移量不是由oc這個字節內容決定的,正確的偏移量是:(Cn-C0)*256+0c。
如果值為193,下一字節為1,那麼具體偏移就是 (193-192)*256+1 。
還有一點要注意到就是可變長度的Name字段以什麼為結尾。有3中結尾方式:
1.長度+內容+~+長度0
2. 偏移標識+偏移量
3.長度+內容+~+偏移標識+偏移量
能成功解決該問題,主要還是靠資料,雖然知道rfc1034和rfc1035裡面一定有我要的內容,可惜外文比較難懂,一直看不下去,通過搜索得到的中文和少量外文資料也沒說清楚。最後還得感謝《TCP-IP詳解卷一:協議》的第14章Dns協議的介紹,雖然只有短短的17頁,但還是幫我解決了問題。所以在同樣搞協議的同學,不妨弄本先去瞧瞧,或者遇到問題也可以先去看看。
現在來說說這個程序了。
我按dns協議的結構把項目分成MyDnsHeader.cs、MyDnsQuestion.cs、MyDnsRecord.cs 這樣的3個大結構。
發送dns請求時只需要構造MyDnsHeader和MyDnsQuestion結構,然後通過GetBytes()函數得到構造好的字節數組,然後通過udp發送出去。然後接受來自服務器的響應,將接收到的字節數組通過Parse(byte[] recvData)方法讓3個結構去解析,最後通過這些結構的屬性字段獲取相應的查詢信息。
其中的資源記錄,目前能分析A記錄、SOA記錄、TXT記錄、CNAME記錄、MX記錄、NS記錄。
示例代碼:
MyDns mydns = new MyDns();//
//想8.8.8.8域名服務器查詢lixin.me這個域名的a記錄,
if (!mydns.Search(“lixin.me”QueryType.A, “8.8.8.8”,null ))
{
//如果服務器返回錯誤信息,則顯示錯誤的內容
MessageBox.Show(mydns.header.RCODE.ToString());
return;
}
txtInfo.Clear();
txtInfo.AppendText (string.Format ("回復記錄數:{0}\n",mydns.header.ANCOUNT) );
txtInfo.AppendText(string.Format("回復額外記錄數:{0}\n", mydns.header.ARCOUNT ));
txtInfo.AppendText(string.Format("回復權威記錄數:{0}", mydns.header.NSCOUNT ));
txtContent.Clear();
foreach (MyDnsRecord item in mydns.record.Records)
{
//循環資源記錄,並打印出來。
txtContent.AppendText(item.QType.ToString() + " " + item.RDDate.ToString()+"\n");
}
界面截圖:
代碼下載及浏覽:
我把代碼放在了CodePlex.com 上面了。地址為:http://mydnspackage.codeplex.com/
歡迎園友測試。如果發現錯誤,請告知我