DNS数据包解析.docx
《DNS数据包解析.docx》由会员分享,可在线阅读,更多相关《DNS数据包解析.docx(8页珍藏版)》请在冰点文库上搜索。
DNS数据包解析
仔细看了看DNS协议的相关东西,其实实际编程的时候根本用不到DNS细节的东西,要获取域名的时候经终端下用host或者nslookup指令就可以,在c里面使用gethostbyname或者getaddrinfo都能很轻松得将dns域名解析为ip地址,写这个纯粹出于个人兴趣,或者说是闲得吧。
在进行域名解析的时候,解析程序向域名服务器发起请求,域名服务器也就是在操作系统网络配置的时候写进去的那个DNS服务器地址,或者也有可能是由ISP提供的自动获取的,原理都一样,域名服务器收到请求后进行处理,首先在本地缓存中查找对应的域名,找到后将IP地址直接返回,找不到就向其它的授权服务器请求数据,又可以分为著名的递归查询和非递归查询。
递归查询就是说自始至终都由一台域名服务器进行查询,它在自己这里找不到的时候会向其它的域名服务器请求并且获取数据,然后返回给请求方。
非递归查询是指域名服务器收到请求后,如果自己有这个域名的信息就返回,如果没有就返回其它域名服务器的指针,请求方再根据这些域名服务器再发起查询。
按自己的理解瞎扯了一通,也不知道准不准确,关于DNS的相关资料网上有的是,中文的都大批大批的。
DNS服务器的原理其实没什么好说的,每天都在跟DNS打交道,但DNS的协议在实现上还是稍微有点意思的,本来想写个程序来测试一个我所了解的DNS协议,后来在写的时候还真发现一个小问题,DNS域名有时候会是一个主域名的别名,比如,它就是这个域名的别名,在DNS请求发送过去之后,response里面会有一个类型为CNAME的Answers项,里面包含了主域名的相关信息(其实也就是主域名的名称和TTL),在这个应答消息里面可能会出现多个域名消息,比如每个Answers的第一个字段就是一个域名,当然为了减少数据包的容量,DNS系统对域名进行了压缩,同一个域名只会出现一次,其它的时候再出现的话就会用一个DNS指针表示。
比如域名:
在数据包中的表示是0377777705626169647503636f6d00
粗体的是长度,将域名中的点去掉,用长度来分隔域名,以0结束。
DNS允许的长度为0-63个字节,所以一个8位的长度最高两位都为0。
而如果此处域名重复出现,信令中便会用DNS指针代替长度,指针为两个字节,16位的最位都为1,剩下的14位表示在在整个数据包中的偏移量,当程序读取到c00c的时候很容易判断它是一个指针而不是一个长度字段,于是根据c00c指向的领移量,即从数据包开始后的第12个字节,跳转过去读取出域名信息。
#include
#include
#include
#include
#include
#include
#include
#include
#defineDNS_SVR"211.68.71.4"
#defineDNS_HOST0x01
#defineDNS_CNAME0x05
intsocketfd;
structsockaddr_indest;
staticvoid
send_dns_request(constchar*dns_name);
staticvoid
parse_dns_response();
/**
*GenerateDNSquestionchunk
*/
staticvoid
generate_question(constchar*dns_name
,unsignedchar*buf,int*len);
/**
*Checkwhetherthecurrentbyteis
*adnspointeroralength
*/
staticint
is_pointer(intin);
/**
*Parsedatachunkintodnsname
*@paramchunkThecompleteresponsechunk
*@paramptrThepointerpointstodata
*@paramoutThiswillbefilledwithdnsname
*@paramlenThiswillbefilledwiththelengthofdnsname
*/
staticvoid
parse_dns_name(unsignedchar*chunk,unsignedchar*ptr
,char*out,int*len);
intmain(intargc,char*argv[]){
if(argc!
=2){
printf("Usage:
%s\n",argv[0]);
exit(-1);
}
socketfd=socket(AF_INET,SOCK_DGRAM,0);
if(socketfd<0){
perror("createsocketfailed");
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();
return0;
}
staticvoidparse_dns_response(){
unsignedcharbuf[1024];
unsignedchar*ptr=buf;
structsockaddr_inaddr;
char*src_ip;
intn,i,flag,querys,answers;
inttype,ttl,datalen,len;
charcname[128],aname[128],ip[20],*cname_ptr;
unsignedcharnetip[4];
size_taddr_len=sizeof(structsockaddr_in);
n=recvfrom(socketfd,buf,sizeof(buf),0
,(structsockaddr*)&addr,&addr_len);
ptr+=4;/*moveptrtoQuestions*/
querys=ntohs(*((unsignedshort*)ptr));
ptr+=2;/*moveptrtoAnswerRRs*/
answers=ntohs(*((unsignedshort*)ptr));
ptr+=6;/*moveptrtoQuerys*/
/*moveoverQuerys*/
for(i=0;i for(;;){
flag=(int)ptr[0];
ptr+=(flag+1);
if(flag==0)
break;
}
ptr+=4;
}
printf("-------------------------------\n");
/*nowptrpointstoAnswers*/
for(i=0;i bzero(aname,sizeof(aname));
len=0;
parse_dns_name(buf,ptr,aname,&len);
ptr+=2;/*moveptrtoType*/
type=htons(*((unsignedshort*)ptr));
ptr+=4;/*moveptrtoTimetolive*/
ttl=htonl(*((unsignedint*)ptr));
ptr+=4;/*moveptrtoDatalenth*/
datalen=ntohs(*((unsignedshort*)ptr));
ptr+=2;/*moveptrtoData*/
if(type==DNS_CNAME){
bzero(cname,sizeof(cname));
len=0;
parse_dns_name(buf,ptr,cname,&len);
printf("%sisanaliasfor%s\n",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(structsockaddr));
printf("%shasaddress%s\n",aname,ip);
printf("\tTimetolive:
%dminutes,%dseconds\n"
,ttl/60,ttl%60);
}
ptr+=datalen;
}
}
ptr+=2;
}
staticvoid
parse_dns_name(unsignedchar*chunk
,unsignedchar*ptr,char*out,int*len){
intn,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;
}
}
}
}
staticintis_pointer(intin){
return((in&0xc0)==0xc0);
}
staticvoidsend_dns_request(constchar*dns_name){
unsignedcharrequest[256];
unsignedchar*ptr=request;
unsignedcharquestion[128];
intquestion_len;
generate_question(dns_name,question,&question_len);
*((unsignedshort*)ptr)=htons(0xff00);
ptr+=2;
*((unsignedshort*)ptr)=htons(0x0100);
ptr+=2;
*((unsignedshort*)ptr)=htons
(1);
ptr+=2;
*((unsignedshort*)ptr)=0;
ptr+=2;
*((unsignedshort*)ptr)=0;
ptr+=2;
*((unsignedshort*)ptr)=0;
ptr+=2;
memcpy(ptr,question,question_len);
ptr+=question_len;
sendto(socketfd,request,question_len+12,0
,(structsockaddr*)&dest,sizeof(structsockaddr));
}
staticvoid
generate_question(constchar*dns_name,unsignedchar*buf,int*len){
char*pos;
unsignedchar*ptr;
intn;
*len=0;
ptr=buf;
pos=(char*)dns_name;
for(;;){
n=strlen(pos)-(strstr(pos,".")?
strlen(strstr(pos,".")):
0);
*ptr++=(unsignedchar)n;
memcpy(ptr,pos,n);
*len+=n+1;
ptr+=n;
if(!
strstr(pos,".")){
*ptr=(unsignedchar)0;
ptr++;
*len+=1;
break;
}
pos+=n+1;
}
*((unsignedshort*)ptr)=htons
(1);
*len+=2;
ptr+=2;
*((unsignedshort*)ptr)=htons
(1);
*len+=2;
}