亚洲av成人无遮挡网站在线观看,少妇性bbb搡bbb爽爽爽,亚洲av日韩精品久久久久久,兔费看少妇性l交大片免费,无码少妇一区二区三区

  免費(fèi)注冊(cè) 查看新帖 |

Chinaunix

  平臺(tái) 論壇 博客 文庫(kù)
最近訪問(wèn)板塊 發(fā)新帖
查看: 1744 | 回復(fù): 1
打印 上一主題 下一主題

[原創(chuàng)] Linux下的地址解析函數(shù)(res_*)的應(yīng)用實(shí)例 [復(fù)制鏈接]

論壇徽章:
0
跳轉(zhuǎn)到指定樓層
1 [收藏(0)] [報(bào)告]
發(fā)表于 2006-07-20 02:06 |只看該作者 |倒序?yàn)g覽
Linux下的地址解析函數(shù)應(yīng)用實(shí)例

作者: 默難 ( monnand@gmail.com )

0        引言
域名系統(tǒng)(DNS)是一種用于TCP/IP應(yīng)用程序的分布式數(shù)據(jù)庫(kù), 它提供主機(jī)名字和IP地址之間的轉(zhuǎn)換及有關(guān)電子郵件的選路信息.[1] 目前, 它已經(jīng)在全球范圍內(nèi)被廣泛應(yīng)用. 從應(yīng)用的角度上看, 對(duì)DNS的訪問(wèn)是通過(guò)一個(gè)地址解析器(resolver)來(lái)完成的. 本文通過(guò)講解一些常用的地址解析函數(shù), 并利用精簡(jiǎn)后的部分qmail代碼, 讓不熟悉DNS相關(guān)函數(shù)的程序員了解并掌握常用的地址解析函數(shù).

1        概述
DNS查詢中, 最常用的兩類分別是A類查詢(A query)和指針查詢(PTR query). 前者是已知主機(jī)名, 詢問(wèn)IP; 后者是已知IP, 詢問(wèn)主機(jī)名. 對(duì)于這些查詢, 在Unix主機(jī)中可以直接調(diào)用基本DNS函數(shù): gethostbyname(3)和gethostbyaddr(3)來(lái)實(shí)現(xiàn). 但是對(duì)于其他類型的查詢(例如MX查詢), 則沒(méi)有專門的函數(shù)來(lái)負(fù)責(zé)處理. 此時(shí), 程序員不得不依賴地址解析函數(shù)來(lái)親自處理這些問(wèn)題. 這需要對(duì)DNS報(bào)文格式有基本的了解, 這些將在下面幾節(jié)進(jìn)行說(shuō)明. 關(guān)于gethostbyname(3)和gethostbyaddr(3)兩個(gè)函數(shù), 讀者可以查閱自己系統(tǒng)上的man手冊(cè).

2        DNS報(bào)文格式
在對(duì)地址解析函數(shù)講解之前, 有必要先了解一下DNS報(bào)文格式. 之后的幾節(jié)會(huì)頻繁地涉及到本節(jié)所講的內(nèi)容. 如果想對(duì)DNS相關(guān)協(xié)議有更深的了解, 可以閱讀參考文獻(xiàn)[1] [2] [3].

DNS定義了一個(gè)用于查詢和響應(yīng)的報(bào)文格式, 圖1 顯示了這個(gè)報(bào)文的總體格式.

[圖1]

每個(gè)DNS查詢(或響應(yīng))報(bào)文都包含有一個(gè)12字節(jié)長(zhǎng)的首部和四個(gè)變長(zhǎng)的字段組成.

對(duì)于本文來(lái)說(shuō), 首部中主要關(guān)心的是問(wèn)題數(shù)和資源記錄數(shù)兩個(gè)字段. 這兩個(gè)字段分別用于說(shuō)明各自對(duì)應(yīng)的變長(zhǎng)字段中的條目數(shù). 問(wèn)題數(shù)說(shuō)明查詢問(wèn)題字段中的條目數(shù); 資源記錄數(shù)則說(shuō)明回答字段中的條目數(shù). 對(duì)于一個(gè)DNS查詢報(bào)文, 問(wèn)題數(shù)通常是1. 對(duì)于應(yīng)答報(bào)文, 回答數(shù)至少是1.

首部以下是四個(gè)變長(zhǎng)字段, 本文所關(guān)心的是查詢問(wèn)題字段和回答字段.

