【网络通信 -- 直播】网络通信协议简介 -- STUN

【网络通信 -- 直播】网络通信协议简介 -- STUN

【0】STUN 简介

在RFC3489中定义为一个完整的NAT穿透解决方案,英文全称是Simple Traversal of UDP Through NATs,即简单的用UDP穿透NAT;
在RFC5389中把STUN协议定位于为穿透NAT提供工具,而不是一个完整的解决方案,英文全称是Session Traversal Utilities for NAT,即NAT会话穿透效用;
RFC5389与RFC3489相比最大的区别是支持TCP穿透;

【1】STUN/RFC3489

STUN(Simple Traversal of User Datagram Protocol Through Network Address Translators),即简单的用UDP穿透NAT,是轻量级的协议,是基于UDP的完整的穿透NAT的解决方案;允许应用程序发现它们与公共互联网之间存在的NAT和防火墙及其类型,也可以让应用程序确定NAT分配给它们的公网IP地址和端口号;STUN是一种Client/Server的协议,也是一种Request/Response的协议,默认端口号是3478;

【1.1】STUN RFC3489 格式

【1.1.1】消息头

消息类型,0x0001,捆绑请求;0x0101,捆绑响应;0x0111,捆绑错误响应;0x0002,共享私密请求;0x0102,共享私密响应;0x0112,共享私密错误响应;
消息长度,是消息大小的字节数,但不包括20字节的头部;
事务 ID,128位的标识符,用于随机请求和响应,请求与其相应的所有响应具有相同的标识符;

【1.1.2】消息属性

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

属性类型定义

  • MAPPED-ADDRESS:MAPPED-ADDRESS属性表示映射过的IP地址和端口,包括8位的地址族,16位的端口号及长度固定的IP地址;

  • RESPONSE-ADDRESS:RESPONSE-ADDRESS属性表示响应的目的地址;
  • CHASNGE-REQUEST:客户使用32位的CHANGE-REQUEST属性来请求服务器使用不同的地址或端口号来发送响应;

只使用了两位(A、B),A 表示改变 IP 标志,为 true 表示请求服务器将捆绑响应发送到不同的 IP 地址,而不是接收到捆绑请求的地址;B 表示改变端口标志,为 true 表示请求服务器将捆绑请求发送到不同的端口,而不是接收到捆绑请求的端口;

  • 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;

分类(Class)表示响应号的百位数,其值在 1~6 之间;数字(Number)表示响应号的 100 的模数,其值在 0~99 之间;

  • UNKNOWN-ATTRIBUTES:UNKNOWN-ATTRIBUTES属性只存在于其ERROR-CODE属性中的响应号为420的捆绑错误响应或共享私密错误响应中;

包含16位值的表,每个表示服务器不认识的属性类型,若未知属性数量是奇数,则表中的最后一个属性必须重复,以确保表的总长度必须是4字节的倍数;

  • 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(全局失败):服务器拒绝完成请求,客户不应该重试;

下表列举了某个消息可能出现的属性,其中,M表示强制在消息中包含该属性,O表示可选择的在消息中包含该属性,C表示根据消息的其他方面决定是否包含属性,N/A表示该消息类型不使用该属性;

【1.2】交互流程

一般情况下,客户会配置STUN服务器提供者的域名,该域名被解析为IP地址和SRV(SRV记录是DNS服务器的数据库中支持的一种资源记录的类型,记录了哪台计算机提供了哪个服务的信息)过程的端口号;

  • 服务器名是“stun”,使用UDP协议发送捆绑请求,使用TCP协议发送共享私密请求;STUN协议的缺省端口号为3478;
  • 若要提供完整性检查,STUN在客户和服务器间使用128位的共享私密,作为在捆绑请求和捆绑响应中的密匙;

1,客户通过发现过程获得其将与之建立TCP连接的IP地址和端口号;客户打开该地址和端口的连接,开始TLS协商,验证服务器的标识,客户发送共享私密请求,该请求没有属性,只有头;服务器生成响应;

  • 客户会在该连接上生成多个请求,但在获得用户名和密码后关闭该连接;

2,服务器收到共享私密请求

2.1,服务器验证从TLS连接上到达的该请求;

  • 若不是通过TLS收到的请求,则生成共享私密错误响应并设置ERROR-CODE属性为响应号433;
    • 若通过TCP收到请求,则错误响应通过收到请求的相同连接发送;
    • 若通过UDP收到请求,则错误响应发送回请求送出的源IP和端口;

