跳到主要内容

15. STUN 属性 (STUN Attributes)

STUN 头部之后是零个或多个属性. 每个属性必须采用 TLV 编码, 包含 16-bit 类型, 16-bit 长度和值. 每个 STUN 属性必须在 32-bit 边界结束. 如上所述, 属性中的所有字段都以最高有效位优先传输.

    0                   1                   2                   3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Type | Length |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Value (variable) ....
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

Figure 4: STUN 属性的格式

length 字段中的值必须包含属性 Value 部分在填充之前的长度, 以字节计. 由于 STUN 将属性对齐到 32-bit 边界, 内容长度不是 4 字节倍数的属性会填充 1, 2 或 3 字节, 使其值包含 4 字节的倍数. 填充位会被忽略, 并且可以是任意值.

任一属性类型可以在一条 STUN 消息中出现多次. 除非另有规定, 出现顺序具有意义: 接收方只需要处理第一次出现的属性, 并且可以忽略任何重复项.

为允许本规范的未来修订在需要时添加新属性, 属性空间被划分为两个范围. 值在 0x0000 到 0x7FFF 之间的属性类型是 comprehension-required 属性, 这意味着 STUN 代理如果不理解该属性, 就无法成功处理消息. 值在 0x8000 到 0xFFFF 之间的属性类型是 comprehension-optional 属性, 这意味着 STUN 代理在不理解这些属性时可以忽略它们.

STUN 属性类型集合由 IANA 维护. 本规范定义的初始集合见 Section 18.2.

本节其余部分描述本规范定义的各类属性的格式.

15.1. MAPPED-ADDRESS

MAPPED-ADDRESS 属性表示客户端的反射传输地址. 它由 8-bit 地址族和 16-bit 端口组成, 后跟表示 IP 地址的固定长度值. 如果地址族是 IPv4, 地址必须为 32 bit. 如果地址族是 IPv6, 地址必须为 128 bit. 所有字段都必须使用网络字节序.

MAPPED-ADDRESS 属性的格式为:

    0                   1                   2                   3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|0 0 0 0 0 0 0 0| Family | Port |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| |
| Address (32 bits or 128 bits) |
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

Figure 5: MAPPED-ADDRESS 属性的格式

地址族可以取以下值:

  • 0x01: IPv4
  • 0x02: IPv6

MAPPED-ADDRESS 的前 8 bit 必须设置为 0, 接收方必须忽略这些 bit. 这些 bit 的存在是为了将参数对齐到自然的 32-bit 边界.

此属性仅由服务器用于实现与 RFC 3489 [RFC3489] 客户端的向后兼容.

15.2. XOR-MAPPED-ADDRESS

XOR-MAPPED-ADDRESS 属性与 MAPPED-ADDRESS 属性相同, 区别在于反射传输地址通过 XOR 函数进行混淆.

XOR-MAPPED-ADDRESS 的格式为:

   0                   1                   2                   3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|x x x x x x x x| Family | X-Port |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| X-Address (Variable)
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

Figure 6: XOR-MAPPED-ADDRESS 属性的格式

Family 表示 IP 地址族, 其编码方式与 MAPPED-ADDRESS 中的 Family 相同.

X-Port 的计算方法是取主机字节序的映射端口, 将其与 magic cookie 的最高有效 16 bit 做 XOR, 然后将结果转换为网络字节序. 如果 IP 地址族是 IPv4, X-Address 的计算方法是取主机字节序的映射 IP 地址, 将其与 magic cookie 做 XOR, 并将结果转换为网络字节序. 如果 IP 地址族是 IPv6, X-Address 的计算方法是取主机字节序的映射 IP 地址, 将其与 magic cookie 和 96-bit transaction ID 的串接结果做 XOR, 并将结果转换为网络字节序.

属性值前 8 bit 的编码和处理规则, 属性多次出现的处理规则, 以及地址族处理规则均与 MAPPED-ADDRESS 相同.

Note: XOR-MAPPED-ADDRESS 和 MAPPED-ADDRESS 只在传输地址的编码方式上不同. 前者通过将传输地址与 magic cookie 做 exclusive-or 来编码. 后者直接以二进制编码. RFC 3489 最初只规定了 MAPPED-ADDRESS. 但是, 部署经验发现, 一些 NAT 会重写包含 NAT 公共 IP 地址的 32-bit 二进制载荷, 例如 STUN 的 MAPPED-ADDRESS 属性, 其意图是提供通用 ALG 功能, 但这种做法虽出于好意却是错误的. 此类行为会干扰 STUN 的运行, 也会导致 STUN 的消息完整性检查失败.

15.3. USERNAME

USERNAME 属性用于消息完整性. 它标识消息完整性检查中使用的用户名和密码组合.

