创建文件dns-test.cpp
-
#include <stdio.h>
-
#include <stdlib.h>
-
#include <string.h>
-
#include <netdb.h>
-
#include <sys/types.h>
-
#include <netinet/in.h>
-
#include <arpa/nameser.h>
-
#include <resolv.h>
-
#include <errno.h>
-
#include <iostream>
-
using namespace std;
-
-
#define DNS_MSG_END -2
-
#define dns_mx_query(str) dns_resolve((str),T_MX)
-
#define dns_mx_expand() dns_findmx(T_MX)
-
#define foreach_mxrr(p,dn) while(dns_mx_expand()!=DNS_MSG_END && (!dns_get_m
-
xrr(&p,dn,MAXDNAME)))
-
-
-
static unsigned short getshort(unsigned char *c)
-
{
-
unsigned short u;
-
u = c[0];
-
return (u << 8) + c[1];
-
}
-
-
unsigned int getint(unsigned char *c)
-
{
-
unsigned int u;
-
u = c[0];
-
return (((((u << 8 ) + c[1]) << 8) + c[2] ) << 8 ) + c[3];
-
}
-
-
static union
-
{
-
HEADER hdr;
-
unsigned char buf[PACKETSZ];
-
} response;
-
-
static int responselen;
-
static unsigned char *responseend;
-
static unsigned char *responsepos;
-
static int numanswers;
-
static char name[MAXDNAME];
-
unsigned short pref;
-
-
int dns_resolve(char *domain, int type)
-
{
-
int n;
-
int i;
-
errno = 0;
-
if (NULL == domain)
-
{
-
return -1;
-
}
-
responselen = res_search(domain, C_IN, type, response.buf, sizeof(response));
-
if (responselen <= 0)
-
{
-
return -1;
-
}
-
if (responselen >= sizeof(response))
-
{
-
responselen = sizeof(response);
-
}
-
responseend = response.buf + responselen;
-
responsepos = response.buf + sizeof(HEADER);
-
n = ntohs(response.hdr.qdcount);
-
-
int m = n;
-
while (n-- > 0)
-
{
-
i = dn_expand(response.buf, responseend, responsepos, name, MAXDNAME);
-
responsepos += i;
-
i = responseend - responsepos;
-
if (i < QFIXEDSZ)
-
{
-
return -1;
-
}
-
//responsepos += QFIXEDSZ;
-
}
-
numanswers = ntohs(response.hdr.ancount);
-
-
cout << "request name : " << name << endl;
-
cout << "request type : " << getshort(responsepos) << endl;
-
cout << "request class : " << getshort(responsepos+2) << endl;
-
cout << "question number : " << m << endl;
-
cout << "answer number : " << numanswers << endl;
-
-
responsepos += QFIXEDSZ;
-
return numanswers;
-
}
-
-
int dns_findmx(int wanttype)
-
{
-
unsigned short rrtype;
-
unsigned short rrdlen;
-
int i;
-
-
if (numanswers <= 0)
-
{
-
return DNS_MSG_END;
-
}
-
numanswers--;
-
if (responsepos == responseend)
-
{
-
return -1;
-
}
-
i = dn_expand(response.buf, responseend, responsepos, name, MAXDNAME);
-
if (i < 0)
-
{
-
return -1;
-
}
-
responsepos += i;
-
i = responseend - responsepos;
-
if (i < 10)
-
{
-
return -1;
-
}
-
-
cout << "domain : " << name << endl;
-
rrtype = getshort(responsepos);
-
int ttl = getint(responsepos+4);
-
cout << "ttl : " << ttl << endl;
-
rrdlen = getshort(responsepos + 8);
-
responsepos += 10;
-
if (rrtype == wanttype)
-
{
-
if (rrdlen < 3)
-
{
-
return -1;
-
}
-
pref = (responsepos[0] << 8) + responsepos[1];
-
memset(name, 0, MAXDNAME);
-
if (dn_expand(response.buf, responseend, responsepos + 2, name, MAXDNAME) < 0)
-
{
-
return -1;
-
}
-
responsepos += rrdlen;
-
return strlen(name);
-
}
-
responsepos += rrdlen;
-
return 0;
-
}
-
-
void dns_init()
-
{
-
res_init();
-
memset(name, 0, MAXDNAME);
-
}
-
-
int dns_get_mxrr(unsigned short *p, unsigned char *dn, unsigned int len)
-
{
-
*p = pref;
-
strncpy((char *)dn, name, len);
-
if (len < (strlen(name) + 1))
-
{
-
return -1;
-
}
-
return 0;
-
}
-
-
int main(int argc, char *argv[])
-
{
-
char dname[MAXDNAME];
-
int i;
-
unsigned short p;
-
dns_init();
-
if (argc != 2)
-
{
-
fprintf(stderr, "bad argumentn");
-
exit(-1);
-
}
-
i = dns_mx_query(argv[1]);
-
if (i < 0)
-
{
-
fprintf(stderr, "errn");
-
return 0;
-
}
-
printf("preftdomain namen");
-
foreach_mxrr(p, (unsigned char *)dname)
-
{
-
printf("%dt%sn", p, dname);
-
}
-
return 0;
-
}
编译: g++ dns-test.cpp -lresolv
./a.out sina.com.cn
request name : qq.com
request type : 15
request class : 1
question number : 1
answer number : 3
pref domain name
domain : qq.com
ttl : 7045
20 mx2.qq.com
domain : qq.com
ttl : 7045
30 mx1.qq.com
domain : qq.com
ttl : 7045
10 mx3.qq.com
原理介绍:
1 概述
DNS 查询中, 最常用的两类分别是A类查询(A query)和指针查询(PTR query). 前者是已知主机名, 询问IP; 后者是已知IP, 询问主机名. 对于这些查询, 在Unix主机中可以直接调用基本DNS函数: gethostbyname(3)和gethostbyaddr(3)来实现. 但是对于其他类型的查询(例如MX查询), 则没有专门的函数来负责处理.
2 DNS报文格式
在对地址解析函数讲解之前, 有必要先了解一下DNS报文格式. 之后的几节会频繁地涉及到本节所讲的内容.
DNS定义了一个用于查询和响应的报文格式, 图1 显示了这个报文的总体格式.
每个DNS查询(或响应)报文都包含有一个12字节长的首部和四个变长的字段组成.
对于本文来说, 首部中主要关心的是问题数和资源记录数两个字段. 这两个字段分别用于说明各自对应的变长字段中的条目数. 问题数说明查询问题字段中的条目数; 资源记录数则说明回答字段中的条目数. 对于一个DNS查询报文, 问题数通常是1. 对于应答报文, 回答数至少是1.
查询
首部以下是四个变长字段, 本文所关心的是查询问题字段和回答字段.
查询问题字段可以包含多个查询问题, 每个问题的格式如图2 所示.
其中, 查询名一项存储着要查找的名字. 它长度可变并以一种特殊的格式存储. 程序可以通过其中存储的内容确定其长度. 具体获得其中存储内容的方法, 将在下一节中进行详细讲解. 每一个问题有一个查询类型, 每个响应(下文中将会提到)也同样有一个类型. 常用的类型有: A类型---表示期望获得查询名的IP地址; PTR查询---表示期望获得一个IP地址对应的域名; MX查询---邮件交换查询(关于MX查询的具体内容, 下文会提到). 查询类指定了所使用的协议簇, 通常是1, 表示Internet地址.
回答
回答字段可以包含多个条目. 每个回答字段是以一种叫做资源记录(Resource Record, RR)的格式存储的. ( 授权字段和额外信息字段也同样以资源记录的格式存储信息). 资源记录的格式如图3 所示.
域名是记录中资源数据对应的名字. 它的格式和前面介绍的查询名字段格式相同. 类型和类字段和前面介绍的查询类型, 查询类字段的功能一样. 类字段的取值通常是1, 表示Internet地址. 生存时间字段是客户程序保留该资源记录的秒数. 资源数据长度说明资源数据包含的字节数. 资源数据则根据类型字段的值有不同的格式. 对于A类型, 资源数据是IP地址. 对于MX查询, 资源数据是优先值和域名, 域名的格式与查询名字段格式相同(MX记录的具体内容下文会有介绍).
阅读(5046) | 评论(0) | 转发(2) |