2.2,服务器检查请求中的任何属性;

  • 若请求中有不理解的小于或等于0x7fff的值,则生成共享私密错误响应,设置ERROR-CODE属性为响应号420,并包括UNKNOWN-ATTRIBUTE属性,列出其不理解的小于或等于0x7fff的属性的值,该错误响应通过TLS连接发送;
  • 若请求正确,服务器创建共享私密响应,包含与请求中相同的事务ID,并包含USERNAME和PASSWORD属性,用户名在10分钟内有效;共享私密响应通过与收到请求的相同的TLS连接发送,服务器保持连接打开状态,连接由客户关闭;

3,客户发送捆绑请求

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

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

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

4.1,检查捆绑请求的MESSAGE-INTEGRITY属性;

  • 若不存在则生成捆绑错误响应,设置ERROR-CODE属性为响应号401;
  • 若存在则计算请求的HMACKey值;

4.2,检查USERNAME属性

  • 若不存在则生成捆绑错误响应,设置ERROR-CODE属性为响应号432;
  • 若存在,但不认识该USERNAME的共享私密(例如,它超时了),生成捆绑错误响应,设置ERROR-CODE属性为响应号430;
  • 若服务器获取了该共享私密,但所计算的HMAC与请求的不同,生成捆绑错误响应,设置ERROR-CODE属性为响应号431;

4.3,假设消息完整性检查通过了,服务器检查请求中的任何属性的值

  • 若遇到不理解的小于或等于0x7fff的值,生成捆绑错误响应,设置ERROR-CODE属性为响应号420,该响应包含UNKNOWN-ATTRIBUTE属性,并列出不理解的小于或等于0x7fff的属性;
  • 若请求正确,服务器生成单个捆绑响应,包含与捆绑请求相同的事务ID;
    • 服务器在捆绑响应中加入MAPPED-ADDRESS属性,该属性的IP地址和端口号为捆绑请求的源IP地址和端口号;
    • 服务器在捆绑响应中加入SOURCE-ADDRESS属性,包含用于发送捆绑响应的源地址和端口号;
    • 加入CHANGED-ADDRESS属性,包含源IP地址和端口号;
    • 若捆绑请求中包含了USERNAME和MESSAGE-INTEGRITY属性,则服务器在捆绑响应中加入MESSAGE-INTEGRITY属性;
    • 若捆绑请求包含RESPONSE-ADDRESS属性,则服务器在捆绑响应中加入REFLECTED-FROM属性;
      • 若捆绑请求使用从共享私密请求获得的用户名进行认证,则REFLECTED-FROM属性包含共享私密请求到达的源IP地址和端口号;
      • 若请求中的用户名不是使用共享私密分配的,则REFLECTED-FROM属性包含获得该用户名的实体的源IP地址和端口号;
      • 若请求中没有用户名,且服务器愿意处理该请求,则REFLECTED-FROM属性包含请求发出的源IP地址和端口号;

5,客户端判断响应的类型

  • 区分响应类型是捆绑错误响应还是捆绑响应
    • 捆绑错误响应通常在请求发送的源地址和端口收到;
    • 捆绑响应通常在请求中的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属性;

【2】STUN/RFC5389

STUN协议在RFC5389中被重新命名为Session Traversal Utilities for NAT,即NAT会话穿透效用,此时,STUN本身不再是一种完整的NAT穿透解决方案,相当于是一种NAT穿透解决方案中的工具;
此时,NAT会话穿透效用被定位为一个用于其他解决NAT穿透问题协议的协议,可以用于终端设备检查由NAT分配给终端的IP地址和端口号;也可以用来检查两个终端之间的连接性,类似一种维持NAT绑定表项的保活协议;STUN可以用于多种NAT类型,并不需要NAT提供特殊的行为;

【2.1】STUN 的用途

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

【2.2】STUN RFC5389 格式

【2.2.1】消息头

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

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

消息类型字段确定消息的类别(如请求、成功回应、失败回应、标志),可以分为2类事务,请求/响应事务、标志事务;

消息类型域中的比特位从最重要(M11)到最不重要(M0)来显示,M11 到 M0 标示一个方法的 12 位编码;C1 和 C0 标示类型的编码;消息类型定义,0b00,表示请求;0b01,表示标志;0b10,表示成功响应;0b11,表示错误响应;