查詢問(wèn)題字段可以包含多個(gè)查詢問(wèn)題, 每個(gè)問(wèn)題的格式如圖2 所示.

[圖2]

其中, 查詢名一項(xiàng)存儲(chǔ)著要查找的名字. 它長(zhǎng)度可變并以一種特殊的格式存儲(chǔ). 程序可以通過(guò)其中存儲(chǔ)的內(nèi)容確定其長(zhǎng)度. 具體獲得其中存儲(chǔ)內(nèi)容的方法, 將在下一節(jié)中進(jìn)行詳細(xì)講解. 每一個(gè)問(wèn)題有一個(gè)查詢類型, 每個(gè)響應(yīng)(下文中將會(huì)提到)也同樣有一個(gè)類型. 常用的類型有: A類型---表示期望獲得查詢名的IP地址; PTR查詢---表示期望獲得一個(gè)IP地址對(duì)應(yīng)的域名; MX查詢---郵件交換查詢(關(guān)于MX查詢的具體內(nèi)容, 下文會(huì)提到). 查詢類指定了所使用的協(xié)議簇, 通常是1, 表示Internet地址.

回答字段可以包含多個(gè)條目. 每個(gè)回答字段是以一種叫做資源記錄(Resource Record, RR)的格式存儲(chǔ)的. ( 授權(quán)字段和額外信息字段也同樣以資源記錄的格式存儲(chǔ)信息). 資源記錄的格式如圖3 所示.

[圖3]

域名是記錄中資源數(shù)據(jù)對(duì)應(yīng)的名字. 它的格式和前面介紹的查詢名字段格式相同. 類型和類字段和前面介紹的查詢類型, 查詢類字段的功能一樣. 類字段的取值通常是1, 表示Internet地址. 生存時(shí)間字段是客戶程序保留該資源記錄的秒數(shù). 資源數(shù)據(jù)長(zhǎng)度說(shuō)明資源數(shù)據(jù)包含的字節(jié)數(shù). 資源數(shù)據(jù)則根據(jù)類型字段的值有不同的格式. 對(duì)于A類型, 資源數(shù)據(jù)是IP地址. 對(duì)于MX查詢, 資源數(shù)據(jù)是優(yōu)先值和域名, 域名的格式與查詢名字段格式相同(MX記錄的具體內(nèi)容下文會(huì)有介紹).

至此, DNS中用到的報(bào)文格式已經(jīng)基本介紹完. 下一節(jié)中將會(huì)介紹一些常用的地址解析函數(shù). 閱讀下文時(shí), 最好隨時(shí)翻閱本節(jié)所講的內(nèi)容以便于理解.

3        地址解析函數(shù)

除了經(jīng)常用到的gethostbyname(3)和gethostbyaddr(3)函數(shù)以外, Linux(以及其它UNIX/UNIX-like系統(tǒng))還提供了一套用于在底層處理DNS相關(guān)問(wèn)題的函數(shù)(這里所說(shuō)的底層僅是相對(duì)gethostbyname和gethostbyaddr兩個(gè)函數(shù)而言). 這套函數(shù)被稱為地址解析函數(shù)(resolver functions). 用戶可以通過(guò)鍵入man resolver來(lái)了解其中的具體信息. 這里將對(duì)其中常用到的函數(shù)做一個(gè)解釋. 常用的地址解析函數(shù)原型如下:

  1.        #include <netinet/in.h>
  2.        #include <arpa/nameser.h>
  3.        #include <resolv.h>
  4.        extern struct state _res;

  5.        int res_init(void);

  6.        int res_query(const char *dname, int class, int type,
  7.               unsigned char *answer, int anslen);

  8.        int res_search(const char *dname, int class, int type,
  9.               unsigned char *answer, int anslen);

  10.        int dn_expand(unsigned char *msg, unsigned char *eomorig,
  11.               unsigned char *comp_dn, unsigned char *exp_dn,
  12.               int length);
復(fù)制代碼


_res: 這個(gè)結(jié)構(gòu)體用于保存相關(guān)的狀態(tài)信息. 它的定義在<resolv.h>中.

res_init: 讀取配置文件并修改環(huán)境變量:LOCALDOMAIN. 在調(diào)用其他地址解析函數(shù)前通常要先調(diào)用res_init. 如果執(zhí)行成功, 函數(shù)返回0; 否則返回-1.

