Skip to main content

7. Base Protocol Procedures (基本协议过程)

本节定义了 STUN 协议的基本过程。它描述了如何形成消息、如何发送消息以及收到消息时如何处理。它还定义了绑定方法 (Binding method) 的详细处理过程。本文档的其他部分描述了用法 (usage) 可以选择在某些情况下使用的可选过程。其他文档可以通过添加新方法、新属性或新错误响应码来定义 STUN 的其他扩展。

7.1. Forming a Request or an Indication (形成请求或指示)

在形成请求或指示消息时,代理必须 (MUST) 在创建头部时遵循第6节中的规则。此外,消息类别必须 (MUST) 是 "Request" 或 "Indication" (视情况而定),方法必须 (must) 是 Binding 或其他文档中定义的某个方法。

然后,代理添加方法或用法指定的任何属性。例如,某些用法可能指定代理使用认证方法 (第10节) 或 FINGERPRINT 属性 (第8节)。

如果代理正在发送请求,它应该 (SHOULD) 向请求添加 SOFTWARE 属性。根据方法的不同,代理可以 (MAY) 在指示中包含 SOFTWARE 属性。STUN 的扩展应该讨论 SOFTWARE 在新指示中是否有用。

对于不使用认证的绑定方法,除非用法另有规定,否则不需要任何属性。

通过 UDP 发送的所有 STUN 消息应该 (SHOULD) 小于路径 MTU (如果已知)。如果路径 MTU 未知,对于 IPv4,消息应该 (SHOULD) 是 576 字节和第一跳 MTU 中较小的那个 [RFC1122],对于 IPv6 是 1280 字节 [RFC2460]。此值对应于整个 IP 数据包的大小。因此,对于 IPv4,实际的 STUN 消息需要小于 548 字节 (576 减去 20 字节 IP 头部,减去 8 字节 UDP 头部,假设不使用 IP 选项)。STUN 不提供处理请求在 MTU 下但响应会大于 MTU 的情况的能力。预计这一限制不会成为 STUN 的问题。MTU 限制是应该 (SHOULD),而不是必须 (MUST),以适应 STUN 本身用于探测 MTU 特性的情况 [BEHAVE-NAT]。在此类或类似应用之外,必须 (MUST) 遵守 MTU 约束。

7.2. Sending the Request or Indication (发送请求或指示)

然后,代理发送请求或指示。本文档指定了如何通过 UDP、TCP 或 TLS-over-TCP 发送 STUN 消息; 将来可能会添加其他传输协议。STUN 用法必须 (must) 指定使用哪种传输协议,以及代理如何确定接收方的 IP 地址和端口。第9节描述了用法可以选择使用的基于 DNS 的服务器 IP 地址和端口确定方法。STUN 可以与任播地址 (anycast addresses) 一起使用,但仅适用于 UDP 以及不使用认证的用法。

在任何时候,客户端可以 (MAY) 与同一 STUN 服务器有多个未完成的 STUN 请求 (即多个正在进行的事务,具有不同的事务 ID)。在没有对新事务速率的其他限制的情况下 (例如 ICE 为连接性检查指定的限制或 STUN 通过 TCP 运行时),客户端应该 (SHOULD) 以 RTO 的间隔向服务器发送新事务,并且应该 (SHOULD) 将自己限制为向同一服务器发送最多十个未完成的事务。

7.2.1. Sending over UDP (通过 UDP 发送)

在 UDP 上运行 STUN 时,STUN 消息可能会被网络丢弃。STUN 请求/响应事务的可靠性通过客户端应用程序本身重传请求消息来实现。STUN 指示不会重传; 因此,通过 UDP 的指示事务是不可靠的。

客户端应该 (SHOULD) 从 RTO ("重传超时", Retransmission TimeOut) 的间隔开始重传 STUN 请求消息,每次重传后加倍。RTO 是往返时间 (RTT, round-trip time) 的估计值,并按照 RFC 2988 [RFC2988] 中的描述进行计算,有两个例外。首先,RTO 的初始值应该 (SHOULD) 是可配置的 (而不是 RFC 2988 中推荐的 3 秒),并且应该 (SHOULD) 大于 500 毫秒。此 "应该" 的例外情况是当使用其他机制来推导拥塞阈值时 (例如 ICE 为固定速率流定义的机制),或当 STUN 在具有已知网络容量的非互联网环境中使用时。在固定线路接入链路中,推荐 (RECOMMENDED) 值为 500 毫秒。其次,RTO 的值不应该 (SHOULD NOT) 向上舍入到最近的秒。相反,应该 (SHOULD) 保持 1 毫秒的精度。与 TCP 一样,推荐 (RECOMMENDED) 使用 Karn 算法 [KARN87]。当应用于 STUN 时,这意味着不应该 (SHOULD NOT) 从导致请求重传的 STUN 事务计算 RTT 估计值。

