原本是一次計算機網絡的實驗,可是尚未徹底寫好,DNS的響應請求報文的冗餘信息太多了,不僅有IP地址。因此此次的實驗主要就是解析DNS報文。同時也須要正確的填充請求報文。若是代碼有什麼bug,歡迎指正啊。代碼排版有點亂。。。windows
本文有如下內容服務器
DNS報文的填充和解析網絡
利用socket API傳輸信息
dom
隨便百度一下,就能夠知道DNS報文的格式。因此這裏只介紹如何填充DNS報文。socket
首先是填充報文首部:this
/* 填充首部的格式大體相同,下面的填充值是參考他人抓包分析的結果 */ buf[0] = 0x00; buf[1] = 0; buf[2] = 0x01; buf[3] = 0; buf[4] = 0; buf[5] = 1; buf[6] = buf[7] = 0; buf[8] = buf[9] = buf[10] = buf[11] = 0;
而後填充報文的問題部分:url
下面是填充域名爲百度(www.baidu.com)的代碼.net
/* 填充域名 */ buf[12] = 3; buf[13] = buf[14] = buf[15] = 'w'; buf[16] = 5; buf[17] = 'b'; buf[18] = 'a'; buf[19] = 'i'; buf[20] = 'd'; buf[21] = 'u'; buf[22] = 3; buf[23] = 'c'; buf[24] = 'o'; buf[25] = 'm'; buf[26] = 0; /* 填充查詢類型和查詢類 */ buf[27] = 0; buf[28] = 1; buf[29] = 0; buf[30] = 1;
2、利用socket發送DNS報文計算機網絡
下面是代碼:指針
int sendDNSPacket(unsigned char *buf, int len, char *recvMsg) { int s; struct sockaddr_in sin; memset(&sin,0,sizeof(sin)); sin.sin_addr.s_addr = inet_addr("127.0.0.1"); /* 本體DNS服務器的地址 */ sin.sin_family = AF_INET; sin.sin_port = htons(SERVER_PORT); /* 端口爲53 */ s = socket(PF_INET,SOCK_DGRAM,0); /* UDP傳報文 */ sendto(s,buf,len,0,(struct sockaddr *)&sin,sizeof(sin)); return recv(s,recvMsg,MAX_SIZE,0); }
這部分就是普通的socket的建立、發送和接收過程。
3、解析DNS響應報文
本身錯將16進制的數錯看爲10進制數了,在這裏坑了很長時間。注意報文中的指針的偏移量,只有指針的偏移量指的是規範名稱該資源記錄纔是IP地址。在報文中還有不少和IP地址無關的資源記錄。下面是代碼,不過遇到複雜的DNS報文可能有bug。這裏只實驗了三個域名:www.ccnu.edu.cn www.baidu.com www.163.com(這個域名有點複雜)。
void resolve(unsigned char *recvMsg, int len, int len_recvMsg) { int pos = len; int cnt = 12; while(pos < len_recvMsg) { unsigned char now_pos = recvMsg[pos+1]; unsigned char retype = recvMsg[pos+3]; unsigned char reclass = recvMsg[pos+5]; unsigned char offset = recvMsg[pos+11]; if(retype == 1) { if(now_pos == cnt && reclass == 1) { printf("%u.%u.%u.%u\n",recvMsg[pos+12],recvMsg[pos+13],recvMsg[pos+14],recvMsg[pos+15]); } } else if(retype == 5) { cnt = pos + 12 ; } pos = pos + 12 + offset; } }
4、完整的代碼和總結
下面是完整的代碼:
/************************************************************************* > File Name: MyFiles/C和C++程序/socket/getIP.c > Author: mr_zys > Mail: 247629929@qq.com > Created Time: Thu 12 Jun 2014 05:22:06 PM CST > Operating System: Ubuntu 12.04 LTS > Programming Language: Linux c > Compiler: gcc > Description: this is a program with Linux socket APIs to ask DNS server for domain name's IP adress! ************************************************************************/ #include<stdio.h> #include<stdlib.h> #include<string.h> #include<errno.h> #include<sys/types.h> #include<sys/socket.h> #include<netinet/in.h> #include<netdb.h> #define MAX_SIZE 1024 #define SERVER_PORT 53 void setHead(unsigned char *buf) { buf[0] = 0x00; buf[1] = 0; buf[2] = 0x01; buf[3] = 0; buf[4] = 0; buf[5] = 1; buf[6] = 0; buf[7] = 0; buf[8] = buf[9] = buf[10] = buf[11] = 0; } void setQuery(char *name, unsigned char *buf, int len) { strcat(buf+12,name); int pos = len + 12; buf[pos] = 0; buf[pos+1] = 1; buf[pos+2] = 0; buf[pos+3] = 1; } int changeDN(char *DN,char *name) { int i = strlen(DN) - 1; int j = i + 1; int k; name[j+1] = 0; for(k = 0; i >= 0; i--,j--) { if(DN[i] == '.') { name[j] = k; k = 0; } else { name[j] = DN[i]; k++; } } name[0] = k; return (strlen(DN) + 2); } void printName(int len, char *name) { int i; for(i = 0; i < len; i++) printf("%x.",name[i]); printf("\n"); } int sendDNSPacket(unsigned char *buf, int len, char *recvMsg) { int s; struct sockaddr_in sin; memset(&sin,0,sizeof(sin)); sin.sin_addr.s_addr = inet_addr("127.0.0.1"); sin.sin_family = AF_INET; sin.sin_port = htons(SERVER_PORT); s = socket(PF_INET,SOCK_DGRAM,0); sendto(s,buf,len,0,(struct sockaddr *)&sin,sizeof(sin)); return recv(s,recvMsg,MAX_SIZE,0); } void resolve(unsigned char *recvMsg, int len, int len_recvMsg) { int pos = len; int cnt = 12; while(pos < len_recvMsg) { unsigned char now_pos = recvMsg[pos+1]; unsigned char retype = recvMsg[pos+3]; unsigned char reclass = recvMsg[pos+5]; unsigned char offset = recvMsg[pos+11]; if(retype == 1) { if(now_pos == cnt && reclass == 1) { printf("%u.%u.%u.%u\n",recvMsg[pos+12],recvMsg[pos+13],recvMsg[pos+14],recvMsg[pos+15]); } } else if(retype == 5) { cnt = pos + 12 ; } pos = pos + 12 + offset; } } int main() { unsigned char buf[MAX_SIZE]; /* socket發送的數據 */ char DN[MAX_SIZE]; /* 將要解析的域名(www.xxx.xxx) */ char name[MAX_SIZE]; /* 轉換爲符合DNS報文格式的域名 */ char recvMsg[MAX_SIZE]; /* 接收的數據 */ int len; /* socket發送數據的長度 */ int s; /* socket handler */ printf("輸入須要解析的域名:"); scanf("%s",DN); len = changeDN(DN,name); //printName(len,name); /* 打印轉換後的域名,檢測是否轉換正確 */ int j; //printf("len is %d\n",len); setHead(buf); setQuery(name,buf,len); len += 16; int len_recvMsg = sendDNSPacket(buf,len,recvMsg); printf("接收的報文長度爲 %d 字節\n",len_recvMsg); printf("下面是接收報文的16進製表示:\n"); int i; for(i = 0; i < len_recvMsg; i++) { printf("%x.",(unsigned char)recvMsg[i]); } printf("\n"); printf("%s的IP地址爲:\n",DN); resolve(recvMsg,len,len_recvMsg); }
總結:剛開始感受無從下手
暫時就這麼多吧!
感謝下面的博客:
http://blog.csdn.net/ericzhong83/article/details/8108103介紹DNS報文
http://blogfeifei.iteye.com/blog/1213628更詳細的介紹
http://blog.csdn.net/kevinshq/article/details/7199573windows系統下的實現,不過沒試過,可是也給了啓發