res_query:  用來(lái)發(fā)出一個(gè)指定類(由參數(shù)class指定)和類型(由參數(shù)type指定)的DNS詢問(wèn). dname是要查詢的主機(jī)名. 返回信息被存儲(chǔ)在answser指向的內(nèi)存區(qū)域中. 信息的長(zhǎng)度不能大于anslen個(gè)字節(jié). 這個(gè)函數(shù)會(huì)創(chuàng)建一個(gè)DNS查詢報(bào)文并把它發(fā)送到指定的DNS服務(wù)器.

res_search: 和res_query的行為類似, 與res_query不同的是, 當(dāng)域名中不包含點(diǎn)時(shí), 會(huì)在域名后面加上默認(rèn)域名; 同時(shí), 支持遞歸查詢(即當(dāng)一個(gè)服務(wù)器沒(méi)有存儲(chǔ)詢問(wèn)的信息時(shí), 會(huì)繼續(xù)向其他服務(wù)器詢問(wèn)). 一般情況下盡量使用res_search. 因?yàn)樗某晒茁蕰?huì)比較大.

res_query和res_search函數(shù)返回值是響應(yīng)報(bào)文的長(zhǎng)度; 如果發(fā)生錯(cuò)誤則返回-1.

dn_expand: 上一節(jié)中已經(jīng)說(shuō)到, DNS報(bào)文中主機(jī)名是以一種特殊格式存儲(chǔ)的. dn_expand函數(shù)則是將這種特殊格式存儲(chǔ)的字符串還原成一般格式. msg參數(shù)值是整個(gè)DNS報(bào)文的首地址; eomorig參數(shù)指向DNS報(bào)文的最后一個(gè)字節(jié)后的一字節(jié), 用于指定報(bào)文的結(jié)束位置; comp_dn參數(shù)指向報(bào)文中需要被還原的主機(jī)名的首地址; 還原后的主機(jī)名被存儲(chǔ)在exp_dn指向的內(nèi)存區(qū)域中, 長(zhǎng)度不大于length個(gè)字節(jié). 函數(shù)返回主機(jī)名在DNS報(bào)文中的長(zhǎng)度(即被還原前的長(zhǎng)度); 如果發(fā)生錯(cuò)誤則返回-1.

需要注意的是, 如果程序中用到了這些地址解析函數(shù), 那么在編譯的時(shí)候需要加上-lresolv選項(xiàng)才能正常編譯.

利用這些地址解析函數(shù), 不僅可以完成A類查詢或PTR查詢, 還可以進(jìn)行其他類型的詢問(wèn). 下一節(jié)將給出利用地址解析函數(shù)進(jìn)行MX查詢的實(shí)例.

4        地址解析函數(shù)應(yīng)用實(shí)例---MX查詢
在發(fā)送電子郵件時(shí), 需要用到MX(Mail eXchange)記錄. 一個(gè)電子郵箱的地址是 "用戶名@域名" 的格式. 當(dāng)要給某個(gè)用戶發(fā)送電子郵件時(shí), 首先需要從這個(gè)用戶的電子郵箱地址中得到域名; 然后向DNS服務(wù)器發(fā)出一個(gè)MX查詢, 詢問(wèn)該域名由哪些服務(wù)器負(fù)責(zé)處理.DNS服務(wù)器會(huì)返回處理該域名的服務(wù)器的主機(jī)名. 每個(gè)主機(jī)名對(duì)應(yīng)一個(gè)16bit的整數(shù)值, 該值稱為優(yōu)先值(preference value), 如果一個(gè)域存在多條MX記錄, 則首先使用優(yōu)先值較小的主機(jī)名. 之后, 就是利用SMTP協(xié)議與相應(yīng)的主機(jī)進(jìn)行連接并發(fā)送郵件.

如果要發(fā)出一個(gè)MX查詢, 可以利用host命令:

  1.         [monnand@monnand-host ~]$ host -t mx gmail.com
  2.         gmail.com mail is handled by 50 gsmtp183.google.com.
  3.         gmail.com mail is handled by 5 gmail-smtp-in.l.google.com.
  4.         gmail.com mail is handled by 10 alt1.gmail-smtp-in.l.google.com.
  5.         gmail.com mail is handled by 10 alt2.gmail-smtp-in.l.google.com.
  6.         gmail.com mail is handled by 50 gsmtp163.google.com.
