3. Establishing and Managing DNS-over-TLS Sessions (建立和管理 DNS-over-TLS 会话)
3.1 Session Initiation (会话启动)
默认情况下, 支持 DNS over TLS 的 DNS 服务器必须 (MUST) 在端口 853 上侦听并接受 TCP 连接, 除非它与其客户端有共同约定使用 853 以外的端口进行 DNS over TLS. 为了使用 853 以外的端口, 客户端和服务器都需要在其软件中有一个配置选项.
默认情况下, 希望从特定服务器获得 DNS over TLS 隐私的 DNS 客户端必须 (MUST) 与该服务器的端口 853 建立 TCP 连接, 除非它与其服务器有共同约定使用 853 以外的端口进行 DNS over TLS. 这样的另一个端口禁止 (MUST NOT) 是端口 53, 但可以 (MAY) 来自 "先到先得" (first-come, first-served) 端口范围. 不建议在端口 53 上使用 DNS over TLS 是为了避免在选择使用或不使用 TLS 时产生复杂性, 并降低降级攻击 (downgrade attacks) 的风险. 此 TCP 连接上的第一次数据交换必须 (MUST) 是客户端和服务器使用 [RFC5246] 中描述的过程启动 TLS 握手.
DNS 客户端和服务器禁止 (MUST NOT) 使用端口 853 传输明文 DNS 消息. DNS 客户端禁止 (MUST NOT) 发送, 且 DNS 服务器禁止 (MUST NOT) 响应任何用于 DNS over TLS 的端口上的明文 DNS 消息 (例如, 在 TLS 握手失败后). 混合受保护和未受保护的数据存在重大安全问题, 因此, 由给定服务器指定用于 DNS over TLS 的端口上的 TCP 连接纯粹保留用于加密通信.
DNS 客户端应该 (SHOULD) 记住不支持 DNS over TLS 的服务器 IP 地址, 包括超时、连接拒绝和 TLS 握手失败, 并在合理的时间内 (例如每个服务器一小时) 不向它们请求 DNS over TLS. 遵循带外密钥固定隐私配置文件 (第 4.2 节) 的 DNS 客户端可以 (MAY) 更积极地重试 DNS-over-TLS 连接失败.
3.2 TLS Handshake and Authentication (TLS 握手和认证)
一旦 DNS 客户端成功通过 TCP 在众所周知的 DNS over TLS 端口上连接, 它就会继续进行 TLS 握手 [RFC5246], 遵循 [BCP195] 中规定的最佳实践.
然后, 如果需要, 客户端将对服务器进行身份验证 (authenticate). 本文档不提出新的身份验证思路. 根据使用的隐私配置文件 (第 4 节), DNS 客户端可以选择不要求对服务器进行身份验证, 或者可以使用受信任的主体公钥信息 (Subject Public Key Info, SPKI) 指纹固定集 (Fingerprint pin set).
TLS 协商完成后, 连接将被加密, 现在可以防止窃听.
3.3 Transmitting and Receiving Messages (发送和接收消息)
在已建立的 TLS 会话中的所有消息 (请求和响应) 必须 (MUST) 使用 [RFC1035] 第 4.2.2 节中描述的双字节长度字段. 出于效率原因, DNS 客户端和服务器应该 (SHOULD) 同时将双字节长度字段和该长度字段描述的消息传递给 TCP 层 (例如, 在单个 "write" 系统调用中), 以使所有数据更有可能在单个 TCP 段中传输 ([RFC7766], 第 8 节).
为了最小化延迟, 客户端应该 (SHOULD) 通过 TLS 会话对多个查询进行流水线处理 (pipeline). 当 DNS 客户端向服务器发送多个查询时, 它不应该 (should not) 在发送下一个查询之前等待未完成的回复 ([RFC7766], 第 6.2.1.1 节).
由于流水线响应可能无序到达, 客户端必须 (MUST) 使用消息 ID (Message ID) 将响应与同一 TLS 连接上的未完成查询匹配. 如果响应包含问题部分 (Question Section), 客户端必须 (MUST) 匹配 QNAME、QCLASS 和 QTYPE 字段. 客户端未能正确地将响应与未完成的查询匹配可能会对互操作性产生严重后果 ([RFC7766], 第 7 节).
3.4 Connection Reuse, Close, and Reestablishment (连接复用、关闭和重建)
对于使用库函数 (如 getaddrinfo() 和 gethostbyname()) 的 DNS 客户端, 已知当前实现会为每个 DNS 查询打开和关闭 TCP 连接. 为了避免过多的 TCP 连接 (每个连接只有一个查询), 客户端应该 (SHOULD) 重用到递归解析器的单个 TCP 连接. 或者, 它们可以更倾向于对同一台机器上启用 DNS-over-TLS 的缓存解析器使用 UDP, 然后该解析器使用系统范围的 TCP 连接到递归解析器.
为了分摊 TCP 和 TLS 连接建立成本, 客户端和服务器不应该 (SHOULD NOT) 在每个响应后立即关闭连接. 相反, 客户端和服务器应该 (SHOULD) 重用现有连接进行后续查询, 只要它们有足够的资源. 在某些情况下, 这意味着客户端和服务器可能需要将空闲连接保持打开一段时间.
正确管理已建立的和空闲的连接对于 DNS 服务器的健康运行很重要. DNS over TLS 的实现者应该 (SHOULD) 遵循 DNS over TCP 的最佳实践, 如 [RFC7766] 中所述. 否则可能导致资源耗尽和拒绝服务.
众所周知, [RFC1035] 时代的客户端和服务器实现具有较差的 TCP 连接管理, 本文档规定, TLS 的成功协商表明双方愿意保持空闲的 DNS 连接打开, 独立于没有 TLS 的 DNS over TCP 的超时或其他建议. 换句话说, 实现此协议的软件被假定支持空闲的持久连接, 并准备好管理多个可能长期存在的 TCP 连接.
本文档不对空闲连接的超时值作出具体建议. 客户端和服务器应根据可用资源的级别重用和/或关闭连接. 超时可能在低活动期间更长, 在高活动期间更短. 该领域当前的工作也可能帮助 DNS-over-TLS 客户端和服务器选择有用的超时值 [RFC7828] [TDNS].
保持空闲连接打开的客户端和服务器必须 (MUST) 对任一方终止空闲连接保持健壮性. 与当前的 DNS over TCP 一样, DNS 服务器可以 (MAY) 随时关闭连接 (可能由于资源约束). 与当前的 DNS over TCP 一样, 客户端必须 (MUST) 处理突然关闭, 并准备好重新建立连接和/或重试查询.
当重新建立已终止的 DNS-over-TCP 连接时, 如 [RFC7766] 中所讨论的, TCP 快速打开 (TCP Fast Open) [RFC7413] 是有益的. 强调在 DNS-over-TLS 端口上仅发送加密 DNS 数据的要求 (第 3.2 节), 当使用 TCP 快速打开时, 客户端和服务器必须 (MUST) 立即启动或恢复 TLS 握手 (禁止 (MUST NOT) 交换明文 DNS). DNS 服务器应该 (SHOULD) 启用快速 TLS 会话恢复 [RFC5077], 并且在重新建立连接时应该 (SHOULD) 使用此功能.
当关闭连接时, DNS 服务器应该 (SHOULD) 使用 TLS close-notify 请求将 TCP TIME-WAIT 状态转移到客户端. [RFC7766] 提供了优化 DNS over TCP 的额外要求和指导.