TCP_IP第二次作业


由于自己的偷懒,现在才开始补作业。
2017年1月9日19点

#第二次 作业域名查询
域名解析协议(DNS)用来把便于人们记忆的主机域名和电子邮件地址映射为计算机易于识别的IP地址。DNS是一种c/s的结构,客户机就是用户用于查找一个名字对应的地址,而服务器通常用于为别人提供查询服务。

本次服务器采用的地址是谷歌的8.8.4.4

主要是利用winsock实现客户端查询。首先介绍几个必要的函数

#1 初始化操作函数
按照之前的框架,进行winsock的初始化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 初始化操作
bool initWSA()
{
WSADATA wsaData;
int Result = WSAStartup( MAKEWORD( 2, 2 ), &wsaData );
if(Result!=0 )
{
return false;
}

if( LOBYTE( wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion)!= 2 )
{
if(WSACleanup()==SOCKET_ERROR)//注释4
cout<<"WSACleanup出错<<endl;
return false;
}
return true;
}

#2 创建套接字

1
2
3
4
5
6
7
8
9
10
//创建套接字,根据传来的类型创建,本程序需要创建UDP套接字
SOCKET CreateSocket(int type)
{
SOCKET sock=socket(AF_INET,type,0);
if (sock == INVALID_SOCKET )
{
return 0;
}
return sock;
}

#3 Dns头部结构

1
2
3
4
5
6
7
8
typedef struct _DNSHEAD{   //dns 头部
USHORT ID;
USHORT tag; // dns 标志(参数)
USHORT numQ; // 问题数
USHORT numA; // 答案数
USHORT numA1; // 权威答案数
USHORT numA2; // 附加答案数
}DnsHead;

#4 dns 查询结构

1
2
3
4
5
6
7
typedef struct _DNSQUERY	//dns 查询结构
{
USHORT type;
//查询类型,大约有20个不同的类型
USHORT classes;
//查询类,通常是A类既查询IP地
}DnsQuery;

#5 设置DNS 头部

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
bool SetDNSHead(char *name,char *buf)
{

memset(buf,0,sizeof(DnsHead));

//设置头部
DnsHead *DnsH = (DnsHead *)buf;
DnsH->ID = (USHORT)1;
DnsH->tag = htons(0x0100);
DnsH->numQ = htons(1);
DnsH->numA = 0;

DnsQuery *DnsQ =(DnsQuery *) ( buf+ sizeof(DnsHead) );
int NameLen = ChName(name,(char *)DnsQ);

//设置查询信息
DnsQ = (DnsQuery *)( (char *)DnsQ + NameLen );
DnsQ->classes = htons(1);
DnsQ->type = htons(1);
return true;
}

6 发送DNS 请求

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
int MySendto(SOCKET sockTo, const char FAR * buf,int len,char *addr,USHORT port)
{
if (addr[0]<'0'||addr[0]>'9') //addr 中不是IP 地址,域名解析
{
struct sockaddr_in addr_in;
HOSTENT *host = NULL;
host = gethostbyname(addr);//域名解析函数
if ( host->h_addr_list[0]!=NULL )
{
memcpy (&addr_in.sin_addr.s_addr,host->h_addr_list[0], host->h_length);
addr = inet_ntoa(addr_in.sin_addr);
}
}
//设置发送数据到的 套接字及地址结构
SOCKADDR_IN addrTo;
addrTo.sin_addr.S_un.S_addr=inet_addr(addr);
addrTo.sin_family=AF_INET;
addrTo.sin_port=htons(port);

return sendto(sockTo, buf, len, 0,(sockaddr*)&addrTo, sizeof(sockaddr));

}

#7 接受DNS请求

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
int MyRecvFrom(SOCKET s, char FAR * buf,int len,char *addr,USHORT port)
{
if (addr[0]<'0'||addr[0]>'9') //addr 中不是IP 地址,域名解析
{
struct sockaddr_in addr_in;
HOSTENT *host = NULL;
host = gethostbyname(addr);//域名解析函数
if ( host->h_addr_list[0]!=NULL )
{
memcpy ( &addr_in.sin_addr.s_addr,
host->h_addr_list[0], host->h_length);
addr = inet_ntoa(addr_in.sin_addr);
}
}

//设置发送数据到的 套接字及地址结构
SOCKADDR_IN addrFrom;
addrFrom.sin_addr.S_un.S_addr=inet_addr(addr);
addrFrom.sin_family=AF_INET;
addrFrom.sin_port=htons(port);
int addrlen = sizeof(SOCKADDR_IN);
return recvfrom( s, buf, len, 0, (SOCKADDR *)&addrFrom, &addrlen);
}