復(fù)制代碼

-t 選項(xiàng)用于指明查詢類型, -t mx表示發(fā)起一個(gè)MX查詢. 后面的參數(shù)是要查詢的域名(這里以gmail.com為例).
顯示出的是關(guān)于查詢域名的MX記錄. 這里關(guān)于gmail.com的MX記錄共5條, 每條記錄都有相應(yīng)的優(yōu)先值(顯示在主機(jī)名前面), 例如第一條記錄的優(yōu)先值是50.

下面, 我們就利用前面講到的地址解析函數(shù)來(lái)實(shí)現(xiàn)一個(gè)類似功能的程序. 即指定查詢域名, 打印出關(guān)于這個(gè)域名的MX記錄. 該程序的代碼是從qmail的代碼中精簡(jiǎn)出來(lái)的, 其中去掉了一些錯(cuò)誤檢測(cè), 并修改了與qmail其他部分相關(guān)聯(lián)的代碼, 使整個(gè)程序能夠獨(dú)立出來(lái)(當(dāng)然, 因?yàn)槿サ袅撕芏噱e(cuò)誤檢測(cè), 程序也失去了原有的健壯性和安全性). 但是整體的思路基本沒(méi)有大的改動(dòng). 有興趣的讀者可以自己閱讀qmail的源代碼. 相信會(huì)有更多的收獲.

整個(gè)程序被寫到了兩個(gè)文件中, 分別名為dns.h和dns.c.

以下是dns.h中的內(nèi)容:

  1. 1        #ifndef DNS_H
  2. 2        #define        DNS_H
  3. 3         
  4. 4        #define        DNS_MSG_END        -2
  5. 5         
  6. 6        #define        dns_mx_query(str)        dns_resolve((str),T_MX)
  7. 7        #define        dns_mx_expand()                dns_findmx(T_MX)
  8. 8         
  9. 9        #define        foreach_mxrr(p,dn)        while(dns_mx_expand()!=DNS_MSG_END        \
  10. 10                                                &&(!dns_get_mxrr(&p,dn,MAXDNAME)))
  11. 11         
  12. 12        void dns_init(void);
  13. 13        int dns_get_mxrr(unsigned short *,unsigned char *,unsigned int);
  14. 14        int dns_resolve(char *,int);
  15. 15        int dns_findmx(int);
  16. 16         
  17. 17        #endif /* #ifndef MONNAND_DNS_H */
復(fù)制代碼