RTO 的值应该 (SHOULD) 在事务完成后由客户端缓存,并用作向同一服务器 (基于 IP 地址相等性) 的下一个事务的 RTO 起始值。该值应该 (SHOULD) 在 10 分钟后被视为过时并丢弃。

重传会持续到收到响应,或者直到已发送总共 Rc 个请求为止。Rc 应该 (SHOULD) 是可配置的,并且应该 (SHOULD) 具有默认值 7。如果在最后一个请求之后,已经过了 Rm 倍的 RTO 时间而没有收到响应 (提供了充足的时间来获取响应,如果只有这最后一个请求实际成功),客户端应该 (SHOULD) 认为事务已失败。Rm 应该 (SHOULD) 是可配置的,并且应该 (SHOULD) 具有默认值 16。如果出现硬 ICMP 错误 [RFC1122],通过 UDP 的 STUN 事务也被视为失败。例如,假设 RTO 为 500 毫秒,请求将在 0 毫秒、500 毫秒、1500 毫秒、3500 毫秒、7500 毫秒、15500 毫秒和 31500 毫秒时发送。如果客户端在 39500 毫秒后仍未收到响应,客户端将认为事务已超时。

7.2.2. Sending over TCP or TLS-over-TCP (通过 TCP 或 TLS-over-TCP 发送)

对于 TCP 和 TLS-over-TCP,客户端打开一个到服务器的 TCP 连接。

在某些 STUN 用法中,STUN 作为唯一的协议通过 TCP 连接发送。在这种情况下,可以在没有任何额外成帧或解复用的帮助下发送。在其他用法中,或者使用其他扩展,它可能会在 TCP 连接上与其他数据多路复用。在这种情况下,STUN 必须 (MUST) 在某种成帧协议之上运行,该成帧协议由用法或扩展指定,允许代理提取完整的 STUN 消息和完整的应用层消息。在知名端口或通过第9节中的 DNS 过程发现的端口上运行的 STUN 服务仅用于 STUN,而不是用于与其他数据多路复用的 STUN。因此,在连接到这些服务器时不使用成帧协议。当使用额外的成帧时,用法将指定客户端如何知道应用它以及要连接到哪个端口。例如,在 ICE 连接性检查的情况下,此信息是通过客户端和服务器之间的带外协商学习的。

当 STUN 单独通过 TLS-over-TCP 运行时,至少必须 (MUST) 实现 TLS_RSA_WITH_AES_128_CBC_SHA 密码套件。实现也可以 (MAY) 支持任何其他密码套件。当收到 TLS 证书消息时,客户端应该 (SHOULD) 验证证书并检查证书标识的站点。如果证书无效或已被吊销,或者如果它没有标识适当的一方,客户端禁止 (MUST NOT) 发送 STUN 消息或以其他方式继续 STUN 事务。客户端必须 (MUST) 验证服务器的身份。为此,它遵循 RFC 2818 [RFC2818] 第 3.1 节中定义的标识过程。这些过程假设客户端正在解引用 URI。为了与本规范一起使用,客户端将第 8.1 节中使用的域名或 IP 地址视为已解引用的 URI 的主机部分。或者,客户端可以 (MAY) 配置一组受信任的域或 IP 地址; 如果收到标识这些域或 IP 地址之一的证书,客户端认为服务器的身份已被验证。

当 STUN 与其他协议一起通过 TLS-over-TCP 连接多路复用时,强制密码套件和 TLS 处理过程按照这些协议的定义操作。

STUN 通过 TCP 和 TLS-over-TCP 的可靠性由 TCP 本身处理,在 STUN 协议级别没有重传。但是,对于请求/响应事务,如果客户端在发送 SYN 以建立连接后 Ti 秒内没有收到响应,它认为事务已超时。Ti 应该 (SHOULD) 是可配置的,并且应该 (SHOULD) 具有默认值 39.5 秒。选择此值是为了使 TCP 和 UDP 超时与默认初始 RTO 相等。

