STUN协议详解

STUN 支持两种类型的事务,分别是 REQUEST/RESPONSE 事务和 INDICATION 事务。STUN 本身并不是一种穿越解决方案,而只是协议,被 NAT 穿越的解决方案(比如 ICE 和 TURN)所使用。

STUN 协议能够帮助处于内网的终端确定 NAT 为其分配的外网 IP 地址和端口。

在 ICE 中,STUN 协议用于连通性检查(BINDING_REQUEST/RESPONSE)和 ICE 的保活(BINDING_INDICATION)。在 TURN 协议中,STUN 协议用于 Allocation 的建立,可以作为中继数据的载体(比如 sendindication 和 dataindication),ICE 和 TURN 是两种不同的 STUN 使用方式。

这里,我们暂时先讨论协议本身:

STUN 报文格式

STUN协议结构由20字节头和若干个属性组成,属性的个数可以为0,且统一是大端字节序的二进制序列。报文分为两部分:

  • 20字节的 STUN Header
  • Body,其中携带0或多个attribute

STUN Header

报文头一共20个字节,其中:

// STUN packet transaction id = 96 bits
#define STUN_TRANSACTION_ID_LEN (UINT16) 12

// STUN Header
typedef struct {
    UINT16 stunMessageType; // 14 bit
    UINT16 messageLength; // 16 bit
    UINT32 magicCookie; // 32 bit
    BYTE transactionId[STUN_TRANSACTION_ID_LEN]; // 96 bit
} StunHeader, *PStunHeader;

Message Type

Message Type是主机字节序,比较重要:

1、BINDING/地址捆绑

+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 0x0001  |   BINDING REQUEST                                   |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 0x0011  |   BINDING INDICATION                                |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 0x0101  |   BINDING RESPONSE SUCCESS                          |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 0x0111  |   BINDING RESPONSE ERROR                            |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

2、SHARED SECRET/共享私密

+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 0x0002  |   SHARED SECRET REQUEST                             |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 0x0102  |   SHARED SECRET RESPONSE                            |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 0x0112  |   SHARED SECRET ERROR RESPONSE                      |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

3、ALLOCATE

+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 0x0003  |   ALLOCATE                                          |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 0x0103  |   ALLOCATE SUCCESS RESPONSE                         |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 0x0113  |   ALLOCATE ERROR RESPONSE                           |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

4、REFRESH

+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 0x0004  |   REFRESH                                           |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 0x0104  |   REFRESH SUCCESS RESPONSE                          |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 0x0114  |   REFRESH ERROR RESPONSE                            |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

5、SEND

+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 0x0006  |   SEND                                              |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 0x0106  |   SEND INDICATION                                   |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

6、DATA

+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 0x0007  |   DATA                                              |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 0x0107  |   DATA INDICATION                                   |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

7、CREATE PERMISSION

+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 0x0008  |   CREATE PERMISSION                                 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 0x0108  |   CREATE PERMISSION SUCCESS RESPONSE                |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 0x0118  |   CREATE PERMISSION ERROR RESPONSE                  |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

8、CHANNEL BIND/通道捆绑

+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 0x0009  |   CHANNEL BIND REQUEST                              |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 0x0109  |   CHANNEL BIND SUCCESS RESPONSE                     |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 0x0119  |   CHANNEL BIND ERROR RESPONSE                       |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

Message Length

这里的长度,指的是所有 STUN Attributes 的长度:

下面这段,就是 STUN Attributes:

Magic Cookie & Transaction ID

在传输中一般都认为 Cookie 是 Transaction ID 的一部分,故 Transaction ID 被认为是16字节内容。但是 Cookie 在 [RFC 3489] 或者 [RFC 5389] 中是固定数值0x2112A442

Transaction ID 是由请求一方产生,在回复时需要和请求时 Transaction ID 一样,同一请求使用相同的Transaction ID,但客户端需为新的事务选择新的Transaction ID。

STUN Attributes