該文件中聲明了4個(gè)函數(shù). 為了便于操作, 定義了三個(gè)宏. 關(guān)于其中具體的用法, 之后會(huì)有介紹. 下面給出dns.c中的源代碼:

  1. 1        #include <stdio.h>
  2. 2        #include <stdlib.h>
  3. 3        #include <string.h>
  4. 4        #include <netdb.h>
  5. 5        #include <sys/types.h>
  6. 6        #include <netinet/in.h>
  7. 7        #include <arpa/nameser.h>
  8. 8        #include <resolv.h>
  9. 9        #include <errno.h>
  10. 10         
  11. 11        #include "dns.h"
  12. 12         
  13. 13        extern int res_query();
  14. 14        extern int res_search();
  15. 15        extern int errno;
  16. 16        extern int h_errno;
  17. 17         
  18. 18        static unsigned short getshort(unsigned char *c) { unsigned short u; u = c[0]; return (u << 8) + c[1]; }
  19. 19         
  20. 20        static union { HEADER hdr; unsigned char buf[PACKETSZ]; } response;
  21. 21        static int responselen;
  22. 22        static unsigned char *responseend;
  23. 23        static unsigned char *responsepos;
  24. 24        static int numanswers;
  25. 25        static char name[MAXDNAME];
  26. 26        unsigned short pref;
  27. 27         
  28. 28        int dns_resolve(char *domain,int type)
  29. 29        {
  30. 30                int n;
  31. 31                int i;
  32. 32                errno=0;
  33. 33                if(NULL == domain)
  34. 34                        return -1;
  35. 35                responselen = res_search(domain,C_IN,type,response.buf,sizeof(response));
  36. 36                if(responselen <= 0)
  37. 37                        return -1;
  38. 38                if(responselen >= sizeof(response))
  39. 39                        responselen = sizeof(response);
  40. 40                responseend = response.buf + responselen;
  41. 41                responsepos = response.buf + sizeof(HEADER);
  42. 42                n = ntohs(response.hdr.qdcount);
  43. 43                while(n-->0)
  44. 44                {
  45. 45                        i = dn_expand(response.buf,responseend,responsepos,name,MAXDNAME);
  46. 46                        responsepos += i;
  47. 47                        i = responseend - responsepos;
  48. 48                        if(i < QFIXEDSZ) return -1;
  49. 49                        responsepos += QFIXEDSZ;
  50. 50                }
  51. 51                numanswers = ntohs(response.hdr.ancount);
  52. 52                return numanswers;
  53. 53        }
  54. 54         
  55. 55        int dns_findmx(int wanttype)
  56. 56        {
  57. 57                unsigned short rrtype;
  58. 58                unsigned short rrdlen;
  59. 59                int i;
  60. 60         
  61. 61                if(numanswers <=0) return DNS_MSG_END;
  62. 62                numanswers--;
  63. 63                if(responsepos == responseend) return -1;
  64. 64                i = dn_expand(response.buf,responseend,responsepos,name,MAXDNAME);
  65. 65                if(i < 0) return -1;
  66. 66                responsepos += i;
  67. 67                i = responseend - responsepos;
  68. 68                if(i < 10) return -1;
  69. 69                rrtype = getshort(responsepos);
  70. 70                rrdlen = getshort(responsepos + 8);
  71. 71                responsepos += 10;
  72. 72                if(rrtype == wanttype)
  73. 73                {
  74. 74                        if(rrdlen < 3)
  75. 75                                return -1;
  76. 76                        pref = (responsepos[0] << 8) + responsepos[1];
  77. 77                        memset(name,0,MAXDNAME);
  78. 78                        if(dn_expand(response.buf,responseend,responsepos + 2,name,MAXDNAME) < 0)
  79. 79                                return -1;
  80. 80                        responsepos += rrdlen;
  81. 81                        return strlen(name);
  82. 82                }
  83. 83                responsepos += rrdlen;
  84. 84                return 0;
  85. 85        }
  86. 86         
  87. 87        void dns_init()
  88. 88        {
  89. 89                res_init();
  90. 90                memset(name,0,MAXDNAME);
  91. 91        }
  92. 92         
  93. 93        int dns_get_mxrr(unsigned short *p,unsigned char *dn,unsigned int len)
  94. 94        {
  95. 95                *p = pref;
  96. 96                strncpy(dn,name,len);
  97. 97                if(len < (strlen(name)+1))
  98. 98                        return -1;
  99. 99                return 0;
  100. 100        }
  101. 101         
  102. 102        int main(int argc, char *argv[])
  103. 103        {
  104. 104                char dname[MAXDNAME];
  105. 105                int i;
  106. 106                unsigned short p;
  107. 107                dns_init();
  108. 108                if(argc!=2)
  109. 109                {
  110. 110                        fprintf(stderr,"bad argument\n");
  111. 111                        exit(-1);
  112. 112                }
  113. 113                i = dns_mx_query(argv[1]);
  114. 114                if(i<0)
  115. 115                {
  116. 116                        fprintf(stderr,"err\n");
  117. 117                        return 0;
  118. 118                }
  119. 119                printf("pref\tdomain name\n");
  120. 120                foreach_mxrr(p,dname)
  121. 121                {
  122. 122                        printf("%d\t%s\n",p,dname);
  123. 123                }
  124. 124                return 0;
  125. 125        }
復(fù)制代碼