此外,如果客户端无法建立 TCP 连接,或者在收到响应之前 TCP 连接被重置或失败,任何正在进行的请求/响应事务都被视为失败。

客户端可以 (MAY) 通过单个 TCP (或 TLS-over-TCP) 连接发送多个事务,并且可以 (MAY) 在收到前一个响应之前发送另一个请求。客户端应该 (SHOULD) 保持连接打开,直到它:

  • 没有更多的 STUN 请求或指示要通过该连接发送,并且

  • 没有计划使用通过该连接上发送的 STUN 请求学习到的任何资源 (例如映射地址 (MAPPED-ADDRESS 或 XOR-MAPPED-ADDRESS) 或中继地址 [BEHAVE-TURN]),并且

  • 如果在该端口上多路复用其他应用协议,已完成使用该其他应用,并且

  • 如果将该学习到的端口与远程对等方一起使用,已与该远程对等方建立通信,这是某些 TCP NAT 穿透技术所要求的 (例如,[MMUSIC-ICE-TCP])。

在服务器端,服务器应该 (SHOULD) 保持连接打开,并让客户端关闭它,除非服务器确定连接已超时 (例如,由于客户端与网络断开连接)。客户端学习到的绑定仅在连接保持打开时在介入的 NAT 中保持有效。只有客户端知道它需要绑定多长时间。如果通过该连接收到了请求但未发送响应,服务器不应该 (SHOULD NOT) 关闭连接。服务器禁止 (MUST NOT) 打开到客户端的连接以发送响应。在过载的情况下,服务器应该 (SHOULD) 遵循有关连接管理的最佳实践。

7.3. Receiving a STUN Message (接收 STUN 消息)

本节指定 STUN 消息的处理。此处指定的处理适用于本规范中定义的 STUN 消息; 第12节定义了用于向后兼容的附加规则。这些附加过程是可选的,用法可以选择使用它们。首先,应用一组独立于类别的处理操作。随后是特定于类别的处理,在后续子节中描述。

当 STUN 代理收到 STUN 消息时,它首先检查消息是否遵守第6节的规则。它检查前两位是否为 0,魔术饼干字段是否具有正确的值,消息长度是否合理,以及方法值是否为支持的方法。它检查消息类别是否允许用于特定方法。如果消息类别是 "Success Response" 或 "Error Response",代理检查事务 ID 是否与仍在进行的事务匹配。如果正在使用 FINGERPRINT 扩展,代理检查 FINGERPRINT 属性是否存在并包含正确的值。如果检测到任何错误,消息将被静默丢弃。在 STUN 与另一个协议多路复用的情况下,错误可能表明这实际上不是 STUN 消息; 在这种情况下,代理应该尝试将消息解析为不同的协议。

然后,STUN 代理执行用法指定的认证机制所需的任何检查 (参见第10节)。

完成认证检查后,STUN 代理检查消息中的未知属性和已知但意外的属性。代理必须 (MUST) 忽略未知的可选理解属性 (comprehension-optional attributes)。代理应该 (SHOULD) 忽略已知但意外的属性。未知的必须理解属性 (comprehension-required attributes) 会导致依赖于消息类别的处理,如下所述。

此时,进一步的处理取决于请求的消息类别。

7.3.1. Processing a Request (处理请求)

如果请求包含一个或多个未知的必须理解属性,服务器使用错误码 420 (Unknown Attribute) 的错误响应进行回复,并在响应中包含 UNKNOWN-ATTRIBUTES 属性,该属性列出未知的必须理解属性。

然后,服务器执行方法或特定用法所需的任何额外检查。如果所有检查都成功,服务器按照下面的描述形成成功响应。

当通过 UDP 运行时,服务器收到的请求可能是事务的第一个请求,也可能是重传。服务器必须 (MUST) 响应重传,以便保留以下属性: 如果客户端收到对重传的响应而不是对原始请求发送的响应,客户端和服务器上的整体状态与仅收到对原始重传的响应的情况相同,或者与收到两个响应的情况相同 (在这种情况下,客户端将使用第一个)。满足此要求的最简单方法是服务器记住在过去 40 秒内通过 UDP 收到的所有事务 ID 及其相应的响应。但是,这需要服务器保持状态,并且对于任何未经认证的请求都不合适。另一种方法是重新处理请求并重新计算响应。后一种技术必须 (MUST) 仅应用于幂等的请求 (当可以安全地重复相同的请求而不影响系统的整体状态时,该请求被认为是幂等的) 并且对相同的请求产生相同的成功响应。绑定方法被认为是幂等的。请注意,某些罕见的网络事件可能导致反射传输地址值发生变化,从而在不同的成功响应中产生不同的映射地址。STUN 的扩展必须 (MUST) 讨论请求重传对不存储事务状态的服务器的影响。

