摘要

  • RFC 8482 允许为特定 ANY 查询合成一条 HINFO:CPU 字符串建议使用 RFC8482,OS 字符串建议留空。它利用既有记录结构形成可缓存回答,不是在报告真实处理器。
  • ANY 的小回答可以只含部分完整记录集合,没有承诺列出该名字下所有类型;返回成功也不意味着应用已经取得全部所需信息。
  • 合成 HINFO 进入缓存后可能遮住真实 HINFO。较长 TTL 能减少近期重复查询,却也会使之后的应答策略调整更难立即生效。

空着的字段仍然存在

HINFO 最初并不是为拒绝大回答准备的。1987 年 11 月的 RFC 1035 为它定义了两个字符字符串,分别描述 CPU 类型与操作系统类型。文档举出的用途,是让 FTP 等协议在面对相同机器或系统时选择适当的特殊处理。

这里的字符串带有长度。空字符串仍然是一个字段,只是长度为零;它不等于把第二个字段从记录里删掉,更不能被解释为那台机器没有操作系统。

三十多年后,同一个结构承担了一项不同任务。2019 年 1 月的 RFC 8482 描述了一种可选办法:面对符合条件的 ANY 查询,返回合成的 HINFO,建议将 CPU 字符串设为 RFC8482,OS 设为空字符串,以保持回答紧凑。

当服务器明确按这个办法构造回答时,CPU 栏里的标准编号当然不是实测硬件型号。然而,接收方不能倒过来声称,只要看到这样的内容,就能确定它必定是合成记录。文档专门警告,不应仅凭这些 RDATA 值判定来源并进行特殊处理。结构可以复用,真假来源却没有因此获得一个可靠的识别开关。

一个名字,不是一整张区域清单

ANY 的名称容易制造过度期待。旧规范把查询类型 255 写成星号,常见实现则使用 ANY 这个名称。读者很容易把它理解成“所有”,再把“所有”扩张为无所遗漏的目录导出。

RFC 1034 的基本结构却区分了查询名字 QNAME、查询类型 QTYPE 与类别 QCLASS。类型栏里的 ANY 不是名字里的通配符,也不是 AXFR 区域传送。询问某个名字下的资料,不会顺便列出整个区域中的全部名字。

该文档的服务器算法还分别处理区域数据与缓存。缓存里能找到的匹配记录,不一定覆盖权威服务器可能持有的所有类型。一个已经缓存的回答,也不会因为查询类型叫 ANY,就自动补成完整清单。

当前 IANA DNS 参数登记表 对类型 255 的说明已明确包括服务器可提供的部分或全部记录;HINFO 则仍以类型 13 登记为主机信息。这说明两者的注册含义,不能说明今天多少服务器采用哪一种应答方式。

因此,CPU 栏里那个奇怪的编号只是表面。更深的问题是:客户端究竟把一次成功应答,当成了多大范围的保证?

新错误码可能让人问得更多

RFC 8482 并没有把所有 ANY 请求都视为攻击。它列出调试、检查某个名字在服务器上的状态等正常用途,也讨论应用试图一次取得 MX、A、AAAA 等多种资料的动机。

但后者必须准备回退办法,不能假设查询一定抵达权威服务器、一定取回所有记录集合。协议的便利,不等于应用可以省略对自己真正需要什么的说明。

运营者也有理由限制这类回答。一个小 UDP 查询可能引出较大应答,成为伪造源地址反射放大的条件;某些实现生成传统 ANY 回答还要付出额外处理成本。减少一条回答的大小,可以降低相应放大吸引力,但不是这里已经测量出的攻击降幅,也不意味着其他正常公开 DNS 数据变成机密。

文档讨论过一种看似直接的替代方案:用新的 RCODE 表示不提供传统 ANY 回答。然而,不认识这个码的解析器会继续向其他可用权威服务器尝试。原本想少做一些工作的服务器,可能只是把更多查询引向了同一组服务中的其他成员。

RFC 8482 选择给解析器一个非空记录集合,让它按既有规则缓存。解析器已经会保存成功取得的记录,无须先理解一套新的拒绝语言。一个小而能留下来的回答,可能比一个新而不被理解的错误更能结束重复工作。

这是文档所描述的设计动机,不是“所有错误都会无限重试”的普遍定律,也不是对今天每一种解析器行为的调查。

少给几组,不是拆散一组

最小回答并非只有合成 HINFO 一条路。RFC 8482 的相关规则针对已经存在的查询名字、IN 类和 ANY 类型;除明确改变的部分外,仍遵循正常应答算法。它没有许可把不存在的名字都伪装成有主机资料。

第一种办法,是从该名字可用的记录集合中选择一组,或一个较小子集。文档明确说明,这种回答没有额外信号告诉客户端:还有其他可用集合没有返回。某个类型没出现在这里,不能证明它不存在。

