DNS数据包解析
仔细看了看DNS 协议的相关东西,其实实际编程的时候根本用不到DNS 细节的东西,要获取域名的时候经终端下用host 或者nslookup 指令就可以,在c 里面使用gethostbyname 或者g
仔细看了看DNS 协议的相关东西,其实实际编程的时候根本用不到DNS 细节的东西,要获取域名的时候经终端下用host 或者nslookup 指令就可以,在c 里面使用
gethostbyname 或者getaddrinfo 都能很轻松得将dns 域名解析为ip 地址,写这个纯粹出于个人兴趣,或者说是闲得吧。
在进行域名解析的时候,解析程序向域名服务器发起请求,域名服务器也就是在操作系统网络配置的时候写进去的那个DNS 服务器地址,或者也有可能是由ISP 提供的自动获取的,原理都一样,域名服务器收到请求后进行处理,首先在本地缓存中查找对应的域名,找到后将IP 地址直接返回,找不到就向其它的授权服务器请求数据,又可以分为著名的递归查询和非递归查询。
递归查询就是说自始至终都由一台域名服务器进行查询,它在自己这里找不到的时候会向其它的域名服务器请求并且获取数据,然后返回给请求方。
非递归查询是指域名服务器收到请求后,如果自己有这个域名的信息就返回,如果没有就返回其它域名服务器的指针,请求方再根据这些域名服务器再发起查询。
按自己的理解瞎扯了一通,也不知道准不准确,关于DNS 的相关资料网上有的是,中文的都大批大批的。
DNS 服务器的原理其实没什么好说的,每天都在跟DNS 打交道,但DNS 的协议在实现上还是稍微有点意思的,本来想写个程序来测试一个我所了解的DNS 协议,后来在写的时候还真发现一个小问题,DNS 域名有时候会是一个主域名的别名,比如www.baidu.com ,它就是www.a.shifen.com 这个域名的别名,在DNS 请求发送过去之后,response 里面会有一个类型为CNAME 的Answers 项,里面包含了主域名的相关信息(其实也就是主域名的名称和TTL) ,在这个应答消息里面可能会出现多个域名消息,比如每个Answers 的第一
,个字段就是一个域名,当然为了减少数据包的容量,DNS 系统对域名进行了压缩,同一个域名只会出现一次,其它的时候再出现的话就会用一个DNS 指针表示。
比如域名:www.baidu.com 在数据包中的表示是03 77 77 77 05 62 61 69 64 75 03 63 6f 6d 00
粗体的是长度,将域名中的点去掉,用长度来分隔域名,以0结束。DNS 允许的长度为0-63个字节,所以一个8位的长度最高两位都为0。
而如果此处域名重复出现,信令中便会用DNS 指针代替长度,指针为两个字节,16位的最位都为1,剩下的14位表示在在整个数据包中的偏移量,当程序读取到c00c 的时候很容易判断它是一个指针而不是一个长度字段,于是根据c00c 指向的领移量,即从数据包开始后的第12个字节,跳转过去读取出域名信息。
#include
#include
#include
#include
#include
#include
#include
#include
#define DNS_SVR "211.68.71.4"
,#define DNS_HOST 0x01
#define DNS_CNAME 0x05
int socketfd;
struct sockaddr_in dest;
static void
send_dns_request(const char *dns_name);
static void
parse_dns_response();
/**
* Generate DNS question chunk */
static void
generate_question(const char *dns_name , unsigned char *buf , int *len);
/**
* Check whether the current byte is * a dns pointer or a length
,*/
static int
is_pointer(int in);
/**
* Parse data chunk into dns name
* @param chunk The complete response chunk
* @param ptr The pointer points to data
* @param out This will be filled with dns name
* @param len This will be filled with the length of dns name */
static void
parse_dns_name(unsigned char *chunk , unsigned char *ptr , char *out , int *len);
int main(int argc , char *argv[]){
if(argc != 2){
printf("Usage : s
}
socketfd = socket(AF_INET , SOCK_DGRAM , 0);
,if(socketfd < 0){
perror("create socket failed"); exit(-1);
}
bzero(&dest , sizeof(dest));
dest.sin_family = AF_INET;
dest.sin_port = htons(53);
dest.sin_addr.s_addr = inet_addr(DNS_SVR);
send_dns_request(argv[1]);
parse_dns_response();
return 0;
}
static void parse_dns_response(){
unsigned char buf[1024];
unsigned char *ptr = buf;
struct sockaddr_in addr;
char *src_ip;
,int n , i , flag , querys , answers;
int type , ttl , datalen , len;
char cname[128] , aname[128] , ip[20] , *cname_ptr; unsigned char netip[4];
size_t addr_len = sizeof(struct sockaddr_in);
n = recvfrom(socketfd , buf , sizeof(buf) , 0 , (struct sockaddr*)&addr , &addr_len); ptr = 4; /* move ptr to Questions */
querys = ntohs(*((unsigned short*)ptr));
ptr = 2; /* move ptr to Answer RRs */
answers = ntohs(*((unsigned short*)ptr)); ptr = 6; /* move ptr to Querys */
/* move over Querys */
for(i= 0 ; i < querys ; i ){
for(;;){
flag = (int)ptr[0];
ptr = (flag 1);
if(flag == 0)
break ;
}
ptr = 4;
,}
printf("-------------------------------n");
/* now ptr points to Answers */
for(i = 0 ; i < answers ; i ){
bzero(aname , sizeof(aname));
len = 0;
parse_dns_name(buf , ptr , aname , &len);
ptr = 2; /* move ptr to Type*/
type = htons(*((unsigned short*)ptr));
ptr = 4; /* move ptr to Time to live */
ttl = htonl(*((unsigned int*)ptr));
ptr = 4; /* move ptr to Data lenth */
datalen = ntohs(*((unsigned short*)ptr));
ptr = 2; /* move ptr to Data*/
if(type == DNS_CNAME){
bzero(cname , sizeof(cname));
len = 0;
parse_dns_name(buf , ptr , cname , &len);
printf("s is an alias for sn" , aname , cname); ptr = datalen;
}
if(type == DNS_HOST){
,bzero(ip , sizeof(ip));
if(datalen == 4){
memcpy(netip , ptr , datalen);
inet_ntop(AF_INET , netip , ip , sizeof(struct sockaddr)); printf("s has address sn" , aname , ip);
printf("tTime to live: d minutes , d secondsn" , ttl / 60 , ttl 60);
}
ptr = datalen;
}
}
ptr = 2;
}
static void
parse_dns_name(unsigned char *chunk
, unsigned char *ptr , char *out , int *len){
int n , alen , flag;
char *pos = out (*len);
for(;;){
,flag = (int)ptr[0];
if(flag == 0)
break ;
if(is_pointer(flag)){
n = (int)ptr[1];
ptr = chunk n;
parse_dns_name(chunk , ptr , out , len); break ;
}else{
ptr ;
memcpy(pos , ptr , flag); pos = flag;
ptr = flag;
*len = flag;
if((int)ptr[0] != 0){
memcpy(pos , "." , 1);
pos = 1;
(*len) = 1;
}
}
}
,}
static int is_pointer(int in){
return ((in & 0xc0) == 0xc0);
}
static void send_dns_request(const char *dns_name){
unsigned char request[256];
unsigned char *ptr = request;
unsigned char question[128];
int question_len;
generate_question(dns_name , question , &question_len);
*((unsigned short*)ptr) = htons(0xff00);
ptr = 2;
*((unsigned short*)ptr) = htons(0x0100);
ptr = 2;
*((unsigned short*)ptr) = htons(1);
ptr = 2;