7.3.1.1. Forming a Success or Error Response (形成成功或错误响应)

在形成响应 (成功或错误) 时,服务器遵循第6节的规则。响应的方法与请求的方法相同,消息类别是 "Success Response" 或 "Error Response"。

对于错误响应,服务器必须 (MUST) 添加包含上述处理中指定的错误码的 ERROR-CODE 属性。原因短语不是固定的,但应该 (SHOULD) 是适合错误码的内容。对于某些错误,会向消息添加额外的属性。这些属性在指定错误码的描述中详细说明。例如,对于错误码 420 (Unknown Attribute),服务器必须 (MUST) 包含 UNKNOWN-ATTRIBUTES 属性。某些认证错误也会导致添加属性 (参见第10节)。扩展可能定义其他错误和/或在错误情况下添加的额外属性。

如果服务器使用认证机制对请求进行了认证,那么服务器应该 (SHOULD) 向响应添加适当的认证属性 (参见第10节)。

服务器还添加特定方法或用法所需的任何属性。此外,服务器应该 (SHOULD) 向消息添加 SOFTWARE 属性。

对于绑定方法,除非用法另有规定,否则不需要额外的检查。在形成成功响应时,服务器向响应添加 XOR-MAPPED-ADDRESS 属性,其中属性的内容是请求消息的源传输地址。对于 UDP,这是请求消息的源 IP 地址和源 UDP 端口。对于 TCP 和 TLS-over-TCP,这是服务器看到的 TCP 连接的源 IP 地址和源 TCP 端口。

7.3.1.2. Sending the Success or Error Response (发送成功或错误响应)

响应 (成功或错误) 通过与收到请求相同的传输发送。如果请求是通过 UDP 收到的,响应的目标 IP 地址和端口是收到的请求消息的源 IP 地址和端口,响应的源 IP 地址和端口等于收到的请求消息的目标 IP 地址和端口。如果请求是通过 TCP 或 TLS-over-TCP 收到的,响应在收到请求的同一 TCP 连接上发送回去。

7.3.2. Processing an Indication (处理指示)

如果指示包含未知的必须理解属性,指示将被丢弃并停止处理。

然后,代理执行方法或特定用法所需的任何额外检查。如果所有检查都成功,代理然后处理指示。不为指示生成响应。

对于绑定方法,除非用法另有规定,否则不需要额外的检查或处理。代理仅仅收到消息就刷新了介入 NAT 中的 "bindings"。

由于指示不会通过 UDP 重传 (与请求不同),因此在发送代理处不需要处理指示的重传。

7.3.3. Processing a Success Response (处理成功响应)

如果成功响应包含未知的必须理解属性,响应将被丢弃,事务被视为失败。

然后,客户端执行方法或特定用法所需的任何额外检查。如果所有检查都成功,客户端然后处理成功响应。

对于绑定方法,客户端检查响应中是否存在 XOR-MAPPED-ADDRESS 属性。客户端检查指定的地址族。如果是不支持的地址族,属性应该 (SHOULD) 被忽略。如果是意外但支持的地址族 (例如,绑定事务是通过 IPv4 发送的,但指定的地址族是 IPv6),则客户端可以 (MAY) 接受并使用该值。

7.3.4. Processing an Error Response (处理错误响应)

如果错误响应包含未知的必须理解属性,或者如果错误响应不包含 ERROR-CODE 属性,那么事务被简单地视为失败。

然后,客户端执行认证机制指定的任何处理 (参见第10节)。这可能导致新的事务尝试。

此时的处理取决于错误码、方法和用法; 以下是默认规则:

  • 如果错误码是 300 到 399,客户端应该 (SHOULD) 将事务视为失败,除非正在使用 ALTERNATE-SERVER 扩展。参见第11节。

  • 如果错误码是 400 到 499,客户端声明事务失败; 在 420 (Unknown Attribute) 的情况下,响应应该包含一个 UNKNOWN-ATTRIBUTES 属性,该属性提供额外的信息。

  • 如果错误码是 500 到 599,客户端可以 (MAY) 重新发送请求; 这样做的客户端必须 (MUST) 限制这样做的次数。

任何其他错误码都会导致客户端将事务视为失败。