记录集合也不是一条随便抽取的记录。RFC 2181 在 1997 年 7 月说明,名字、类别、类型相同而数据可以不同的一组记录构成 RRset。所选集合仍应完整传送;若必需集合装不下,要按截断规则处理。

所以,“只给一个 RRset”与“一个 RRset 只给一条”不是同一个操作。前者限制类型范围,后者可能破坏集合内部的完整性。用少量回答控制工作量,不等于放弃原有的数据单位。

同样,TC 位未设置也不是完整清单证书。RFC 2181 讨论的是必需集合无法完整装入时的截断;RFC 8482 对部分集合回答不携带完整性提示的说明,则限制了读者可以推导的结论。不能把一个传输标志扩大成“这个名字再没有其他类型”。

借用 HINFO 的代价

第二种办法才是合成 HINFO,而且要求匹配名字处没有 CNAME。建议的一条 HINFO 以标准编号和空 OS 字符串形成小回答;这不是要求管理员把它永久写进区域文件。

它的吸引力在于既有解析器能处理这种记录。但既有处理也会带来既有效果:一旦缓存了合成 HINFO,后续真正查询 HINFO 的请求,可能先拿到这条记录,权威区域内原有的真实主机信息反而被遮住。

RFC 8482 没有把这个代价藏起来。依赖传统 HINFO 用途的运营者,应选择已有 RRset 的办法或其他适当类型。旧字段被认为使用不多,并不代表它没有仍在使用的人。

文档关于 HINFO 较少使用的判断,来自作者当时的观察,不能不加日期地写成今天的采用率统计。即便大多数使用者不受影响,也不能由此推断所有环境都适合借这个位置。

第三种办法则尝试猜测应用意图:返回存在的 CNAME、MX、A、AAAA 集合,略去 TXT、DNSKEY 等其他类型。这种启发式可能更接近部分应用需求,也可能产生比另外两种办法更大的回答。猜测仍不是知道,不能代替应用明确查询所需类型。

三种办法都是可选项。ANY 没有被废除,HINFO 没有成为每台服务器必须给出的统一答复。

TTL 保存的是效果,也保存了旧决定

合成记录的 TTL 由运营者按本地情况选择。它需要足够长,才能减少同一发起者对同一名字的频繁 ANY 追问;太长又会让未来的 ANY 策略变化不易体现。规范没有给出一个适合所有人的固定秒数。

RFC 2181 还指出,TTL 表示最多可保留多久,不是每个缓存必须保留那么久。实现可以设置上限。因此,这种设计既不能保证所有缓存同秒过期,也不能保证运营者改了权威端设置,远端缓存就立刻忘记旧记录。

RFC 8482 允许正常缓存小回答,也分别描述了在有相应 HINFO 缓存时选择抑制后续 ANY,或按通常方式从缓存应答的可能。不能把这个许可写成所有客户端必须安装一种新状态;也不能因为文档反对凭 RDATA 确认“合成来源”,就误写成这种记录不得进入普通缓存。

一个成功的短回答因此不仅占据空间,还会改变接下来哪些工作暂时不再发生。这正是它有用的地方,也是调整时需要考虑的惯性。

小回答不是否定所有没说出的事

1998 年 3 月的 RFC 2308 区分了否定应答:NXDOMAIN 是名字错误;NODATA 则要从应答内容判断某个有效名字下没有所问类型,并不是另一个独立 RCODE。相应否定缓存还涉及 SOA 等条件。

返回一条 HINFO,或返回少数已有集合,与这些否定语义不同。它没有顺便断言所有未列出的类型都不存在。应用要用哪一种资料,应针对那一种资料发问,而不是用 ANY 回答里的空白替代证据。

传输方式也可能改变策略。RFC 8482 允许服务器对 TCP 与 UDP 的 ANY 采用不同处理,例如前者给传统回答、后者给小回答。这是允许的例子,不是要求,更不是保证改用 TCP 就能绕过所有运营者的选择。

签名没有把子集变成全集

DNSSEC 的 DO 位也必须放回自己的范围。2001 年 12 月的 RFC 3225 用它表达客户端能够接收 DNSSEC 安全记录。它不表示验证已经成功,更不表示 ANY 的清单完整。

RFC 8482 保留相应签名条件。合成回答在 DO 为一、且应答方知道区域已签名时,需要有效 RRSIG;在该分支中 DO 为零时则建议省略。已有集合子集与猜测意图的办法,也分别有其签名要求。追求小回答,并没有一并取消这些条件。

从这些规则可以得出一个有限结论:返回集合的真实性,与是否列尽其他类型,是两件事。签名能伴随一个被选择的集合;不能因此把未作出的完整性承诺补上。