原网站:https://powerdns.org/hello-dns/
本页原地址:https://powerdns.org/hello-dns/basic.md.html
1 DNS基础
DNS主要用于提供IP地址和邮件服务器的详细信息,但它也可以包含任意数据。DNS就是关于名字的。每个名称都可以有几种 类型 (type)的数据。最著名的外部有用类型是用于IPv4地址的A记录,用于IPv6地址的AAAA记录和用于邮件服务器详细信息的MX记录。DNS也有自己使用的类型,如NS、CNAME和SOA。
常用域名解析记录,DNS名词解释
当我们问DNS问题时,我们称之为查询(query)。我们将应答称为响应(response)。这些查询和响应包含在DNS消息(message)中。当使用UDP协议时,消息也是数据包。注意,2018年起DNS必须支持TCP。
一个DNS消息包含:
- 头部(header)
- 查询名(query name) 和 查询类型(query type)
- 回答部分(answer section)
- 权威/授权部分(authority section)
- 附加部分(additional section)
在基本的DNS中,查询消息应该有空的回答、权威和附加部分。
消息头部有以下字段,对查询和应答是有用的:
- ID:在查询和响应匹配过程中的16位标识符
- QR:查询请求/响应的标志信息。查询请求时,值为 0;响应时,值为 1。
- OPCODE(操作码):0 表示标准查询;1 表示反向查询;2 表示服务器状态请求。
- RD:设置这个值来表明是否需要递归
和应答(response)相关的字段:
DNS报文
AA:这个应答有权威回答(Authoritative Answers)
授权应答,该字段在响应报文中有效。值为 1 时,表示名称服务器是权威服务器;值为 0 时,表示不是权威服务器。
RA(Recursion Available):递归服务可用
可用递归。该字段只出现在响应报文中。当值为 1 时,表示服务器支持递归查询。
TC(Truncated):UDP中不含有全部的应答(response)所需字段
表示是否被截断。值为 1 时,表示响应已超过 512 字节并已被截断,只返回前 512 个字节。
RCODE(reply code):结果代码。0表示连接成功,2表示连接失败(SERVFAIL),3表示不存在(NXDOMAIN)
返回码字段,表示响应的差错状态。当值为 0 时,表示没有错误;当值为 1 时,表示报文格式错误(Format error),服务器不能理解请求的报文;当值为 2 时,表示域名服务器失败(Server failure),因为服务器的原因导致没办法处理这个请求;当值为 3 时,表示名字错误(Name Error),只有对授权域名解析服务器有意义,指出解析的域名不存在;当值为 4 时,表示查询类型不支持(Not Implemented),即域名服务器不支持查询类型;当值为 5 时,表示拒绝(Refused),一般是服务器由于设置的策略拒绝给出应答,如服务器不希望对某些请求者给出应答。
DNS查询大都通过UDP发送,但UDP数据包很容易被欺骗。要识别对查询(query)的真实响应,重要的是ID字段必须是随机的,或者至少是不可预测的。然而,这样的保护还不够,所以UDP DNS查询的源端口也必须是不可预知的。
DNS消息(dns message)也可以通过 TCP/IP 发送。由于 TCP 不是面向数据报的协议,因此 TCP/IP 中的每条 DNS 消息前面都有一个 16 位网络端长字段。
DNS服务器必须同时侦听UDP和TCP端口53。
有关www.ietf.org的IPv6地址的查询的头如下:
请注意,我们没有花时间在字段Z上,因为这个字段始终定义为0。 该数据包不请求递归。 QDCOUNT = 1表示有1个查询请求。 从理论上讲,DNS在一条消息中支持多个查询请求,但这尚未实现。 ANCOUNT,NSCOUNT和ARCOUNT均为零,表示此查询包中没有响应。
这是实际的查询
它由以DNS有线格式编码的“ www.ietf.org”(见下文)组成,后跟16位类型字段。 对于表示IPv6地址的AAAA记录,它是28。然后是问题的“类”(class)。 DNS记录原本打算存在于不同的“类”中,但是其语义并未完全指定,也未真正实现。 现在,始终将class设置为1。
查询名称,类型和类也分别称为“ qname”,“ qtype”和“ qclass”。
需要特别注意的是,域名“ www.ietf.org”在DNS中序列化的方式有些不寻常。 “ www.ietf.org”由3个长度分别为3、4和3的“标签”组成。 在DNS消息中,其编码为值3,然后是www,然后是值4,然后是ietf,然后是3,后跟org。 然后有一个结尾的0,表示这是结束。
这种格式是不寻常的,但是具有一些极具吸引力的特性。 例如,它是二进制安全的,不需要转义。 在编写DNS软件时,可能很想将DNS名称作为“ ASCII”传递。 然后,这导致在许多地方转义和转义代码。 强烈建议使用本机DNS编码来存储DNS名称。 处理带有空格或点的DNS名称时,这将省去很多麻烦。
最后,DNS查询不区分大小写。 但是,这是相当机械地定义的,并且仅限于ASCII。 当不区分大小写进行比较时,操作员无需知道Ü在某些编码中等于ü。 出于DNS的目的,在比较a-z和A-Z中的八位字节时,将忽略第五位(0×20)。
请注意,名称的各个标签的长度限制在63个八位组(octet)。
接下来,是一个DNS响应。 请注意,这又是一条DNS消息,它看起来很像原始DNS查询。 这是响应的开始:
请注意,QR现在设置为1表示响应。 设置“ AA”位是因为此响应来自该名称的权威服务器。
此外,ANCOUNT现在设置为’1’,表示在消息中找到一个回答,就在原始问题之后,该问题已经在查询消息中重复了。
要识别正确的响应,需检查ID字段与查询中的ID字段相同,并确保响应到达正确的源端口,并且查询名称和类型与原始查询匹配。 此外,请确保在仍等待响应时不要发出多个同等查询,因为这样做会带来安全漏洞。
在头部和原始问题之后,我们找到了回答:
前两个字节(0xc0 0x0c)看起来很神秘。 创建DNS时,认为512个八位位组是UDP数据报的最大大小,因此不使用(慢速)TCP协议传输的DNS消息的最大大小。
为了将尽可能多的信息压缩到512字节中,可以压缩DNS名称(通常必须)。 这种压缩的细节很神秘,很容易出错,从而导致无限循环或缓冲区溢出。 所以要非常小心。 如果您还记得一件事,请确保始终将指针移到数据包中的较低位置。 也要注意有符号/无符号算术。
在这种情况下,答案的DNS名称被编码为0xc0 0x0c
。 c0部分设置了两个最高有效位,表示接下来的6 + 8位是指向消息中较早位置的指针。 在这种情况下,它指向数据包中紧靠DNS头的位置12(= 0x0c
十六进制)。 在那里我们可以找到“ www.ietf.org”。
这意味着DNS名称www.ietf.org的回答也被称为www.ietf.org。(这句看不懂)
然后在数据包中紧随其后的是’28’,它表示AAAA 记录(IPv6),和通常的’class’为1。然后整个32位用于记录的生存时间(TTL),然后是一个16位长度的字段。因为这是一个IPv6地址,所以实际的应答有效负载长度是16字节(或128位)。
2400:cb00:2048:1::6814:55
就是www.ietf.org的IPv6地址的二进制表示。
如果有更多的回答,它们会跟随第一个,并且ANCOUNT会高于1。如果在“权威”和“附加”部分有数据,那么在这里也会有相应的调整“NSCOUNT”和“ARCOUNT”字段。稍后将详细介绍这些部分。
1.1 RRSETs
在上面的例子中,关于“www.ietf.org”的AAAA记录的问题只有一个相应的资源记录。以一种符合人类阅读习惯的“区域文件”,这将被存储为:
www.ietf.org IN AAAA 3600 2400:cb00:2048:1::6814:55
但是,同一名称可能有多个AAAA记录。即使只有一条记录,DNS规范也会讨论“资源记录集”或RRSETs。它们在统一中运作。因此,即使DNS包中的编码允许在单个RRSET中使用不同的TTL值,也不应该发生这种情况。
1.2 Zone files
区域文件是存储DNS数据的一种方式,但它们不是名称服务器操作的组成部分。区域文件格式是标准化的(RFC 1035的第5节),但是它的解析并不简单。完全有可能写有用的名称服务器,但不读取或写入DNS区域文件。在开始解析区域文件时,不要轻易这样做。例如,单行中的各个字段可以以多种顺序出现。大多数字段是可选的,有些字段将从前面的行复制过来。但并不是所有。
需要特别注意的是,许多人都试图为zonefile编写语法(比如用Yacc编写),但这几乎是不可能的。
1.3 DNS Names
DNS 名称的概念不是微不足道的,而且经常被误解。尽管从左到右www.ietf.org写”www.ietf.org”,但 DNS 中将其描述为根节点下方的”org”更合适,在”org”节点下方,一个名为”ietf”的节点。最后,将”ietf”节点附加到名为”www”的节点。
用流程图的形式来描述
如上所示的节点“树”是真实的,而不仅仅是另一种可视化DNS名称的方式。例如,这意味着如果有一个名为’ns1.ord.ietf.org’的名称,并且有一个关于’ns2.fra.ietf.org’的查询,这个名称存在-即使可能没有记录被分配给它。
‘org’区域可能会像如下所示
注意:这意味着任何将 DNS 视为简单”密钥/值”存储的实现,其中只有存在的记录可以匹配,则只会遇到问题。DNS 标准允许实现假定如果”ord.ietf.org”不存在,则 ns1 也不存在。这将保存查询,但会杀死您的域名,如果你弄错了。
1.4 Zones
如前所述,DNS 比简单的键/值存储更为复杂。这不仅因为名称的树样式性质,还因为相同的数据可以位于多个位置,但始终生活在”区域”中。
随着时间的推移,各种DNS实现已经发现,对于简单的名称服务器或负载均衡器,你基本上可以忽略“区域”的概念,但不正确地实现区域将最终绊倒你。
让人困惑的是,“www.ietf.org”可以在四个不同的地方定义。它可能在“根”区本身,完全写出来
www.ietf.org IN AAAA 3600 2400:cb00:2048:1::6814:55
也可以在组织区,看起来像这样
$origin ORG
www.ietf IN AAAA 3600 2400:cb00:2048:1::6814:55
或者(实际上就是这样),这个名称可以存在于’ietf.org’区域中
$origin ietf.org
www IN AAAA 3600 2400:cb00:2048:1::6814:55
最后,甚至有可能存在一个名为“www.ietf.org”的区域,记录就保存在这里
$origin www.ietf.org
@ IN AAAA 3600 2400:cb00:2048:1::6814:55
1.4.1 Start of Authority
区域始终以 SOA 或授权开始记录(Start of Authority)开头。SOA 记录是 DNS 元数据。它存储对区域可能感兴趣的各种内容,例如维护者的电子邮件地址、最权威服务器的名称。它还具有描述如何或是否需要复制区域的值。最后,SOA 记录有一个影响不存在名称的 TTL 值的数字。
确保只有一种SOA存在于Internet上,这是用于根区域的SOA(称为“.”)。
截至2018年,它看起来像这样:
. 86400 IN SOA a.root-servers.net. nstld.verisign-grs.com. 2018032802 1800 900 604800 86400
有关所有这些字段的含义的详细信息,请参阅权威服务器文档。
但是,最终数字在这里很重要。 86400表示如果响应说不存在名称或RRSET,则第二天将继续不存在该名称或RRSET,并且该信息可能会被缓存。
1.4.2 Zone cuts
如前所述,“ www.ietf.org”可以存在于四个地方。 如果它住在当前所在的位置,则在“ ietf.org”区域中进行两次区域切割:从’.’到’org’,从’org’到’ietf.org’。
当权威服务器接收到一个关于“www.ietf.org”的查询时,它会查询它知道的那些区域,并从它可用的最特定的区域给出答案。
对根服务器而言,它只知道根区域,这意味着查找’.’区域。如前文所述,’www.ietf.org’是树状结构,’org’->’ietf’->’www’。幸运的是,第一个节点’org’出现在根区域中。
附加到该节点的是一个NS RRSET,其中包含托管ORG区域的命名服务器的名称。。
如果我们向这些服务器询问“ www.ietf.org”,它们也会找到最佳答案,在这种情况下为“ org”。然后在’org’区域中找到’ietf’节点,该节点同样包含一个NS RRSET。
当我们向该RRSET中命名的服务器询问有关“ www.ietf.org”时,它们会找到一个名为“ www”的节点,上面带有多个RRSET,其中一个用于AAAA,并包含我们正在寻找的IPv6地址。
任何不以这种方式实现“区域”的权威服务器最终都会遇到麻烦。 仅查询已知名称列表并回答附加在这些名称上的记录是不够的。
1.4.3 NS Records
这些是区域的必需部分,位于“顶点”。 “ apex”是区域的名称,这时还有一条SOA记录。 因此,典型的区域将像这样开始:
$ORIGIN ietf.org.
@ IN SOA ns1 admin 2018032802 1800 900 604800 86400
IN NS ns1
IN NS ns2
注意,在这个区域文件示例中,文件名不是以’.’结尾的。都被解释为ietf.org的一部分。“@”是指定顶点名称的一种方式。第二行和第三行省略了一个名称,因此它们默认也为’@’。
该区域列出了ns1.ietf.org和ns2.ietf.org作为其名称服务器。 作为区域的一部分,此数据具有权威性。 发送到此名称服务器的“ ietf.org”的NS RRSET的任何查询都将收到带有AA位置1的响应。
但是请注意,上面我们了解到,父区域“ org”还需要列出example.org的名称服务器,如下:
$ORIGIN org.
...
ietf IN NS ns1.ietf
ietf IN NS ns2.ietf
如果我们向“org”名称服务器索要“ ietf.org”的NS RRSET,则会收到AA = 0的响应,表明“org”服务器知道它们不是ietf.org的“权威”。
1.4.4 Glue records
精明的读者会在这里发现先有鸡还是先有蛋的问题。 如果ns1.ietf.org是ietf.org的名称服务器…我们在哪里获取ns1.ietf.org的IP地址?
为了解决这个问题,父区域会提供一只免费的“鸡”,在’org’区域,可以找到:
$ORIGIN org.
...
ietf IN NS ns1.ietf
ietf IN NS ns2.ietf
ns1.ietf IN A 192.0.2.1
ns2.ietf IN A 198.51.100.1
这些条目被镜像到托管在ns1.ietf.org和ns2.ietf.org上的“ietf.org”区域中。与NS记录一样,发送到org服务器的任何对ns1.ietf.org的查询都会收到AA=0的回答,而ns1.ietf.org本身的回答是AA=1。
注意,由于各种原因,来自父区域的AA=0的答案可能与AA=1的答案不同,并且解析器必须意识到这种差异。
2 Further aspects
到目前为止的描述是正确的,但即使对于基本的DNS,功能也远远不够完整。以下部分描述了基本DNS的其他方面
2.1 CNAME
CNAME为DNS提供了另外一个“规范名称”,例如:
www IN CNAME www.ietf.org.cdn.cloudflare.net.
这通常用于重定向到内容分发网络。CNAME用于名称,而不是用于类型。这意味着对www.ietf.org的任何查询都被发送到Cloudflare。这同时意味着每个人都想要的是不可能的:
$ORIGIN ietf.org
@ IN CNAME this.does.not.work.int.
这会与SOA和NS记录发生冲突,这些记录也会被重定向,但无法找到。通常,使用这个“apex CNAME”似乎有用,但实际上并没有。
事后看来,应该对CNAME进行“类型化”,使其仅适用于特定的查询类型。
当服务器遇到CNAME时,其名称就是它所寻找的名称,它将“跟随”它所指向的链。 并且请注意,这可能会循环。
2.2 Wildcards
允许下列通配符:
$ORIGIN ietf.org.
* IN A 192.0.2.1
IN AAAA 2001:db8:85a3::8a2e:0370:7334
smtp IN A 192.0.2.222
对“smtp.ietf.org”的A记录的查询将返回192.0.2.222。然而,查询“www.ietf.org”将返回192.0.2.1。
有趣的是,作为DNS树状结构的另一个示例,对smtp.ietf.org的AAAA记录的查询将返回……没有任何结果。 这是因为节点“ smtp.ietf.org”确实存在,并且处理在那里结束。 通配符匹配不会继续到“ *”条目。
通配符可以合成新的应答。这意味着,除非显式查询,否则不会提供’*.ietf.org’记录。相反,一个’www.ietf.org’记录是动态创建的。
2.3 Truncation
如果不实现可选的EDNS协议扩展,所有UDP响应必须适合512字节的有效负载。如果服务器在写应答时发现自己超过了这个限制,它必须截断数据包并将TC位设置为1。
然后,查询的发起者将通过TCP重新发送查询。
有时DNS响应包含可以省略的可选数据,这样做可以保持在512字节限制下。
但是,建议保持简单,当达到字节限制时,发送一个TC=1的空响应包。
2.4 Names and nodes that do not exist
两种情况下DNS请求会匹配失败:整个节点不存在,或者,请求的类型在节点上不存在。
第一种情况的示例:’doesnotexist.ietf.org’不存在,这导致响应带有RCODE NXDOMAIN,并且没有应答记录。
第二种情况的示例:’www.ietf.org’确实存在,但没有MX记录。RCODE正常,但是没有应答记录。
然而,空的应答很难缓存。为了缓解这种情况,在这些情况下,权威服务器将在响应的授权部分发送SOA记录的副本。该记录的TTL告诉我们“no such name”或“no such data”的信息可以缓存多长时间。