注釋:
18: getshort函數(shù)從指定地址讀取16bit網(wǎng)絡(luò)字節(jié)順序的數(shù)據(jù), 并將其轉(zhuǎn)換成little-endian的順序返回.
20: 定義了一個(gè)聯(lián)合體變量名為response, 用于存儲(chǔ)DNS響應(yīng)報(bào)文. HEADER是用于存儲(chǔ)DNS首部的結(jié)構(gòu)體, 定義在<arpa/nameser_compat.h>中(通常在/usr/include/arpa/nameser_compat.h中. <arpa/nameser_compat.h>這個(gè)頭文件則在<arpa/nameser.h>中被include. 后面用到的很多宏都定義在<arpa/nameser_compat.h>這個(gè)頭文件中). HEADER結(jié)構(gòu)體中本文會(huì)用到的成員是dncount和ancout, 分別表示問(wèn)題數(shù)和資源記錄數(shù)(參見(jiàn)圖1).PACKETSZ也定義在<arpa/nameser_compat.h>中, 這個(gè)宏表示一個(gè)報(bào)文的最大長(zhǎng)度.
21-26: responselen是響應(yīng)報(bào)文的長(zhǎng)度. responseend指向了響應(yīng)報(bào)文最后一個(gè)字節(jié)之后的一字節(jié), 即response.buf+responselen. responsepos指向了即將處理的字段. numanswers是還未處理的回答數(shù). name用來(lái)存儲(chǔ)主機(jī)名, 長(zhǎng)度是MAXDNAME個(gè)字節(jié). MAXDNAME定義在<arpa/nameser.h>中, 表示域名的最大長(zhǎng)度. pref用來(lái)存儲(chǔ)MX記錄的優(yōu)先值.

28: dns_resolve函數(shù)發(fā)起一個(gè)指定查詢名(domain)和查詢類型(type)的DNS查詢. type可取的值在<arpa/nameser_compat.h>中定義成了宏. 這些宏都是以T_開(kāi)頭. 例如, A類查詢的值是T_A. 對(duì)于MX查詢, 則可以調(diào)用dns_resolve(domain,T_MX)(這也就是dns.h中, dns_mx_query這個(gè)宏所做的). 如果發(fā)生錯(cuò)誤, 函數(shù)返回-1, 否則返回資源記錄數(shù)(參見(jiàn)圖1).
35: 利用res_search函數(shù)發(fā)起一條指定類型的DNS查詢. 其中的參數(shù)C_IN表示Internet地址. C_IN這個(gè)宏也定義在<arpa/nameser_compat.h>中. 返回值賦給responselen, 即響應(yīng)報(bào)文長(zhǎng)度.
36: 檢查是否有錯(cuò)誤發(fā)生, 如果發(fā)生返回-1. 實(shí)際上這種錯(cuò)誤檢測(cè)是不完善的. 關(guān)于詳細(xì)的錯(cuò)誤檢測(cè)方法可以參考qmail源代碼中的dns.c文件.
40-42: 調(diào)整responseend和responsepos的值. 并將報(bào)文中的問(wèn)題數(shù)存儲(chǔ)在臨時(shí)變量n中, 用于之后的處理. 由于響應(yīng)報(bào)文中的數(shù)據(jù)都是網(wǎng)絡(luò)字節(jié)順序, 因此需要調(diào)用ntohs函數(shù)進(jìn)行轉(zhuǎn)換. ntohs函數(shù)的具體內(nèi)容可以參考man手冊(cè).調(diào)整后, responsepos指向第一個(gè)查詢問(wèn)題的首地址.
43-50: 跳過(guò)所有的查詢問(wèn)題, 讓responsepos指向第一個(gè)回答的首地址. 此時(shí), 需要參考第二節(jié)中的內(nèi)容來(lái)幫助理解. 首先利用dn_expand函數(shù)獲得查詢問(wèn)題字段中查詢名的長(zhǎng)度, 并把該值賦給i. 之后, responsepos+=i使得responsepos指向了查詢類型字段的首地址(參見(jiàn)圖2). QFIXEDSZ宏定義在<arpa/nameser_compat.h>中, 其值等于4. 它表示DNS查詢報(bào)文中問(wèn)題部分的定長(zhǎng)字段的字節(jié)數(shù), 即查詢類型和查詢類兩個(gè)字段的總長(zhǎng)度. i = responseend - responsepos令i的值等于responsepos和responseend之間的距離, 由于此時(shí)responsepos指向問(wèn)題部分查詢類型的第一個(gè)字節(jié), 因此, 對(duì)于一個(gè)正常的報(bào)文來(lái)說(shuō), i的值應(yīng)該至少等于4. 因此, 在第48行進(jìn)行了檢測(cè), 若i < QFIXEDSZ, 表明該報(bào)文格式有錯(cuò). 則返回-1. 否則, responsepos+=QFIXEDSZ. 此時(shí), responsepos指向了下一個(gè)查詢問(wèn)題字段(或第一個(gè)回答字段)的首地址. 如此反復(fù), 直至跳過(guò)全部的查詢問(wèn)題字段. 則循環(huán)執(zhí)行完, responsepos指向第一個(gè)回答字段的首地址.
51-52: 將資源記錄數(shù)的值賦給numanswers. 返回numansers.