消息长度字段不包括20字节的STUN头部;所有的STUN属性必须填充为4字节的倍数;消息长度字段的最后2位总是0,这可以成为区分STUN包与其他协议的包一个标准;

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

事务ID字段用于唯一识别STUN事务;对于请求/响应事务,事务ID由STUN客户端来选择;对于标志事务,由代理(代理指支持STUN的客户端或服务器)来选择并发送;事务ID主要服务于与请求相关的响应,可以帮助阻止确定类型的攻击;服务器使用事务ID来唯一的标识出所有客户端的每一个事务;事务ID本身必须是唯一的,并且随机的从0到2的96-1次方中选择;重新发送相同的请求时,必须使用新的事务ID;成功或错误响应必须携带与相对应的请求相同的事务ID;

【2.2.2】消息属性

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

属性空间被划分为2个范围,属性的类型值在0x0000到0x7fff是强制理解属性,表明STUN代理必须能够理解这些属性,否则将不能正常处理包含该属性的消息;属性的类型值在0x8000到0xffff范围是可选理解属性,表明STUN代理可以忽略无法理解的属性;

  • MAPPED-ADDRESS:MAPPED-ADDRESS属性表示映射过的IP地址和端口,包括8位的地址族,16位的端口号及长度固定的IP地址;

  • XOR-MAPPED-ADDRESS:XOR-MAPPED-ADDRESS属性表示映射过的IP地址和端口,映射的传输地址是被 XOR 函数模糊化的;

  • USERNAME:USERNAME属性用于消息完整性验证;
  • MESSAGE-INTEGRITY:MESSAGE-INTEGRITY属性包含STUN消息的HMAC-SHA1,可以出现在任何 STUN 消息中;除了出现在 MESSAGE-INTEGRITY 属性之后的 FINGERPRINT 属性,代理必须忽略其他跟在 MESSAGE-INTEGRITY 后面的属性,其内容决定了HMAC输入的Key值;
  • FINGERPRINT:FINGERPRINT属性用于辅助分离STUN数据包与其他协议的数据包,可能出现在任何STUN消息的最后,并且位于MESSAGE-INTEGRITY属性之后;
  • ERROR-CODE:ERROR-CODE属性出现在错误响应消息中;其响应号数值范围从300到699;

  • Reserved 比特位必须为0,用于32位边界对齐,分类(Class)表示响应号的百位数,其值在 3~6 之间;数字(Number)表示响应号的 100 的模数,其值在 0~99 之间;
    • 300(重定向),该请求只有当请求拥有一个USERNAME属性以及有效的MESSAGE-INTEGRITY属性时才会被发送;
    • 400(错误请求):请求变形了,客户在修改先前的尝试前不应该重试该请求;
    • 401(未授权):捆绑请求没有包含MESSAGE-INTERITY属性;
    • 420(未知属性):服务器不认识请求中的强制属性;
    • 438(无效 Nonce):客户端使用的 Nonce 不再有效;
    • 500(服务器错误):服务器遇到临时错误,客户应该再次尝试;
  • REALM:REALM属性包含realm-value语法描述的字符,可能用于请求与响应,包含于请求表明长期证书被用于认证,包含于一些错误响应表明服务器希望客户端使用长期证书进行认证;
  • NONCE:NONCE属性包含一系列在RFC3261中定义的引号对,可能出现在响应或请求中;
  • UNKNOWN-ATTRIBUTES:UNKNOWN-ATTRIBUTES属性只存在于其ERROR-CODE属性中的响应号为420的错误响应中;

包含16位值的表,每个表示服务器不认识的属性类型,若未知属性数量是奇数则采用一般的填充规则填充属性;

  • SOFTWARE:SOFTWARE属性包含发送消息的代理所使用的软件的文字描述
  • ALTERNATE-SERVER:ALTERNATE-SERVER属性包含指定STUN客户端可尝试的另一个STUN服务器的地址信息;

