STUN协议与ICE

STUN协议与ICE

1、 什么是STUN协议

https://datatracker.ietf.org/doc/rfc5389/
STUN(Session Traversal Utilities for NAT,NAT会话穿越应用程序)是一种网络协议,它允许位于NAT(或多重NAT)后的客户端找出自己的公网地址,查出自己位于哪种类型的NAT之后以及NAT为某一个本地端口所绑定的Internet端端口。这些信息被用来在两个同时处于NAT路由器之后的主机之间创建UDP通信。该协议由RFC 5389定义。
在WEBRTC中,STUN协议主要用于NAT穿越和认证消息(ICE的流程)

2、 什么是ICE

交互式连接建立是一种标准穿透协议,利用Stun和Turn服务器来帮助端点建立连接,ICE主要完成的就是NAT穿透和用户认证的过程

ICE的具体流程
在这里插入图片描述
在上图过程中,1.3.5步骤均采用STUN协议完成,可以联想到,STUN协议在其中主要有三个功能:
 帮助获取主叫/被叫的外网地址
 帮助完成主叫和被叫之间的权限认证
 保活并处理地址变化情况

由上述三个功能,那么就能引出如何做到这三个功能的问题,那么就要从STUN协议的内容来了解

3、 STUN协议的内容

STUN协议基于TCP或者UDP,大部分是UDP,发送数据报格式为STUN HEADER+STUN BODY

STUN HEADER
在这里插入图片描述
字段解析:
前两位00,代表STUN版本
Message Length 代表整个报文长度
Magic Cookie 标识STUN协议,固定为0x2112A442
Transaction ID 标识固定的事务ID,根据这个可以判断STUN协议的来源
STUN Message Type:
格式如下:
在这里插入图片描述
消息类型许可的值如下:

0x0001:捆绑请求

0x0101:捆绑响应

0x0111:捆绑错误响应

其实主要就是看C0,C1的值

STUN body内容:
Body中可以加上众多的属性(STUN Attributes),结构如下:
在这里插入图片描述
具体Value为:
MAPPED-ADDRESS同时也是classic STUN的一个属性,之所以还存在也是为了前向兼容。其包含了NAT客户端的反射地址,
Family为IP类型,即IPV4(0x01)或IPV6(0x02),Port为端口,Address为32位或128位的IP地址。注意高8位必须全部置零,
而且接收端必须要将其忽略掉。

USERNAME 和PASSWORD,
这个内容里可以添加上交互的用户名和密码,随后对端进行校验

4、 ICE中P2P的实现

回到第2结那个图里,P2P的实现第一步,即和STUN服务器交互,获取到对应的外网IP,这里采用的就是MAPPED-ADRESS,此时SDP信息中就可以添加上主叫的内网IP和外网IP了(通过STUN服务器获取)

对端接收到这两个ip后,也回复自己的外网IP和内网IP,此时就可以进入打洞的阶段,打洞时A端先从SDP中选择所有对端ip发送建立连接,此时,如果对端收到后,也对A发来的所有SDP信息进行连接,那么此时如果A端收到了B的返回,那么就可以从中获取到对应的STUN协议里的ip地址,与之前发送地址比较,确认是哪个ip发送回来的,这个时候就可以建立keepalive连接,打洞就完成了,在这个过程中,STUN报文里可以带上SDP信息交换的username 和password,此时也可以完成用户的鉴权

对于流媒体服务器来讲,大多数本身就架设在外网,因此服务器来说,STUN服务的第一步就可以省略了,可从STUN检查的过程开始,那么其主要的目的也就是要完成ICE中的用户名和密码了确认了

MediaSoup中的STUN代码部分

创建webrtcTransport时会随机生成本机的username和password,随后创建udpSocket负责接受客户端发送过来的消息以及创建ICEserver来应对接受到数据后的stun数据处理工作,
此后libuv接收到数据后会将数据传递至webrtcTransport的OnStunDataReceived接口,然后调用iceServer->ProcessStunPacket,至此就把消息传递到了STUN处理接口里:
webrtcTransport构造函数代码:

	WebRtcTransport::WebRtcTransport(const std::string& id, RTC::Transport::Listener* listener, json& data)
	  : RTC::Transport::Transport(id, listener, data)
	{
		//省略一部分代码
		// This may throw.这里会创建接受服务器
		auto* udpSocket = new RTC::UdpSocket(this, listenIp.ip);
			// Create a ICE server.创建ICEserver负责处理stun
			this->iceServer = new RTC::IceServer(
			  this, Utils::Crypto::GetRandomString(16), Utils::Crypto::GetRandomString(32));

			// Create a DTLS transport.创建DTLS负责处理dtls握手
			this->dtlsTransport = new RTC::DtlsTransport(this);
	}

创建ICEusername和password代码:

	void WebRtcTransport::HandleRequest(Channel::Request* request)
{
		//随机生成password和username
		std::string usernameFragment = Utils::Crypto::GetRandomString(16);
		std::string password         = Utils::Crypto::GetRandomString(32);

		this->iceServer->SetUsernameFragment(usernameFragment);
		this->iceServer->SetPassword(password);
}

mediasoup处理STUN协议只有BINDING REQUEST,主要代码如下:

void IceServer::ProcessStunPacket(RTC::StunPacket* packet, RTC::TransportTuple* tuple)
	{
		switch (packet->GetClass())
		{
			case RTC::StunPacket::Class::REQUEST:
			{
							switch (packet->CheckAuthentication(this->usernameFragment, this->password))
				{
					case RTC::StunPacket::Authentication::OK:
					{
						if (!this->oldPassword.empty())
						{
							MS_DEBUG_TAG(ice, "new ICE credentials applied");

							this->oldUsernameFragment.clear();
							this->oldPassword.clear();
						}

						break;
					}
				// Create a success response.
				RTC::StunPacket* response = packet->CreateSuccessResponse();

				// Add XOR-MAPPED-ADDRESS.
				response->SetXorMappedAddress(tuple->GetRemoteAddress());

				// Authenticate the response.
				if (this->oldPassword.empty())
					response->Authenticate(this->password);
				else
					response->Authenticate(this->oldPassword);

				// Send back.
				response->Serialize(StunSerializeBuffer);
				this->listener->OnIceServerSendStunPacket(this, response, tuple);

				delete response;

				// Handle the tuple.
				HandleTuple(tuple, packet->HasUseCandidate());

				break;					
			}
		}

	}

从上代码中可以看到,服务器在接受到bunding request之后认证了username和password,完成之后发送XOR-MAPADDRESS并带上了客户端的外网地址。可以看出来media’soup采用的时ice-lite,只能部署在公网环境下,并没有实现对STUN协议的完整使用,在STUN上只是用来进行身份校验。

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值