Linux C DNS 查询IP地址
生活随笔
收集整理的這篇文章主要介紹了
Linux C DNS 查询IP地址
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
一、填充DNS請求報文
隨便百度一下,就可以知道DNS報文的格式。所以這里只介紹如何填充DNS報文。
首先是填充報文首部:
/* 填充首部的格式大致相同,下面的填充值是參考他人抓包分析的結果 */ 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;然后填充報文的問題部分:
- 域名格式:該部分一數字開始以0結束。
- 查詢類型:1代表IP地址、2代表名字服務器、5代表規范名稱、12代表指針記錄
- 查詢類:1代表互聯網
下面是填充域名為百度(www.baidu.com)的代碼
/* 填充域名 */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;?
二、利用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的創建、發送和接收過程。
?
三、解析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;} }?
四、完整的代碼和總結
下面是完整的代碼:
/*************************************************************************> 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 53void 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);}?
總結
以上是生活随笔為你收集整理的Linux C DNS 查询IP地址的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Python twisted框架使用解析
- 下一篇: SQL中的条件语句case-when-t