DNS 协议
DNS资源记录(RR)
在介绍DNS层协议之前,先了解一下DNS服务器存储的资源记录(Resource Records,RRs),一条资源记录(RR)记载着一个映射关系。每条RR通常包含如下表所示的一些信息:
字段 含义
NAME 名字
TYPE 类型
CLASS 类
TTL 生存时间
RDLENGTH RDATA所占的字节数
RDATA 数据
NAME和RDATA表示的含义根据TYPE的取值不同而不同,常见的:
若TYPE=A,则name是主机名,value是其对应的ip;
若TYPE=NS,则name是一个域,value是一个权威DNS服务器的主机名。该记录表示name域的域名解析将由value主机名对应的DNS服务器来做;
若TYPE=CNAME,则value是别名为name的主机对应的规范主机名;
若TYPE=MX,则value是别名为name的邮件服务器的规范主机名;
……
TYPE实际上还有其他类型,所有可能的type及其约定的数值表示如下:
TYPE value meaning
A 1 a host address
NS 2 an authoritative name server
MD 3 a mail destination (Obsolete - use MX)
MF 4 a mail forwarder (Obsolete - use MX)
CNAME 5 the canonical name for an alias
SOA 6 marks the start of a zone of authority
MB 7 a mailbox domain name (EXPERIMENTAL)
MG 8 a mail group member (EXPERIMENTAL)
MR 9 a mail rename domain name (EXPERIMENTAL)
NULL 10 a null RR (EXPERIMENTAL)
WKS 11 a well known service description
PTR 12 a domain name pointer
HINFO 13 host information
MINFO 14 mailbox or mail list information
MX 15 mail exchange
TXT 16 text strings
DNS请求
DNS请求与响应的格式是一致的,其整体分为Header、Question、Answer、Authority、Additional5部分,如下图所示:
_Header
Header部分是一定有的,长度固定为12个字节;其余4部分可能有也可能没有,并且长度也不一定,这个在Header部分中有指明。Header的结构如下:
下面说明一下各个字段的含义:
ID:占16位。该值由发出DNS请求的程序生成,DNS服务器在响应时会使用该ID,这样便于请求程序区分不同的DNS响应。
QR:占1位。指示该消息是请求还是响应。0表示请求;1表示响应。
OPCODE:占4位。指示请求的类型,有请求发起者设定,响应消息中复用该值。0表示标准查询;1表示反转查询;2表示服务器状态查询。3~15目前保留,以备将来使用。
AA(Authoritative Answer,权威应答):占1位。表示响应的服务器是否是权威DNS服务器。只在响应消息中有效。
TC(TrunCation,截断):占1位。指示消息是否因为传输大小限制而被截断。
RD(Recursion Desired,期望递归):占1位。该值在请求消息中被设置,响应消息复用该值。如果被设置,表示希望服务器递归查询。但服务器不一定支持递归查询。
RA(Recursion Available,递归可用性):占1位。该值在响应消息中被设置或被清除,以表明服务器是否支持递归查询。
Z:占3位。保留备用。
RCODE(Response code):占4位。该值在响应消息中被设置。取值及含义如下:
0:No error condition,没有错误条件;
1:Format error,请求格式有误,服务器无法解析请求;
2:Server failure,服务器出错。
3:Name Error,只在权威DNS服务器的响应中有意义,表示请求中的域名不存在。
4:Not Implemented,服务器不支持该请求类型。
5:Refused,服务器拒绝执行请求操作。
6~15:保留备用。
QDCOUNT:占16位(无符号)。指明Question部分的包含的实体数量。
ANCOUNT:占16位(无符号)。指明Answer部分的包含的RR(Resource Record)数量。
NSCOUNT:占16位(无符号)。指明Authority部分的包含的RR(Resource Record)数量。
ARCOUNT:占16位(无符号)。指明Additional部分的包含的RR(Resource Record)数量。
如下:
import * as dns from 'dns';
var server = new dns.Resolver();
server.setServers(['127.0.0.1:5553']);
server.resolve('google.com', 'A', (err, addresses) => {
console.log(err);
console.log(addresses);
});
============= connected <<<< 127.0.0.1:36336 =============
ArrayBuffer {
[Uint8Contents]: <bd e7 01 00 00 01 00 00 00 00 00 00 06 67 6f 6f 67 6c 65 03 63 6f 6d 00 00 01 00 01>,
byteLength: 28
}
该例子中,一个A查询:
|-> Header部分结束
|
<bd e7, 01 00, 00 01, 00 00,00 00,00 00, 06 67 6f 6f 67 6c 65 03 63 6f 6d 00, 00 01, 00 01>
ID Question数量 .....其它数量为0 ? g o o g l e ? c o m
Question
Question部分的每一个实体的格式如下图所示:
QNAME:字节数不定,以0x00作为结束符。表示查询的主机名。注意:众所周知,主机名被"."号分割成了多段标签。在QNAME中,每段标签前面加一个数字,表示接下来标签的长度。比如:api.sina.com.cn表示成QNAME时,会在"api"前面加上一个字节0x03,"sina"前面加上一个字节0x04,"com"前面加上一个字节0x03,而"cn"前面加上一个字节0x02;
QTYPE:占2个字节。表示RR类型,见以上RR介绍;
QCLASS:占2个字节。表示RR分类,见以上RR介绍。
Answer
Answer、Authority、Additional部分格式一致,每部分都由若干实体组成,每个实体即为一条RR,之前有过介绍,格式如下图所示:
NAME:长度不定,可能是真正的数据,也有可能是指针(其值表示的是真正的数据在整个数据中的字节索引数),还有可能是二者的混合(以指针结尾)。若是真正的数据,会以0x00结尾;若是指针,指针占2个字节,第一个字节的高2位为11。
TYPE:占2个字节。表示RR的类型,如A、CNAME、NS等,见以上RR介绍;
CLASS:占2个字节。表示RR的分类,见以上RR介绍;
TTL:占4个字节。表示RR生命周期,即RR缓存时长,单位是秒;
RDLENGTH:占2个字节。指定RDATA字段的字节数;
RDATA:即之前介绍的value,含义与TYPE有关,见以上RR介绍。
一个正常的响应如下:
var msg = [0x48, 0xc3, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x03, 0x63, 0x6f, 0x6d, 0x00, 0x00, 0x02, 0x00, 0x01];
var udp4 = udp.createSocket('udp4');
udp4.send(Buffer.from(msg), 53, '1.1.1.1', (error, bytes) => {
error && console.error(error);
console.log({ bytes });
udp4.on('message', (msg, rinfo) => {
console.log(rinfo);
console.log(msg.buffer);
})
});
{ address: '1.1.1.1', family: 'IPv4', port: 53, size: 100 }
ArrayBuffer {
[Uint8Contents]: <48 c3 81 80 00 01 00 04 00 00 00 00 06 67 6f 6f 67 6c 65 03 63 6f 6d 00 00 02 00 01 c0 0c 00 02 00 01 00 05 43 c1 00 06 03 6e 73 32 c0 0c c0 0c 00 02 00 01 00 05 43 c1 00 06 03 6e 73 31 c0 0c c0 0c 00 02 00 01 00 05 43 c1 00 06 03 6e 73 33 c0 0c c0 0c 00 02 00 01 00 05 43 c1 00 06 03 6e 73 34 c0 0c>,
byteLength: 100
}
解析:
48 c3 81 80 00 01 00 04 00 00 00 00 06 67 6f 6f 67 6c 65 03 63 6f 6d 00 00 02 00 01 c0 0c 00 02 00 01 00 05 43 c1 00 06 03 6e 73 32 c0 0c c0 0c 00 02 00 01 00 05 43 c1 00 06 03 6e 73 31 c0 0c c0 0c 00 02 00 01 00 05 43 c1 00 06 03 6e 73 33 c0 0c c0 0c 00 02 00 01 00 05 43 c1 00 06 03 6e 73 34 c0 0c