文章目錄
- 1簡介
- 2 報文結構
- 2.1 Message Header
- 2.1.1 頭部2bits
- 2.1.2 Stun Message Type
- 2.1.3 Message Length
- 2.1.4 Magic Cookie
- 2.1.5 Transaction ID (96bits)
- 2.2 Stun Attributes
- 3 分配
- 3.1 Client ---> Server Allocate
- 3.2 Server ---> Client Error
- 3.3 Client ---> Server Allocate
- 3.4 Server ---> Client Success
- 4 刷新
- 5 CreatePermission
- 6 連接Peer
- 6.1 Server ---> Client ConnectionAttempt
- 6.2 Client ---> Server ConnectionBind
- 6.3 Server --->Client Error
- 6.4 Client ---> Server ConnectionBind
- 6.5 Server --->Client Success
- 7 一些記錄
參考文章:
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。