STUN协议简介

STUN结构

注意:be表示big endian;le表示little endian

         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| STUN Message Type (14bit) |    Message Length (16bit)     |
        +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
        |                         Magic Cookie (32bit)                  |
        +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
        |                                                               |
        |                     Transaction ID (96 bits)                  |
        |                                                               |
        +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

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

1、开头两位永远为0
2、Message Type、Message Length
// 图示为大端字节序排列
                        0                 1
                        2  3  4 5 6 7 8 9 0 1 2 3 4 5
                       +--+--+-+-+-+-+-+-+-+-+-+-+-+-+
                       |M |M |M|M|M|C|M|M|M|C|M|M|M|M|
                       |11|10|9|8|7|1|6|5|4|0|3|2|1|0|
                       +--+--+-+-+-+-+-+-+-+-+-+-+-+-+

STUN Message Type用于定义消息类(request, success response, failure response, or indication)和消息方法
STUN虽然有四个消息类,但是只有两种事务:request/response transactions 和 indication transactions

C0 - C1指定消息类,M0 - M11指定消息方法
0x0001 : Binding Request
0x0101 : Binding Response
0x0111 : Binding Error Response
0x0002 : Shared Secret Request
0x0102 : Shared Secret Response
0x0112 : Shared Secret Error Response

// msg_type是主机字节序,为上面中的一种
#define IS_REQUEST(msg_type)       (((msg_type) & 0x0110) == 0x0000)
#define IS_INDICATION(msg_type)    (((msg_type) & 0x0110) == 0x0010)
#define IS_SUCCESS_RESP(msg_type)  (((msg_type) & 0x0110) == 0x0100)
#define IS_ERR_RESP(msg_type)      (((msg_type) & 0x0110) == 0x0110)

Message Length是后面属性的总字节数,不包括20字节的STUN头部

3、Magic Cookie 和 Transaction ID

在传输中,一般都认为Cookie是Transaction ID的一部分,故Transaction ID被认为是16字节内容但是cookie在[RFC 3489]中是固定数值0x2112A442(be)Transaction ID是由请求一方产生,在回复时需要和请求时Transaction ID一样,同一请求使用相同的Transaction ID,但客户端需为新的事务选择新的Transaction ID

4、STUN Attributes
       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 (16bit)          |            Length (16bit)     |
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
      |                         Value (variable)                      |
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

Attributes紧跟在STUN头部之后,每个Attributes组成方式为Type-Length-Value。
Attributes的length字段表示Value字段的长度,由于要求Attributes的长度需是4字节倍数,
故会对Value部分进行填充,因此length表示的是填充之前的Value的长度,value中填充部分在处理时忽略

下面是Attributes Type定义的原话,无法直接翻译

A STUN Attribute type is a hex number in the range 0x0000 - 0xFFFF.
STUN attribute types in the range 0x0000 - 0x7FFF are considered comprehension-required;
STUN attribute types in the range 0x8000 - 0xFFFF are considered comprehension-optional.

Comprehension-required range (0x0000-0x7FFF):
0x0000: (Reserved)
0x0001: MAPPED-ADDRESS
0x0002: RESPONSE-ADDRESS
0x0003: CHANGE-ADDRESS
0x0004: SOURCE-ADDRESS
0x0005: CHANGED-ADDRESS
0x0006: USERNAME
0x0007: PASSWORD
0x0008: MESSAGE-INTEGRITY
0x0009: ERROR-CODE
0x000A: UNKNOWN-ATTRIBUTES
0x000B: REFLECTED-FROM
0x0014: REALM
0x0015: NONCE
0x0020: XOR-MAPPED-ADDRESS
Comprehension-optional range (0x8000-0xFFFF):
0x8022: SOFTWARE/SERVER
0x8023: ALTERNATE-SERVER
0x8028: FINGERPRINT

4.1、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 (8bit) |       Port (16bit)            |
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
      |                                                               |
      |                 Address (32 bits or 128 bits)                 |
      |                                                               |
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

Family 取值有两种
0x01:IPv4
0x02:IPv6

4.2、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)
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