【2.2.3】Binding 请求中新增的属性

  • PRIORITY 和 USE-CANDIDATE,终端必须在其 request 中包含 PRIORITY 属性,指明其优先级,优先级由公式计算获得,如果有需要也可以给出特别指定的候选(即 USE-CANDIDATE 属性);
    • 开始 Binding 时,可能没有 USE-CANDIDATE 字段,当这个通道可以使用时,即 ICE 提名使用时,STUN 消息添加该字段,表示使用该通道开始建联 DTLS 连接,这时候服务端开始和客户端握手建立安全加密的 UDP 连接;
  • ICE-CONTROLLED 和 ICE-CONTROLLING,ICE 流程中定义了两种角色,controlling 和 controlled;不同的角色在 candidate pair 优先级的计算,pair nominate 的决策上有所不同;
    • 一般流程下,会话的双方各自的角色选择是与会话协商的流程相关的,offerer 是 controlling,answerer 是 controlled;ICE-CONTROLLED 或者 ICE-CONTROLLING 都会携带一个 Tie breaker 字段,其中包含一个本机产生的随机值;收到该 bind request 的一方会检查这两个字段,如果和当前本机的 role 冲突,则检查本机的 tie breaker 值和消息中携带的 tie breaker 值进而判定本机合适的 role,判定的方法为 Tie breaker 值大的一方为 controlling,如果判定本端变更角色则直接修改;如果判定对端变更角色,则对此 bind request 发送 487 错误响应,收到此错误响应的一方变更角色即可;

【2.3】交互流程

1,构造一个请求消息或一个指示消息

  • 当构造一个请求或指示消息时,消息类别必须是“Request”或者“Indication”,方法必须是绑定或者其他文档定义的方法;
  • 代理添加由方法或者用法指定的任何属性;例如,某些用法可能指定代理使用一个鉴权方法或者 FINGERPRINT 属性;
  • 若代理发送一个请求,它可以在请求中添加一个 SOFTWARE 属性;代理可能根据方法,在指示消息中包含一个 SOFTWARE 属性;STUN 的扩展应该协商 SOFTWARE 在新的指示中是否有效;
  • 对于没有鉴权的绑定方法,除非用法指明否则不需要任何属性;
  • 在确定的情况下,所有通过 UDP 发送的 STUN 消息应该小于路径的 MTU;若路径的 MTU 不可知,则消息应该小于 576 字节(使用 Ipv4) 以及 Ipv4 首跳的 MTU 以及 1280 字节(使用 Ipv6);
    • 该值与 IP 包的总大小有关;
    • STUN 无法处理请求小于 MTU 但是响应大于 MTU 的情况,注意该限制不是 STUN 的问题;
    • 对于 STUN 本身被用于探测 MTU 的特征的情况 [BEHAVE-NAT],MTU 的限制是可选的并不是必须的;除此之外或者类似的应用,MTU 限制必须被遵从;

2,发送请求消息或者指示消息
2.1,通过 UDP 发送

  • 当在 UDP 之上运行 STUN,STUN 数据包可能会丢失;可靠的 STUN 请求/响应事务通过客户事务本身重传请求消息实现;STUN 指示不会被重传,因此,通过 UDP 的指示事务是不可靠的;
  • 客户端应该间隔一个 RTO 重传一个请求消息,每次重传,重传间隔时间翻倍;
    • RTO 是 round-trip(RTT)的一个估计值,其计算方法见(RFC2988),此处有两点不同;
      • 1. RTO 的初始值应该是可配置的并且应该大于 500ms,在固定线路访问链路中推荐值为 500ms;
        • 例外的“应该”情况包含
          • 当用其他机制获取拥塞门限(如 ICE 为固定流率定义的那些机制)
          • STUN 用于已知网路容量的非 Internet 环境;
      • 2. RTO 估计值的精度至少为 1 ms;当应用于 STUN,RTT 估计值不应该根据导致重传请求的 STUN 事务来计算;
  • 客户端结束事务后应该缓存 RTO 值,并且作为下个事务重传到相同 server 的初始值(基于相同 IP 地址),该值应该在 10 分钟后被认为失效并被丢弃;
  • 重传会持续到一个响应被接收,或者直到 Rc 次请求被发送,Rc 应该是可配置的并且应该有一个默认值 7;
  • 在最后的请求之后,若持续 Rm 倍的 RTO 时长而没有收到响应(对于一个成功的请求,此时已经提供了足够的时间来等待一个响应),客户端应该认为事务已经失败了;Rm 应该是可配置的并且有一个默认值 16;
  • 若产生严重的 ICMP 错误,一个 STUN 事务会被认为失败;

因此,假设 RTO 的值是 500ms,请求会在 0ms,500ms,1500ms,3500ms,7500ms,15500ms 和 31500ms 被发送,若 client 在 39500ms 后没有收到响应,客户端会认为事务超时;

