dns
DNS协议详解
一、前言
foxmail新版中有一个《邮件特快专递》的功能。起先搞不懂如何用,后来知道要在工具->系统选项那边设置本地DNS服务器的IP地址。
觉得这个新功能蛮好用的。不需要通过SMTP代理,可以直接通过本地往邮箱所在的邮件交换器发送邮件。在暑假一开始想在VC++中实现这个功能。用IRIS截包后,发现程序后mx8.263.net发送邮箱,不知道这个是什么东西所以作罢。 后来才想到这个就是263.net的MX记录主机,原来特快专递的原理就是往这个主机上发送数据就行。
运行nslookup程序:
settype=mx
263.net
有了,有了,得到结果:
Non-authoritativeanswer:
263.netMXpreference=10,mailexchanger=mx06.263.net
263.netMXpreference=10,mailexchanger=mx08.263.net
263.netMXpreference=10,mailexchanger=mx09.263.net
263.netMXpreference=10,mailexchanger=mx11.263.net
263.netMXpreference=10,mailexchanger=mx12.263.net
263.netMXpreference=40,mailexchanger=mx03.263.net
263.netMXpreference=10,mailexchanger=mx01.263.net
没有错了。就是这个了。后来因为不知道怎么实现nslookup的功能,就放弃了,学了半个多月的C#。后来偶然在网上查找到了一些相关的文档。几次实验。把我的开发过程拿过来分享,我第一次写教程性文档。所以不规范之处,请大家包涵。本文涉及的域名、邮箱及IP均为真实的。
二、DNS协议原理
我认为,要想成为一个好的网络软件程序员,必须得读懂RFC文档。因为本文是面向大多广泛程序爱好者,所以我尽量从细节上写,如果高手的话,可以跳过此部分。
DNS协议的相关RFC文档:
RFC1034-《DOMAINNAMES-CONCEPTSANDFACILITIES》
RFC1035-《DOMAINNAMES-IMPLEMENTATIONANDSPECIFICATION》
网上的计算机用形如 220.162.75.1 这样称为IP地址的数字串来标识一台计算机。而如果每次访问一台计算机都是通过输入这样的东东来访问,那不就太可怕了?以是出了DNS这样的好东东,用要指示其绑定的IP地址,当我们在浏览器内输入http://zzsy.com时,浏览器不知道网页该到哪里取,于是就向设定好的DNS服务器查询zzsy.com这个域名。DNS服务器会先寻找自己的记录库,如果没有发现就转向上一级DNS服务器进行查询(转发请求)。把找到后的IP告知你的浏览器。这里边浏览器查询的记录类型是A记录。RFC1035文档第11页中定义有16种记录类型,而常见的有A(地址)记录、CNAME(别名)记录、MX(邮件交换)记录。我们本篇要关心的是MX记录。
查询的过程一般是:客户向DNS服务器的53端口发送UDP报文,DNS服务器收到后进行处理,并把结果记录仍以UDP报文的形式返回过来。
此UDP报文的一般格式:
+---------------------+
|报文头|
+---------------------+
|问题 |向服务器提出的查询部分
+---------------------+
|回答 |服务器回复的资源记录
+---------------------+
|授权|权威的资源记录
+---------------------+
|格外的|格外的资源记录
+---------------------+
除了报文头是固定的12字节外,其他每一部分的长度均为不定字节数。
我们在这边关心的是报文头、问题、回答这三个部分。
其中报文头的格式:
111111
0123456789012345
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|ID|
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|QR|Opcode|AA|TC|RD|RA|Z|RCODE|
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|QDCOUNT|
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|ANCOUNT|
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|NSCOUNT|
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|ARCOUNT|
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
好家伙,是什么鬼画符!
其中最上边是位的数字标识,0-15(注意,后边的10-15写成上下的形式了,一开始我楞没看懂)。
接下来是:
ID:占16位,2个字节。此报文的编号,由客户端指定。DNS回复时带上此标识,以指示处理的对应请应请求。
QR:占1位,1/8字节。0代表查询,1代表DNS回复
Opcode:占4位,1/2字节。指示查询种类:0:标准查询;1:反向查询;2:服务器状态查询;3-15:未使用。
AA:占1位,1/8字节。是否权威回复。
TC:占1位,1/8字节。因为一个UDP报文为512字节,所以该位指示是否截掉超过的部分。
RD:占1位,1/8字节。此位在查询中指定,回复时相同。设置为1指示服务器进行递归查询。
RA:占1位,1/8字节。由DNS回复返回指定,说明DNS服务器是否支持递归查询。
Z:占3位,3/8字节。保留字段,必须设置为0。
RCODE:占4位,1/2字节。由回复时指定的返回码:0:无差错;1:格式错;2:DNS出错;3:域名不存在;4:DNS不支持这类查询;5:DNS拒绝查询;6-15:保留字段。
QDCOUNT:占16位,2字节。一个无符号数指示查询记录的个数。
ANCOUNT:占16位,2字节。一个无符号数指明回复记录的个数。
NSCOUNT:占16位,2字节。一个无符号数指明权威记录的个数。
ARCOUNT:占16位,2字节。一个无符号数指明格外记录的个数。
其中每个查询的资源记录格式:
111111
0123456789012345
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
||
/QNAME/
//
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|QTYPE|
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|QCLASS|
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
QNAME:不定长,表示要查询的域名。(两边的方框用/来表示不定长)
QTYPE:2字节,根据RFC1035及nslookup的帮助文档,我定义以下枚举类型:
enumQueryType//查询的资源记录类型。
{
A=0x01,//指定计算机IP地址。
NS=0x02,//指定用于命名区域的DNS名称服务器。
MD=0x03,//指定邮件接收站(此类型已经过时了,使用MX代替)
MF=0x04,//指定邮件中转站(此类型已经过时了,使用MX代替)
CNAME=0x05,//指定用于别名的规范名称。
SOA=0x06,//指定用于DNS区域的“起始授权机构”。
MB=0x07,//指定邮箱域名。
MG=0x08,//指定邮件组成员。
MR=0x09,//指定邮件重命名域名。
NULL=0x0A,//指定空的资源记录
WKS=0x0B,//描述已知服务。
PTR=0x0C,//如果查询是IP地址,则指定计算机名;否则指定指向其它信息的指针。
HINFO=0x0D,//指定计算机CPU以及操作系统类型。
MINFO=0x0E,//指定邮箱或邮件列表信息。
MX=0x0F,//指定邮件交换器。
TXT=0x10,//指定文本信息。
UINFO=0x64,//指定用户信息。
UID=0x65,//指定用户标识符。
GID=0x66,//指定组名的组标识符。
ANY=0xFF//指定所有数据类型。
};
QTYPE:2字节。根据RFC1035及nslookup的帮助文档,我定义以下枚举类型:
enumQueryClass//指定信息的协议组。
{
IN=0x01,//指定Internet类别。
CSNET=0x02,//指定CSNET类别。(已过时)
CHAOS=0x03,//指定Chaos类别。
HESIOD=0x04,//指定MITAthenaHesiod类别。
ANY=0xFF//指定任何以前列出的通配符。
};
QTYPE中的A,MX,CNAME为常用,QCLASS中的IN为常用。
其中每个回复的记录格式:
111111
0123456789012345
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
||
//
/NAME/
||
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|TYPE|
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|CLASS|
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|TTL|
||
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|RDLENGTH|
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--|
/RDATA/
//
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
NAME:回复查询的域名,不定长。
TYPE:回复的类型。2字节,与查询同义。指示RDATA中的资源记录类型。
CLASS:回复的类。2字节,与查询同义。指示RDATA中的资源记录类。
TTL:生存时间。4字节,指示RDATA中的资源记录在缓存的生存时间。
RDLENGTH:长度。2字节,指示RDATA块的长度。
RDATA:资源记录。不定义,依TYPE的不同,此记录的格示不同,通常一个MX记录是由一个2字节的指示该邮件交换器的优先级值及不定长的邮件交换器名组成的。
这边述说一下名称的组合形式。名称由多个标识序列组成,每一个标识序列的首字节说明该标识符的长度,接着用是ASCII码表示字符,多个序列之后由字节0表示名字结束。其中某一个标识序列的首字符的长度若是0xC0的话,表示下一字节指示不是标识符序列,而是指示接下部分在本接收包内的偏移位置。
比如 bbs.zzsy.com 以.分开bbs、zzsy、com三个部分。每个部分的长度为3、4、3
在DNS报文中的形式就如3bbs4zzsy3com0
假如在包内的第12个字节位置存在有4zzsy3com0这样的名称了。
那此时有可能为:3bbs4zzsy0xC00x0C这样的形式。
三、DNS协议实例讲解
说了这么多理论屁话,可能头都有两个大了吧。还是用一个实例的方法来说明吧。
我选用著名的网络截包及协议分析工具IRIS4.05,您可以从我的站点上下载:
http://itboy.cn/data/Iris405Full.rar
运行Iris,点击菜单的Filters选Port标签页运用53端口后点确定。
点击Iris工具栏上的绿色运行图标进行监听。
在windows中运行nslookup程序。
输入以下命令:
settype=mx
然后返回nslookup程序。
再输入命令:
yahoo.com.cn
会得到
yahoo.com.cnMXpreference=20,mailexchanger=mx5.mail.yahoo.com
yahoo.com.cnMXpreference=10,mailexchanger=mta-v1.mail.
=====================================================================
DNS报文格式:
该报文由12字节的首部和4个长度可变的字段组成。
标识字段由客户程序设置并有服务器返回结果。
16bit的标志字段如下:
QR:0表示查询报文,1表示响应报文
Opcode:通常值为0(标准查询),其他值为1(反向查询)和2(服务器状态请求)。
AA:表示授权回答(authoritativeanswer).
TC:表示可截断的(truncated)
RD:表示期望递归
RA:表示可用递归
随后3bit必须为0
Rcode:返回码,通常为0(没有差错)和3(名字差错)
后面4个16bit字段说明最后4个变长字段中包含的条目数。
问题部分:
报文格式:
查询名为要查找的名字,它由一个或者多个标示符序列组成。每个标示符已首字节数的计数值来说明该标示符长度,每个名字以0结束。计数字节数必须是0~63之间。该字段无需填充字节。如:gemini.tuc.noao.edu
每个问题有一个查询类型,通常查询类型为A(由名字获得IP地址)或者PTR(获得IP地址对应的域名)
资源记录部分:
报文格式:
DNS最后3个字段,回答字段,授权字段和附加信息字段均采用资源记录RR(ResourceRecord)的相同格式。
域名是记录中资源数据对应的名字。它的格式和查询名字段格式相同。
类型说明RR的类型码。类通常为1,指Internet数据。
生存时间字段是客户程序保留该资源记录的秒数。
资源数据长度说明资源数据的数量。该数据的格式依赖于类型字段的值。对于类型1(A记录)资源数据是4字节的IP地址。
数据包DNS查询:(DNSquery)
00000019566e19bf0017a41ab2e008004500..Vn..........E.
0010003bedc600008011e3c3ac150f04ac15.;..............
002001f904a9003500272fbd3e3a01000001.....5.'/.>:....
00300000000000000377777706676f6f676c.......www.googl
00406502636e0000010001e.cn.....
说明:
前面三段分别为以太网包头,ip包头和UDP包头。
从0020行后面开始为DNS数据包.
3e3a为标识字段
0100为标志字段,该字段设置了TC表示该报文是可截断的。
0001查询报文数量为1。
000000000000表示回答,授权和额外信息都为0。
0377777706676f6f676c6502636e00表示查询的名字为
www.google.com
0001为类型,1表示A查询
0001为类,1表示Internet数据。
数据包DNSresponse(DNSresponse)
00000017a41ab2e00019566e19bf08004500........Vn....E.
0010007848af00007d118b9eac1501f9ac15.xH...}.........
00200f04003504a9006475db3e3a81800001...5...du.>:....
00300003000000000377777706676f6f676c.......www.googl
00406502636e0000010001c00c0005000100e.cn............
0050000542001102636e016c06676f6f676c..B...cn.l.googl
00606503636f6d00c02b000100010000005fe.com..+......._
00700004cbd02165c02b000100010000005f....!e.+......._
00800004cbd02164....!d
说明:
前面三段分别为以太网包头,ip包头和UDP包头。
3e3a为标识字段
8180为标志字段,其中设置了QR=1,RD=1,RA=1
0001问题数1,0003回答数3,其余两个为0。
0377777706676f6f676c6502636e00表示查询的名字为
www.google.com
0001为类型,1表示A查询
0001为类,1表示Internet数据。
接下来为回答报文,
c00c为域名指针
0005表示CNAME(规范名称)
0001类,表示为Internet数据
00000542生存时间
0011数据长度
02636e016c06676f6f676c6503636f6d00为数据cn.l.google.cn
然后接下来两段为另外两个回答。
最后的数据为IP地址