Linux socket编程 DNS查询IP地址
本来是一次计算机网络的实验,但是还没有完全写好,DNS的响应请求报文的冗余信息太多了,不只有IP地址。所以这次的实验主要就是解析DNS报文。同时也需要正确的填充请求报文。如果代码有什么bug,欢迎指正啊。代码排版有点乱。。。
本文有以下内容
DNS报文的填充和解析
利用socket API传输信息
一、填充DNS请求报文
随便百度一下,就可以知道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; |
然后填充报文的问题部分:
- 域名格式:该部分一数字开始以0结束。
- 查询类型:1代表IP地址、2代表名字服务器、5代表规范名称、12代表指针记录
- 查询类:1代表互联网
下面是填充域名为百度(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: [email protected] > 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); } |
总结:刚开始感觉无从下手
- 不知道如何与DNS服务器交换信息 就用socket向服务器发送报文
- 不知道如何发送请求报文 百度喽
- 不知道如何处理响应报文 自己查看报文格式
暂时就这么多吧!
感谢下面的博客:
http://blog.csdn.net/ericzhong83/article/details/8108103介绍DNS报文
http://blogfeifei.iteye.com/blog/1213628更详细的介绍
http://blog.csdn.net/kevinshq/article/details/7199573windows系统下的实现,不过没试过,但是也给了启发
郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。