2.2,通过 TCP 或者 TLS-over-TCP 发送

  • 对于 TCP 或者 TLS-over-TCP 客户端开启一个到 server 的连接;
  • 对于 STUN 的某些用法,STUN 只能通过 TCP 协议发送;
    • 在这种情况下,STUN 能够在没有任何分帧和复用操作的辅助下被发送; 
  • 对于 STUN 的另外一些用法/扩展,STUN 可能和其他数据复用 TCP 连接;
    • 在这种情况下,STUN 必须运行在允许代理提取完整的 STUN 消息和完整的应用层消息的,由用法/扩展指定的,某些分帧协议的顶层;
  • STUN 服务运行在众所周知的端口或者单独为 STUN 描述的 DNS 过程所发现的端口,并且不能和其他数据复用;因此,没有被用于连接这些 server 的分帧协议;
    • 当使用附加的分帧协议,用法会说明客户端如何获取该分帧协议的使用方法以及连接的端口;
    • 例如,在 ICE 连接检测的情况下,这些信息通过服务器和客户端的带外协商学习得到;
  • 当 STUN 被自己运行在 TLS-over-TCP 时,必须至少使用 TLS_RSA_WITH_AES_128_CBC_SHA 密码套件,同时可以支持其他密码套件;
    • 当接收到 TLS 的认证消息后,客户端应该验证证书并且检测证书认证的站点;
    • 如果认证是无效的或者被取消或者没有标识合适的组,客户端不允许发送 STUN 消息或继续处理 STUN 事务;
    • Client 必须验证 server 的身份;身份验证过程详见 RFC2818;
  • 当 STUN 和其他协议复用并运行在基于 TLS-over-TCP 的连接时,密码套件和 TLS 处理过程将由其他协议定义;
  • TCP 或者 TLS-over-TCP 处理的 STUN 的传输可靠性将由 TCP 协议自身保证,在 STUN 协议层没有重传机制;
    • 对于请求/响应事务,若 client 在发送 SYN 建立连接后经过 Ti 秒没有接收到响应,则认为事务已经超时;Ti 应该是可配置的并且应该有一个默认值 39.5s;在使用 RTO 默认初始值时,该值被用于统一 TCP 和 UDP 的超时值;
    • 若 client 不能建立 TCP 连接,或者在响应被接受前连接被重置或者失败,任何正在进行的请求/响应事务都被认为失败;
  • Client 可能通过一个 TCP(或者 TLS-over-TCP)连接发送多个事务,并且可能在接受先前请求响应之前发送其他请求;Client 应该保持连接打开直到如下情况发生;
    • 1. 没有新的 STUN 请求或者指示通过该连接发送
    • 2. 没有计划使用通过该连接发送的 STUN 请求学习到的任何资源(如一个映射地址(MAPPED-ADDRESS or XOR-MAPPED-ADDRESS) 或者一个中传地址[BEHAVE-TURN])
    • 3. 在该端口复用的其他应用层协议被其他应用程序处理完毕
    • 4. 使用与一个远端学习到的端口,并已经按照某些 TCP NAT 穿越技术的要求与该远端建立通讯连接
  • 在 server 端,server 应该保持连接打开,让 client 关闭该连接,除非 server 确定该连接已经超时(例如,由于 client 从网络断开);
    • 只有当连接仍然打开时,Client 学习到的绑定,在穿越 NAT 的时候才会仍然有效;
    • 只有 client 知道需要绑定多长的时间;
    • 如果接收到请求的响应没有发送出去,Server 不应该关闭连接;
    • 不允许 server 为了发送响应而向 client 另开一个连接;
    • 在过载的情况下,Server 应该采用有关连接管理的最佳的做法;

3,接受一个 STUN 消息

  • 当一个 STUN 代理接收到一个 STUN 消息
    • 1. 检查消息头前两位是否是 0
    • 2. 检查 magic cookie 域是否正确
    • 3. 检查消息长度是否是切合实际
    • 4. 检查方法域的值是否是支持的方法;
  • 如果消息类别是“success response”或“Error Response”,代理将检查事务 ID 匹配的事务是否仍在处理过程中;
  • 若 FINGERPRINT 扩展被使用,代理检查 FINGERPRINT 属性是否存在并且其值是否正确;
  • 若任何错误被检测到,消息将毫无声息地被丢弃;
  • 在 STUN 和其他协议复用的时候,一个错误可能表示消息不是 STUN 消息,在这种情况下,代理应该试图以另外一个协议解析消息;

