Turn Client 和 Server交互簡單流程


參考文章:
1.RFC3489
https://www.rfc-editor.org/info/rfc3489
2.RFC5389
https://www.rfc-editor.org/info/rfc5389
3.RFC5766
https://www.rfc-editor.org/rfc/rfc5766.html
4.RFC6062
https://www.rfc-editor.org/rfc/rfc6062.html
5.P2P通信標準協議(一)之Stun
https://www.cnblogs.com/pannengzhi/p/5041546.html
6.P2P通信標準協議(二)之Turn
https://www.cnblogs.com/pannengzhi/p/5048965.html

1簡介

前面的參考文章詳細介紹了Turn的定義、結構以及流程。

Turn Client和Turn Server簡單流程描述如下:Turn Client的內網IP:PORT(A1),通過路由的公網IP:PORT(A2), 通過Turn命令向Turn Server IP:PORT(B1) 創建ALLOCATION。Turn serve隨後會給Turn Client分配一箇中繼地址IP:PORT(B2)。若Peer要向這個Turn Client發送數據,只要直接向這個Turn Client的中繼地址B2發送數據即可,然後Turn Server會把發給B2的數據轉發給A2。

Turn Client和Turn Server通信以及通過中繼轉發數據時候可以使用UDP/TCP/TLS over TCP。但是,Turn Server和Peer通信是基於UDP的,所以若Turn Client和Turn Server通過TCP進行交互,會在Turn Server端進行協議轉換,RFC5766中畫圖如下:
在這裏插入圖片描述

2 報文結構

在開始分析Client 和 Server 交互流程之前,需要先對Turn的報文結構做個簡單梳理,Turn是Stun的一個擴展,所以Turn的報文結構遵循Stun的報文結構規範。

2.1 Message Header

所有的Turn 報文都有20個字節的頭部信息,並且採用大端模式。
在RFC3489中,頭部結構如下:
在這裏插入圖片描述
在RFC5389中,爲了兼容RFC3489,將頭部結構定義如下:
在這裏插入圖片描述

2.1.1 頭部2bits

在上面頭部的兩個字節必須爲0。

2.1.2 Stun Message Type

關於Message Type,在RFC8389中描述如下:

消息類型定義了Stun消息的消息類別(請求,成功響應,失敗響應或指示)和消息方法(主要功能)。 儘管有四種消息類別,但Stun中只有兩種事務類型:請求/響應事務(由請求消息和響應消息組成)和指示事務(由單個指示消息組成)。 響應類別分爲錯誤響應和成功響應,以幫助快速處理Stun消息。

這個字段又可以分爲如下結構:
在這裏插入圖片描述
可以看到上面有M0 ~ M11 C0 ~ C1這兩種類型。
M0 ~ M11代表 message type,而C0 ~ C11代表message class。
message class有如下定義:

0b00:   Requeset
0b01:   Indication
0b10:   Success response
0b11:   Error response

message type 有如下定義:

0x003 :  Allocate          (only request/response semantics defined)
0x004 :  Refresh           (only request/response semantics defined)
0x006 :  Send              (only indication semantics defined)
0x007 :  Data              (only indication semantics defined)
0x008 :  CreatePermission  (only request/response semantics defined
0x009 :  ChannelBind       (only request/response semantics defined)

2.1.3 Message Length

message length 表示信息的長度,不包含20字節的頭部。另外,Stun的屬性都爲4字節對齊,因此最後兩位始終爲0。

2.1.4 Magic Cookie

固定爲0x2112A442。

2.1.5 Transaction ID (96bits)

是一個96個字節的標識符,用於區分Stun傳輸事務。詳細介紹見RFC5389。

2.2 Stun Attributes

在頭部之後,會有0個或多個Stun屬性。每一個Stun屬性必須是TLV結構。
T:Type 16bit L:Length 16bit V:Value 32bit對齊, 結構圖如下:
在這裏插入圖片描述

2.2.1 Type

屬性的類型,0x0000-0x7FFF之間的屬性被指定爲強制理解,意思是Stun終端必須要理解此屬性,否則將返回錯誤信息;而0x8000-0xFFFF之間的屬性爲選擇性理解,即如果Stun終端不識別此屬性則將其忽略。
Type部分取值如下:

0x0000: (Reserved)
0x0001: MAPPED-ADDRESS
0x0002: (Reserved; was RESPONSE-ADDRESS)
0x0003: (Reserved; was CHANGE-ADDRESS)
0x0004: (Reserved; was SOURCE-ADDRESS)
0x0005: (Reserved; was CHANGED-ADDRESS)
0x0006: USERNAME
0x0007: (Reserved; was PASSWORD)
0x0008: MESSAGE-INTEGRITY
0x0009: ERROR-CODE
0x000A: UNKNOWN-ATTRIBUTES
0x000B: (Reserved; was REFLECTED-FROM)
0x0014: REALM
0x0015: NONCE
0x0020: XOR-MAPPED-ADDRESS
0x8022: SOFTWARE
0x8023: ALTERNATE-Server
0x8028: FINGERPRINT

2.2.2 Length

value的長度,因爲是4字節對齊,所以若屬性的長度不是4字節,會被補數據,而長度也會加上補數據的長度,所以最終長度一定是4字節的倍數。

2.2.3 Value

下面幾個是比較常見的(不做詳細介紹,詳細介紹見RFC5389):

2.2.3.1 MAPPED-ADDRESS

結構如下:
在這裏插入圖片描述
Family表示使用的是IPv4(0x01)或者IPv6(0x02)
Port表示端口號
Address表示IP地址

2.2.3.2 XOR-MAPPED-ADDRESS

結構如下:
在這裏插入圖片描述
和上面的MAPPED-ADDRESS類似,只不過將Port和Address進行了異或。

2.2.3.3 USERNAME

用於message integrity,它標識在message integrity檢查中使用的用戶名和密碼組合。

2.2.3.4 MESSAGE-INTEGRITY

message integrity可以出現在任何Stun消息類型中。它是由 username:realm:password進行MD5加密得到的數據。

2.2.3.5 REALM

可以出現在請求和響應中,在請求中存在REALM屬性表示正在使用長期憑證進行身份驗證。

2.2.3.6 NONCE

可以出現在請求和響應中,是由服務器創建的。

2.2.3.7 ERROR-CODE

用於error response報文中。其包含了300-699表示的錯誤代碼,以及一個UTF-8格式的文字出錯信息,部分錯誤類型如下:

300:  嘗試替代
400:  錯誤請求
401:  未授權
420:  未知屬性
438:  過期Nonce
500:  服務器錯誤

下面對Turn Server和Turn Client整個交互過程做個簡單分析。

3 分配

Client會向Server發送allocate請求,但是沒有攜帶驗證信息,Server會返回error信息。然後,Client會解析error信息中的有效信息,添加進驗證信息中,再次給Server發送allocate請求,Server解析到驗證信息並進行驗證,會返回成功信息。所以, Client會向Server發送兩次allocate請求。
流程如下:
在這裏插入圖片描述
抓取的包如下:
在這裏插入圖片描述

3.1 Client —> Server Allocate

報文如下:
在這裏插入圖片描述
Client使用TCP和Server通信,從上面可以看到,Client第一次向Server發送allocate請求,沒有攜帶任何的Stun屬性,只是攜帶了頭部信息,並且type值爲0x03。

3.2 Server —> Client Error

報文如下:
在這裏插入圖片描述
Server返回401錯誤,並返回了幾個Turn屬性,其中我們需要使用NONCE再次向Server發送allocate請求。

同時,需要注意服務器返回的頭部信息中的Transaction ID和Client發送出去的一致。

3.3 Client —> Server Allocate

報文如下:
在這裏插入圖片描述
Client再次向Server發送allocate請求,並攜帶一些Stun屬性信息。NONCE爲Server上次傳遞過來的。

3.4 Server —> Client Success

報文如下:
在這裏插入圖片描述
在Server返回的報文信息中,XOR-RELAYED-ADDRESS表示的Client的路由公網IP:PORT(A2),XOR-MAPPED-ADDRESS表示的是Server爲Client分配的中繼地址IP:PORT(B2)。

至此,Client已經申請到了中繼地址。

4 刷新

在上面的3.3 和 3.4中,我們可以看到有個字段:LIFETIME。
這個屬性標識在沒有刷新情況下Server將維持分配的持續時間,單位爲秒。若在這個時間之內,Client沒有向Server發送refresh請求,那麼Client被分配的IP:PORT將會被回收,因此我們需要定時在LIFETIME之內向Server發送refresh請求。
流程如下:
在這裏插入圖片描述
報文如下:
在這裏插入圖片描述

4.1 Client —> Server Refresh

報文如下:
在這裏插入圖片描述
在上面的報文中,Client向Server發出了refersh請求,刷新時間是600s。

4.2 Server —> Client Success

報文如下:
在這裏插入圖片描述
Server返回成功,並且給出LIFETIME爲300s。

5 CreatePermission

在RFC5766中,Send Mechanism章節介紹到,當Client發送數據給Server時候,如果Client的Send indications沒有被驗證, 會被Server當做攻擊,這些數據會被丟棄。因此在向Server發送數據前,先向Server發送CreatePermission 請求,請求中包含了要發送的Peer的IP地址。

如下,如果Client沒有向Server發送Peer B的CreatePermission 請求,數據就會被丟棄。
在這裏插入圖片描述
報文如下:
在這裏插入圖片描述

5.1 Client —> Server CreatePermission

報文如下:
在這裏插入圖片描述
上面報文的XOR-Peer-ADDRESS便是Client要發送Peer的IP地址。

5.2 Server —> Client Success

報文如下:
在這裏插入圖片描述
Server返回成功。

6 連接Peer

當Client向Server 發送allocate請求成功後,並且定時進行refresh。此時,Client就會等待Server發送Peer的信息,然後Client會向Server發送ConnectionBind請求,和Peer建立連接,然後就可以正常和Peer進行通信。

注意:以下的流程,只是根據測試現象得出的,可能有些服務器因爲功能不同或者參考的RFC不同導致交互和下面的不一致,下面的僅供參考

在RFC6062中,這樣描述:

Server成功處理了分配請求後,當一個Peer和Client的中繼地址建立連接時,Client都會接收Server發送的ConnectionAttempt indication。這個indication包含了CONNECTION-ID和XOR-Peer-ADDRESS屬性。如果Client希望接收這個連接,就會重新創建一個新的socket,和Server建立新的連接,並使用新的socket向Server發送ConnectionBind請求,這個請求包含了Server發送ConnectionAttempt 時候的CONNECTION-ID。

後續Client和Peer通信都會使用新的socket,而原有的socket則會繼續等待新的Peer連接,等到Server的ConnectionAttempt indication。

報文如下:
在這裏插入圖片描述

6.1 Server —> Client ConnectionAttempt

報文如下:
在這裏插入圖片描述
Server發送過來的請求攜帶了CONNECTION-ID和XOR-Peer-ADDRESS這兩個重要的屬性。

6.2 Client —> Server ConnectionBind

報文如下:
在這裏插入圖片描述
上面的報文中,SRC PORT是8507,和以前的端口不一致,說明Client已經使用新的socket向Server發送ConnectionBind請求,並且攜帶了一些必要的屬性。

6.3 Server —>Client Error

在這裏插入圖片描述
由於是新的連接,使用的NONCE爲原有的NONCE,服務器可能會返回401,並且攜帶新的NONCE。

6.4 Client —> Server ConnectionBind

報文如下:
在這裏插入圖片描述
Client使用新的NONCE重新向Server發送ConnectionBind請求。

6.5 Server —>Client Success

報文如下:
在這裏插入圖片描述
服務器返回success請求。

至此,Client已經和Peer建立了連接,後續就可以使用這個新的socket進行數據收發。

7 一些記錄

7.1 多路複用時候,如何判斷是Turn包?

可以通過以下進行判斷
Turn頭部最高兩位必需是0;
Turn頭部的Magic Cookie 爲固定值;
Turn頭部的Message Length以字節爲單位,因爲所有的Stun是4字節對齊的,所以這個字段的最後兩位恆等於0。

發佈了116 篇原創文章 · 獲贊 54 · 訪問量 28萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章