55: dns_findmx函數(shù)用于在調(diào)用了dns_resolve函數(shù)之后, 分析DNS響應(yīng)報(bào)文中的回答字段. 如果當(dāng)前responsepos指向的回答字段的類型是參數(shù)wanttype指定的類型, 則對(duì)該回答字段進(jìn)行處理. 參數(shù)wanttype可取的值與dns_resolve函數(shù)中的type參數(shù)可取值一樣. 對(duì)于MX記錄, 值為T_MX. dns.h中dns_mx_expand宏的定義就是dns_findmx(T_MX). 該函數(shù)若發(fā)生錯(cuò)誤則返回-1, 若報(bào)文中已經(jīng)沒(méi)有可以處理的字段, 則返回DNS_MSG_END. 否則返回name數(shù)組存儲(chǔ)的字符串長(zhǎng)度(不包括結(jié)尾的'\0').
61-62: 檢查未處理的回答字段數(shù)目, 若達(dá)到或小于零, 則返回DNS_MSG_END. 否則numanswers值減一.
64: 回答字段是以資源記錄格式存儲(chǔ)的. 第一項(xiàng)是域名(參見(jiàn)圖3). 因此用dn_expand函數(shù)將該字段還原為普通字符串格式. 并將返回的該字段長(zhǎng)度賦值給i.
66: 調(diào)整responsepos的值, 使其跳過(guò)域名部分, 指向類型字段(參見(jiàn)圖3).
67-68: 資源記錄中, 定長(zhǎng)字段的總長(zhǎng)度為10字節(jié),即類型, 類, 生存時(shí)間和資源數(shù)據(jù)長(zhǎng)度字段的總長(zhǎng)度(參見(jiàn)圖3). 若responseend-responsepos的值小于10, 則表明該報(bào)文非法.
69-71: 此時(shí)responsepos指向了資源記錄中類型字段的首地址. 利用getshort函數(shù)分別獲得類型和資源數(shù)據(jù)長(zhǎng)度字段的值, 存儲(chǔ)在rrtype和rrdlen變量中. 之后,調(diào)整responsepos的值, 使其指向資源數(shù)據(jù)的首地址(參見(jiàn)圖3). 對(duì)于一個(gè)MX查詢, 資源數(shù)據(jù)中的內(nèi)容是優(yōu)先值和主機(jī)名.
72-82: 如果這條資源記錄的類型是函數(shù)參數(shù)所指定的類型, 則對(duì)其進(jìn)行處理. MX記錄中, 資源數(shù)據(jù)內(nèi)第一項(xiàng)存儲(chǔ)著16bit的優(yōu)先值, 之后存儲(chǔ)著主機(jī)名. 優(yōu)先值長(zhǎng)度為2字節(jié), 主機(jī)名長(zhǎng)度至少1字節(jié), 則資源數(shù)據(jù)長(zhǎng)度至少要3字節(jié). 否則報(bào)文格式為非法. 第74, 75行就是利用這種方法檢測(cè)資源數(shù)據(jù)是否為非法. 第76行從資源數(shù)據(jù)中獲得優(yōu)先值存儲(chǔ)在pref中. 由于報(bào)文中的優(yōu)先值是按照網(wǎng)絡(luò)字節(jié)順序存儲(chǔ)的, 因此需要將其轉(zhuǎn)換成little-endian順序后存到pref中. 接著對(duì)name數(shù)組清零. 之后從資源數(shù)據(jù)中提取主機(jī)名. responsepos+=rrdlen令responsepos指向了下一條資源數(shù)據(jù). 最后利用strlen函數(shù)返回主機(jī)名的字符串長(zhǎng)度.
83: 該資源記錄的類型與參數(shù)指定類型不符, 則跳過(guò)該條記錄, 將responsepos調(diào)整至下一條資源記錄.

87-91: 調(diào)用res_init函數(shù)執(zhí)行相關(guān)初始化操作. 并對(duì)name數(shù)組清零. 此處沒(méi)有對(duì)res_init的返回值進(jìn)行檢測(cè).