USERNAME 的值是可变长度值. 它必须包含小于 513 字节的 UTF-8 [RFC3629] 编码序列, 并且必须已经使用 SASLprep [RFC4013] 处理.

15.4. MESSAGE-INTEGRITY

MESSAGE-INTEGRITY 属性包含 STUN 消息的 HMAC-SHA1 [RFC2104]. MESSAGE-INTEGRITY 属性可以出现在任何 STUN 消息类型中. 由于它使用 SHA1 hash, HMAC 长度为 20 字节. 作为 HMAC 输入的文本是 STUN 消息, 包括头部, 一直到并包含 MESSAGE-INTEGRITY 属性之前的属性. 除 FINGERPRINT 属性 (出现在 MESSAGE-INTEGRITY 之后) 外, 代理必须忽略 MESSAGE-INTEGRITY 后面的所有其他属性.

HMAC 的密钥取决于使用长期凭据还是短期凭据. 对于长期凭据, 密钥为 16 字节:

key = MD5(username ":" realm ":" SASLprep(password))

也就是说, 16 字节密钥通过对以下五个字段串接结果取 MD5 hash 形成: (1) 来自 USERNAME 属性 (如果存在) 的 username, 去除任何引号和尾随 null; (2) 一个冒号; (3) realm, 去除任何引号和尾随 null; (4) 一个冒号; (5) password, 去除任何尾随 null, 并经过 SASLprep 处理. 例如, 如果 username 是 'user', realm 是 'realm', password 是 'pass', 则 16 字节 HMAC 密钥就是对字符串 'user:realm:pass' 执行 MD5 hash 的结果, 该 hash 为 0x8493fbc53ba582fb4c044c456bdc40eb.

对于短期凭据:

key = SASLprep(password)

其中 MD5 在 RFC 1321 [RFC1321] 中定义, SASLprep() 在 RFC 4013 [RFC4013] 中定义.

与长期凭据一起使用时, 该密钥结构便于在也使用 SIP 的系统中部署. 通常, 使用 SIP 摘要认证机制的 SIP 系统并不会在数据库中实际存储密码. 相反, 它们存储一个称为 H(A1) 的值, 该值等于上面定义的密钥.

根据上述规则, 用于构造 MESSAGE-INTEGRITY 的 hash 包含 STUN 消息头部中的 length 字段. 在执行 hash 之前, MESSAGE-INTEGRITY 属性必须插入消息中 (带有虚拟内容). 随后 length 必须设置为指向消息长度, 该长度截至并包括 MESSAGE-INTEGRITY 属性本身, 但不包括其后的任何属性. 计算完成后, 可以填入 MESSAGE-INTEGRITY 属性的值, 并将 STUN 头部中的 length 值设置为其正确值 -- 整条消息的长度. 类似地, 验证 MESSAGE-INTEGRITY 时, 在计算 HMAC 之前应调整 length 字段, 使其指向 MESSAGE-INTEGRITY 属性的末尾. 当 FINGERPRINT 等属性出现在 MESSAGE-INTEGRITY 之后时, 这种调整是必要的.

15.5. FINGERPRINT

FINGERPRINT 属性可以出现在所有 STUN 消息中. 该属性的值计算方式为: 对 STUN 消息中截至 (但不包括) FINGERPRINT 属性本身的部分计算 CRC-32, 再与 32-bit 值 0x5354554e 做 XOR (当应用程序包本身也使用 CRC-32 时, XOR 会有所帮助). 32-bit CRC 是 ITU V.42 [ITU.V42.2002] 中定义的 CRC, 其生成多项式为 x32+x26+x23+x22+x16+x12+x11+x10+x8+x7+x5+x4+x2+x+1. 当存在时, FINGERPRINT 属性必须是消息中的最后一个属性, 因此会出现在 MESSAGE-INTEGRITY 之后.

FINGERPRINT 属性有助于将 STUN 包与其他协议的包区分开. 见 Section 8.

与 MESSAGE-INTEGRITY 一样, FINGERPRINT 属性中使用的 CRC 覆盖 STUN 消息头部中的 length 字段. 因此, 在计算 CRC 之前, 该值必须正确, 并且必须将 CRC 属性作为消息长度的一部分包含在内. 在消息中使用 FINGERPRINT 属性时, 先将该属性以虚拟值放入消息, 然后计算 CRC, 再更新该属性的值. 如果 MESSAGE-INTEGRITY 属性也存在, 则在计算 CRC 之前它必须具有正确的消息完整性值, 因为 CRC 也覆盖 MESSAGE-INTEGRITY 属性的值.

15.6. ERROR-CODE

