1.程序体系结构
1.1 DNS体系结构
设计DNS的体系结构如图1-1所示。
图1-1 DNS体系结构逻辑图
其除了基本的域名解析功能,中包括对设定的域名的过滤以及DNS缓存等功能。具体功能流程将在后边程序具体实现中详细介绍。
1.2 Socket通信
图1-2 Socket通信逻辑功能图
设计的DNS实际即是一个服务器端又是一个客户端,这里主要起到一个桥梁作用,逻辑功能图如1-2所示。
2.数据结构设计
2.1 DNS报文数据结构
DNS报文数据结构参考RFC文档,设计的数据结构在头文件Structure.h中定义,下面详述介绍一下。
2.1.1 DNS Header
在RFC1034和RFC1035中对DNS进行了详细介绍,DNS报文格式如下:
其中对于Header结构具体如下:
对于C++/C语言来说自然想到的是位字段或者位域,表示一个字节中的各位数据。所以设计Header结构如图1-1所示:
图2-1 the structure of the DNS Header
2.1.2 Question Part
Question部分的数据结构如下所示:
当然这里要注意的是QNAME部分是可变的,所以设计数据需要特殊考虑,数据结构如图1-2所示。
图2-2 the structure of Question Part
2.1.3 Resource Record
在DNS报文协议中剩下的Answer,Authority以及Additional三部分格式都类似Resource
Record(RR)格式,形式如下所示:
其中,NAME和RDATA部分是可变的,其余部分固定。因此,设计数据结构如图1-3所示:
图2-3 the structure of RR
2.2 类的设计
通过前面介绍的程序体系结构的逻辑功能,如图1-1所示。这里自然可以想到类的设计就很明显了,类的分工明确。类的具体如下:
class DNS
类的定义与实现分别在文件DNS.h和DNS.cpp中,类的主要功能就是实现与客户端的通信。
class Filtrate
类的定义与实现分别在文件Filerate.h和Filtrate.cpp中,类的主要功能是实现对设定域名的过滤。
class Database
类的定义与实现分别在文件Database.h和Database.cpp中,类的主要功能是实现对相应DNS包的备份与缓存。
class QueryAndAnswer
类的定义与实现分别在QueryAndAnswer.h和QueryAndAnswer.cpp中,类的主要功能是实现与Foreign
Name
Server的通信,以及组织整个DNS工作流程,其中该类继承自类DNS,组合类Filtrate和类Database,可以说是整个程序的核心。
类的分工可以形象的如图2-4。
图2-4 类的分工逻辑图
3.程序具体设计与实现
3.1 程序执行流程
程序执行大致流程如图3-1所示。
图3-1 程序执行流程
程序首先初始化必要数据,完成初始化后偏自我阻塞,在53端口等待用户的查询的数据,当捕获用户的查询数据,首先解析其中Question中的域名,将此域名转换成正常顺序,然后到维护的过滤域名名单中查询,如果是要过滤的域名,此时不做任何处理,如果不是正常域名,则到自己缓存中去查询是否存在对此域名的缓存包,如果存在,则要进行适当的更改,比如标识字段,然后将封装完成的相应包发送给用户;如果不存在,则将此查询包转发给某个外部DSN,收到响应包后,先做必要缓存,然后再将此包转发给用户,至此,一次查询完成,程序继续等待用户请求。
3.2 程序设计
3.2.1 类DNS
类DNS主要实现与Client通信任务,捕获相应的数据包,进行必要解析已经将响应数据发送给Client,类的定义如图3-2所示。
图3-2 类DNS的定义
在类中,存储用户的查询数据包,分析其中要查询域名。同时,DNS也是一个基类,相应的数据成员设计成protected。
3.2.2 类Filtrate
类Filtrate的主要功能是实现过滤名单的查询,将维护过滤域名的文件中内容读入内存中,提供查询判断某一个域名是否在过滤名单中,这个功能在成员函数check()中实现。类的定义如图3-3所示。
图3-3 类Filtrate的定义
过滤文件名为filtrate.txt,其中添加了两个域名,分别为www.baidu.com和www.cntv.cn要过滤其他域名只需要向文件中添加必要信息即可,当然要重启程序。
3.2.3 类Database
类Database的主要功能是实现对相应DNS包的备份与缓存。类的具体定义如图3-4所示。
图3-4 类Database的定义
这部分功能本来打算不来实现的,后来发现这个实现过程真是异常的艰难,代码量超过了工程中其他工作量的总和。这里主要是实现对数据包的缓冲备份以及封装一个DNS响应包的功能,同时类中提供了相当数量的接口,具体代码请参考代码文件。
3.2.4 类QueryAndAnswer
图3-5 类QueryAndAnswer的定义
类QueryAndAnswer的主要功能是实现与Foreign Name
Server的通信,以及组织整个DNS工作流程,类的具体定义如图3-5所示。
类QueryAndAnswer继承自类DNS,组合了类Filtrate和Database的对象,是整个程序的核心。所有逻辑实现过程均在该类中实现。成员函数filtrate()通过Filtrate对象实现了对过滤域名的判断,成员函数RetriveDnsServersFormRegistry()用来获得在本地注册的DNS服务器地址,最关键的是成员函数operate(),整个程序的流程都是在这个成员函数中得到组织的。
3.3 注意内容
(1)DNS包中的域名跟正常的域名有很大不同,以www.google.com为例在包中的格式为3www6google3com’ ’。所以,在类Database中提供了两种方式的转换,这个两个成员函数分别是
(2)DNS响应包中的Answer域中第一个数据可能采用的是压缩方式,对于第一字节的最高两位如果都为’1’,则表明前两个字节是指针模式,指向Question域中的域名的位置,这样可以节省一部分空间。
(3)使用WinSock编程之前,在Window环境下要使用WSAStartup()装载必要库文件,同时这可能还会出问题,还要#pragma
comment(lib,
"WS2_32")来进一步完成上述工作,程序中的套接字编程使用的是最基本的数据报UDP套接字的阻塞方式。
4.结果分析与评价
4.1 实验结果
由于考虑到没有局域网的测试条件(当然后来也试过没问题),工程文件中提供了测试程序,下面是通过测试程序与本次实现的DNS程序之间的展示。
(1)程序启动会进行必要的初始化,执行结果如图4-1所示。
图4-1 DNS程序启动的必要初始化过程
(2)启动测试程序,然后输入要查询的域名,这里以www.google.com为例,在测试程序中会封装一个完成DNS
Query包发送给DNS服务器,这里也就是自己完成的这个DNS服务器,程序过程如图4-2所示。
图4-2 启动测试程序,输入查询内容
(3)DNS程序的操作显示,如图4-3所示。
图4-3 DNS程序操作显示
(4)测试程序收到DNS程序发送回来的DNS响应包,做必要解析后的结果如图4-4所示。
图4-4 测试程序收到返回结果
(5)测试程序发送一个要被DNS过滤的域名,前面已经讲过要过滤的域名,这里以www.baidu.com为例,操作结果如图4-5所示。
(6)DNS程序发现这是一个要过滤的域名,不做任何操作,如图4-6所示。
图4-6 DNS程序提示这是一个过滤的域名
4.2 分析与评价
(1)程序实现基本的DNS,使用的是简单的Socket阻塞通信模式,没有考虑并发以提高程序的运行效率。
(2)在Database中对DNS做缓存与备份出现了很多问题,有的现在还没有解决。单独封装一个DNS包,但是某些情况下不成功,这里可能对DNS协议需要进一步发掘。
这里要特别说明:
对于缓存与封装我试过两种方法,一种是完全自己封装一个DNS包,但是最后Additional
part貌似有问题导致包不能解析,我实在找不到错误在那里,前面部分没有问题。另一种方法就是对返回的DNS包做备份,但是这里同样有一个问题就是DNS包中有大量的' ',导致部分C语言函数难以完成,另外可以考虑string类的push_back()但是同样有一个问题,就是不知道包的大小,导致如果设定的包的大小过大,导致后面一堆“烫烫烫烫....”。当然这里两部分我都那代码实现了,如果谁要解决这个问题,我提供我写的源代码,期望你的参入。