6. 名称服务器实现 (Name Server Implementation)
本章讨论 DNS 名称服务器的实现注意事项.
6.1. 体系结构 (Architecture)
名称服务器的最佳结构取决于主机操作系统, 以及名称服务器是否与解析器操作集成, 例如是否支持递归服务, 或者是否与解析器共享数据库.
本节讨论与解析器共享数据库的名称服务器的实现注意事项, 但其中大多数问题也存在于任何名称服务器中.
6.1.1. 控制 (Control)
名称服务器必须采用多个并发活动, 无论这些活动是实现为主机 OS 中的独立任务, 还是在单个名称服务器程序内部复用.
要求 (Requirements):
-
UDP 非阻塞 (UDP Non-Blocking): 名称服务器在等待用于刷新或查询活动的 TCP 数据时阻塞 UDP 请求服务, 是不可接受的.
-
并行处理 (Parallel Processing): 名称服务器不应在不并行处理递归请求的情况下尝试提供递归服务, 尽管它可以选择:
- 串行化来自单个客户端的请求.
- 把来自同一客户端的相同请求视为重复请求.
-
区域操作非阻塞 (Non-Blocking Zone Operations): 名称服务器在执行以下操作时, 不应显著延迟请求:
- 从主文件重新加载区域.
- 把新刷新的区域合并进数据库.
6.1.2. 数据库 (Database)
虽然名称服务器实现可以自由选择任意内部数据结构, 建议的结构由三个主要部分组成:
建议的数据库结构
1. 目录数据结构 (Catalog Data Structure):
- 列出此服务器可用的区域.
- 包含指向区域数据结构的"指针".
- 主要用途: 对到达的标准查询查找最近的祖先区域, 如果存在的话.
2. 区域数据结构 (Zone Data Structures):
- 为名称服务器持有的每个区域提供独立数据结构.
- 包含该区域的权威数据.
3. 缓存数据结构 (Cache Data Structure):
- 用于缓存数据的数据结构.
- 可以为不同类维护独立缓存.
实现注意事项
树结构 (Tree Structure):
所有这些数据结构都可以用相同的树结构格式实现, 只是在不同部分的节点上链接不同数据:
- 在目录中: 数据是指向区域的指针.
- 在区域和缓存数据结构中: 数据是 RR.
查询处理要求 (Query Processing Requirements):
设计树框架时, 设计者应认识到:
- 查询处理需要使用大小写不敏感的标签比较来遍历树.
- 在真实数据中, 少数节点具有非常高的分支因子, 例如 100 到 1000 或更多.
- 绝大多数节点具有非常低的分支因子, 例如 0 到 1.
大小写不敏感存储方案
解决大小写问题的一种方法是把每个节点的标签存储为两部分:
- 标签的标准大小写表示, 其中所有 ASCII 字符都采用同一种大小写.
- 一个位掩码, 指示哪些字符实际使用另一种大小写.
分支因子优化
分支因子的差异可以这样处理:
- 在节点分支因子超过某个阈值前, 使用简单链表.
- 超过阈值后, 转换为哈希结构.
重要: 用于存储树片段的哈希结构必须确保哈希函数和过程保留 DNS 的大小写约定.
独立结构的好处
为数据库的不同部分使用独立结构有几个动机:
1. 目录稳定性 (Catalog Stability):
- 目录结构可以是几乎静态的结构.
- 只有系统管理员改变服务器支持的区域时才需要变化.
- 也可以存储用于控制刷新活动的参数.
2. 原子区域替换 (Atomic Zone Replacement):
- 区域的独立数据结构允许只通过改变目录中的一个指针来替换区域.
- 区域刷新操作可以构建新结构, 完成后通过简单的指针替换把它拼接进数据库.
- 关键点: 当区域刷新时, 查询不应同时使用旧数据和新数据.
3. 数据优先级 (Data Precedence):
- 通过适当的搜索过程, 区域中的权威数据总是会"遮蔽"缓存数据, 因而优先于缓存数据.
4. 错误隔离 (Error Isolation):
- 导致区域重叠的区域定义错误可能会让查询得到错误响应.
- 问题定位会因此简化.
- 一个"坏"区域的内容不能破坏另一个区域.
5. 缓存管理 (Cache Management):
- 由于缓存最频繁更新, 它在系统重启期间最容易损坏.
- 缓存也可能充满过期 RR 数据.
- 无论哪种情况, 都可以轻易丢弃缓存, 而不影响区域数据.
崩溃恢复 (Crash Recovery)
数据库设计的一个主要方面是选择能让名称服务器处理其宿主机崩溃的结构.
名称服务器应跨系统崩溃保存的状态信息:
- 目录结构, 包括每个区域的刷新状态.
- 区域数据本身.
6.1.3. 时间 (Time)
RR 的 TTL 数据以及刷新活动的计时数据都依赖以秒为单位的 32 位计时器.
时间表示
概念模型 (Conceptual Model):
- 在数据库内部, 刷新计时器和缓存数据的 TTL 在概念上会"倒计时".
- 区域中的数据保持恒定 TTL.
推荐实现策略 (Recommended Implementation Strategy):
以两种方式存储时间: 相对增量和绝对时间.
一种做法是使用:
- 正 32 位数表示一种类型.
- 负数表示另一种类型.
用法 (Usage):
- 区域中的 RR 使用相对时间.
- 刷新计时器和缓存数据使用绝对时间.
- 绝对数值相对于某个已知原点取得, 并在放入查询响应时转换为相对值.
- 当绝对 TTL 转换为相对值后为负时, 表示数据已过期, 应忽略.
6.2. 标准查询处理 (Standard Query Processing)
RFC-1034 给出了标准查询处理的主要算法.
特殊情况和规则
QCLASS= 处理*:
- 处理
QCLASS=*查询, 或其他匹配多个类的 QCLASS 时. - 除非服务器能够保证响应覆盖所有类, 否则响应不应是权威的.
重复 RR 处理 (Duplicate RR Handling):
- 构造响应时, 如果要插入 additional 部分的 RR 与 answer 或 authority 部分中的 RR 重复, 可以从 additional 部分省略这些 RR.
截断策略 (Truncation Policy):
- 当响应很长且需要截断时.
- 截断应从响应末尾开始, 并在数据报中向前进行.
- 因此, 如果 authority 部分存在任何数据, answer 部分就能保证是唯一的.
SOA MINIMUM 字段:
- SOA 中的 MINIMUM 值应作为从区域分发的数据 TTL 的下限.
- 这个下限函数应在数据复制到响应中时执行.
- 这将允许未来的动态更新协议改变 SOA MINIMUM 字段, 而不会产生语义歧义.
6.3. 区域刷新和重新加载处理 (Zone Refresh and Reload Processing)
错误处理
尽管服务器会尽最大努力, 它仍可能无法:
- 由于语法错误等原因从主文件加载区域数据.
- 在区域过期参数限定时间内刷新区域.
在这种情况下: 名称服务器应回答查询, 表现得像它本不应持有该区域一样.
区域传送一致性
如果主服务器正在通过 AXFR 向外发送区域, 而传输期间创建了新版本:
- 如果可能, 主服务器应继续发送旧版本.
- 无论如何, 都绝不能发送一个版本的一部分和另一个版本的一部分.
- 如果无法完成, 主服务器应重置正在进行区域传送的连接.
6.4. 反向查询 (Inverse Queries, Optional)
反向查询是 DNS 的可选部分.
支持要求
- 名称服务器不要求支持任何形式的反向查询.
- 如果名称服务器收到它不支持的反向查询, 它会返回在头部设置了"Not Implemented"错误的错误响应.
- 虽然反向查询支持是可选的, 但所有名称服务器至少必须能够返回这种错误响应.
6.4.1. 反向查询和响应的内容
反向查询会反转标准查询操作执行的映射:
- 标准查询把域名映射到资源.
- 反向查询把资源映射到域名.
示例 (Example):
- 标准查询可能把域名绑定到主机地址.
- 对应的反向查询则把主机地址绑定到域名.
格式
反向查询形式为:
- 消息 answer 部分中的单个 RR.
- 空 question 部分.
- 查询 RR 的所有者名称及其 TTL 不重要.
响应
响应在 question 部分携带问题, 这些问题标识拥有查询 RR 的所有名称, 但仅限名称服务器已知的名称.
重要限制 (Important Limitations):
- 由于没有任何名称服务器知道整个域名空间, 响应永远不能假定为完整.
- 因此, 反向查询主要用于数据库管理和调试活动.
- 反向查询不是把主机地址映射到主机名的可接受方法; 应使用 IN-ADDR.ARPA 域.
大小写不敏感比较
在可能情况下, 名称服务器应为反向查询提供大小写不敏感比较:
- 请求
Venera.isi.edu的 MX RR 的反向查询, 应得到与请求VENERA.ISI.EDU相同的响应. - 请求 HINFO RR
IBM-PC UNIX的反向查询, 应产生与请求IBM-pc unix相同的结果.
注意: 这不能保证, 因为名称服务器可能持有包含字符串的 RR, 但名称服务器并不知道这些数据是字符.
处理结果
当名称服务器处理反向查询时, 它会返回以下之一:
- 为指定资源找到的零个, 一个或多个域名, 作为 question 部分中的 QNAME.
- 一个错误代码, 表示名称服务器不支持对指定资源类型执行反向映射.
响应修改
当反向查询响应包含一个或多个 QNAME 时:
- answer 部分中定义反向查询的 RR 的所有者名称和 TTL 会被修改, 使其与第一个 QNAME 处找到的 RR 完全匹配.
缓存限制
反向查询中返回的 RR 不能使用标准查询回复所使用的同一机制缓存.
原因: 一个名称可能有多个相同类型的 RR, 但反向查询只会出现其中一个. 例如, 对多宿主主机的单个地址进行反向查询, 可能造成只有一个地址存在的错觉.
6.4.2. 反向查询和响应示例
用于检索 Internet 地址 10.1.0.52 对应域名的反向查询整体结构如下:
查询 (Query):
+-----------------------------------------+
| Header | OPCODE=IQUERY, ID=997 |
+-----------------------------------------+
| Question | <empty> |
+-----------------------------------------+
| Answer | <anyname> A IN 10.1.0.52 |
+-----------------------------------------+
| Authority | <empty> |
+-----------------------------------------+
|Additional | <empty> |
+-----------------------------------------+
说明 (Explanation):
- 此查询请求一个问题, 该问题的答案是 Internet 风格地址 10.1.0.52.
- 由于所有者名称未知, 可以使用任意域名作为占位符, 且会被忽略.
- 通常使用表示根的单个零八位组, 因为这能最小化消息长度.
- RR 的 TTL 不重要.
响应 (Response):
+-----------------------------------------+
| Header | OPCODE=RESPONSE, ID=997 |
+-----------------------------------------+
| Question | QTYPE=A, QCLASS=IN, |
| | QNAME=VENERA.ISI.EDU |
+-----------------------------------------+
| Answer | VENERA.ISI.EDU A IN |
| | 10.1.0.52 |
+-----------------------------------------+
| Authority | <empty> |
+-----------------------------------------+
|Additional | <empty> |
+-----------------------------------------+
注意: 反向查询响应中的 QTYPE 与反向查询 answer 部分中的 TYPE 字段相同. 当反向结果不唯一时, 反向查询响应可以包含多个 question. 如果响应中的 question 部分非空, 则 answer 部分中的 RR 会被修改为与第一个 QNAME 处的 RR 精确对应的副本.
6.4.3. 反向查询处理
支持反向查询的名称服务器可以通过穷举搜索其数据库来支持这些操作, 但随着数据库规模增大, 这种方式会变得不实际.
替代方法 (Alternative Approach): 根据搜索键反转数据库.
大型服务器建议
对于支持多个区域和大量数据的名称服务器, 推荐方法是:
- 为每个区域分别维护反向索引.
- 当某个区域在刷新期间发生变化时, 只需要重建该区域的反向索引.
注意: 未来版本的域系统可能包含这种反向索引的传送支持, 但本版本不支持.