STUN 代理进行用法指定的鉴权机制的任何需要的检查,一旦鉴权检查被处理,

  • 未知属性、熟知但不期望的属性,未知但可选理解的属性必须被代理忽略;
  • 熟知的但不期望的属性应该被代理忽略;
  • 未知但要求理解的属性将产生依赖消息类别的处理过程;

3.1,处理一个请求

  • 若请求包含一个或者多个未知但要求理解的属性,server 回复一个包含 420 的错误响应码(未知属性)的回复,并且包含一个 UNKNOWN-ATTRIBUTES 属性列举未知但要求理解的属性列表;
  • 当运行在 UDP 上的时候, server 接收到的请求可能是一个事务或一个重传事务的第一个请求;Server 必须响应重传从而保留满足如下条件的属性,即若 client 接收到重传的响应并且该响应不是发送给原始请求的,则 client 和 server 的全部状态等同于只有原始重传响应被收到的情况或者所有响应都收到的情况(在这种情况下, client 使用先收到的);
    • Server 最早为满足此要求而使用的处理方法,在过去的 40s 内记住所有通过 UDP 收到的事务 ID 和他们相关的响应;
      • 该方法要求 server 保存状态,并且不适用于不要求鉴权的请求;
    • 另一种方法是重处理请求并重新计算响应;
    • 最新的技术必须仅应用于幂等的请求(当一个请求的相同请求能够被安全重复而不影响系统的全局状态的时候,该请求被认为是幂等的)并且对于相同的请求产生相同的响应;
      • 绑定方法被认为是等幂的,注意相当少的网络事件能产生反射通讯地址值的改变,STUN 的扩展必须讨论 server 上的不存储事物状态的请求重传的实现;

3.2,处理一个指示

  • 若指示包含未知但要求理解的属性则指示被丢弃并且被停止处理;
  • 代理进行用法和方法要求的额外的任何检查,若所有的检查成功,代理将处理该指示消息,对指示消息不产生任何响应;
  • 对于绑定方法,除非用法指定否则不进行任何额外的检查或处理,代理接受消息仅仅用于刷新在 intervening NAT(NAT 中介) 中的 “binding”;
  • 指示消息不会通过 UDP 重传,因此发送代理没有必要处理指示重传;

3.3,处理一个成功响应

  • 若成功响应包含未知但要求理解的属性则响应被丢弃并且事务被认为失败;
  • Client 进行方法或用法指定的任何额外的检查,若所有检查通过,client 将处理成功响应;
  • 对于绑定方法,
    • client 检查响应中的 XOR-MAPPED-ADRESS 属性;
    • client 检查地址族,若是一个不支持的地址族,属性将被忽略;若是一个支持但不期望的地址族(例如,绑定事务通过 Ipv4 发送,而地址族指定的却是 Ipv6),client 可能接受并使用该值;

3.4,处理一个错误响应

  • 若错误响应包含未知但要求理解的属性,或者若错误响应不包含一个 ERROR-CODE 属性,事务被认为失败;
  • Client 进行鉴权机制指定的任何处理,这将导致尝试产生一个新事务;
    • 在这种情况下的处理过程依赖于错误码,方法和用法;默认的规则如下
      • 1. 若错误码是 300 到 399,client 应该认为事务失败除非 ALTERNATE-SERVER 扩展被使用
      • 2. 若错误码是 400 到 499,client 声明事务失败,在 420 的情况下(未知属性),响应应该包含一个给出附加信息的 UNKONW-ATTRIBUTES 属性;
      • 3. 若错误码是 500 到 599,client 可能重发请求;client 在此情况下必须限制重发的次数
      • 任何其他的错误代码使 client 认为事务失败

【2.4】STUN RFC5389 新特性

【2.4.1】指纹机制
FINGERPRINT机制是一种可选的,用于其他协议多路复用STUN,发送给相同的传输地址时,区分STUN数据包的机制;在一些用途中,基于相同的传输地址时,多个协议会多路复用STUN消息,例如RTP协议;STUN消息必须首先从应用层数据包分离;目前,在STUN报头中有3种固定的字段可以用于该目的(1. STUN消息头的前两位;2. 魔术字字段;3. 消息长度字段的最后两位);尽管如此,在一些案例中,三种固定字段仍然不能充分的区别STUN消息与其他协议数据;当扩展的指纹机制被使用时,STUN代理在发送给其他STUN代理的消息中包括FINGERPRINT属性;当其他STUN代理收到消息时,除基本的检查之外,还将检查是否包含FINGERPRINT属性及它是否包含正确的值,至此,STUN代理将相信这是一个STUN消息;指纹机制帮助STUN代理检查其他协议看起来类似STUN消息的消息;

