學(xué)習(xí)DNS協(xié)議
時(shí)間:2023-02-02 15:40:01 | 來(lái)源:建站知識(shí)
時(shí)間:2023-02-02 15:40:01 來(lái)源:建站知識(shí)
DNS
DNS是一個(gè)查詢(xún)域名的IP的協(xié)議,訪問(wèn)服務(wù)器的之前,需要解析域名的地址。因?yàn)镮P一串?dāng)?shù)字不好記憶。
DNS協(xié)議結(jié)構(gòu)
- TransID的作用是標(biāo)記請(qǐng)求和響應(yīng)。當(dāng)結(jié)果回來(lái)的時(shí)候,讀取TransID就可以知道這個(gè)響應(yīng)是那個(gè)請(qǐng)求的。
- Parameter 是標(biāo)記這個(gè)數(shù)據(jù)是請(qǐng)求,還是響應(yīng)。是否標(biāo)準(zhǔn)查詢(xún),是否截取,是否遞歸查詢(xún),是否接受不認(rèn)證的響應(yīng)。一般就是控制請(qǐng)求和響應(yīng)的行為
- Number of Questions 是響應(yīng)的時(shí)候設(shè)置,標(biāo)記多少個(gè)Question 段
- Number of Answers 是響應(yīng)的時(shí)候設(shè)置,標(biāo)記多少個(gè)Anserr段
- Number of Authority是響應(yīng)的時(shí)候設(shè)置,標(biāo)記多少個(gè)Authority段
- Number of Additional是響應(yīng)的時(shí)候設(shè)置,標(biāo)記多少個(gè)Additional段
- Query Section。請(qǐng)求的時(shí)候設(shè)置
1.域名數(shù)據(jù)
2.域名類(lèi)型Type。一般是A(IPv4是A,IPv6是AAA)
3.Class類(lèi)型,默認(rèn)用INET
發(fā)送DNS時(shí)數(shù)據(jù)包結(jié)構(gòu)的大小
我們只看左邊的Example1,Example2一樣UDP頭是8 Byte,整個(gè)UDP是39 Byte。所以DNS協(xié)議占用31 Byte。
ID占用2Byte,F(xiàn)lags都是占用2Byte。域名的屬性Type和Class都是2Byte的大小,而域名的字符內(nèi)容數(shù)據(jù)不是固定的。
上面說(shuō)是Example1的域名數(shù)據(jù)是15Byte。但是我抓包看實(shí)現(xiàn)的時(shí)候發(fā)現(xiàn)dns-packet
庫(kù)發(fā)出的
域名數(shù)據(jù)的字符前后都會(huì)有1個(gè)空數(shù)據(jù)的字節(jié),也就是2Byte是空數(shù)據(jù)的。而直接使用dig查詢(xún)時(shí)沒(méi)有的為什么會(huì)增加1-2個(gè)字節(jié)呢?是不是因?yàn)閾?dān)心域名計(jì)算字節(jié)實(shí)際大小比計(jì)算的結(jié)果可能要大,所以估計(jì)增加空間,保證成功呢?可是為什么字符串之前也要留空一個(gè)數(shù)據(jù)呢?還是存在其它問(wèn)題需要這樣處理的呢?暫時(shí)想不明白Node的dns-packet
如果是服務(wù)器發(fā)起DNS查詢(xún)請(qǐng)求,可以使用dns-packet庫(kù)
瀏覽器二進(jìn)制數(shù)據(jù)傳輸
因?yàn)闉g覽器不能使用Node的Buffeer,只能如下組裝數(shù)據(jù)。
TextEncoder是把域名字符串轉(zhuǎn)換為二進(jìn)制數(shù)據(jù)
ArrayBuffer和Uint16Array都是字節(jié)數(shù)組容器
DataView是操作字節(jié)數(shù)組容器
let utf8Encode = new TextEncoder(); function createDNSPacket(domain) { var domainUint8array = new TextEncoder("utf-8").encode(domain); var domainSize = domainUint8array.byteLength + 2; console.log(domainSize); const headerBuffer = new ArrayBuffer(12); const header = new DataView(headerBuffer); header.setUint16(0, 1) header.setUint16(2, 100) header.setUint16(4, 1) header.setUint16(6, 0) header.setUint16(8, 0) header.setUint16(10, 0) const questionBuffer = new ArrayBuffer(4); const question = new DataView(questionBuffer); question.setUint16(0, 1) question.setUint16(2, 1) var concateBuff = new Uint8Array(domainUint8array.byteLength + headerBuffer.byteLength + questionBuffer.byteLength + 2); concateBuff.set(new Uint8Array(headerBuffer), 0); concateBuff.set(new Uint16Array(1), headerBuffer.byteLength); // 仿dns-packet實(shí)現(xiàn) concateBuff.set(domainUint8array, headerBuffer.byteLength + 1); concateBuff.set(new Uint16Array(1), headerBuffer.byteLength + 2); // 仿dns-packet實(shí)現(xiàn) concateBuff.set(new Uint8Array(questionBuffer), headerBuffer.byteLength + domainUint8array.byteLength + 2); return concateBuff; }
注意的坑
這個(gè)查詢(xún)只能是一個(gè)Demo,是基于Chrome的UDP插件Demo來(lái)實(shí)現(xiàn)的。
Chrome無(wú)法直接發(fā)出UDP包,必須經(jīng)過(guò)socket連接到服務(wù)端后,才能發(fā)UDP。
點(diǎn)擊下載demo代碼,服務(wù)的代碼在Server文件夾下
參考
http://www.firewall.cx/networking-topics/protocols/domain-name-system-dns/160-protocols-dns-query.html
關(guān)鍵詞:協(xié)議,學(xué)習(xí)