ERROR-CODE 属性用于错误响应消息. 它包含范围为 300 到 699 的数字错误码值, 以及用 UTF-8 [RFC3629] 编码的文本原因短语, 并且其代码分配和语义与 SIP [RFC3261] 和 HTTP [RFC2616] 一致. 原因短语供用户阅读, 可以是任何适合该错误码的内容. 已定义错误码的推荐原因短语包含在错误码的 IANA 注册表中. 原因短语必须是小于 128 字符的 UTF-8 [RFC3629] 编码序列 (最长可达 763 字节).

    0                   1                   2                   3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Reserved, should be 0 |Class| Number |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Reason Phrase (variable) ..
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

Figure 7: ERROR-CODE 属性

为便于处理, 错误码的 class (百位数字) 与代码的其余部分分开编码, 如 Figure 7 所示.

Reserved bit 应该为 0, 用于对齐到 32-bit 边界. 接收方必须忽略这些 bit. Class 表示错误码的百位数字. 其值必须在 3 到 6 之间. Number 表示错误码 modulo 100 后的值, 其值必须在 0 到 99 之间.

定义了以下错误码及其推荐原因短语:

300 Try Alternate: 客户端应为此请求联系备用服务器. 仅当请求包含 USERNAME 属性和有效的 MESSAGE-INTEGRITY 属性时, 此错误响应必须才能发送; 否则, 它不得发送, 并建议使用错误码 400 (Bad Request). 此错误响应必须使用 MESSAGE-INTEGRITY 属性保护, 接收方在将自身重定向到备用服务器之前必须验证此响应的 MESSAGE-INTEGRITY.

Note: 如果未为 300 响应生成并验证消息完整性, 路径上攻击者就可以伪造 300 响应, 从而导致后续 STUN 消息被发送给受害者.

400 Bad Request: 请求格式错误. 客户端不应该在不修改上一次尝试的情况下重试请求. 服务器可能无法为此错误生成有效的 MESSAGE-INTEGRITY, 因此客户端不得期望此响应上有有效的 MESSAGE-INTEGRITY 属性.

401 Unauthorized: 请求未包含继续处理所需的正确凭据. 客户端应使用适当凭据重试请求.

420 Unknown Attribute: 服务器收到的 STUN 包包含其不理解的 comprehension-required 属性. 服务器必须将此未知属性放入其错误响应的 UNKNOWN-ATTRIBUTE 属性中.

438 Stale Nonce: 客户端使用的 NONCE 不再有效. 客户端应使用响应中提供的 NONCE 重试.

500 Server Error: 服务器遇到临时错误. 客户端应再次尝试.

15.7. REALM

REALM 属性可以出现在请求和响应中. 它包含符合 RFC 3261 [RFC3261] 中 "realm-value" 语法的文本, 但不包含双引号及其周围的空白. 也就是说, 它是不带引号的 realm-value (因此是 qdtext 或 quoted-pair 的序列). 它必须是小于 128 字符的 UTF-8 [RFC3629] 编码序列 (最长可达 763 字节), 并且必须已经使用 SASLprep [RFC4013] 处理.

请求中存在 REALM 属性表示正在使用长期凭据进行认证. 在某些错误响应中存在该属性表示服务器希望客户端使用长期凭据进行认证.

15.8. NONCE

NONCE 属性可以出现在请求和响应中. 它包含 qdtext 或 quoted-pair 序列, 它们在 RFC 3261 [RFC3261] 中定义. 注意, 这意味着 NONCE 属性不会包含实际的引号字符. 关于服务器中 nonce 值选择的指导, 见 RFC 2617 [RFC2617] 的 Section 4.3.

它必须小于 128 字符 (最长可达 763 字节).

15.9. UNKNOWN-ATTRIBUTES

UNKNOWN-ATTRIBUTES 属性仅在 ERROR-CODE 属性中的响应码为 420 时出现在错误响应中.

该属性包含一个 16-bit 值列表, 其中每个值表示服务器未理解的属性类型.

    0                   1                   2                   3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Attribute 1 Type | Attribute 2 Type |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Attribute 3 Type | Attribute 4 Type ...
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

Figure 8: UNKNOWN-ATTRIBUTES 属性的格式

Note: 在 [RFC3489] 中, 此字段通过复制最后一个属性来填充到 32 bit. 在本版本规范中, 改为使用属性的常规填充规则.

15.10. SOFTWARE

SOFTWARE 属性包含发送消息的代理所使用软件的文本描述. 客户端和服务器都会使用它. 其值应该包含制造商和版本号. 该属性不影响协议运行, 仅作为诊断和调试用途的工具. SOFTWARE 的值为可变长度. 它必须是小于 128 字符的 UTF-8 [RFC3629] 编码序列 (最长可达 763 字节).

15.11. ALTERNATE-SERVER

备用服务器表示一个备用传输地址, 用于标识 STUN 客户端应尝试的另一台 STUN 服务器.

它以与 MAPPED-ADDRESS 相同的方式编码, 因此通过 IP 地址引用单个服务器. IP 地址族必须与请求源 IP 地址的地址族相同.