案例吐個槽,命苦啊,要自己動手解包。
另外,這里的內容是半路找來的,如果有沖突,自行翻閱rfc1035。我還沒校正過。
The Structure
如下圖:
所有的DNS message都包含了下面這幾個部分:
1、HEADER。基本上HEADER都是些概述啥的。
2、QUESTION。描述你的請求,比如,www.qkdemo.com的IP是多少。
3、Answer。描述你的應答,比如,www.qkdemo.com的IP是1.2.3.4。
4、不知道干嗎的。
5、繼續不知道。
DNS HEADER部分
首先,需要明確一下DNS的message的結構:
如上圖所示,不管是DNS的query還是response,DNS message都分為這么幾個部分:
1、上圖每行表示一個16bit的數,也就是兩個byte。
2、ID是這個message的編號,response會把query的id給copy過來,好讓請求的program識別response是哪一個。一個16bit的隨機數。
3、ID之后的flag是一個組合的雙字節,包括了:
- QA.1:message的類型,0是query,1是response
- Opcode.4:query的類型。0000表示是一個標准的query。
- AA.1:沒看懂是干嘛的,但是貌似對我沒用。設0是沒問題的。
- TC.1:繼續設0。
- RD.1:Recursion Desired,query是否請求遞歸,1表示請求,0則不需要。繼續設0是可以的,bonjour里面是這么干的。
- RA.1:Recursion Available,遞歸是否可用。和上面這個是有關系的,但是是response里面帶來的。
- Z.3:保留字段。所有的保留字段都是渣。不過,反正不是標准請求的,你可以自己愛怎么玩怎么玩。
- RCODE.4:這個是response必須要要帶的一部分。0表示沒有錯誤,1表示query格式錯了,2表示server自己出問題了,3表示name沒找到,4表示query的類型server沒法處理,5表示拒絕進行解析。
- 所以,我的心得是,對於一個標准的query,設0x0000就好了;對於一個標准的response,設0x8000也沒問題。
4、我帶了幾個請求。設成1表示只有一個。恩,我們就設1就好了。
5、我帶了幾個answer。0表示我帶了0個解答。
6、服務器上有幾條記錄?我沒搞懂。但是,可以忽略這個字段的值。
7、有幾條額外的記錄?繼續搞不懂。好吧,繼續忽略。
Question描述域
Question分為三個部分,如圖:
具體講來:
1、QNAME:這里是要請求的域名的描述。以上面www.qkdemo.com為例,描述為3www6qkdemo3com00;如果是qkdemo.local,則為6qkdemo5local00。
2、QTYPE:請求的類型。A記錄是0x0001。Bonjour里面query的類型是0x00ff,wireshark解釋是any。
3、QCLASS:請求的類。Bonjour里面用的0x8001,而且wireshark是分成兩部分來解讀的,大概是1+7,前面是query,后面是class。0x0001表示internet address。
Answer域
Answer域稍微復雜一點,有6個域:
1、NAME,和上面QNAME的描述方式是一樣的。
2、TYPE,和上面QTYPE含義是一樣的。局域網里胡亂收來的一個包把這個域設為0x00,不知道搞飛機。
3、同QCLASS。
4、TTL,這個結果灰白cache多久。
5、下面的RDATA有多長。
6、這個域的描述方法根據class不同。比如,如果class是一個A記錄,表示地址,那么它就是一個IP地址;如果是0x000f,表示是一個郵件服務器,那么,它包含了preference和cname兩個部分。
DNS message的壓縮
作為互聯網使用最頻繁的機制之一,所占我們網絡日常開銷的比重是相當相當大滴!所以,需要考慮怎么來減小message的大小。這里的壓縮主要是減少重復信息。
使用壓縮的主要有三種內容:QNAME,NAME,RDATA。
壓縮的方式也很簡單:保存一個指針,指向這一數據第一次出現的地址。比如,假設我們某query攜帶三個Question,而有兩個Question的QNAME是一樣的,那么第二個QNAME的位置,就會放着一個指向第一個QNAME的指針。
該指針占了16bit的空間,而且,前兩個bit必須是11。因為域名的每個部分必須在63 Byte以內,所以,域名每個部分長度(label,www.qkdemo.com有三個部分,就有三個label)寫作0x0000格式時,它的最高兩位不會是11,這樣,pointer和label就被區別開來。至於offset,offset為零,代表了header中ID部分的第一個Byte,后面的一次計算。
另外需要注意的是,如果我們在RDATA中使用了壓縮,那么,我們在計算RDATA長度時,使用的將是2,即一個Pointer的長度,而不是域名本來的長度。
舉例,我們先假設忽略message里面的其它域,假設我們先后使用了F.ISI.ARPA,FOO.F.ISI.ARPA和ARPA三個域名,那么,我們可以將message壓縮:
如上,F.ISI.ARPA是從20位置開始,那么FOO.F.ISI.ARPA就可以表示為3F 00 E0(E0是0b1110 0000),而ARPA則可以表示為E6。需要注意的是,一個比較容易忽視的問題,offset是從0開始的,所以清點byte的個數后記得減一。
一個response的例子
暫缺,重啟后有心情再添加。
其它
multiCast DNS的文檔是rfc6762,可查閱:http://tools.ietf.org/html/rfc6762