指纹算法

FINGERPRINT上面的所有部分进行CRC-32运算,然后XOR 0x5354554E

【2.4.2】通过DNS发现服务器机制
STUN客户端可以使用DNS来发现STUN服务器的IP地址和端口,客户端必须知道服务器的域名;当客户端希望找出服务器在公网上的位置就采用捆绑请求/响应事务,SRV(资源记录表)中服务器名称是“stun”;当通过TLS会话采用捆绑请求/响应事务,SRV中服务器名称为“stuns”;STUN用户可以定义额外的DNS资源记录服务名称;STUN请求的默认端口是3478,用于TCP和UDP;STUN在TLS上的默认端口是5349;服务器能够在TLS上运行STUN并与STUN在TCP上时使用相同的端口,只要服务器软件支持判定初始消息是TLS或STUN消息;如果SRV中没有记录可查,客户端执行A或AAAA记录查找域名,结果将会是1张IP地址表,每一个都可以独立于STUN使用TCP或UDP采用默认端口号连接;对于使用TLS的用法,客户端使用STUN在TLS上的默认端口号连接其中一个IP地址;

【2.4.3】认证和消息完整性机制
短期证书机制
短期证书机制假设在STUN事务之前,客户端和服务器已经使用了其他协议以username和password形式交换了证书;这个证书是有时间限制的;例如,在ICE用途中,两个终端使用带外方式交换信息来对username和password达成一致,并在媒体会话期间使用;该证书被用于在每个请求和多个响应中进行消息完整性检查;与长期证书机制相比,没有挑战和响应方式,这种证书的时间限制特性的优点是可以阻止重播;

长期证书机制
长期证书机制依赖于一个username和password形式的长期证书,该证书在客户端和服务器中是共用的;该证书从它提供给用户开始将一直是有效的,直到该用户不再是该系统的用户,这本质上是一个提供给用户username和password的传统的登入方式;客户端初始发送一个请求,没有提供任何证书和任何完整性检测;服务器拒绝这个请求,并提供给用户一个范围(用于指导用户或代理选择username和password)和一个nonce;该nonce提供重放保护,是一个由服务器选择的cookie,以这样一种方式来标示有效时间或客户端身份是有效的;客户端重试这个请求,此次包括其username、realm以及服务器提供的nonce;服务器确认该nonce并检查该消息完整性;若它们匹配,则请求通过认证;若该nonce过期,则服务器拒绝该请求并提供一个新的nonce;在随后到同一服务器的请求,客户端重新使用这个nonce、username和realm,以及先前使用的password;这样,随后的请求不会被拒绝直到这个nonce变成无效;需要注意的是由于Indications不能被改变,因此长期证书机制不能用来保护Indications,从而使用Indications时要么使用短期证书,要么就省略认证和消息完整性;长期证书机制对离线字典攻击敏感,部署的时候应该使用很难猜测的密码;

【2.4.4】备份服务器机制
服务器使用增强的重定向功能将一个客户端转向另一个服务器,通过回应一个错误响应号为300(尝试备份)的错误响应;服务器在错误响应中携带一个ALTERNATE-SERVER属性;客户端收到错误响应号为300的错误响应后,在该响应中查找ALTERNATE-SERVER属性;若找到一个,客户端就会将当前的事务作废,并重新尝试发送请求到该属性中列出的服务器;若请求报文已经通过认证,则必须使用与先前发送给执行重定向操作的服务器同样的证书;若客户端在最后5分钟里重试发送请求时已经重定向到了一个服务器,客户端必须忽略重定向操作并将当前的事务作废,这是为了防止无限的重定向循环;