Family同MAPPED-ADDRESS的Family
X-Port的计算方法是将主机字节序的NAT映射端口与cookie的高16位异或,再将结果转为网络字节序
IPv4情况下
X-Address的计算方法是将主机字节序的NAT端IP与cookie进行异或,再将结果转为网络字节序
IPv6情况下
X-Address的计算方法是将cookie和Transaction ID组成的128位进行异或,并将结果转为网络字节序

举例:以下是一段STUN报文
01 01 00 24 68 e7 4e 45 2c 28 53 47 9e 44 15 86
0f 60 73 60 00 01 00 08 00 01 08 67 6f 1e 20 52
00 04 00 08 00 01 0d 96 0a 00 18 11 00 20 00 08
00 01 60 80 07 f9 6e 17

红色部分是XOR-MAPPED-ADDRESS,蓝色部分是MAPPED-ADDRESS
type: 0x0020; length: 0x0008;
family: 0x01; x-port: 0x6080(24704); x-address: 0x07f96e17(7.249.110.23)
实际NAT映射端口是0x0867(be)(2151) cookie的高16位为0x68e7(be)
0x0867 XOR 0x68e7 = 0x6080

4.3、USERNAME

USERNAME属性用于消息完整性检查。
It identifies the username and password combination used in the MESSAGE-INTEGRITY check.

4.4、MESSAGE-INTEGRITY
4.5、SOURCE-ADDRESS

此属性的结构组成和MAPPED-ADDRESS一样,但是其内容是STUN服务器的IP和端口

4.6、CHANGE-REQUEST
		    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 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 A B 0|
		   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

CHANGE-REQUEST是用于客户端请求STUN服务器发送另一个STUN服务器的IP和端口,以便探测其NAT类型
服务器会回复带有CHANGED-ADDRESS属性的响应
虽然CHANGE-REQUEST是32位的长度但是只使用AB两位,其余为0
A位设置为1表示修改IP
B位设置为1表示修改Port

4.7、CHANGED-ADDRESS

其结构与MAPPED-ADDRESS结构一样,其组成是新的STUN服务器的IP、Family、Port
此属性只能出现在Binding Response中

4.8、ERROR-CODE
		     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                     |Class|     Number    |
		    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
		    |      Reason Phrase (variable)                                ..
		    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

Class表示错误号的百位数字,只可取 1-6
Number是错误号的十位和个位的数字,只可取0-99
下面列出错误号
400 (Bad Request) – 请求格式不正确
401 (Unauthorized) – 绑定请求不包含MESSAGE-INTEGRITY属性
420 (Unknown Attribute) – 服务器无法理解请求中的属性
430 (Stale Credentials) – 绑定请求确实包含MESSAGE-INTEGRITY属性,但它使用了已过期的共享密
431 (Integrity Check Failure) – 绑定请求包含MESSAGE-INTEGRITY属性,但HMAC验证失败
432 (Missing Username) – 绑定请求包含MESSAGE-INTEGRITY属性,但不包含USERNAME属性
433 (Use TLS) – 共享密钥请求必须通过TLS发送,但未通过TLS接收
500 (Server Error) – 服务器出现临时错误
600 (Global Failure) – 服务器拒绝完成请求。客户端不应重试

5、举例
5.1、Binding Request
00 01 00 00 90 a2 69 8a f8 84 b5 4e ac 84 89 43 9f 45 58 63
Message Type: Binding Request (0x0001)
Message Length: 0x0000
Message Transaction ID: 90a2698af884b54eac8489439f455863
5.2、Binding Response
01 01 00 44 90 a2 69 8a f8 84 b5 4e ac 84 89 43
9f 45 58 63 00 01 00 08 00 01 08 5e 6f 1e 20 52
00 04 00 08 00 01 0d 96 d9 0a 44 91 00 05 00 08
00 01 0d 97 d9 74 7a 8d 80 20 00 08 00 01 98 fc
ff bc 49 d8 80 22 00 10 56 6f 76 69 64 61 2e 6f
72 67 20 30 2e 39 36 00
Message Type: Binding Response (0x0101)
Message Length: 0x0044
Message Transaction ID: 90a2698af884b54eac8489439f455863
Attribute: MAPPED-ADDRESS
    Attribute Type: MAPPED-ADDRESS (0x0001)
    Attribute Length: 8
    Protocol Family: IPv4 (0x0001)
    Port: 2142
    IP: 111.30.32.82