#8 主函数设计

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
int main() 
{
int Result=0;
char buf[1024]={0};
char addr[16] = "8.8.4.4";// dns 服务器地址
char *name = NULL; //要查询的域名

if ( !initWSA() )//初始化
{
displayErrWSA("initWSA err!");
return 1;
}

//创建套接字
SOCKET sockTo ;
if ( (sockTo = CreateSocket(SOCK_DGRAM)) == 0)
{
displayErrWSA("CreatSocket err!");
return 1;
}


while (1)
{

char temp[1024]={0};
cout<<"\n请输入要查询的域名:";
cin>>temp;
if (temp[0] == 'q' ||temp[0] == 'Q')
{
break;
}
name = temp;


//设置dns 头部
SetDNSHead(name,buf);

//发送出去的请求数据长度
int len = sizeof(DnsHead)+sizeof(DnsQuery)+strlen(name)+2;


//发送DNS 请求
if ( ( Result =MySendto(sockTo,buf,len,addr,53) ) <= 0)
{
displayErrWSA("sendto err!");
continue;
}

//接收应答
if ( (Result =MyRecvFrom(sockTo,buf,1024,addr,53) ) <= 0)
{
displayErrWSA("recvfrom err!");
continue;
}

//简单的取得返回的 IP 地址( 收到的最后4字节 )
DnsHead *DnsH = (DnsHead *)buf;
if (DnsH->numA == 0)
{
printf("无法解析 %s 的IP 地址。\n",name);
continue;
}
char *getIP =(char *)buf +Result - 4;
printf("%s 的IP地址为: ",name);
for (int Result= 0 ;Result<4 ;Result++)
{
printf("%u.",(UCHAR )getIP[Result]);
}
printf("\n");
}
return 0;
}

#9 运行实例
image_1b61qnj0e1jf1jg476a1c225ll9.png-26.7kB

#10 所有程序

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
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
#include   <iostream>
#include <Winsock2.h>
#include <windows.h>
#pragma comment(lib,"ws2_32.lib")
using namespace std;

typedef struct _DNSHEAD{ //dns 头部
USHORT ID;
USHORT tag; // dns 标志(参数)
USHORT numQ; // 问题数
USHORT numA; // 答案数
USHORT numA1; // 权威答案数
USHORT numA2; // 附加答案数
}DnsHead;

typedef struct _DNSQUERY //dns 查询结构
{

USHORT type;
//查询类型,大约有20个不同的类型
USHORT classes;
//查询类,通常是A类既查询IP地
}DnsQuery;



// 初始化操作
bool initWSA()
{
WSADATA wsaData;
int Result = WSAStartup( MAKEWORD( 2, 2 ), &wsaData );
if(Result!=0 )
{
return false;
}

if( LOBYTE( wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion)!= 2 )
{
if(WSACleanup()==SOCKET_ERROR)//注释4
cout<<"WSACleanup出错\n"<<endl;
return false;
}
return true;
}

bool SetDNSHead(char *name,char *buf)
{

memset(buf,0,sizeof(DnsHead));

//设置头部
DnsHead *DnsH = (DnsHead *)buf;
DnsH->ID = (USHORT)1;
DnsH->tag = htons(0x0100);
DnsH->numQ = htons(1);
DnsH->numA = 0;

DnsQuery *DnsQ =(DnsQuery *) ( buf+ sizeof(DnsHead) );
int NameLen = ChName(name,(char *)DnsQ);

//设置查询信息
DnsQ = (DnsQuery *)( (char *)DnsQ + NameLen );
DnsQ->classes = htons(1);
DnsQ->type = htons(1);
return true;
}



//显示错误信息
void displayErrWSA(char *str)
{
printf("\n%s,err = %d\n",str,WSAGetLastError());
getchar();
}