属性是继在Header之后,以一个个排列的结构(STUN Attributes)组成:

其中,STUN Attributes 由 Attribute Header 和 Attribute Body 组成。其中 Attribute Header 包括了 Type 和 Length,长度固定为 4 字节。Attribute Body 长度不固定,由 Length 决定。

下面,解释一些比较重要的属性:

/**
 * STUN attribute types
 */
typedef enum {
    STUN_ATTRIBUTE_TYPE_MAPPED_ADDRESS = (UINT16)0x0001,
    STUN_ATTRIBUTE_TYPE_RESPONSE_ADDRESS = (UINT16)0x0002,
    STUN_ATTRIBUTE_TYPE_CHANGE_REQUEST = (UINT16)0x0003,
    STUN_ATTRIBUTE_TYPE_SOURCE_ADDRESS = (UINT16)0x0004,
    STUN_ATTRIBUTE_TYPE_CHANGED_ADDRESS = (UINT16)0x0005,
    STUN_ATTRIBUTE_TYPE_USERNAME = (UINT16)0x0006,
    STUN_ATTRIBUTE_TYPE_PASSWORD = (UINT16)0x0007,
    STUN_ATTRIBUTE_TYPE_MESSAGE_INTEGRITY = (UINT16)0x0008,
    STUN_ATTRIBUTE_TYPE_ERROR_CODE = (UINT16)0x0009,
    STUN_ATTRIBUTE_TYPE_UNKNOWN_ATTRIBUTES = (UINT16)0x000A,
    STUN_ATTRIBUTE_TYPE_REFLECTED_FROM = (UINT16)0x000B,
    STUN_ATTRIBUTE_TYPE_XOR_MAPPED_ADDRESS = (UINT16)0x0020,
    STUN_ATTRIBUTE_TYPE_PRIORITY = (UINT16)0x0024,
    STUN_ATTRIBUTE_TYPE_USE_CANDIDATE = (UINT16)0x0025,
    STUN_ATTRIBUTE_TYPE_FINGERPRINT = (UINT16)0x8028,
    STUN_ATTRIBUTE_TYPE_ICE_CONTROLLED = (UINT16)0x8029,
    STUN_ATTRIBUTE_TYPE_ICE_CONTROLLING = (UINT16)0x802A,
    STUN_ATTRIBUTE_TYPE_CHANNEL_NUMBER = (UINT16)0x000C,
    STUN_ATTRIBUTE_TYPE_LIFETIME = (UINT16)0x000D,
    STUN_ATTRIBUTE_TYPE_XOR_PEER_ADDRESS = (UINT16)0x0012,
    STUN_ATTRIBUTE_TYPE_DATA = (UINT16)0x0013,
    STUN_ATTRIBUTE_TYPE_REALM = (UINT16)0x0014,
    STUN_ATTRIBUTE_TYPE_NONCE = (UINT16)0x0015,
    STUN_ATTRIBUTE_TYPE_XOR_RELAYED_ADDRESS = (UINT16)0x0016,
    STUN_ATTRIBUTE_TYPE_EVEN_PORT = (UINT16)0x0018,
    STUN_ATTRIBUTE_TYPE_REQUESTED_TRANSPORT = (UINT16)0x0019,
    STUN_ATTRIBUTE_TYPE_DONT_FRAGMENT = (UINT16)0x001A,
    STUN_ATTRIBUTE_TYPE_RESERVATION_TOKEN = (UINT16)0x0022,

} STUN_ATTRIBUTE_TYPE;

MAPPED-ADDRESS/映射地址

表示 “NAT客户端的反射地址”。其中,Family 标识协议族,包括 IPv4 和 IPv6。其中,IPv4 为 0x01,IPv6 为 0x02。需要注意的是,Port 和 Address 都是以网络序存在,本地查看时需要转换到主机序。

XOR-MAPPED-ADDRESS/亦或映射