Attribute: SOURCE-ADDRESS
    Attribute Type: SOURCE-ADDRESS (0x0004)
    Attribute Length: 8
    Protocol Family: IPv4 (0x0001)
    Port: 3478
    IP: 217.10.68.145
Attribute: CHANGED-ADDRESS
    Attribute Type: CHANGED-ADDRESS (0x0005)
    Attribute Length: 8
    Protocol Family: IPv4 (0x0001)
    Port: 3479
    IP: 217.116.122.141
Attribute: XOR_MAPPED_ADDRESS
    Attribute Type: XOR_MAPPED_ADDRESS (0x8020)
    Attribute Length: 8
    Protocol Family: IPv4 (0x0001)
    Port (XOR-d): 39164
    [Port: 2142]
    IP (XOR-d): 255.188.73.216
    [IP: 111.30.32.82]
Attribute: SERVER
    Attribute Type: SERVER (0x8022)
    Attribute Length: 16
    Server version: Vovida.org 0.96
5.3、Binding Request With CHANGE-REQUEST
00 01 00 08 0a e1 ce 2e aa bc 1f 43 9e 0f 5d 53
67 14 93 c8 00 03 00 04 00 00 00 06
Message Type: Binding Request (0x0001)
Message Length: 0x0008
Message Transaction ID: 0ae1ce2eaabc1f439e0f5d53671493c8
Attributes
    Attribute: CHANGE-REQUEST
        Attribute Type: CHANGE-REQUEST (0x0003)
        Attribute Length: 4
        .... .... .... .1.. = Change IP: Set
        .... .... .... ..1. = Change Port: Set
5.4、Binding Response With CHANGED-ADDRESS
01 01 00 44 90 a2 69 8a f8 84 b5 4e ac 84 89 43
9f 45 58 63 00 01 00 08 00 01 08 5e 6f 1e 20 52
00 04 00 08 00 01 0d 96 d9 0a 44 91 00 05 00 08
00 01 0d 97 d9 74 7a 8d 80 20 00 08 00 01 98 fc
ff bc 49 d8 80 22 00 10 56 6f 76 69 64 61 2e 6f
72 67 20 30 2e 39 36 00
Message Type: Binding Response (0x0101)
Message Length: 0x0044
Message Transaction ID: 90a2698af884b54eac8489439f455863
Attribute: MAPPED-ADDRESS
    Attribute Type: MAPPED-ADDRESS (0x0001)
    Attribute Length: 8
    Protocol Family: IPv4 (0x0001)
    Port: 2142
    IP: 111.30.32.82
Attribute: SOURCE-ADDRESS
    Attribute Type: SOURCE-ADDRESS (0x0004)
    Attribute Length: 8
    Protocol Family: IPv4 (0x0001)
    Port: 3478
    IP: 217.10.68.145
Attribute: CHANGED-ADDRESS
    Attribute Type: CHANGED-ADDRESS (0x0005)
    Attribute Length: 8
    Protocol Family: IPv4 (0x0001)
    Port: 3479
    IP: 217.116.122.141
Attribute: XOR_MAPPED_ADDRESS
    Attribute Type: XOR_MAPPED_ADDRESS (0x8020)
    Attribute Length: 8
    Protocol Family: IPv4 (0x0001)
    Port (XOR-d): 39164
    [Port: 2142]
    IP (XOR-d): 255.188.73.216
    [IP: 111.30.32.82]
Attribute: SERVER
    Attribute Type: SERVER (0x8022)
    Attribute Length: 16
    Server version: Vovida.org 0.96
6、发现过程

