我们通常使用机器的域名来访问这台机器,而不直接使用其IP地址,比如访问因特网上的各种网站。那么如何将机器的域名转换成IP地址呢?这就需要使用域名查询服务,域名查询服务有很多种实现方式,比如NIS(Network Information Service,网络信息服务)、DNS和本地静态文件等。
DNS是一套分布式的域名服务系统,每个DNS服务器上都存放着大量的机器名和IP地址的映射,并且是动态更新的,众多网络客户端程序都使用DNS协议来向DNS服务器查询目标主机的IP地址。
查询过程一般是:客户向DNS服务器的53端口发送UDP/TCP报文,DNS服务器收到后进行处理,并把结果记录仍以UDP/TCP报文的形式返回过来。
备注:每个DNS请求和响应都包括5个部分,Header固定12个字节,Header的字段QDcount、ANcount、NScount和ARcount决定了是否有Question、Answer、Authority和Addition等阶段。
(1)ID:长度为16位,是一个用户发送查询的时候定义的随机数,当服务器返回结果的时候,返回包的ID与用户发送的一致;(客户端可以区分不同的请求应答)
(2)QR:长度1位,值0是请求,1是应答;
Opcode:长度4位,值0是标准查询(QUERY),1是反向查询(IQUERY),2是服务器状态查询(STATUS),3-15是保留值暂未使用;
AA:长度1位,授权应答(Authoritative Answer),这个比特位在应答的时候才有意义,指出给出应答的服务器是查询域名的授权解析服务器;
TC:长度1位,截断(TrunCation),用来指出报文比允许的长度还要长,导致被截断;
RD:长度1位,期望递归(Recursion Desired),这个比特位被请求设置,应答的时候使用的相同的值返回,如果设置了RD,就建议域名服务器进行递归解析,递归查询的支持是可选的;
RA:长度1位,支持递归(Recursion Available),这个比特位在应答中设置或取消,用来代表服务器是否支持递归查询;
Z:长度3位,保留值,暂未使用,在所有请求和应答报文中必须设置为0;
RCode:长度4位,应答码,类似http的stateCode一样,值0没有错误、1格式错误(Format Error)、2服务器错误(Server Failure)、3名字错误(Name Error)、4服务器不支持(Not Implemented)、5拒绝(Refused,由于设置的策略拒绝给出应答)和6-15(保留值,暂未使用);
(3)QDCount(Questions):长度16位,报文请求段中的问题记录数;
(4)NCount(Answer RRs):长度16位,报文回答段中的回答记录数;
(5)NSCOUNT(Authority RRs):长度16位,报文授权段中的授权记录数;
(6)ARCOUNT(Additional RRs):长度16位,报文附加段中的附加记录数;
(1)QNAME:域名被编码为一些labels序列,每个labels包含一个字节表示后续字符串长度,以及这个字符串,以0长度和空字符串来表示域名结束,注意这个字段可能为奇数字节,不需要进行边界填充对齐;(不定长字段,格式为长度(1字节)+N字节内容+长度为0)
(2)QTYPE:2个字节表示查询类型,取值可以为任何可用的类型值,以及通配码来表示所有的资源记录;
//查询的资源记录类型
enum QueryType
{
A=0×01, //指定计算机IP地址,AAAA是IPV6的地址?
NS=0×02, //指定用于命名区域的 DNS 名称服务器
MD=0×03, //指定邮件接收站(此类型已经过时了,使用MX代替)
MF=0×04, //指定邮件中转站(此类型已经过时了,使用MX代替)
CNAME=0×05, //指定用于别名的规范名称
SOA=0×06, //指定用于 DNS区域的“起始授权机构”
MB=0×07, //指定邮箱域名
MG=0×08, //指定邮件组成员
MR=0×09, //指定邮件重命名域名
NULL=0x0A, //指定空的资源记录
WKS=0x0B, //描述已知服务
PTR=0x0C, //如果查询是IP地址,则指定计算机名;否则指定指向其它信息的指针
HINFO=0x0D, //指定计算机 CPU 以及操作系统类型
MINFO=0x0E, //指定邮箱或邮件列表信息
MX=0x0F, //指定邮件交换器
TXT=0×10, //指定文本信息
UINFO=0×64, //指定用户信息
UID=0×65, //指定用户标识符
GID=0×66, //指定组名的组标识符
ANY=0xFF //指定所有数据类型
};
(3)QCLASS:2个字节表示查询的协议类,比如,IN代表Internet;
//指定信息的协议组
enum QueryClass
{
IN=0×01, //指定 Internet 类别
CSNET=0×02, //指定 CSNET 类别(已过时)
CHAOS=0×03, //指定 Chaos 类别
HESIOD=0×04, //指定 MIT Athena Hesiod 类别
ANY=0xFF //指定任何以前列出的通配符
};
(1)NAME:2字节,资源记录包含的域名;(4个字节被压缩成2个字节,一般以c0开头)
(2)QTYPE:2字节,表示资源记录的类型如A、CNAME、NS等,指出RDATA数据的含义;
(3)QCLASS:2字节,表示RDATA的类比如IN等;
(4)TTL:4字节,表示资源记录可以缓存的时间,0代表只能被传输,但是不能被缓存;
(5)RDLENGTH:2字节,表示RDATA的长度;
(6)RDATA:不定长,表示记录,格式与TYPE和CLASS有关,比如TYPE是A,CLASS 是 IN,那么RDATA就是一个4个字节的ARPA网络地址;
为了减小报文,域名系统使用一种压缩方法来消除报文中域名的重复,使用这种方法,后面重复出现的域名或者labels被替换为指向之前出现位置的指针。
前两个比特位都为1,因为lablels限制为不多于63个字节,所以label的前两位一定为0,这样就可以让指针与label进行区分(10 和 01 组合保留,以便日后使用),偏移值(OFFSET)0表示从报文开始的字节指针,偏移量为0表示ID字段的第一个字节(即都以c0开头)。
指针只能在域名不是特殊格式的时候使用,否则域名服务器或解析器需要知道资源记录的格式。
如果报文中的域名需要计算长度,并且使用了压缩算法,那么应该使用压缩后的长度,而不是压缩前的长度。
程序可以自由选择是否使用指针,虽然这回降低报文的容量,而且很容易产生截断。不过所有的程序都应该能够理解收到的报文中包含的指针。
DNS报文以数据报UDP或者字节流TCP进行传输。字节流可以用来任何的DNS的传输,数据报可以减少代价提高传输性能,区域刷新必须使用TCP,因为需要一个可靠的传输,因特网中DNS支持端口53的TCP[RFC-793]和端口53的UDP [RFC-768]传输。
(1)使用UDP:消息通过UDP的53端口进行传输;
UDP传输的消息严格要求限制在512字节内(不包括IP和UDP头),长报文被截断,同时置报文头的TC标志位;
UDP不能用于区域传输,主要用在标准的域名查询,报文通过UDP可能会丢失,所以重传机制是需要的,请求和应答可能在网络中或者服务器处理的时候被重新排序,所以解析客户端不能依赖请求的发送顺序;
UDP的最优重传策略会因为网络的性能,客户的需要而不同,但是下面是推荐的:
客户端在对一台固定的服务器重试之前,尝试一下其他的服务器;
如果可能的话,重传的时间间隔需要建立在统计分析数据的基础上,太快的重试可能因为量太大导致服务器响应慢,建议的重试时间为2-5秒;
(2)使用TCP:通过TCP发送的报文使用53端口,报文的前面有个字节表示后面报文的长度,长度不包括自己占用的2个字节,这个长度使得底层收取完整的报文后在交给上层处理。很多连接管理策略如下:
服务器不能阻塞其他传输TCP数据的请求;
服务器需要支持多连接;
服务器要等客户端主动关闭连接,除非所有的数据都已经传输完了;
如果服务器想关闭没有通讯的连接来释放资源,那么需要等待大约2分钟的时间。特别是要等SOA和AXFR(刷新操作中)在一个连接上传输完,服务器关闭连接的时候可以单方面的关闭,或者直接reset掉连接;
主DNS:8.8.8.8
辅DNS:8.8.4.4
主DNS:114.114.114.114
辅DNS:114.114.115.115
主DNS:223.5.5.5
辅DNS:223.6.6.6
主DNS:101.226.4.6
辅DNS:123.125.82.6
DNS协议格式:
http://09105106.blog.163.com/blog/static/2483578201342584441807/
EDNS:
http://www.cnblogs.com/cobbliu/p/3188632.html