与 MAPPED-ADDRESS 属性基本相同,区别在于反射地址经过一次异或(XOR)处理,异或运算是其自身的逆运算,客户端经过一次异或运算获得真实的反射地址。之所以这么做,目的就是解决ALG篡改地址和端口的问题。

USERNAME/用户名

用户名,用于消息完整性,识别在消息完整性检查中使用的用户名和密码组合。

MESSAGE-INTEGRITY/消息认证

STUN 消息的 HMAC-SHA1 值,长度 20 字节,用于消息完整性认证。

FINGERPRINT/指纹认证

FINGERPRINT 属性可以在所有的 STUN 消息中出现。该属性的值被计算为 STUN 消息的 CRC-32 值,与 32 位值 0x5354554e 进行异或操作(这种异或操作有助于处理应用包也在其中使用 CRC-32 的情况)。

32 位 CRC 是 ITU V.42 [ITU.V42.2002] 中定义的,具有生成多项式 x32+x26+x23+x22+x16+x12+x11+x10+x8+x7+x5+x4+x2+x+1。当存在时,FINGERPRINT 属性必须是消息中的最后一个属性,因此会出现在 MESSAGE-INTEGRITY 之后。

FINGERPRINT 属性可以帮助区分 STUN 数据包与其他协议的数据包。与 MESSAGE-INTEGRITY 一样,FINGERPRINT 属性中使用的 CRC 也覆盖了 STUN 消息头的长度字段。因此,在计算 CRC 之前,此值必须正确,并将 CRC 属性作为消息长度的一部分包括在内。

在消息中使用 FINGERPRINT 属性时,首先将属性放入消息中并赋予虚拟值,然后计算 CRC,最后更新属性的值。如果 MESSAGE-INTEGRITY 属性也存在,则在计算 CRC 之前必须出现具有正确 message-integrity 值的 MESSAGE-INTEGRITY 属性,因为 CRC 也会对 MESSAGE-INTEGRITY 属性的值进行操作。

PRIORITY 和 USE-CANDIDATE

终端必须在其 request 中包含 PRIORITY 属性,指明其优先级,优先级由公式计算而得。如果有需要也可以给出特别指定的候选(即 USE-CANDIDATE 属性)

UNKNOWN-ATTRIBUTES/未知属性

此属性只会在错误代码为 420 的错误响应中出现

ICE-CONTROLLING 和 ICE-CONTROLLED

ICE流程中定义了两种角色:Controlling 和 Controlled。

不同的角色在 Candidate Pair 优先级的计算,Pair Nominate 决策上有所不同。一般流程下,会话的双发各自的角色选择是与会话协商的流程相关的,Offer端是 Controlling,Answer端是 Controlled。

ICE-CONTROLLED 或者是 ICE-CONTROLLING,这两个属性都会携带一个 Tie breaker 这样的字段,其中包含一个本机产生的随机值。收到该 Binding Request 的一方会检查这两个字段,如果和当前本机的 Role 冲突,则检查本机的 Tie breaker 值和消息中携带的 Tie breaker 值进行判定本机合适的 Role。

判定的方法为 Tie breaker 值大的一方为 Controlling。如果判定本端变更角色,这直接修改;如果判定对端变更角色,则对此 Binding Request 发送487错误响应,收到此错误响应的一方变更角色即可。

ERROR-CODE/错误码

属性用于 “error response” 报文中,其中包含了300-699表示的错误码,以及一个UTF-8格式的文字出错信息(Reason phrase)。

SOFTWARE

此属性用于代理发送消息时包含版本的描述,用于客户端和服务器。它的值包括制造商和版本号。该属性对于协议的运行没有任何影响,仅为诊断和调试目的提供服务。SOFTWARE属性是个可变长度的,采用UTF-8编码的小于128个字符的序列号。

ALTERNATE-SERVER

属性表示 STUN 客户可以尝试的不同的 STUN 服务器地址,属性格式与 MAPPED-ADDRESS 相同。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

毕加索解锁

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值