DNS课程设计报告.docx
《DNS课程设计报告.docx》由会员分享,可在线阅读,更多相关《DNS课程设计报告.docx(23页珍藏版)》请在冰点文库上搜索。
DNS课程设计报告
DNS服务器程序
实验报告
班级:
2011211301
小组成员:
曹晓欢2011211139
杨静怡2011211140
系统功能设计
⏹设计一个DNS服务器程序,读入“IP地址-域名”对照表,当客户端查询域名对应的IP地址时,用域名检索该对照表,有三种可能检索结果:
◆检索结果:
ip地址0.0.0.0,则向客户端返回“域名不存在”的报错消息(不良网站拦截功能)
◆检索结果:
普通IP地址,则向客户端返回该地址(服务器功能)
◆表中未检到该域名,则向因特网DNS服务器发出查询,并将结果返给客户端(中继功能)
Ø考虑多个计算机上的客户端会同时查询,需要进行消息ID的转换
系统和运行环境描述
Windows7操作系统平台,VS2010编程环境。
使用C/C++编写dns中继服务器。
主要数据结构
SOCKETsockfd;
Socket套接字
SOCKADDR_INser_addr,nser_addr;
表达地址结构信息,等价于sockaddr结构
typedefstructreq_inform
{//DNS请求包信息
SOCKADDR_INcli_addr;
unsignedshortid;//id和cli_addr唯一标识一个DNS请求
}req_inform;
该结构唯一标示了一个来自客户端的dns请求。
mapurl_ip_table;//本地域名解析表
用来构建本地存储的dnsrelay.txt中域名和IP的映射。
mapreq_cache[cache_num];//id转换表
这一个map映射,把客户端dns请求映射到一个unsignedshort上面,用它来存储id转换信息。
Unsignedshort类型的key值为新的id号,用于标识转发给外部服务器,而客户端的信息和旧id作为value存在map中,与key形成映射。
待服务器发回应答包,根据包中id号,即可找到原id与客户端信息。
#definecache_num2//转换表数目
#definecache_size500//每个转换表容量
intidThen_max=cache_num*cache_size;//总条目数
intcur_cache=0;
intidThen=0;
cache_num指定了id转换表的个数,cache_size是每个id转换表的大小,
cur_cache指向是当前正在装入的id转换表,idThen是一个从0到1000一直循环的被映射到的id号。
这样的设计可循环利用id转换表,并及时清除旧记录。
具体流程是:
生成id转换的item(idThen,structreq_inform的一个变量)
把id转换的item加入到req_cache[cur_cache]中
如果req_cache[cur_cache]已经达到cache_size
{
cur_cache指向下一个id转换表,并将其清空
}
idThen加1
函数划分(模块划分)
intget_url_ip_table(map&iptable)//失败返回-1
从文件中读入url和ip的映射表。
intinit();
用来初始化ser_addr、nser_addr、sockfd,以及对sockfd绑定到本地的ser_addr的
地址上。
intis_req(char*buffer);
用来判断收到的包是上级服务器的回答包,还是来自客户端的请求包。
voidget_url(char*buf,string&url);
从请求包中提取URL。
voidask_next_server(char*buffer,structsockaddr_inreq_addr,intbuffer_size);
询问上级dns服务器。
voidcreate_respose(char*buffer,structsockaddr_inreq_addr,int
buffer_size,stringip);
根据url-ip表中找到的结果自己构造响应。
voiddeal_req(char*buffer,structsockaddr_inreq_addr,intbuffer_size);
客户端请求包的处理
voiddeal_res(char*buffer,intbuffer_size);
上级服务器回答包的处理。
intmain();
主函数
软件流程图
失败
成功
失败
成功
请求包应答包
NO
YES
否
是
源代码
#include
#include
#include
#include
#include
#include
#include
#include"WinSock2.h"//socket头文件
#pragmacomment(lib,"ws2_32.lib")//链接Ws2_32.lib库,不必再setting里设置
usingnamespacestd;
//id转换表参数定义
#definecache_num2//转换表数目
#definecache_size500//每个转换表容量
intidThen_max=cache_num*cache_size;//总条目数
intcur_cache=0;
intidThen=0;
SOCKETsockfd;
SOCKADDR_INser_addr,nser_addr;/*使用sockaddr_in表达地址结构
信息,等价于sockaddr结构*/
constchar*nx_ip="211.68.71.4";
constchar*file_name="dnsrelay.txt";
typedefstructreq_inform
{//DNS请求包信息
SOCKADDR_INcli_addr;
unsignedshortid;//id和cli_addr唯一标识一个DNS请求
}req_inform;
mapurl_ip_table;//本地域名解析表
mapreq_cache[cache_num];//id转换表
/*
structdns_ans_add
{//构造响应包所要添加的信息除了请求包外,包括指针、类型、生存时间、类、资源大小、资源等等
unsignedshorturl_pointer;
unsignedshorttype;
unsignedshortclas_s;
unsignedlongttl;
unsignedshortsourse_size;
unsignedlongsourse;
};
*/
intget_url_ip_table(map&iptable)//失败返回-1
{
stringtmpip,tmpurl;
fstreamfs;
fs.open("dnsrelay.txt");
if(!
fs.is_open())
{
cout<<"txtcan'topen."<return-1;
}
else
{
iptable.clear();
while(!
fs.eof())
{
fs>>tmpip;
fs>>tmpurl;
iptable.insert(pair(tmpurl,tmpip));
}
fs.close();
return0;
}
}
intinit()//失败返回-1
{
WSADatawsa;//socket初始化
if(WSAStartup(MAKEWORD(2,2),&wsa)!
=0)//Winsock服务的初始化
{
WSACleanup();
cout<<"winsock初始失败"<return0;
}
for(inti=0;ireq_cache[i].clear();
sockfd=socket(AF_INET,SOCK_DGRAM,0);//创建socket套接字
ser_addr.sin_family=AF_INET;//sin_family指代协议族
ser_addr.sin_port=htons(53);//使用53端口即DNS服务器端口
ser_addr.sin_addr.s_addr=INADDR_ANY;//0.0.0.0,sin_addr存储IP地址
nser_addr.sin_family=AF_INET;
nser_addr.sin_port=htons(53);
nser_addr.sin_addr.s_addr=inet_addr(nx_ip);//返回值作Internet地址
if(bind(sockfd,(SOCKADDR*)&ser_addr,sizeof(ser_addr))==-1){
cout<<"绑定端口失败"<return-1;
}
else
cout<<"绑定端口成功"<return0;
}
intis_req(char*buffer)
{
unsignedshortflags=0;
memcpy(&flags,buffer+2,2);//取标志位信息
if(flags==0x0001)//3号位002号位01请求包(0001为网络顺序)本地:
0100
return1;
else
return0;
}
voidget_url(char*buf,string&url)
{//从请求包中提取URL
intindex=0;
unsignedcharnum,i;
url.clear();
num=buf[index++];//第一节字段长度
while(num)//当num=0为结尾跳出
{
for(i=0;iurl.push_back(buf[index++]);//push_back字符串之后插入一个字符
num=buf[index++];
if(num!
=0)//说明没结束
url.push_back('.');
}
}
voidask_next_server(char*buffer,sockaddr_inreq_addr,intbuffer_size)
{
structreq_informtem;
cout<<"本地域名解析表未找到,询问外部服务器..."<tem.cli_addr=req_addr;//请求端地址
memcpy(&(tem.id),buffer,2);//原请求端id
memcpy(buffer,&idThen,2);//赋予新的id号idThen
if(sendto(sockfd,buffer,buffer_size,0,(structsockaddr*)&nser_addr,sizeof(structsockaddr))==-1)
{
cout<<"未成功发送请求至外部服务器"<return;
}
req_cache[cur_cache].insert(pair(idThen,tem));//增加本id转换项,至映射表项
if(req_cache[cur_cache].size()>=cache_size)//当前本映射表已满,跳到另一张表
{
cur_cache=(cur_cache+1)%cache_num;//指向新的一张表
req_cache[cur_cache].clear();//新表的历史记录清空
}
idThen=(idThen+1)%idThen_max;//下一个转发请求的id号
}
voidcreate_respose(char*buffer,sockaddr_inreq_addr,intbuffer_size,stringip)
{
charans[1000];//响应包
unsignedshortflags=0x8081;//响应包标志位8180(8180为x86顺序)
unsignedshortans_num=0x0100;//正常的响应数设置响应个数1
cout<<"在本地域名解析表缓存中找到。
。
"<memcpy(ans,buffer,buffer_size);//拷贝请求包
memcpy(ans+2,&flags,2);//改标志位
if(pare("0.0.0.0")==0)
{//若是屏蔽,则直接返回
ans_num=0x0000;//重置回复数为0,屏蔽
memcpy(&ans[6],&ans_num,sizeof(unsignedshort));
if(sendto(sockfd,ans,buffer_size,0,(structsockaddr*)&req_addr,sizeof(structsockaddr))==-1)
{
cout<<"发送至客户端失败!
"<return;
}
printf("url已屏蔽...\n");
return;
}
else
{//构造响应包
charc_ip[50];
ip.copy(c_ip,ip.length());
c_ip[ip.length()]='\0';
printf("该请求包所要找的的ip是%s...\n",c_ip);
memcpy(ans+6,&ans_num,sizeof(unsignedshort));//修改、添加信息,包括响应数、多出的响应字段等等
//构造DNS响应部分
intcurLen=0;
charanswer[16];
unsignedshortName=htons(0xc00c);
memcpy(answer,&Name,sizeof(unsignedshort));
curLen+=sizeof(unsignedshort);
unsignedshortTypeA=htons(0x0001);
memcpy(answer+curLen,&TypeA,sizeof(unsignedshort));
curLen+=sizeof(unsignedshort);
unsignedshortClassA=htons(0x0001);
memcpy(answer+curLen,&ClassA,sizeof(unsignedshort));
curLen+=sizeof(unsignedshort);
unsignedlongtimeLive=htonl(0x7b);
memcpy(answer+curLen,&timeLive,sizeof(unsignedlong));
curLen+=sizeof(unsignedlong);
unsignedshortIPLen=htons(0x0004);
memcpy(answer+curLen,&IPLen,sizeof(unsignedshort));
curLen+=sizeof(unsignedshort);
unsignedlongIP=(unsignedlong)inet_addr(c_ip);
memcpy(answer+curLen,&IP,sizeof(unsignedlong));
curLen+=sizeof(unsignedlong);
curLen+=buffer_size;
//请求报文和响应部分共同组成DNS响应报文存入sendbuf
memcpy(ans+buffer_size,answer,curLen);
if(sendto(sockfd,ans,curLen,0,(structsockaddr*)&req_addr,sizeof(structsockaddr))==-1)
{
cout<<"发送客户端失败!
"<return;
}
}
}
voiddeal_req(char*buffer,structsockaddr_inreq_addr,intbuffer_size)
{
stringurl;
map:
:
iteratoruit_iter;
printf("收到请求包,正在处理...\n");
get_url(buffer+12,url);//包头12个字节
printf("请求解析的url是:
");
cout<uit_iter=url_ip_table.find(url);//找ip
if(uit_iter==url_ip_table.end())
{
ask_next_server(buffer,req_addr,buffer_size);//未找到,向上级服务器询问
}
else//自己构造响应
{
cout<<"在本地缓存找到ip:
"+uit_iter->second<create_respose(buffer,req_addr,buffer_size,uit_iter->second);
}
}
voiddeal_res(char*buffer,intbuffer_size)
{
unsignedshortcur_id,i;
map:
:
iteratormiter;
structreq_informcur_inform;
printf("从外部服务器得到响应包...");
memcpy(&cur_id,buffer,2);
for(i=0;i{//检索映射表找到相匹配的id信息,从而正确返回dns请求回复
miter=req_cache[i].find(cur_id);
if(miter!
=req_cache[i].end())
{
cur_inform=miter->second;//获得之前dns请求信息
memcpy(buffer,&(cur_inform.id),2);//得到旧id复制到buffer,修改id
if(sendto(sockfd,buffer,buffer_size,0,(structsockaddr*)&(cur_inform.cli_addr),sizeof(structsockaddr))==-1)
printf("发送至客户端失败!
\n");
else
printf("成功发送至客户端!
\n");
return;
}
}
printf("couldnotbefoundinthecache...\n");
}
intmain()
{
charrecv_buffer[512];
SOCKADDR_INrecv_addr;
intrecv_size;
printf("读入url-ip表...");
if(get_url_ip_table(url_ip_table)==-1)
{
perror("读入url-ip表错误!
\n");
return0;
}
printf("缓存一共有%d条记录\n",url_ip_table.size());//TXT中表项
//初始化socket和sockaddr_in,bind端口
printf("初始化socket...");
if(init()==-1)
return0;
printf("初始化成功...\n");
printf("dns服务器启动...\n");
while
(1)
{
intrecv_len=sizeof(sockaddr);
if((recv_size=recvfrom(sockfd,recv_buffer,sizeof(recv_buffer),0,(sockaddr*)&recv_addr,&recv_len))==-1)
{
perror("接收错误包!
\n");
continue;
}
elseif(is_req(recv_buffer))
{//处理请求包
deal_req(recv_buffer,recv_addr,recv_size);
}
else
{//处理响应包
deal_res(recv_buffer,recv_size);
}
printf("\n\n");
}
return0;
}
测试用例以及运行结果
(1)首先先把本地连接中的DNS服务器地址改成本地的127.0.0.1
此时无法正常使用网页:
(2)开启本实验设计的DNS服务器:
正常浏览网页:
能正常ping通网址:
服务器显示信息:
能正常使用nslookup命令查ip地址:
a.本地解析表存在的记录:
服务器信息:
b.查询本地解析表中,被屏蔽的记录
服务器信息:
C.本地没有记录,转发出去:
服务端信息:
遇到并解决的问题
1.Id转换问题
构造一个map映射:
mapreq_cache[cache_num];
把客户端dns请求映射到一个unsignedshort上面,用它来