P2P学习(三)网络传输基本知识---STUN协议(二)

转载自:http://www.52im.net/thread-557-1-1.html

与前面P2P学习(三)网络传输基本知识---STUN协议(一)无关联,只是另外一篇转载,因为看一篇太无语了....看两篇应该会更好

一:RFC3489/STUN

STUN(Simple Traversal of User Datagram Protocol Through Network Address Translators),即简单的用UDP穿透NAT,是个轻量级的协议,是基于UDP的完整的穿透NAT的解决方案。

允许应用程序发现它们与公共互联网之间存在的NAT和防火墙及其他类型。它也可以让应用程序确定NAT分配给它们的公网IP地址和端口号

STUN是一种Client/Server的协议,也是一种Request/Response的协议,默认端口号是3478。(IETF官方文档RFC3489/STUN点此进入

(一)报文结构

1.消息头:所有的STUN消息都包含20个字节的消息头,包括16位的消息类型,16位的消息长度和128位的事务ID。

消息类型Type许可的值如下:

  • 0x0001:捆绑请求
  • 0x0101:捆绑响应
  • 0x0111:捆绑错误响应
  • 0x0002:共享私密请求
  • 0x0102:共享私密响应
  • 0x0112:共享私密错误响应

消息长度,是消息大小的字节数,但不包括20字节的头部。

事务ID,128位的标识符,用于随机请求和响应,请求与其相应的所有响应具有相同的标识符。

2.消息属性(消息体):消息头之后是0或多个属性,每个属性进行TLV编码,包括16位的属性类型、16位的属性长度和变长属性值。

 属性类型Type定义如下:

  • MAPPED-ADDRESS:MAPPED-ADDRESS属性表示映射过的IP地址和端口。它包括8位的地址族,16位的端口号及长度固定的IP地址。
  • RESPONSE-ADDRESS:RESPONSE-ADDRESS属性表示响应的目的地址
  • CHASNGE-REQUEST:客户使用32位的CHANGE-REQUEST属性来请求服务器使用不同的地址或端口号来发送响应。
  • SOURCE-ADDRESS:SOURCE-ADDRESS属性出现在捆绑响应中,它表示服务器发送响应的源IP地址和端口。
  • CHANGED-ADDRESS:如果捆绑请求的CHANGE-REQUEST属性中的“改变IP”和“改变端口”标志设置了,则CHANGED-ADDRESS属性表示响应发出的IP地址和端口号。
  • USERNAME:USERNAME属性用于消息的完整性检查,用于消息完整性检查中标识共享私密。USERNAME通常出现在共享私密响应中,与PASSWORD一起。当使用消息完整性检查时,可有选择地出现在捆绑请求中。
  • PASSWORD:PASSWORD属性用在共享私密响应中,与USERNAME一起。PASSWORD的值是变长的,用作共享私密,它的长度必须是4字节的倍数,以保证属性与边界对齐。
  • MESSAGE-INTEGRITY:MESSAGE-INTEGRITY属性包含STUN消息的HMAC-SHA1,它可以出现在捆绑请求或捆绑响应中;MESSAGE-INTEGRITY属性必须是任何STUN消息的最后一个属性。它的内容决定了HMAC输入的Key值。
  • ERROR-CODE:ERROR-CODE属性出现在捆绑错误响应或共享私密错误响应中。它的响应号数值范围从100到699。
  • UNKNOWN-ATTRIBUTES:UNKNOWN-ATTRIBUTES属性只存在于其ERROR-CODE属性中的响应号为420的捆绑错误响应或共享私密错误响应中。
  • REFLECTED-FROM:REFLECTED-FROM属性只存在于其对应的捆绑请求包含RESPONSE-ADDRESS属性的捆绑响应中。属性包含请求发出的源IP地址,它的目的是提供跟踪能力,这样STUN就不能被用作DOS攻击的反射器。

具体的ERROR-CODE(响应号),与它们缺省的原因语句一起,目前定义如下:

  • 400(错误请求):请求变形了。客户在修改先前的尝试前不应该重试该请求。
  • 401(未授权):捆绑请求没有包含MESSAGE-INTERITY属性。
  • 420(未知属性):服务器不认识请求中的强制属性。
  • 430(过期资格):捆绑请求没有包含MESSAGE-INTEGRITY属性,但它使用过期
  • 的共享私密。客户应该获得新的共享私密并再次重试。
  • 431(完整性检查失败):捆绑请求包含MESSAGE-INTEGRITY属性,但HMAC验
  • 证失败。这可能是潜在攻击的表现,或者客户端实现错误
  • 432(丢失用户名):捆绑请求包含MESSAGE-INTEGRITY属性,但没有
  • USERNAME属性。完整性检查中两项都必须存在。
  • 433(使用TLS):共享私密请求已经通过TLS(Transport Layer Security,即安全
  • 传输层协议)发送,但没有在TLS上收到。
  • 500(服务器错误):服务器遇到临时错误,客户应该再次尝试。
  • 600(全局失败):服务器拒绝完成请求,客户不应该重试。

Type属性空间分为可选部分与强制部分,值超过0x7fff的属性是可选的,即客户或服务器即使不认识该属性也能够处理该消息值小于或等于0x7fff的属性是强制理解的,即除非理解该属性,否则客户或服务器就不能处理该消息,将返回错误信息。

(二)实现原理

STUN协议的完整交互过程如下图,下面我们来介绍具体实现步骤。

一般情况下,客户会配置STUN服务器提供者的域名,该域名被解析为IP地址和SRV过程的端口号。服务器名是“stun”,使用UDP协议发送捆绑请求,使用TCP协议发送共享私密请求。STUN协议的缺省端口号为3478。

若要提供完整性检查,STUN在客户和服务器间使用128位的共享私密,作为在捆绑请求和捆绑响应中的密匙。

通信步骤:

1.首先,客户通过发现过程获得它将与之建立TCP连接的IP地址和端口号。客户打开该地址和端口的连接,开始TLS协商,验证服务器的标识。

2.客户发送共享私密请求。该请求没有属性,只有头。

3.服务器生成响应。

注意:客户会在该连接上生成多个请求(所以不止前面那个没有属性的请求,后面还可以包含有属性的请求),但在获得用户名和密码后关闭该连接。

4.服务器收到共享私密请求。

3.1验证从TLS连接上到达的该请求;如果不是通过TLS收到的请求,则生成共享私密错误响应,并设置ERROR-CODE属性为响应号433;这里区分两种情况:若通过TCP收到请求,则错误响应通过收到请求的相同连接发送;若通过UDP收到请求,则错误响应发送回请求送出的源IP和端口

3.2服务器检查请求中的任何属性,当其中有不理解的小于或等于0x7fff的值,则生成共享私密错误响应,设置ERROR-CODE属性为响应号420,并包括UNKNOWN-ATTRIBUTE属性,列出它不理解的小于或等于0x7fff的属性的值。该错误响应通过TLS连接发送。若请求正确,服务器创建共享私密响应,包含与请求中相同的事务ID,并包含USERNAME和PASSWORD属性。用户名在10分钟内有效。

5.共享私密响应通过与收到请求的相同的TLS连接发送,服务器保持连接打开状态,由客户关闭它。

6.接着,客户发送捆绑请求,携带的属性包括:

  • 可选属性:RESPONSE-ADDRESS属性和CHANGE-REQUEST属性;
  • 强制属性:MESSAGE-INTEGRITY属性和USERNAME属性。

客户发送捆绑请求,通过客户重传来提供可靠性。客户开始用100ms的间隔重传,每次重传间隔加倍,直至1.6秒。之间间隔1.6秒的重传继续,直到收到响应或总共已经发送了9次。因此,若9500ms后,还未收到响应,客户认为传输已经失败。

7.服务器检查捆绑请求的MESSAGE-INTEGRITY属性,不存在则生成捆绑错误响应,设置ERROR-CODE属性为响应号401;若存在,计算请求的HMACKey值。

服务器检查USERNAME属性,不存在则生成捆绑错误响应,设置ERROR-CODE属性为响应号432;若存在,但不认识该USERNAME的共享私密(例如,它超时了),生成捆绑错误响应,设置ERROR-CODE属性为响应号430

若服务器知道该共享私密,但所计算的HMAC与请求的不同,生成捆绑错误响应,设置ERROR-CODE属性为响应号431

假设消息完整性检查通过了,服务器检查请求中的任何属性的值,若遇到不理解的小于或等于0x7fff的值,生成捆绑错误响应,设置ERROR-CODE属性为响应号420,该响应包含UNKNOWN-ATTRIBUTE属性,并列出不理解的小于或等于0x7fff的属性。

9.若请求正确,服务器生成单个捆绑响应,包含与捆绑请求相同的事务ID。服务器在捆绑响应中加入MAPPED-ADDRESS属性,该属性的IP地址和端口号为捆绑请求的源IP地址和端口号

捆绑响应的源地址和端口号取决于捆绑请求中CHANGE-REQUEST属性的值及捆绑请求收到的地址和端口号相关。总结如下:

服务器在捆绑响应中加入SOURCE-ADDRESS属性,包含用于发送捆绑响应的源地址和端口号;加入CHANGED-ADDRESS属性,包含源IP地址和端口号。

如果捆绑请求中包含了USERNAME和MESSAGE-INTEGRITY属性,则服务器在捆绑响应中加入MESSAGE-INTEGRITY属性

如果捆绑请求包含RESPONSE-ADDRESS属性,则服务器在捆绑响应中加入REFLECTED-FROM属性:如果捆绑请求使用从共享私密请求获得的用户名进行认证,则REFLECTED-FROM属性包含共享私密请求到达的源IP地址和端口号;若请求中的用户名不是使用共享私密分配的,则

REFLECTED-FROM属性包含获得该用户名的实体的源IP地址和端口号;若请求中没有用户名,且服务器愿意处理该请求,则REFLECTED-FROM属性包含请求发出的源IP地址和端口号。

服务器不会重传响应,可靠性通过客户周期性地重发请求来保障,每个请求都会触发服务器进行响应。

客户端判断响应的类型是捆绑错误响应还是捆绑响应。捆绑错误响应通常在请求发送的源地址和端口收到;捆绑响应通常在请求中的RESPONSE-ADDRESS属性的地址和端口收到,若没有该属性,则捆绑响应将在请求发送的源地址和端口号收到。

    • 若是捆绑错误响应,客户检查响应中的ERROR-CODE属性的响应号:400至499之间的未知属性按属性400处理,500至599之间的未知属性按500处理,600至699之间的未知属性按600处理。任何100和399之间的响应都会使请求重传中止,但其他则忽略;若客户收到响应的属性类型大于0x7fff,则忽略该属性,若小于或等于0x7fff,则请求重传停止,并忽略整个响应;
    • 若是捆绑响应,客户检查响应的MESSAGE-INTEGRITY属性:如果不存在,客户在请求中加入MESSAGE-INTEGRITY属性,并放弃该响应;如果存在,客户计算响应的HMAC。如果计算出的HMAC与响应中的不同,则放弃该响应,并警告客户可能受到了攻击;若计算出的HMAC与响应中的匹配,则过程继续;
    • 不论收到捆绑响应还是捆绑错误响应,都将中止该请求的重传。客户在第一次响应后继续监听捆绑请求的响应10秒钟,如果这期间它收到任何消息类型不同的响应或不同的MAPPED-ADDRESS属性,它将警告用户可能受到攻击;并且,如果客户收到的捆绑响应次数超过它发送的捆绑请求数的两倍,它将警告用户可能受到攻击;若捆绑响应经过认证,上述攻击并未导致客户丢弃MAPPED-ADDRESS,则客户可以使用该MAPPED-ADDRESS和SOURCE-ADDRESS属性。

(三)STUN功能举例(重点)

1.客户通过带外方式获得STUN服务器信息后,就打开对应的地址和端口的连接,并开始与STUN服务器进行TLS协商。

2.一旦打开了连接,客户就通过TCP协议发送共享私密请求,服务器生成共享私密响应。STUN在客户和服务器间使用共享私密,用作捆绑请求和捆绑响应中的密匙。

3.之后,客户使用UDP协议向STUN服务器发送捆绑请求,当捆绑请求消息到达服务器的时候,它可能经过了一个或者多个NAT。

4.结果是STUN服务器收到的捆绑请求消息的源IP地址被映射成最靠近STUN服务器的NAT的IP地址,STUN服务器把这个源IP地址和端口号复制到一个捆绑响应消息中,发送回拥有这个IP地址和端口号的客户端---获取了主机的外网IP和端口信息

5.当STUN客户端收到捆绑响应消息之后,它会将自己发送捆绑请求时绑定的本地IP地址和端口号同捆绑响应消息中的IP地址和端口号进行比较,如果不匹配,就表示客户端正处于一个或者多个NAT的前面。----判断了是否是公网IP,还是说是在NAT内网中

6.在Full-Cone NAT的情况下,在捆绑响应消息中的IP地址和端口是属于公网的,公网上的任何主机都可以使用这个IP地址和端口号向这个应用程序发送数据包,应用程序只需要在刚才发送捆绑请求的IP地址和端口上监听即可。

当然,客户可能并不在一个Full-Cone NAT的前面,实际上,它并不知道自己在一个什么类型的NAT的前面。为了确定NAT的类型,客户端使用附加的捆绑请求。具体过程是很灵活的,但一般都会像下面这样工作:

6.1 客户端再发送一个捆绑请求,这次发往另一个IP地址,但是使用的是跟上一次同一个源IP地址和源端口号,如果返回的数据包里面的IP地址和端口号和第一次返回的数据包中的不同,客户端就会知道它是在一个对称NAT的前面---判断了是否是对称NAT
6.2 客户端为了确认自己是否在一个完全锥形NAT的前面,客户端可以发送一个带有标志的捆绑请求,这个标志告诉服务器使用另一个IP地址和端口发送捆绑响应。
换句话说,如果客户端使X/Y的IP地址端口对向A/B的IP地址端口对发送捆绑请求,服务器就会使用源IP地址和源端口号为C/D的地址端口对向X/Y发送捆绑响应。如果客户端收到了这个响应,它就知道它是在一个Full-Cone NAT前面。---判断了是否是全锥NAT
6.3 STUN协议允许客户端请求服务器从收到捆绑请求的IP地址往回发捆绑响应,但是要使用不同的端口号。这可以用来检查客户端是否在Port Restricted Cone NAT的前面还是在Restricted Cone NAT的前面。----判断了是否是IP限制还是端口限制NAT

二:RFC5389/STUN

STUN协议在RFC5389中被重新命名为Session Traversal Utilities for NAT,即NAT会话穿透效用。在这里,NAT会话穿透效用被定位为一个用于其他解决NAT穿透问题协议的协议。它可以用于终端设备检查由NAT分配给终端的IP地址和端口号。

同时,它也被用来检查两个终端之间的连接性,好比是一种维持NAT绑定表项的保活协议。STUN可以用于多种NAT类型,并不需要它们提供特殊的行为。

STUN本身不再是一种完整的NAT穿透解决方案,它相当于是一种NAT穿透解决方案中的工具。这是与RFC3489/STUN版本相比最重要的改变。

(一)STUN用途

目前定义了三种STUN用途:

  • Interactive Connectivity Establishment(ICE)[MMUSIC-ICE],交互式连接建立
  • Client-initiated connections for SIP [SIP-OUTBOUND],用于SIP的客户端初始化连接
  • NAT Behavior Discovery [BEHAVE-NAT],NAT行为发现

(二)报文结构

1.消息头:STUN消息头为20字节,后面紧跟0或多个属性。STUN头部包含一STUN消息类型、magic cookie、事务ID和消息长度。

 

每个STUN消息的最高位前2位必须为0。当STUN协议为多个协议多路复用时若使用的是同一个端口,这可以用于与其他协议区分STUN数据包。

消息类型确定消息的类别(如请求、成功回应、失败回应、标志)。虽然这里有四种消息类型,但可以分为2类事务:请求/响应事务、标志事务。

消息类型字段可进一步划分为下面结构:

 消息类型定义如下:

  • 0b00,表示请求
  • 0b01,表示标志
  • 0b10,表示成功响应
  • 0b11,表示错误响应

魔术字域必须包含固定的值0x2112A442。在RFC3489中,该域是事务ID的一部分。配置魔术字允许服务器检测客户是否理解某些在改进的版本中增加的属性。另外,还可用于STUN多路复用时与其他协议的包进行区分。

96位的事务ID用于唯一的识别STUN事务。

对于请求/响应事务,事务ID由STUN客户端来选择;

对于标志事务,由代理(代理指支持STUN的客户端或服务器)来选择并发送。它主要服务于与请求相关的响应,因此它也扮演着一个帮助阻止确定类型的攻击的角色。

服务器使用事务ID来唯一的标识出所有客户端的每一个事务。事务ID本身必须是唯一的,并且随机的从0到2的96-1次方中选择。重新发送相同的请求时,也必须使用新的事务ID。成功或错误响应必须携带与相对应的请求相同的事务ID。

消息长度字段不包括20字节的STUN头部。所有的STUN属性必须填充为4字节的倍数。消息长度字段的最后2位总是为0,这为区分STUN包与其他协议的包提供了另外一种方法。

2.消息属性(消息体

STUN头之后是0或多个属性。每个属性都采用TLV编码,16位的类型、16位的长度及可变长度的值。每个STUN属性必须是4字节边界对齐。

 

属性空间被划分为2个范围。属性的类型值在0x0000到0x7fff是强制理解属性,这意味着除非STUN代理能够理解这些属性,否则将不能正常处理包含该属性的消息;属性的类型值在0x8000到0xffff范围是可选理解属性,这意味着如果STUN代理不能理解它们的话这些属性可以被忽略。

STUN属性类型集由IANA维护,具体定义详见IETF官方文档 RFC5389

 

posted @ 2021-05-22 10:07  山上有风景  阅读(1103)  评论(0编辑  收藏  举报