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