93-100: dns_get_mxrr函數(shù)將pref的值(優(yōu)先值)存儲(chǔ)到p指向的地址中, 并將name中存儲(chǔ)的主機(jī)名復(fù)制到dn指向的地址中, 復(fù)制的長(zhǎng)度不超過(guò)len個(gè)字節(jié). 如果len個(gè)字節(jié)不足以存儲(chǔ)整個(gè)主機(jī)名, 則返回-1, 否則返回0.

102: main函數(shù), argv[1]中會(huì)存儲(chǔ)著要查詢的域名.
104-106: dname用于存儲(chǔ)查找到的主機(jī)名. 變量i用于存儲(chǔ)相關(guān)函數(shù)的返回值. p用于存儲(chǔ)優(yōu)先值.
108-112: 用戶提供的參數(shù)有錯(cuò)誤.
113: 利用dns.h中定義的宏, 發(fā)起MX查詢. 該語(yǔ)句相當(dāng)與i = dns_resolve(argv[1],T_MX);
120: 利用dns.h中定義的宏, 對(duì)響應(yīng)報(bào)文中的每條資源記錄進(jìn)行處理. foreach_mxrr宏在dns.h中定義, 該語(yǔ)句相當(dāng)于while(dns_findmx(T_MX)!=DNS_MSG_END&&(!dns_get_mxrr(&p,dname,MAXDNAME)))
121-124: 打印每條資源記錄后函數(shù)返回.

該程序在Magic Linux 2.0正式版上編譯成功( 內(nèi)核版本2.6.15.3; gcc版本3.4.4; i386體系結(jié)構(gòu) ).編譯和運(yùn)行結(jié)果如下:

  1.         [monnand@monnand-host src]$ gcc -lresolv dns.c -o dns
  2.         [monnand@monnand-host src]$ ./dns gmail.com
  3.         pref    domain name
  4.         50      gsmtp163.google.com
  5.         50      gsmtp183.google.com
  6.         5       gmail-smtp-in.l.google.com
  7.         10      alt1.gmail-smtp-in.l.google.com
  8.         10      alt2.gmail-smtp-in.l.google.com
復(fù)制代碼

5        后記
本文簡(jiǎn)要介紹了DNS報(bào)文格式, 講解了常用的地址解析函數(shù)并給出了這些函數(shù)的應(yīng)用實(shí)例. 第4節(jié)中的代碼可以應(yīng)用在其他程序內(nèi), 但是使用前最好對(duì)其進(jìn)行一定的修改從而增強(qiáng)健壯性和安全性. 修改時(shí)可以參考qmail的源代碼中dns.c和dns.h兩個(gè)文件. 如果想了解更多的地址解析函數(shù), 可以查閱man resolver. 如果想對(duì)DNS相關(guān)的協(xié)議及報(bào)文格式有更深的了解, 可以查閱附錄中的參考文獻(xiàn).

附錄        參考文獻(xiàn)
[1]W. Richard Stevens.TCP/IP詳解 卷1:協(xié)議.北京:機(jī)械工業(yè)出版社.1993.
[2]Mockapetris, P. V. "Domain Names: Concepts and Facilities," RFC 1034. 1987
[3]Mockapetris, P. V. "Domain Names: Implementation and Specification," RFC1035. 1987[/code]

論壇徽章:
0
2 [報(bào)告]
發(fā)表于 2006-07-20 22:55 |只看該作者
抱歉抱歉,這個(gè)帖子好像放錯(cuò)地方了,如果可能,勞駕轉(zhuǎn)移到 Linux->程序開(kāi)發(fā) 那里。抱歉抱歉~
您需要登錄后才可以回帖 登錄 | 注冊(cè)

本版積分規(guī)則 發(fā)表回復(fù)

  

北京盛拓優(yōu)訊信息技術(shù)有限公司. 版權(quán)所有 京ICP備16024965號(hào)-6 北京市公安局海淀分局網(wǎng)監(jiān)中心備案編號(hào):11010802020122 niuxiaotong@pcpop.com 17352615567
未成年舉報(bào)專區(qū)
中國(guó)互聯(lián)網(wǎng)協(xié)會(huì)會(huì)員  聯(lián)系我們:huangweiwei@itpub.net
感謝所有關(guān)心和支持過(guò)ChinaUnix的朋友們 轉(zhuǎn)載本站內(nèi)容請(qǐng)注明原作者名及出處

清除 Cookies - ChinaUnix - Archiver - WAP - TOP