标签:des class blog code http tar
本来是一次计算机网络的实验,但是还没有完全写好,DNS的响应请求报文的冗余信息太多了,不只有IP地址。所以这次的实验主要就是解析DNS报文。同时也需要正确的填充请求报文。如果代码有什么bug,欢迎指正啊。代码排版有点乱。。。
本文有以下内容
DNS报文的填充和解析
利用socket API传输信息
随便百度一下,就可以知道DNS报文的格式。所以这里只介绍如何填充DNS报文。
首先是填充报文首部:
1
2
3
4
5
6
7
8
9 |
/* 填充首部的格式大致相同,下面的填充值是参考他人抓包分析的结果 */ 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; |
然后填充报文的问题部分:
下面是填充域名为百度(www.baidu.com)的代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 |
/* 填充域名 */ 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报文
下面是代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 |
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(这个域名有点复杂)。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 |
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+13],recvMsg[pos+14]); } } else
if (retype == 5) { cnt = pos + 12 ; } pos = pos + 12 + offset; } } |
四、完整的代码和总结
下面是完整的代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137 |
/************************************************************************* > 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+13],recvMsg[pos+14]); } } 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系统下的实现,不过没试过,但是也给了启发
Linux socket编程 DNS查询IP地址,布布扣,bubuko.com
标签:des class blog code http tar
原文地址:http://www.cnblogs.com/mr-zys/p/3784629.html