2013年1月9日 星期三

STUN協議簡介


轉載自 http://basiccoder.com/stun-protocal-introduction.html


STUN( Simple Traversal of User Datagram Protocol (UDP) Through Network Address Translators (NATs))是一种轻量级的协议,它允许应用程序发现它们和公网之间是否存在NATs和防火墙,并确定NATs和防火墙的类型。它也可以让应用程序确定NAT分配给它们的公网IP地址和端口号。STUN是一种client-server的协议,也是一种request-response的协议,STUN的默认端口是3478。
NAT的类型:
Full Cone NAT:
所有从同一个内网IP和端口号发送过来的请求都会被映射成同一个外网IP地址和端口号。而且任何一个外网主机都可以通过这个映射的外网IP和端口号向这台内网主机发关怉。
Restricted Cone:
它也是所有从同一个内网IP和端口号发送过来的请求都会被映射成同一个外网IP和端口号。和Full-Cone NAT不同的是,一个拥有IP地址X的外网主机如果想要给内网主机发送数据包,必须是这台内网主机之前给IP地址X发送过数据包才可以。
Port Restricted Cone:
它和Restricted Cone很相似,只不过它包括端口号,也就是,一台IP地址X和端口P的外网主机想给内网主机发送数据包,必须是这台内网主机之前给这个IP地址X和端口P发送过数据包才可以。
Symmetric:
对称NAT就是,所有从同一个内网IP和端口发送到一个特定的目的IP和端口的请求,都会被映射到同一个IP和端口。如果同一台主机用相同的源地址和端口号发送数据包,但是发往不同的目的地,NAT将会使用不同的映射。进一步说,只有当外网主机收到内网主机发送来的数据包之后才能向内网主机往回发送数据包。
STUN Binding Request使用UDP协议发送到STUN服务器,当Binding Request消息到达服务器的时候它可能经过了一个或者多个NAT。结果是STUN服务器收到的request消息的源IP地址被映射成最靠近STUN服务器的NAT的IP地址,STUN服务器把这个源IP地址和端口号复制到一个Bind Response消息中,通过发送回拥有这个IP地址和端口号的客户端,对于上面提到的所有类型的NAT,这个消息都会到达客户端。
当STUN客户端收到STUN Binding Response消息之后,它会将自己发送Request时bind的本地IP地址和端口号同Response消息中的IP地址端口号进行比较,如果不匹配,就表示客户端正处于一个或者多个NAT的前面。在Full-Cone NAT的情况下,在STUN Response消息中的IP地址和端口是属于公网的,公网上的任何主机都可以使用这个IP地址和端口号向这个应用程序发送数据包,应用程序只需要在刚才发送STUN Binding Request的IP地址和端口上监听即可。
当然,主机可能并不在一个full-core NAT的前面,实际上,它并不知道自己在一个什么类型的NAT的前面。为了确定NAT的类型,客户端使用附加的STUN Binding Request.具体过程是很灵活的,但一般都会像下面这样工作。客户端再发送一个STUN Binding Request,这次发往另一个IP地址,但是使用的是跟上一次同一个源IP地址和源端口号,如果返回的数据包里面的IP地址和端口号和第一次返回的数据包中的不同,客户端就会知道它是在一个对称NAT的前面。客户端为了确定自己是否在一个完全锥形NAT的前面,客户端可以发送一个带有标志的STUN Binding Request,这个标志告诉STUN Server另一个IP地址和端口发送Response,这个IP地址和端口要和刚才收到Request的IP地址和端口不同。换句话说,如果客户端使用x:y的IP地址:端口对向A:B的IP地址:端口对发送Binding Request,STUN Server会使用源IP地址和源端口号为C:D的地址对向X:Y发送Response.如果客户端收到了这个Response,它就知道它是在一个Full-Cone NAT前面。
STUN协议允许客户端请求服务器从收到Request的IP地址往回发Binding Response,但是要使用不同的端口号。这可以用来检查客户端是在Port Restricted Cone NAT的前面还是在Restricted Cone NAT的前面。
STUN 消息是使用大端字节流编码的TLV(type-length-value).所有的STUN消息都以一个STUN头开始,紧跟着STUN的载核数据(Payload)。Payload是一系列的STUN属性集合,它们取决于STUN消息的类型。STUN消息的type可以是Binding Request,Binding Response,Binding ERROR Response, Shared Secret Request,Shared Secrect Response 或 Shared Secret Error Response.
Transaction ID的作用是将请求(Request)和响应(Response)联系起来。长度字段代表STUN Payload数据的整个长度。Shared Secret Requests一直都是承载于TCP之上发送的(实际上,是使用了承载于TCP这上的TLS发送的)。
STUN协议也定义了很多的STUN属性。第一个是MAPPED-ADDRESS属性,它是一个IP地址和端口对,Binding Response里面一直都有它,它代表了服务器在Binding Request中看到的源IP地址和源端口号。还有一个RESPONSE-ADDRESS属性,包含一个IP地址和端口。RESPONSE-ADDRESS可以被放到Binding Request中,它告诉服务器Binding Request将会被发送到哪里。它是可选的,当不填写的时候,Binding Request会被卧发送到Binding Request的源IP地址和源端口号。
第三个属性是CHANGE-REQUEST 属性,它包含了两个flag,这两个flag控制用来发送Response的IP地址和端口号。这两个标志被称为“change IP”和“change port”标志,CHANGE-REQUEST标志只允许在Binding Request中出现,在确定客户端是在Restricted Cone NAT之前还是Port Restricted Cone NAT之前的时候,这两个标志是很有用的。它们指示Server从不同的源地址和源端口发送Binding Response。在Binding Request中CHANGE-REQUEST属性是可选的。
第四个属性是CHANGED-ADDRESS属性,它出现在Binding Response中。如果客户端请求使用”change IP”和”change port”行为,它会通知客户端将会使用的源IP地址和源端口号。
第五个属性是SOURCE-ADDRESS属性,它只出现在Binding Response中,它指示发送Response的源IP地址和源端口号,它在检测两个NAT配置的时候是很有用的。
第五个是USERNAME,它只在Shared Secret Response中出现,忽略了…
第六个属性是ERROR-CODE属性,它出现在Binding Error Response和Shared Secret Error Response中,它指出发生的错误。
忽略了三四个属性…..
消息头:
所有的STUN消息都包含20个字节的消息头:
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
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|          STUN Message Type                   |        Message Length                    |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Transaction ID
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Message Type可以取下面这些值:
0×0001 : Binding Request
0×0101 : Binding Response
0×0111 : Binding Error Response
0×0002 : Shared Secret Request
0×0102 : Shared Secret Response
0×0112 : Shared Secret Error Response
Message Length是载核数据的字节长度,不包含消息头的长度。
Transaction ID是一个128位的标识符,可以随机生成。
消息属性
STUN消息头后面跟着0个或多个属性,所有的属性都是TLV形式的,包含16位的类型,16位的长度,和变长的值。
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 ….
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
下面是类型的定义:
0×0001: MAPPED-ADDRESS
0×0002: RESPONSE-ADDRESS
0×0003: CHANGE-REQUEST
0×0004: SOURCE-ADDRESS
0×0005: CHANGED-ADDRESS
0×0006: USERNAME
0×0007: PASSWORD
0×0008: MESSAGE-INTEGRITY
0×0009: ERROR-CODE
0x000a: UNKNOWN-ATTRIBUTES
0x000b: REFLECTED-FROM
MAPPED-ADDRESS
这个属性表示映射的IP地址和端口。
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        |                     Port                           |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                                                 Address                                                  |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Family一般都是0×01,表示IPV4
RESPONSE-ADDRESS
这个属性的数据包形式跟MAPPED-ADDRESS相同。
其它的就不列出来了,具体去看rfc就好了