Test I:仅发送Binding Request
响应时携带MAPPED-ADDRESS、SOURCE-ADDRESS、CHANGED-ADDRESS、XOR_MAPPED_ADDRESS、SERVER(没啥作用)
Test II:发送带有修改IP和Port标志的CHANGE-REQUEST属性的请求
Test III:发送仅修改Port标志的CHANGE-REQUEST请求

                        +--------+
                        |  Test  |
                        |   I    |
                        +--------+
                             |
                             |
                             V
                            /\              /\
                         N /  \ Y          /  \ Y             +--------+
          UDP     <-------/Resp\--------->/ IP \------------->|  Test  |
          Blocked         \ ?  /          \Same/              |   II   |
                           \  /            \? /               +--------+
                            \/              \/                    |
                                             | N                  |
                                             |                    V
                                             V                    /\
                                         +--------+  Sym.      N /  \
                                         |  Test  |  UDP    <---/Resp\
                                         |   II   |  Firewall   \ ?  /
                                         +--------+              \  /
                                             |                    \/
                                             V                     |Y
                  /\                         /\                    |
   Symmetric  N  /  \       +--------+   N  /  \                   V
      NAT  <--- / IP \<-----|  Test  |<--- /Resp\               Open
                \Same/      |   I    |     \ ?  /               Internet
                 \? /       +--------+      \  /
                  \/                         \/
                  |                           |Y
                  |                           |
                  |                           V
                  |                           Full
                  |                           Cone
                  V              /\
              +--------+        /  \ Y
              |  Test  |------>/Resp\---->Restricted
              |   III  |       \ ?  /
              +--------+        \  /
                                 \/
                                  |N
                                  |       Port
                                  +------>Restricted

1、测试从Test I开始,如果此测试未产生响应,则客户端立刻知道无法进行udp连接
如果接收到响应,则检测MAPPED-ADDRESS属性,验证返回的IP、Port是否和本地的Ip、Port一致
如果相同,则知道ip和port未被nat转变,可能处于公网环境或处于类似Full Cone的nat环境,进行TEST II
如果收到响应,则表明上述猜测正确,否则处于防火墙下
2、如果Test I返回的IP、Port响应和本地的不一致,客户端知道其处于Nat后面,则执行Test II
如果收到响应则表明处于Full Cone Nat
如果未收到响应,则向’Test I’返回的CHANGED-ADDRESS中的Ip和port发送Test I
当收到响应时(一般可以收到)检测MAPPED-ADDRESS属性,是否和第一步Test I返回的一致,不一致则表明是对称NAT symmetric
如果相同,则表明处于restricted NAT或者port restricted NAT后面,如此则需要进行Test III以确定具体是哪一种。
当收到回应表明是restricted NAT,未收到响应表明是port restricted NAT

7、检测绑定生存期

原理:二分查找法
准备两个udp socket,X和Y,Y发送一次Binding Request请求。
用X定时T秒发送带有RESPONSE-ADDRESS属性的bind request
RESPONSE-ADDRESS中是Y的MAPPED-ADDRESS的IP、Port
当Y收到响应时增加时间一秒,如此反复
当Y未收到响应时减少一秒,如此反复
当找到在[T, T + 1]区间内的值,换成每次增加或减少100ms,在反复

虽然这样做确实可以找到,但是对于程序来说耗时太久,故找到一个合适的T即可,但一般来说30s以内的映射不会失效,所以可以以30s做一个参考

8、NAT穿透

假设 Full Cone = 1, Restricted = 2, Port Restricted = 3, symmetric = 4
则有 NAT-A + NAT-B <= 6才能使用stun穿透成功,故当两个nat是Port Restrictedsymmetric其中之一时无法穿透,但是Port RestrictedPort Restricted除外
注意:以下指NAPT型

8.1、处于同一NAT下时

在这里插入图片描述

通过STUN服务可以获取其NAT的公网IP和端口,从服务器中获取到想要连接的对端公网IP和端口后可以比较IP,相同则处于同一NAT下,此时只需内部连接即可

8.2、处于不同NAT下时

在这里插入图片描述

1、Client A,Client B分别登录STUN服务器,获取其对外ip和port
记作A:202.120.23.79:5500 B: 202.120.36.181:6600
假如A想同B建立连接,则只需要通过STUN服务向B发送A的IP和Port,让B主动向A发消息,这样在B的NAT会有一条202.120.36.181:6600 -> 202.120.23.79:5500 的纪录
此时A再向B发消息就可以通过NAT到达B

参考:

RFC 3489
RFC 5389

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值