DNS數(shù)據(jù)包解析
仔細(xì)看了看DNS 協(xié)議的相關(guān)東西,其實(shí)實(shí)際編程的時(shí)候根本用不到DNS 細(xì)節(jié)的東西,要獲取域名的時(shí)候經(jīng)終端下用host 或者nslookup 指令就可以,在c 里面使用gethostbyname 或者g
仔細(xì)看了看DNS 協(xié)議的相關(guān)東西,其實(shí)實(shí)際編程的時(shí)候根本用不到DNS 細(xì)節(jié)的東西,要獲取域名的時(shí)候經(jīng)終端下用host 或者nslookup 指令就可以,在c 里面使用
gethostbyname 或者getaddrinfo 都能很輕松得將dns 域名解析為ip 地址,寫這個(gè)純粹出于個(gè)人興趣,或者說是閑得吧。
在進(jìn)行域名解析的時(shí)候,解析程序向域名服務(wù)器發(fā)起請(qǐng)求,域名服務(wù)器也就是在操作系統(tǒng)網(wǎng)絡(luò)配置的時(shí)候?qū)戇M(jìn)去的那個(gè)DNS 服務(wù)器地址,或者也有可能是由ISP 提供的自動(dòng)獲取的,原理都一樣,域名服務(wù)器收到請(qǐng)求后進(jìn)行處理,首先在本地緩存中查找對(duì)應(yīng)的域名,找到后將IP 地址直接返回,找不到就向其它的授權(quán)服務(wù)器請(qǐng)求數(shù)據(jù),又可以分為著名的遞歸查詢和非遞歸查詢。
遞歸查詢就是說自始至終都由一臺(tái)域名服務(wù)器進(jìn)行查詢,它在自己這里找不到的時(shí)候會(huì)向其它的域名服務(wù)器請(qǐng)求并且獲取數(shù)據(jù),然后返回給請(qǐng)求方。
非遞歸查詢是指域名服務(wù)器收到請(qǐng)求后,如果自己有這個(gè)域名的信息就返回,如果沒有就返回其它域名服務(wù)器的指針,請(qǐng)求方再根據(jù)這些域名服務(wù)器再發(fā)起查詢。
按自己的理解瞎扯了一通,也不知道準(zhǔn)不準(zhǔn)確,關(guān)于DNS 的相關(guān)資料網(wǎng)上有的是,中文的都大批大批的。
DNS 服務(wù)器的原理其實(shí)沒什么好說的,每天都在跟DNS 打交道,但DNS 的協(xié)議在實(shí)現(xiàn)上還是稍微有點(diǎn)意思的,本來想寫個(gè)程序來測(cè)試一個(gè)我所了解的DNS 協(xié)議,后來在寫的時(shí)候還真發(fā)現(xiàn)一個(gè)小問題,DNS 域名有時(shí)候會(huì)是一個(gè)主域名的別名,比如www.baidu.com ,它就是www.a.shifen.com 這個(gè)域名的別名,在DNS 請(qǐng)求發(fā)送過去之后,response 里面會(huì)有一個(gè)類型為CNAME 的Answers 項(xiàng),里面包含了主域名的相關(guān)信息(其實(shí)也就是主域名的名稱和TTL) ,在這個(gè)應(yīng)答消息里面可能會(huì)出現(xiàn)多個(gè)域名消息,比如每個(gè)Answers 的第一
,個(gè)字段就是一個(gè)域名,當(dāng)然為了減少數(shù)據(jù)包的容量,DNS 系統(tǒng)對(duì)域名進(jìn)行了壓縮,同一個(gè)域名只會(huì)出現(xiàn)一次,其它的時(shí)候再出現(xiàn)的話就會(huì)用一個(gè)DNS 指針表示。
比如域名:www.baidu.com 在數(shù)據(jù)包中的表示是03 77 77 77 05 62 61 69 64 75 03 63 6f 6d 00
粗體的是長度,將域名中的點(diǎn)去掉,用長度來分隔域名,以0結(jié)束。DNS 允許的長度為0-63個(gè)字節(jié),所以一個(gè)8位的長度最高兩位都為0。
而如果此處域名重復(fù)出現(xiàn),信令中便會(huì)用DNS 指針代替長度,指針為兩個(gè)字節(jié),16位的最位都為1,剩下的14位表示在在整個(gè)數(shù)據(jù)包中的偏移量,當(dāng)程序讀取到c00c 的時(shí)候很容易判斷它是一個(gè)指針而不是一個(gè)長度字段,于是根據(jù)c00c 指向的領(lǐng)移量,即從數(shù)據(jù)包開始后的第12個(gè)字節(jié),跳轉(zhuǎn)過去讀取出域名信息。
#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;