5. 协议细节
DNS Stateful Operations 的整体流程会经历一系列阶段:
连接建立 (Connection Establishment): 客户端建立到服务器的连接 (第 4.2 节).
已连接但无会话 (Connected but Sessionless): 连接已经存在, 但尚未建立 DSO Session. 客户端可以向服务器发送 DNS 消息, 服务器也可以向客户端发送 DNS 响应. 在此状态下, 希望使用 DSO 的客户端可以尝试建立 DSO Session (第 5.1 节). 标准 DNS-over-TCP 非活动超时处理仍然生效 [RFC7766] (见本文第 7.1.2 节).
DSO Session 建立进行中: 客户端在过去 30 秒内发送了 DSO 请求, 但尚未收到该请求的 DSO 响应. 在此阶段, 客户端可以发送更多 DSO 请求和更多 DNS 请求, 但禁止发送 DSO 单向消息 (第 5.1 节).
DSO Session 建立超时: 客户端已发送 DSO 请求, 但 30 秒后仍未收到该请求的 DSO 响应. 这意味着服务器现在处于不确定状态. 客户端会强制中止连接. 如适用, 客户端可以在不使用 DSO 的情况下重新连接.
DSO Session 建立失败: 客户端已发送 DSO 请求, 并收到对应 DSO 响应, 但 RCODE 非零. 这意味着建立 DSO Session 的尝试没有成功. 此时, 客户端允许继续在没有 DSO Session 的状态下运行 (已连接但无会话), 但不再发送后续 DSO 消息 (第 5.1 节).
DSO Session 已建立: 客户端已发送 DSO 请求, 并收到 RCODE 为 NOERROR (0) 的对应 DSO 响应. DSO Session 现在已成功建立. 客户端和服务器都可以发送 DSO 消息和 DNS 消息; 双方都可以响应其收到的消息 (第 5.2 节). 非活动定时器 (第 6.4 节) 处于活动状态; 保活定时器 (第 6.5 节) 处于活动状态. 标准 DNS-over-TCP 非活动超时处理不再生效 [RFC7766] (见本文第 7.1.2 节).
服务器关闭 (Server Shutdown): 服务器决定优雅终止会话, 并向客户端发送 Retry Delay 消息 (第 6.6.1 节). 客户端可能仍有未处理消息; 服务器会忽略这些消息. 服务器不会再向客户端发送任何消息 (第 6.6.1.1 节).
客户端关闭 (Client Shutdown): 客户端决定断开连接, 原因可能是它不再需要服务, 连接处于非活动状态 (第 6.4.1 节), 或服务器向它发送了 Retry Delay 消息 (第 6.6.1 节). 客户端会优雅关闭连接 (第 5.3 节).
重新连接 (Reconnect): 客户端因服务器关闭而断开连接. 客户端要么等待服务器指定的 Retry Delay 到期 (第 6.6.3 节), 要么联系不同的服务实例. 如果客户端不再需要服务, 则不会重新连接.
强制中止 (Forcibly Abort): 客户端或服务器检测到协议错误, 继续通信会产生未定义行为. 客户端或服务器会强制中止连接 (第 5.3 节).
中止后的重连等待 (Abort Reconnect Wait): 客户端已强制中止连接但仍需要服务. 或者, 服务器已强制中止连接但客户端仍需要服务. 客户端要么连接到不同服务实例 (第 9.1 节), 要么等待后重新连接 (第 6.6.3.1 节).
5.1. DSO Session 建立
为了在客户端和服务器之间建立会话, 客户端必须先使用适用传输建立到服务器的连接 (见第 4.2 节).
在某些环境中, 可以通过外部手段预先知道客户端和服务器都支持 DSO. 在这些情况下, 客户端或服务器可以随时发起 DSO 消息. 此时, 连接一建立, 会话就建立了; 这称为隐式 DSO Session 建立.
然而在典型情况下, 服务器不会预先知道客户端是否支持 DSO. 因此, 除非通过其他方式预先知道客户端确实支持 DSO, 服务器通常禁止发起 DSO request message 或 DSO unidirectional message, 直到客户端发起至少一次成功的 DSO request/response 交换并相互建立 DSO Session, 如下所述. 这称为显式 DSO Session 建立.
在 DSO Session 隐式或显式建立之前, 客户端禁止发起 DSO unidirectional message.
客户端通过在连接上发送 DSO request message (例如 DSO Keepalive request message, 第 7.1 节), 并收到 MESSAGE ID 匹配且 RCODE 设置为 NOERROR (0) 的响应, 表明该 DSO 请求成功, 从而建立 DSO Session.
某些 DSO 消息允许作为 early data (第 11.1 节). 其他消息不允许. 单向消息绝不允许作为 early data, 除非存在隐式 DSO Session.
如果服务器在 early data 中收到 Primary TLV 不允许出现在 early data 中的 DSO 消息, 服务器必须强制中止连接. 如果客户端在 early data 中收到 DSO 消息且不存在隐式 DSO Session, 客户端必须强制中止连接. 这只能在 TLS 连接上执行; 因此, 当监听不要求 TLS 的连接时, 服务器禁止启用 TCP Fast Open (TFO).
5.1.1. DSO Session 建立失败
如果响应 RCODE 设置为 NOTIMP (4), 或实践中为 NOERROR (0) 或 DSOTYPENI (如下定义) 以外的任何值, 客户端必须假定服务器完全未实现 DSO. 在这种情况下, 客户端允许继续在该连接上发送 DNS 消息, 但禁止在该连接上再发出 DSO 消息.
如果响应中的 RCODE 设置为 DSOTYPENI ("DSO-TYPE Not Implemented"; RCODE 11), 表示服务器支持 DSO, 但没有实现该 DSO request message 中 Primary TLV 的 DSO-TYPE. 实现 DSO 的服务器禁止对 DSO Keepalive request message 返回 DSOTYPENI, 因为 Keepalive TLV 是强制实现的. 但未来如果客户端尝试用某个新定义且需要响应的 DSO-TYPE 建立 DSO Session, 而服务器不理解该 DSO-TYPE, 就会产生 DSOTYPENI 响应. 如果服务器返回 DSOTYPENI, 则不认为 DSO Session 已建立. 不过, 客户端仍允许在该连接上继续发送 DNS 消息, 包括其他 DSO 消息, 例如 DSO Keepalive, 这可能产生成功的 NOERROR 响应, 从而建立 DSO Session.
当现有 DNS 服务器收到无法识别 DSO OPCODE 的 DSO 消息时, 还有两种可能结果: 服务器可能不对 DSO 消息发送响应, 或者服务器可能断开连接.
如果服务器不对 DSO 消息发送响应, 客户端应等待 30 秒, 然后假定服务器不支持 DSO. 如果服务器在 30 秒内没有响应, 可以假定它不会响应; 这会使服务器处于未规定状态: 没有规范要求必须对未知消息发送响应, 也没有规范说明如果不发送响应服务器处于什么状态. 因此, 客户端必须强制中止到服务器的连接. 如适用, 客户端可以重新连接但不使用 DSO (第 6.6.3.1 节). 通过断开并重新连接, 客户端确保服务器在发送任何后续请求前处于已知状态.
如果服务器断开连接, 客户端应把该服务实例标记为不支持 DSO, 并在失败尝试后的一段时间内 (至少一小时) 不再尝试 DSO 连接. 如适用, 客户端可以重新连接但不使用 DSO (第 6.6.3.2 节).
5.1.2. DSO Session 建立成功
当服务器收到客户端的 DSO request message, 并为该请求发送成功的 NOERROR 响应时, 服务器认为 DSO Session 已建立.
当客户端收到服务器对其 DSO request message 的 NOERROR 响应时, 客户端认为 DSO Session 已建立.
一旦 DSO Session 建立, 任一端都可以在任何时候单方面发送适当的 DSO 消息, 因而客户端或服务器都可以成为消息发起方.
5.2. DSO Session 建立后的操作
一旦 DSO Session 建立, 客户端和服务器应按本规范中关于非活动超时和会话终止的规定行事, 而不是按先前 DNS-over-TCP 规范 [RFC7766] 的规定行事.
由于支持 DNS Stateful Operations 的服务器在收到 Keepalive TLV DSO request message 时必须返回 "NOERROR" RCODE, Keepalive TLV 是建立 DSO Session 的理想候选. 任何其他只会在发送到所需类型服务器时成功的选项, 也都是建立 DSO Session 的良好候选. 对只实现本基础规范定义的 DSO-TYPE 的客户端而言, 发送 Keepalive TLV 是它们可用于发起 DSO Session 的唯一 DSO request message. 即使客户端实现了其他未来 DSO-TYPE, 为简单起见, 也可以选择始终发送初始 DSO Keepalive request message 作为发起 DSO Session 的方式. 未来对某个新的需要响应的 DSO-TYPE 的定义, 会给实现者提供愿意时使用该新 DSO-TYPE 的选项, 但不会改变发送 Keepalive TLV 仍然是发起 DSO Session 的有效方式这一事实.
5.3. DSO Session 终止
底层连接关闭时, DSO Session 即被终止. DSO Session 的 "优雅关闭" 可能由服务器因过载关闭 DSO Session, 客户端因完成任务关闭 DSO Session, 或客户端因会话非活动关闭 DSO Session 导致. 当客户端或服务器因协议错误关闭连接时, DSO Session 被 "强制中止".
-
本规范中 "优雅关闭" 指发送 TLS close_notify (如果使用 TLS) 后跟 TCP FIN, 或其他协议中的等效操作. 当本规范要求优雅关闭连接时, 发起该优雅关闭的责任放在客户端, 以便把 TCP TIME-WAIT 状态的负担放在客户端而不是服务器上.
-
本规范中 "强制中止" 指发送 TCP RST 或其他协议中的等效操作. 在 BSD Sockets API 中, 这通过在关闭套接字前把 SO_LINGER 选项设置为零来实现.
5.3.1. 处理协议错误
在协议实现中, 软件作者通常要处理两类错误. 第一类是环境因素导致的情况, 例如临时连通性丢失. 虽然这些情况并不理想, 但它们不表示软件存在缺陷, 软件通常应能从中恢复.
第二类是在与合规 DSO 实现通信时绝不应发生的情况. 如果发生, 就表示协议实现存在严重缺陷, 超出了合理期望软件恢复的范围. 本文把后一类错误条件称为 "致命错误 (fatal error)", 并规定实现遇到致命错误条件时 "必须立即强制中止连接".
5.4. 消息格式
DSO 消息以标准 12 字节 DNS 消息头 [RFC1035] 开始, 其中 OPCODE 字段设置为 DSO OPCODE (6). 但与标准 DNS 消息不同, question section, answer section, authority records section 和 additional records section 都不存在. 对应的计数字段 (QDCOUNT, ANCOUNT, NSCOUNT, ARCOUNT) 发送时必须设为零.
如果收到的 DSO 消息中任一计数字段不为零, 则必须返回 FORMERR.
1 1 1 1 1 1
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| MESSAGE ID |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|QR | OPCODE (6) | Z | RCODE |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| QDCOUNT (MUST be zero) |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| ANCOUNT (MUST be zero) |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| NSCOUNT (MUST be zero) |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| ARCOUNT (MUST be zero) |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| |
/ DSO Data /
/ /
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
5.4.1. DSO 消息中的 DNS 头字段
在 DSO unidirectional message 中, MESSAGE ID 字段必须设为零. 在 DSO request message 中, MESSAGE ID 字段必须设为发起方当前未在该连接上的其他活动操作中使用的唯一非零值. 在这里, 如果发起方已经在某个 DSO request message 中使用 MESSAGE ID 且仍在等待响应, 或者客户端已经用它建立了尚未取消的长期操作, 则该 MESSAGE ID 在此 DSO Session 中被视为正在使用. 例如, 长期操作可以是 Push Notification subscription [Push] 或 Discovery Relay interface subscription [Relay].
某条消息是 DSO request message 还是 DSO unidirectional message, 只由 Primary TLV 的规范决定. 不能通过在按其 Primary TLV 要求应为单向的消息中包含非零 MESSAGE ID 来请求确认. 也不能通过在按其 Primary TLV 要求应为 DSO request message 的消息中发送零 MESSAGE ID 来阻止确认. 响应方收到任一此类格式错误消息时, 必须将其视为致命错误并立即强制中止连接.
在 DSO request message 或 DSO unidirectional message 中, DNS Header Query/Response (QR) bit 必须为零 (QR=0). 如果 QR bit 非零, 该消息就不是 DSO request 或 DSO unidirectional message.
在 DSO response message 中, DNS Header QR bit 必须为一 (QR=1). 如果 QR bit 不是一, 该消息就不是 DSO response message.
在 DSO response message (QR=1) 中, MESSAGE ID 字段禁止为零, 且必须包含被响应的 DSO request message 中非零 MESSAGE ID 字段值的副本. 如果收到 MESSAGE ID 为零的 DSO response message (QR=1), 这是致命错误, 接收方必须立即强制中止连接.
DNS Header OPCODE 字段保存 DSO OPCODE 值 (6).
Z bit 当前在 DSO 消息中未使用; 在 DSO request message 和 DSO response 中, Z bit 发送时必须设为零 (0), 接收时必须忽略.
在 DSO request message (QR=0) 中, RCODE 根据请求定义设置. 例如, 在 Retry Delay message (第 6.6.1 节) 中, RCODE 指示终止原因. 但在大多数 DSO request message (QR=0) 中, 除非有明确相反规定, RCODE 发送时设为零, 接收时静默忽略.
response message (QR=1) 中的 RCODE 值可以是下列值之一:
| Code | Mnemonic | Description |
|---|---|---|
| 0 | NOERROR | 操作处理成功 |
| 1 | FORMERR | 格式错误 |
| 2 | SERVFAIL | 服务器因自身问题未能处理 DSO request message |
| 4 | NOTIMP | 不支持 DSO |
| 5 | REFUSED | 因策略原因拒绝操作 |
| 11 | DSOTYPENI | Primary TLV 的 DSO-Type 未实现 |
上述 RCODE 很可能在 DSO 中常用, 但并不排除未来使用 DSO 的文档定义和使用其他代码.
如果定义新 DSO-TYPE 的文档使用了本文未定义的响应码, 则该文档必须说明这些 RCODE 值在该新 DSO TLV 语境中的具体解释.
RCODE 字段之后是四个值为零的计数字段, 再之后是 DSO Data.
5.4.2. DSO Data
标准 12 字节 DNS 消息头及其值为零的计数字段之后是 DSO Data, 使用 TLV 语法表示, 如第 5.4.4 节所述.
DSO request message 或 DSO unidirectional message 必须至少包含一个 TLV. DSO request message 或 DSO unidirectional message 中的第一个 TLV 称为 "Primary TLV", 它决定所执行操作的性质, 包括该操作是 DSO request 还是 DSO unidirectional operation. 在某些情况下, 可以在 DSO request message 或 DSO unidirectional message 中包含其他 TLV, 例如 DSO Encryption Padding TLV (第 7.3 节). 其他 TLV 跟随 Primary TLV. Additional TLV 不限于本文定义的内容. 未来可以定义新的 Additional TLV. 其定义会描述何时适合使用它们.
无法识别的 Primary TLV 会导致 DSOTYPENI 错误响应. 无法识别的 Additional TLV 会被静默忽略, 如第 5.4.5 节和第 8.2 节所述.
DSO response message 可以不包含 TLV, 也可以包含一个或多个适合所传递信息的 TLV.
任何与对应 DSO request message 中 Primary TLV 具有相同 DSO-TYPE 的 TLV 都是 Response Primary TLV, 并且必须首先出现在 DSO response message 中. DSO response message 可以包含多个 Response Primary TLV, 或单个 Response Primary TLV, 或在某些情况下不包含 Response Primary TLV. Response Primary TLV 不是必需的; 对大多数 DSO 操作而言, DNS 消息头中的 MESSAGE ID 字段足以识别特定 response message 所关联的 DSO request message.
DSO response message 中的任何其他 TLV 都是 Response Additional TLV, 例如 DSO Encryption Padding TLV (第 7.3 节). 如果存在 Response Primary TLV, Response Additional TLV 跟随其后. Response Additional TLV 不限于本文定义的内容. 未来可以定义新的 Response Additional TLV. 其定义会描述何时适合使用它们. 无法识别的 Response Additional TLV 会被静默忽略, 如第 5.4.5 节和第 8.2 节所述.
每个 DSO TLV 的规范决定使用该 TLV 的 DSO request message 的响应中需要哪些 TLV. 如果某个操作的规范要求响应携带特定 TLV, 而收到的 DSO response 中缺少所需 TLV, 则这是致命错误, 有缺陷响应消息的接收方必须立即强制中止连接. 类似地, 如果某个 TLV 的实例数量超过规定数量, 也是致命错误, 有缺陷响应消息的接收方必须立即强制中止连接.
5.4.3. DSO 单向消息
预计大多数 DSO 操作会被规定为使用 DSO request message, 并产生对应 DSO response. 在某些专门的高流量用例中, 规定使用 DSO unidirectional message 可能是合适的. DSO unidirectional message 在网络上更高效, 因为它们不会生成一串对应回复消息. 使用 DSO unidirectional message 在某些情况下也能简化软件, 因为发起方不需要在等待自己并不关心的回复时维护状态. 当某个 TLV 作为外发 DSO request message (即 QR=0) 中的 Primary TLV (即第一个 TLV) 使用时, 若该 TLV 的规范说明该消息应为单向, 则 MESSAGE ID 字段必须设为零, 接收方禁止生成与该 DSO unidirectional message 对应的任何 response message.
前一点, 即接收方禁止为 DSO unidirectional message 生成响应, 即使在错误情况下也适用.
当收到 QR bit 和 MESSAGE ID 字段均为零的 DSO 消息时, 接收方禁止生成任何响应. 例如, 如果 Primary TLV 中的 DSO-TYPE 无法识别, 则禁止返回 DSOTYPENI 错误; 接收方必须改为立即强制中止连接.
在发送方不知道接收方是否支持消息中的 Primary TLV 时, 禁止 "推测性" 使用 DSO unidirectional message, 因为没有任何方式接收响应以表示成功或失败. DSO unidirectional message 只适用于发送方已经知道接收方支持并希望接收这些消息的情况.
例如, 客户端订阅 Push Notifications [Push] 后, 后续事件通知会作为 DSO unidirectional message 发送. 这是合适的, 因为客户端通过发起 Push Notification subscription 指示了它支持 Push Notifications 且希望接收这些通知.
类似地, Discovery Relay 客户端订阅从 Discovery Relay 接收入站 multicast DNS (mDNS) [RFC6762] 流量后, 后续接收到的数据包流会通过 DSO unidirectional message 发送. 这是合适的, 因为客户端通过发起 Discovery Relay link subscription 指示了它支持 Discovery Relay, 并希望通过该 DSO Session 接收入站 mDNS 数据包 [Relay].
5.4.4. TLV 语法
所有 TLV, 无论作为 "Primary", "Additional", "Response Primary" 还是 "Response Additional" 使用, 都采用相同编码语法.
定义新 TLV 的规范必须说明该 DSO-TYPE 是否可作为 Primary TLV 使用, 以及该 DSO-TYPE 是否可作为 Additional TLV 使用. 某些 DSO-TYPE 具有双重用途, 在一些消息中可作为 Primary TLV, 在另一些消息中可作为 Additional TLV. DSO-TYPE 的规范还必须说明, 当它作为 DSO 消息 (即 QR=0) 中的 Primary (即第一个) TLV 使用时, 该 DSO 消息是单向消息, 还是需要响应的 DSO request message.
如果 DSO request message 需要响应, 规范还必须说明响应中应包含哪些 TLV (如有), 以及每种 TLV 允许多少个实例. Primary TLV 是否包含在响应中取决于该 TLV 的规范. 如果允许 Primary TLV 的多个实例, 规范应清楚描述如何处理它们.
1 1 1 1 1 1
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| DSO-TYPE |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| DSO-LENGTH |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| |
/ DSO-DATA /
/ /
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
DSO-TYPE: 16 位无符号整数, 采用网络字节序 (big endian), 给出当前 DSO TLV 在 IANA "DSO Type Codes" 注册表中的 DSO-TYPE.
DSO-LENGTH: 16 位无符号整数, 采用网络字节序 (big endian), 给出 DSO-DATA 的字节大小.
DSO-DATA: 特定类型代码的格式. 通用 DSO 机制把 DSO-DATA 视为不透明 "blob", 不尝试解释它. 解释特定 DSO-TYPE 的 DSO-DATA 含义, 是实现该 DSO-TYPE 的软件的责任.
5.4.5. 无法识别的 TLV
如果收到的 DSO request message 包含无法识别的 Primary TLV, 且 MESSAGE ID 非零 (表示期望响应), 则接收方必须发送 MESSAGE ID 匹配且 RCODE 为 DSOTYPENI 的错误响应. 错误响应禁止包含无法识别的 Primary TLV 的副本.
如果收到的 DSO unidirectional message 同时包含无法识别的 Primary TLV 和零 MESSAGE ID (表示不期望响应), 则这是致命错误, 接收方必须立即强制中止连接.
如果收到的 DSO request message 或 DSO unidirectional message 中 Primary TLV 可识别, 但包含一个或多个无法识别的 Additional TLV, 则无法识别的 Additional TLV 必须被静默忽略, 消息其余部分按无法识别部分不存在来解释和处理.
类似地, 如果收到的 DSO response message 包含一个或多个无法识别的 TLV, 这些 TLV 会被静默忽略, 消息其余部分按无法识别部分不存在来解释和处理.
5.4.6. EDNS(0) 和 TSIG
由于 ARCOUNT 字段必须为零, DSO 消息不能在 additional records section 中包含有效 EDNS(0) option. 如果希望在 DSO 消息中使用当前或未来 EDNS(0) option 提供的功能, 就需要定义一个或多个新的 DSO TLV 来携带必要信息.
例如, 用于安全目的的 EDNS(0) Padding Option [RFC7830] 不允许出现在 DSO 消息中, 因此如果 DSO 消息需要消息填充, 就必须使用第 7.3 节所述 DSO Encryption Padding TLV.
DSO 消息不能包含 TSIG record, 因为 TSIG record 包含在消息的 additional section 中, 这会使 ARCOUNT 大于零. DSO 消息要求 ARCOUNT 为零. 因此, 如果未来需要对 DSO 消息使用签名, 就必须定义新的 DSO TLV 来执行该功能.
不过请注意, 虽然 DSO messages 不能包含 EDNS(0) 或 TSIG record, DSO session 通常用于承载一整串不同类型的 DNS 消息, 包括 DSO 消息以及 Query [RFC1034] [RFC1035] 和 Update [RFC2136] 等其他 DNS 消息类型. 这些消息可以携带 EDNS(0) 和 TSIG record.
虽然消息可以按需包含其他 EDNS(0) option, 本规范明确禁止在 DSO Session 上发送的 任何 消息中使用 edns-tcp-keepalive EDNS(0) Option [RFC7828], 因为它已被 DSO Keepalive operation 提供的功能取代. 如果在 DSO Session 上发送的任何消息包含 edns-tcp-keepalive EDNS(0) Option, 这是致命错误, 有缺陷消息的接收方必须立即强制中止连接.
5.5. 消息处理
如第 5.4.1 节所述, DNS 头中 QR bit 设为零的外发 DSO 消息是 DSO request 还是 DSO unidirectional message, 由 Primary TLV 的规范决定, 该规范又决定外发消息中的 MESSAGE ID 字段是零还是非零.
DNS 头中 QR bit 设为零且 MESSAGE ID 字段非零的每条 DSO 消息都是 DSO request message, 并且必须引发对应响应; 对应响应的 DNS 头中 QR bit 设为一, MESSAGE ID 字段设为对应 DSO request message 中给出的值.
客户端发送的带非零 MESSAGE ID 字段的有效 DSO request message 会引发服务器响应, 服务器发送的带非零 MESSAGE ID 字段的有效 DSO request message 会引发客户端响应.
QR bit 和 MESSAGE ID 字段均设为零的每条 DSO 消息都是 DSO unidirectional message, 禁止引发响应.
5.5.1. 延迟确认管理
通常, 大多数良好的 TCP 实现都会采用 delayed acknowledgement timer, 以更高效地利用网络并改善性能.
在 TCP 上的双向交换中, 例如 DSO request message, 操作系统 TCP 实现会等待应用层客户端软件生成对应 DSO response message. 随后 TCP 实现可以发送一个合并包, 其中包含 TCP acknowledgement, TCP window update 和应用生成的 DSO response message. 这比立即确认包含 DSO request 的 TCP 包并发送三个独立包更高效.
对于 DSO unidirectional message 或 DSO response message, 不存在对应的应用生成 DSO response message, 因而也没有任何提示告诉传输协议何时应发送 acknowledgement 和 window update.
某些网络 API 提供一种机制, 允许应用层客户端软件向传输协议发出信号, 表示不会再有响应 (实际上可视为零长度 "empty" write). 在所用网络 API 提供该机制的情况下, DSO unidirectional message 或 DSO response message 的接收方在解析和解释消息后, 应使用网络 API 提供的该机制发出信号, 表示不会为该消息产生响应. TCP 实现随后可以立即发送 acknowledgement 和 window update, 不再继续延迟. 第 9.5 节进一步讨论了这为何重要.
5.5.2. MESSAGE ID 命名空间
16 位 MESSAGE ID 的命名空间在两个方向上彼此独立. 这意味着客户端和服务器同时使用相同 MESSAGE ID 在不同方向发送 DSO request message 并不是错误. 这种简化是协议可实现所必需的. 要求客户端和服务器就新唯一 MESSAGE ID 的分配彼此协调是不可行的, 也没有必要. 16 位 MESSAGE ID 值结合发起方身份 (客户端或服务器) 足以明确标识相关操作. 可以把它看作一个 17 位消息标识符空间, 使用 0x00001-0x0FFFF 表示客户端到服务器的 DSO request message, 使用 0x10001-0x1FFFF 表示服务器到客户端的 DSO request message. 低 16 位显式存储在 DSO 消息的 MESSAGE ID 字段中, 最高位由消息方向隐式给出.
如第 5.4.1 节所述, 发起方禁止重用已经用于未完成 DSO request message 的 MESSAGE ID (除非相关 DSO-TYPE 的规范另有规定). 至少, 这意味着在特定方向上的特定 DSO Session 中, 当发起方仍在等待使用该 MESSAGE ID 的先前 DSO request message 的响应时, 不能重用该 MESSAGE ID (除非相关 DSO-TYPE 的规范另有规定). 对长期操作而言, 只要该操作仍处于活动状态, 该操作的 MESSAGE ID 就不能重用.
如果客户端或服务器收到 response (QR=1), 其中 MESSAGE ID 为零, 或为不匹配其任何未完成操作的其他值, 这是致命错误, 接收方必须立即强制中止连接.
如果响应方收到 MESSAGE ID 非零的 DSO request message (QR=0), 且该响应方会跟踪 request MESSAGE ID, 并且该 MESSAGE ID 与它收到但尚未发送响应的某个 DSO request message 的 MESSAGE ID 匹配, 则它必须立即强制中止连接. 此行为用于防止利用此情况下未定义行为的假设攻击. 但是, 如果响应方不以这种方式跟踪 MESSAGE ID, 则不存在此风险. 因此, 并不要求仅为实现该健全性检查而跟踪 MESSAGE ID.
5.5.3. 错误响应
当 DSO request message 因某种原因不成功时, 响应方向发起方返回错误码.
如果服务器向客户端返回错误码以响应不成功的 DSO request message, 服务器可以选择结束 DSO Session, 也可以选择允许 DSO Session 保持打开. 对只影响单个相关操作的错误条件, 服务器应向客户端返回错误响应, 并让 DSO Session 保持打开以供后续操作使用.
对很可能使近期所有操作都不成功的错误条件, 服务器应向客户端返回错误响应, 然后按第 6.6.1 节所述发送 Retry Delay message 来结束 DSO Session.
客户端收到服务器错误响应后, 不应自动关闭 DSO Session. 与某个特定操作相关的错误并不必然意味着该 DSO Session 上所有其他操作也失败, 或未来操作也会失败. 客户端应假定服务器会自行决定是否结束 DSO Session, 决定依据是服务器判断该错误条件只涉及该特定操作, 还是也涉及后续操作. 如果服务器没有通过发送 Retry Delay message (第 6.6.1 节) 结束 DSO Session, 客户端应继续把该 DSO Session 用于后续操作.
当收到 DSO unidirectional message 类型 (MESSAGE ID 字段为零) 时, 接收方应已经期待该 DSO 消息类型. 第 5.4.5 节描述了未知 DSO 消息类型的处理. 当收到意外类型的 DSO unidirectional message 时, 接收方应强制中止连接. 对于处理 DSO unidirectional message 时发生的其他内部错误, 是否应强制中止连接取决于错误严重程度, 属于实现相关.
5.6. 响应方发起的操作取消
本文作为 DNS Stateful Operations 的基础规范, 自身不定义任何长期操作, 但定义了支持 Push Notification subscription [Push] 和 Discovery Relay interface subscription [Relay] 等长期操作的框架.
长期操作如果成功, 会保持活动直到发起方终止该操作.
不过, 某个长期操作在发起时可能有效, 但后续环境变化可能使该操作无效. 例如, 某个长期客户端操作可能涉及服务器具有权威的名称, 但随后服务器配置改变, 使其不再对该名称具有权威.
在这种情况下, 与其终止整个会话, 响应方选择性取消那些已经无效的操作可能更合适.
响应方通过发送新的 DSO response message 执行这种选择性取消. 该消息的 MESSAGE ID 字段包含要终止的长期操作的 MESSAGE ID (响应方先前曾以 NOERROR RCODE 确认该操作), 新 DSO response message 的 RCODE 字段给出取消原因.
发送带非零 RCODE 的 DSO response message 后, 从响应方视角看该操作已经终止, 响应方不再发送与该操作相关的消息.
发起方收到带非零 RCODE 的 DSO response message 后, 从发起方视角看该操作已经终止, 被取消操作的 MESSAGE ID 现在可以重用.