今天我们来介绍一些STUN协议。
STUN存在的目的就是进行NAT穿越
STUN 存在的目的就是进行NAT穿越,NAT有四种类型,每种类型如何穿越它的基本原理是什么,都是属于STUN协议中的一部分。
STUN是典型的客户端/服务器模式。客户端发送请求,服务端进行响应
RFC STUN规范
RFC STUN规范中,实际上有两套STUN规范
第一套叫做RFC3489/STUN
Simple Traversal of UDP Through NAT
它就是将STUN定义成简单的通过UDP进行NAT穿越的一套规范,也就是告诉你如何一步一步通过UDP进行穿越,但是这套规范在穿越的过程中还是存在很多问题,尤其是现在的网络路由器对UDP的限制比较多,有的路由器甚至不允许进行UDP传输,所以这就导致了我们通过RFC3489这套规范进行NAT穿越的时候它的失败率会非常高。所以为了 解决这个问题,又定义了另一套标准,RFC5389.
第二套叫做RFC5389/STUN
Session Traversal Utilities for NAT
RFC5389是在RFC3489的基础上又增加了一些功能,但是它对整个STUN的描述就不一样了, 它是把STUN描述成一系列穿越NAT的工具,所以都叫STUN,但是他们的含义完全就不一样了。RFC5389在UDP尝试可能失败的情况下,尝试使用TCP,也就是说RFC5389是包括UDP和TCP的两种协议进行NAT穿越的,这是两套规范最本质的区别。当然在协议的具体内容上,包括协议头还有协议体中的属性都有很多的变化,但是那些都不是最关键的,最关键的是RFC5389里面将TCP纳入进来。你可以通过TCP进行穿越。
这是STUN 的RFC的两套规范。大家一定要清楚,尤其是查文档的 时候一定要清楚查询的是哪一套规范。
下面我们就具体看看这个STUN协议
包括20字节的STUN header
Body中可以有0个或多个Attribute
STUN这个协议它是包括了消息头和消息体,消息头是20字节固定的消息头,Body中可以有0个或者多个Attribute属性,后面我们会 介绍属性的作用。
那么这20字节头是由哪些组成呢?
其中2个字节(16bit)类型
2个字节(16bit)消息长度,不包括消息头
在网络协议中有很多的协议规范,它的这个消息头中都有长度,有的是包括这个头的有的是不包括这个头的,这个大家一定要记清楚 。
对于STUN来说,消息头的长度length是不包括消息头的;
16个字节(128bit)事物 ID,请求与响应事物ID相同
第三个是事物ID,它是16字节或128bit组成,它的作用就是请求和响应事物是相同的ID,用于请求与响应的匹配的;比如我客户端 发送了好几个请求,那服务端对于每一个请求都要返回一个响应 ,那怎么知道某个响应是对应到的请求呢,就通过这个事物ID。
如果它事物ID号相同,说明这两是匹配的,整个逻辑就知道怎么做了,否则的话就很难判断。所以它有三部分组成,第一是两个字节的类型,第二是两个类型的消息长度,不包括这个消息头,第三个是16字节的128位的事物ID,请求与响应的匹配。这个是消息头。
我们再来看消息头的格式
上图的格式是最新的RFC5389的格式,刚刚我们上面说三个的是RFC3489,那么RFC5389和RFC3489之间有什么区别呢?
首先我看消息类型,消息类型的最低两位必须是 0 0,就是RFC5389最新的协议 ,这是第一点的不同;第二点的不同是,事物ID,老的里面是128位事物ID,在新的里面是 96位,其中有32位单独划出来了单独作为 Magic Cookie,一个魔法树,这就是RFC3489和5389的STUN消息头的区别。
下面我们再来看每一项,首先是这个Massage Type
STUN Message Type
前两位必须是00,以区分复用同一端口时STUN协议
就是不同的协议复用同一个端口的时候,用它来区分哪个是STUN协议哪个不是STUN协议,这是两位00的作用。
2位用于分类,即C0和C1
剩下的14位中有两位用于分类,就是C0和C1,C0和C1是占两位,所以它有四种类型,也就是四个分类,第一种是请求,第二种 是指示,第三种是成功,第四种是错误应答,所以它将Message Type消息类型分成了四类。
12位用于定义请求/指示
剩下的12位是用于不同请求的定义,比如1代表绑定,2代表私有消息隐私数据,……,但是在STUN 3489里面定义了两个,就是用 这12位定义了两种,但是5389里面就一种,一种就够了。下图就是STUN Message Type的结构。
14位其中C0和C1分别在这个位置,两个不是放在一起的,它是隔着的,隔着有什么好处,就是按照16进制的话,我们4位为一组,
所有的M代表的是请求的值,C是分类,这样划分,四个一组,第一组 里面全都是请求,都是消息类型,第二组里面代表分类,第三组里面又代表另外一种分类。所以这两个进行不同的组合就能分成不同的类型了。
我们来看 C0C1
C0C1
C0C1第一个0是C0第二个零是C1。因为这个C1是处于这个高字节,对于网络数据对于协议分析对于二进制数据处理的人来说,这些都是比较容易理解的,对于没有接触过这些知识的人可能稍微有点麻烦。
0b00:表式是一个请求
0b01:表示是一个指示
0b10:表示是请求成功的响应
0b11:表示是请求 失败的响应
所以这里我们只要记住成功响应和失败的响应就好了。
下面我们就可以看一下STUN的消息类型,它一共定义了6个消息类型
这个就是STUN的消息类型,但是RCF5389就将这个私密类型去掉了。就剩下一个 ,那完全够用了。
在下面就是大小端模式
对于这部分类型,为什么要了解呢,就是后面要对C0和C1要做详细的解释。
大端模式:数据的高字节保存在内存的低地址中
小端模式:数据的高字节保存在内存的高地址中
网络字节顺序:采用大端排序方式
了解这个模式之后,我们在聊C0和C1它是怎么排序的,大家就会很清楚它是怎么排序的。
这块知识是要仔细琢磨一下的,琢磨明白了之后就非常简单了。如果你没有绕过这个弯过来会一直认为是不是搞错了,但是这也不是重点,大家基本了解一下就可以了。
下面这个字段是事物ID
Transaction ID
4字节,32位,固定值0x2112A442. 通过它可以判断客户端是否可以识别某些属性
新的RFC5389将它分成了两部分,前四个字节也就是32位是一个magic cookie,magic就是一个魔法树了,在这里是固定的0x2112A442,所以我们看到固定的值是这个的话,就是RFC5389否则就是RFC3489。
通过它是可以识别客户端是否可以识别某些属性,因为不同的规范属性实际上是有变化的,所以你看到 这个魔法树之后就是RFC5389, RFC5389定义了一些新的属性,如果不是的话,这些新的属性就可以忽略掉。
12字节 ,96位,标识同一个事物的请求和响应
剩下的12个字节,96位,标识同一个事物的请求和响应,当我们客户端发送多个请求的时候,就能通过这个事物ID将服务端返回的响应与之前发送的请求进行一一匹配。
下面在看STUN Message Body 消息体
消息头后有0或多个属性
每个属性都使用TLV(动态)编码:Type,Length,Value
Value这个值是可以变化的,变化的程度怎么知道有多长呢?通过这个Length,这个Length标示了Value的长度,最终的消息是一个32位对齐的,如果最后不是32位对齐,要通过补0来达到对齐,这是整个Body。
对于我们来说,这些知识已经够用了。