//域名转化
int ChName(char *fname,char *tname)
{

int j =0;
int i =strlen(fname)-1;
tname[i+2] = 0;
int k = i+1;
for (; i>=0;i--,k--)
{
if (fname[i] == '.')
{
tname[k] = j;
j=0;
}
else
{
tname[k] = fname[i];
j++;
}
}
tname[k] = j;
return strlen(tname)+1;
}


//创建套接字
SOCKET CreateSocket(int type)
{
SOCKET sock=socket(AF_INET,type,0);
if (sock == INVALID_SOCKET )
{
return 0;
}
return sock;
}


//UDP sendto
int MySendto(SOCKET sockTo, const char FAR * buf,int len,char *addr,USHORT port)
{
if (addr[0]<'0'||addr[0]>'9') //addr 中不是IP 地址,域名解析
{
struct sockaddr_in addr_in;
HOSTENT *host = NULL;
host = gethostbyname(addr);//域名解析函数
if ( host->h_addr_list[0]!=NULL )
{
memcpy ( &addr_in.sin_addr.s_addr,
host->h_addr_list[0], host->h_length);
addr = inet_ntoa(addr_in.sin_addr);
}
}
//设置发送数据到的 套接字及地址结构
SOCKADDR_IN addrTo;
addrTo.sin_addr.S_un.S_addr=inet_addr(addr);
addrTo.sin_family=AF_INET;
addrTo.sin_port=htons(port);

return sendto( sockTo, buf, len, 0,
(sockaddr*)&addrTo, sizeof(sockaddr) );

}


// UDP recvfrom
int MyRecvFrom(SOCKET s, char FAR * buf,int len,char *addr,USHORT port)
{
if (addr[0]<'0'||addr[0]>'9') //addr 中不是IP 地址,域名解析
{
struct sockaddr_in addr_in;
HOSTENT *host = NULL;
host = gethostbyname(addr);//域名解析函数
if ( host->h_addr_list[0]!=NULL )
{
memcpy ( &addr_in.sin_addr.s_addr,
host->h_addr_list[0], host->h_length);
addr = inet_ntoa(addr_in.sin_addr);
}
}

//设置发送数据到的 套接字及地址结构
SOCKADDR_IN addrFrom;
addrFrom.sin_addr.S_un.S_addr=inet_addr(addr);
addrFrom.sin_family=AF_INET;
addrFrom.sin_port=htons(port);
int addrlen = sizeof(SOCKADDR_IN);
return recvfrom( s, buf, len, 0, (SOCKADDR *)&addrFrom, &addrlen);
}


int main()
{
int Result=0;
char buf[1024]={0};
char addr[16] = "8.8.4.4";// dns 服务器地址
char *name = NULL; //要查询的域名

if ( !initWSA() )//初始化
{
displayErrWSA("initWSA err!");
return 1;
}

//创建套接字
SOCKET sockTo ;
if ( (sockTo = CreateSocket(SOCK_DGRAM)) == 0)
{
displayErrWSA("CreatSocket err!");
return 1;
}


while (1)
{

char temp[1024]={0};
cout<<"\n请输入要查询的域名:";
cin>>temp;
if (temp[0] == 'q' ||temp[0] == 'Q')
{
break;
}
name = temp;


//设置dns 头部
SetDNSHead(name,buf);

//发送出去的请求数据长度
int len = sizeof(DnsHead)+sizeof(DnsQuery)+strlen(name)+2;


//发送DNS 请求
if ( ( Result =MySendto(sockTo,buf,len,addr,53) ) <= 0)
{
displayErrWSA("sendto err!");
continue;
}

//接收应答
if ( (Result =MyRecvFrom(sockTo,buf,1024,addr,53) ) <= 0)
{
displayErrWSA("recvfrom err!");
continue;
}

//简单的取得返回的 IP 地址( 收到的最后4字节 )
DnsHead *DnsH = (DnsHead *)buf;
if (DnsH->numA == 0)
{
printf("无法解析 %s 的IP 地址。\n",name);
continue;
}
char *getIP =(char *)buf +Result - 4;
printf("%s 的IP地址为: ",name);
for (int Result= 0 ;Result<4 ;Result++)
{
printf("%u.",(UCHAR )getIP[Result]);
}
printf("\n");
}
return 0;
}