【2.5】STUN RFC5389与RFC3489的区别

  • 协议的名称变为NAT会话穿透效用,去掉STUN是一种完整的NAT穿透方案的概念,现在定位为用于提供NAT穿透解决方案的工具;
  • 定义了STUN的用途;
  • 去掉了STUN关于NAT类型检测和绑定生命期发现的用法,去掉了RESPONSE-ADDRESS、CHANGED-ADDRESS、CHANGE-REQUEST、SOURCE-ADDRESS和REFLECTED-FROM属性;
  • 增加了一个固定的32位的魔术字字段,事务ID字段减少了32位长度;
  • 增加了XOR-MAPPED-ADDRESS属性,若魔术字在捆绑请求中出现时,该属性包括在捆绑响应中;否则,RFC3489中的行为是保留的(即捆绑响应中包括MAPPED-ADDRESS);
  • 介绍了消息类型字段的正式结构,带有一对明确的位来标识Request、Response、Error-Response或Indication消息;消息类型字段被划分为类别和方法两部分;
  • 明确的指出了STUN的最高2位是0b00,当用于ICE时可以简单的与RTP包区分;
  • 增加指纹属性来提供一种明确的方法来检测当STUN协议多路复用时,STUN与其他协议之间的差异;
  • 增加支持IPv6,IPv4客户端可以获取一个IPv6映射地址,反之亦然;
  • 增加一个long-term-credential-based认证机制;
  • 增加了SOFTWARE、REALM、NONCE和ALTERNATE-SERVER属性;
  • 去掉了共享密匙方法,去除PASSWORD属性;
  • 去掉了使用连续10秒侦听STUN响应来识别一个攻击的做法;
  • 改变事务计时器来增加TCP友好性;
  • 去掉了STUN示例中如集中分离控制和媒体面,在使用STUN协议时提供了更多的信息;
  • 定义了一类填充机制来改变长度属性的说明;
  • REALM、SERVER、原因语句和NONCE限制在127个字符,USERNAME限制在513个字节以内;
  • 为TCP和TLS改变了DNS SRV规程,UDP仍然和以前保持一致;

【2.6】RFC5389与RFC3489的兼容

  • RFC3489中UDP是唯一支持的传输协议
  • RFC5389中的魔术字字段是RFC3489中事务ID的一部分,事务ID长128位
  • RFC3489中没有XOR-MAPPED-ADDRESS属性,绑定方法是使用MAPPED-ADDRESS属性代替
  • RFC3489中有3个需要强制理解的属性,分别是 RESPONSE-ADDRESS、CHANGE-REQUEST、CHANGED-ADDRESS属性,而RFC5389中不再支持这些属性;

客户端处理的改变
客户端想要与RFC3489的服务器互操作,应发送一个使用绑定方法的请求消息,不包含任何消息,使用UDP协议发送给服务器;若成功,将收到服务器发回的包含MAPPED-ADDRESS属性而不是XOR-MAPPED-ADDRESS属性的成功响应;客户端试图与基于RFC3489的应用服务器互操作必须准备好接收任意一个属性;此外,客户端必须忽略任何在响应中出现的保留的强制理解的属性;RFC3489中规定保留属性中的0x0002、0x0004、0x0005和0x000B可能出现在绑定响应中;
服务器处理的改变
服务器能够察觉由RFC3489中的客户端发送的携带有不正确的魔术字的捆绑请求消息;当服务器察觉到RFC3489中的客户端,它应该将捆绑请消息中魔术字域中的值拷贝到捆绑响应中的魔术字字段中,并且插入一个MAPPED-ADDRESS属性代替XOR-MAPPED-ADDRESS属性;客户端在极少的环境下可能包括RESPONSE-ADDRESS或CHANGE-REQUEST属性中的一个;在这些情况下,服务器把这些属性看做是一个不认识的强制理解的属性,并回应一个错误响应;RFC3489版本中的STUN缺少魔术字和指纹属性这两种能够高可靠性的正确标识其他协议多路复用时的STUN消息;因此,STUN执行与RFC3489兼容时不应该被用于多个协议;

资料链接

STUN RFC3489 中/英文版,链接:https://pan.baidu.com/s/1UMS3Uxw93DcOHf_G2Dtrug 提取码:9u6k 

STUN RFC5389 中/英文版,链接:https://pan.baidu.com/s/1cgSgssJyultDUipSNG8VAA 提取码:28il,中文版不太好,翻译的太差了;

参考致谢
本博客为博主的学习实践总结,并参考了众多博主的博文,在此表示感谢,博主若有不足之处,请批评指正。

【1】STUN_RFC5389

【2】P2P技术详解(四):P2P技术之STUN、TURN、ICE详解

【3】WebRTC网络基础 九、第四节 STUN协议

【4】WebRTC笔记2-STUN算法

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值