Linux下的地址解析函数应用实例(2)

res_query和res_search函数返回值是响应报文的长度; 如果发生错误则返回-1.

dn_expand: 上一节中已经说到, DNS报文中主机名是以一种特殊格式存储的. dn_expand函数则是将这种特殊格式存储的字符串还原成一般格式. msg参数值是整个DNS报文的首地址; eomorig参数指向DNS报文的最后一个字节后的一字节, 用于指定报文的结束位置; comp_dn参数指向报文中需要被还原的主机名的首地址; 还原后的主机名被存储在exp_dn指向的内存区域中, 长度不大于length个字节. 函数返回主机名在DNS报文中的长度(即被还原前的长度); 如果发生错误则返回-1.

需要注意的是, 如果程序中用到了这些地址解析函数, 那么在编译的时候需要加上-lresolv选项才能正常编译.

利用这些地址解析函数, 不仅可以完成A类查询或PTR查询, 还可以进行其他类型的询问. 下一节将给出利用地址解析函数进行MX查询的实例.

4    地址解析函数应用实例---MX查询
在发送电子邮件时, 需要用到MX(Mail eXchange)记录. 一个电子邮箱的地址是 "用户名@域名" 的格式. 当要给某个用户发送电子邮件时, 首先需要从这个用户的电子邮箱地址中得到域名; 然后向DNS服务器发出一个MX查询, 询问该域名由哪些服务器负责处理.DNS服务器会返回处理该域名的服务器的主机名. 每个主机名对应一个16bit的整数值, 该值称为优先值(preference value), 如果一个域存在多条MX记录, 则首先使用优先值较小的主机名. 之后, 就是利用SMTP协议与相应的主机进行连接并发送邮件.

如果要发出一个MX查询, 可以利用host命令:

    [monnand@monnand-host ~]$ host -t mx gmail.com
    gmail.com mail is handled by 50 gsmtp183.google.com.
    gmail.com mail is handled by 5 gmail-smtp-in.l.google.com.
    gmail.com mail is handled by 10 alt1.gmail-smtp-in.l.google.com.
    gmail.com mail is handled by 10 alt2.gmail-smtp-in.l.google.com.
    gmail.com mail is handled by 50 gsmtp163.google.com.

-t 选项用于指明查询类型, -t mx表示发起一个MX查询. 后面的参数是要查询的域名(这里以gmail.com为例).
显示出的是关于查询域名的MX记录. 这里关于gmail.com的MX记录共5条, 每条记录都有相应的优先值(显示在主机名前面), 例如第一条记录的优先值是50.

下面, 我们就利用前面讲到的地址解析函数来实现一个类似功能的程序. 即指定查询域名, 打印出关于这个域名的MX记录. 该程序的代码是从qmail的代码中精简出来的, 其中去掉了一些错误检测, 并修改了与qmail其他部分相关联的代码, 使整个程序能够独立出来(当然, 因为去掉了很多错误检测, 程序也失去了原有的健壮性和安全性). 但是整体的思路基本没有大的改动. 有兴趣的读者可以自己阅读qmail的源代码. 相信会有更多的收获.

整个程序被写到了两个文件中, 分别名为dns.h和dns.c.

以下是dns.h中的内容:

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


该文件中声明了4个函数. 为了便于操作, 定义了三个宏. 关于其中具体的用法, 之后会有介绍. 下面给出dns.c中的源代码:

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

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/24899.html