Linux socket編程 DNS查詢IP地址

原本是一次計算機網絡的實驗,可是尚未徹底寫好,DNS的響應請求報文的冗餘信息太多了,不僅有IP地址。因此此次的實驗主要就是解析DNS報文。同時也須要正確的填充請求報文。若是代碼有什麼bug,歡迎指正啊。代碼排版有點亂。。。windows

本文有如下內容服務器

  DNS報文的填充和解析網絡

  利用socket API傳輸信息
dom

1、填充DNS請求報文

隨便百度一下,就能夠知道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

  • 域名格式:該部分一數字開始以0結束。
  • 查詢類型:1表明IP地址、2表明名字服務器、5表明規範名稱、12表明指針記錄
  • 查詢類:1表明互聯網

下面是填充域名爲百度(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);

}

 總結:剛開始感受無從下手

  • 不知道如何與DNS服務器交換信息 就用socket向服務器發送報文
  • 不知道如何發送請求報文 百度嘍
  • 不知道如何處理響應報文 本身查看報文格式

暫時就這麼多吧!

感謝下面的博客:

http://blog.csdn.net/ericzhong83/article/details/8108103介紹DNS報文

http://wenku.baidu.com/link?url=RNPg5HHx3YkkupGsmr4sePZqplCLefkM4SI458prY4IgQz0qgNRJg8hnBAcPKz7Ry_q8khoTUCLiB3ZmvMpTBrM8LHiZ6tj_TXtlDRyU0QG介紹DNS報文

http://blogfeifei.iteye.com/blog/1213628更詳細的介紹

http://blog.csdn.net/kevinshq/article/details/7199573windows系統下的實現,不過沒試過,可是也給了啓發

相關文章
相關標籤/搜索