resolver,DNS 解析庫算法
/etc/resolv.conf;resolver 的配置文件,resolver 會讀取該文件初始化解析器的行爲.數組
文件格式;resolv.conf 文件是由一組關鍵詞及其值組成,若一行以";"/"#"開頭,則代表該行是註釋行;不然該行存放着一個關鍵詞及其對應的取值;以下文件包含了"nameserver",以及"options"兩個關鍵詞;其中"127.0.1.1"爲關鍵詞"nameserver"的取值;"ndots:1"爲關鍵詞"options"的取值.服務器
; helloworld,註釋行. nameserver 127.0.1.1 options ndots:1
nameserver;參考 man 5 resolv.conf;關鍵是 resolver 在有多個 DNS 服務器時的使用算法:dom
// resolver 對含有多個 DNS Server 時使用的算法,假設當前有 A,B 2 個 DNS 服務器 for( i=0;i<最大重試次數;++i) do{ ret = use(A); // 使用 A. if( ret == SUCCESS) // 若 A 成功返回結果,則停止 break; ret = use(B); // 不然使用 B 來解析請求. if( ret == SUCCESS) break; // 若是 B 也失敗,則再來一次. }while(0); // 我最初的想法是,當 A 超時時,再對 A 發送請求,直至達到最大重試次數後,再使用 B.
這裏只介紹了 man 手冊中不清晰的地方,測試
RES_DEFNAMES,若在 _res.options 中指定該標誌,則若 res_search() 中指定的 dname 中不含有一個'.',則會將 resolv.conf 中 domain 關鍵詞的值追加到在 dname 以後,造成一個徹底限定域名.
spa
RES_DNSRCH,若在 _res.options 中指定該標誌,則 res_search() 會根據 ndots 的值來判斷 dname 是不是一個徹底限定域名(如 dname 中'.'的個數 < ndots,則認爲不是).若 dname 被認爲是一個徹底限定域名,則直接查詢 dname;不然若 dnmae 被認爲不是徹底限定域名,則會依次在 search 關鍵詞中指定的域表下搜索 dname(若返回"沒有域名"的錯誤,則使用下一個域測試);以下:.net
// resolv.conf 配置 search xxx.yyy.net yyy.net net options ndots:2 res_search("www.xxx",,,); // 則此時會依次查詢
int dn_comp(unsigned char *domain, unsigned char *comp_dn,int length, unsigned char **dnptrs, unsigned char **lastdnptr);
domain;待壓縮的域名,命令行
comp_dn,length;[comp_dn,comp_dn+length) 存放 domain 壓縮後的結果.指針
[dnsptr,lastdnptr) 肯定了 u_char* 類型的指針數組,其中 dnptrs 指向着數組的第一個元素,lastdnptr-dnptrs 肯定了數組的長度.這個數組存放着在當前 DNS 消息中,已經壓縮的域名的地址.其中第一個元素(dnptrs[0])指向着 DNS 消息的首部,以 NULLL 來表示有效元素的結束.在調用 dn_comp() 壓縮域名以後,會將 comp_dn 也追加到數組 dnptrs 中.code
若 dnptrs 爲 0,則不對 domain 進行壓縮,即直接將其存放 [comp_dn,comp_dn+length) 中;
若 lastdnptr 不爲 0,則不更新數組 dnptrs,即不將 comp_dn 追加到當前數組中.
void printDnptrs(u_char **dnptrs,int dnptrsSize){ for(int i=0;i<dnptrsSize;++i) printf("%p ",dnptrs[i]); putchar('\n'); } /// 依據 16 進制(不帶 0x 前綴)輸出 [src,src+size) 中每個字節的值 void dump(void *src,size_t size){ unsigned char *s = (unsigned char*)src; for(size_t i = 0;i<size;++i) printf("%02hhx ",s[i]); putchar('\n'); } int main(int argc,char *argv[]){ const int bufSize = 512; u_char buf[bufSize] = {0}; u_char *dnsMsgBuf = buf; u_char * const dnsMsgBufEnd = dnsMsgBuf+bufSize; // [dnsMsgBuf,dnsMsgBufEnd) 存放 dns 消息. // 如今但願將用戶在命令行參數中指定的域名都以壓縮存儲的形式存放在 dns 消息中. // dnptrsSize 取值爲 7,去除 dnptrs[0],以及表示有效信息結束的 NULL,剩下 5 個元素用於 // 存放壓縮過的域名地址,這代表在壓縮第 6 個域名時會根據 dnptrs 中存放的地址獲得前 5 個 // 域名壓縮信息,從而在壓縮第 6 個域名時可使用這些信息;但在壓縮以後因爲 dnptrs 沒有空間, // 因此第 6 個域名壓縮以後的地址不能存放在 dnptrs 中,即在壓縮第 7 個域名時使用的還是前 // 5 個域名壓縮後的信息. const int dnptrsSize = 7; u_char *dnptrs[dnptrsSize] = {dnsMsgBuf,0}; // 在調用 dn_comp 以前務忘初始化 u_char ** const dnptrsEnd = dnptrs + dnptrsSize; int ret = 0; u_char *nextDomainPtr = dnsMsgBuf+12; // 第一個域名的存放地址,12 爲 DNS 首部長. for(int i = 1; i<argc; ++i){ printf("%d:壓縮;域名:%s;",i,argv[i]); ret = dn_comp(argv[i],nextDomainPtr,dnsMsgBufEnd-nextDomainPtr, dnptrs,dnptrsEnd); if(ret < 0){ printf("err:%m\n"); break; } printf("壓縮後長度:%d\n",ret); nextDomainPtr += ret; nextDomainPtr += 4; printf("%d:",i); printDnptrs(dnptrs,dnptrsSize); } if(ret < 0) return 1; puts("構造好的 DNS 報文:"); // dump(dnsMsgBuf,dnsMsgBufEnd-dnsMsgBuf); write(2,dnsMsgBuf,dnsMsgBufEnd-dnsMsgBuf); return 0; } // 運行: $ ./Test www.360.com www.qikoo.com www.haosou.com 2>dnsmsg.txt 1:壓縮;域名:www.360.com;壓縮後長度:13 1:0x7fffa45e8080 0x7fffa45e808c (nil) (nil) (nil) (nil) (nil) 2:壓縮;域名:www.qikoo.com;壓縮後長度:12 2:0x7fffa45e8080 0x7fffa45e808c 0x7fffa45e809d (nil) (nil) (nil) (nil) 3:壓縮;域名:www.haosou.com;壓縮後長度:13 3:0x7fffa45e8080 0x7fffa45e808c 0x7fffa45e809d 0x7fffa45e80ad (nil) (nil) (nil) 構造好的 DNS 報文: $ od -A d -t x1z dnsmsg.txt 0000000 00 00 00 00 00 00 00 00 00 00 00 00 03 77 77 77 >.............www< 0000016 03 33 36 30 03 63 6f 6d 00 00 00 00 00 03 77 77 >.360.com......ww< 0000032 77 05 71 69 6b 6f 6f c0 14 00 00 00 00 03 77 77 >w.qikoo.......ww< 0000048 77 06 68 61 6f 73 6f 75 c0 14 00 00 00 00 00 00 >w.haosou........< 0000064 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 >................< * 0000512 // 能夠看到 與 中的".com"都是使用 的壓縮.
int dn_expand(unsigned char *msg, unsigned char *eomorig, unsigned char *comp_dn, char *exp_dn,int length);
msg,eomorig;[msg,eomorig) 中存放着一條完整的 DNS 報文.在 man 手冊中,只說明 msg 指向着報文的開始,而沒有說明 eomorig 的功能,通過猜想與實現,eomorig 的全稱應該爲 end of msg origin;
// 如今[dnsMsgBuf,dnsMsgBufEnd)存放着 DNS 報文;並且數組 dnptrs 中存放着域名的地址. // 此時調用 dn_expand() 解壓出這些域名. char domainBuf[256]; for(int i=1;dnptrs[i]!=0;++i){ ret = dn_expand(dnsMsgBuf,dnsMsgBufEnd, dnptrs[i],domainBuf,sizeof(domainBuf)); if(ret < 0){ printf("err:%m\n"); break; } printf("%d:%s\n",i,domainBuf); } // 該段代碼追加在上面代碼"write(2,,)"以後,return 0 以前. // 運行: 1:www.360.com 2:www.qikoo.com 3:www.haosou.com