原文地址http://www.52im.net/thread-557-1-1.html
|
《P2P技術(shù)詳解》系列文章
? 本文是《P2P理論詳解》系列文章中的第2篇,總目錄如下:
- 《P2P技術(shù)詳解(一):NAT詳解——詳細(xì)原理、P2P簡(jiǎn)介》
- 《P2P技術(shù)詳解(二):P2P中的NAT穿越(打洞)方案詳解》
- 《P2P技術(shù)詳解(三):P2P技術(shù)之STUN、TURN、ICE詳解》(本文)
? P2P相關(guān)的其它資源:
- 《通俗易懂:快速理解P2P技術(shù)中的NAT穿透原理》
- 《最新收集NAT穿越(p2p打洞)免費(fèi)STUN服務(wù)器列表 [附件下載]》
- 《一款用于P2P開發(fā)的NAT類型檢測(cè)工具 [附件下載]》
另外,如果你覺(jué)得本文對(duì)網(wǎng)絡(luò)通信的基礎(chǔ)知識(shí)講的不夠系統(tǒng)話,可繼續(xù)看看下面這些精華文章大餐。
? 網(wǎng)絡(luò)編程基礎(chǔ)知識(shí):
- 《TCP/IP詳解 - 第11章·UDP:用戶數(shù)據(jù)報(bào)協(xié)議》
- 《TCP/IP詳解 - 第17章·TCP:傳輸控制協(xié)議》
- 《TCP/IP詳解 - 第18章·TCP連接的建立與終止》
- 《TCP/IP詳解 - 第21章·TCP的超時(shí)與重傳》
- 《通俗易懂-深入理解TCP協(xié)議(上):理論基礎(chǔ)》
- 《通俗易懂-深入理解TCP協(xié)議(下):RTT、滑動(dòng)窗口、擁塞處理》
- 《理論經(jīng)典:TCP協(xié)議的3次握手與4次揮手過(guò)程詳解》
- 《理論聯(lián)系實(shí)際:Wireshark抓包分析TCP 3次握手、4次揮手過(guò)程》
- 《計(jì)算機(jī)網(wǎng)絡(luò)通訊協(xié)議關(guān)系圖(中文珍藏版)》
? 如果覺(jué)得上面的文章枯燥,則《網(wǎng)絡(luò)編程懶人入門》系列可能是你的菜:
- 《網(wǎng)絡(luò)編程懶人入門(一):快速理解網(wǎng)絡(luò)通信協(xié)議(上篇)》
- 《網(wǎng)絡(luò)編程懶人入門(二):快速理解網(wǎng)絡(luò)通信協(xié)議(下篇)》
- 《網(wǎng)絡(luò)編程懶人入門(三):快速理解TCP協(xié)議一篇就夠》
- 《網(wǎng)絡(luò)編程懶人入門(四):快速理解TCP和UDP的差異》
- 《網(wǎng)絡(luò)編程懶人入門(五):快速理解為什么說(shuō)UDP有時(shí)比TCP更有優(yōu)勢(shì)》
? 如果感到自已已經(jīng)很牛逼了,《不為人知的網(wǎng)絡(luò)編程》應(yīng)該是你菜:
- 《不為人知的網(wǎng)絡(luò)編程(一):淺析TCP協(xié)議中的疑難雜癥(上篇)》
- 《不為人知的網(wǎng)絡(luò)編程(二):淺析TCP協(xié)議中的疑難雜癥(下篇)》
- 《不為人知的網(wǎng)絡(luò)編程(三):關(guān)閉TCP連接時(shí)為什么會(huì)TIME_WAIT、CLOSE_WAIT》
- 《不為人知的網(wǎng)絡(luò)編程(四):深入研究分析TCP的異常關(guān)閉》
- 《不為人知的網(wǎng)絡(luò)編程(五):UDP的連接性和負(fù)載均衡》
- 《不為人知的網(wǎng)絡(luò)編程(六):深入地理解UDP協(xié)議并用好它》
- 《不為人知的網(wǎng)絡(luò)編程(七):如何讓不可靠的UDP變的可靠?》
? 如果看完上面的文章還是躁動(dòng)不安,那看看《高性能網(wǎng)絡(luò)編程系列》吧:
- 《高性能網(wǎng)絡(luò)編程(一):?jiǎn)闻_(tái)服務(wù)器并發(fā)TCP連接數(shù)到底可以有多少》
- 《高性能網(wǎng)絡(luò)編程(二):上一個(gè)10年,著名的C10K并發(fā)連接問(wèn)題》
- 《高性能網(wǎng)絡(luò)編程(三):下一個(gè)10年,是時(shí)候考慮C10M并發(fā)問(wèn)題了》
- 《高性能網(wǎng)絡(luò)編程(四):從C10K到C10M高性能網(wǎng)絡(luò)應(yīng)用的理論探索》
1、內(nèi)容概述
在現(xiàn)實(shí)Internet網(wǎng)絡(luò)環(huán)境中,大多數(shù)計(jì)算機(jī)主機(jī)都位于防火墻或NAT之后,只有少部分主機(jī)能夠直接接入Internet。很多時(shí)候,我們希望網(wǎng)絡(luò)中的兩臺(tái)主機(jī)能夠直接進(jìn)行通信,即所謂的P2P通信,而不需要其他公共服務(wù)器的中轉(zhuǎn)。由于主機(jī)可能位于防火墻或NAT之后,在進(jìn)行P2P通信之前,我們需要進(jìn)行檢測(cè)以確認(rèn)它們之間能否進(jìn)行P2P通信以及如何通信。這種技術(shù)通常稱為NAT穿透(NAT Traversal)。最常見的NAT穿透是基于UDP的技術(shù),如RFC3489中定義的STUN協(xié)議。
STUN,首先在RFC3489中定義,作為一個(gè)完整的NAT穿透解決方案,英文全稱是Simple Traversal of UDP Through NATs,即簡(jiǎn)單的用UDP穿透NAT。
在新的RFC5389修訂中把STUN協(xié)議定位于為穿透NAT提供工具,而不是一個(gè)完整的解決方案,英文全稱是Session Traversal Utilities for NAT,即NAT會(huì)話穿透效用。RFC5389與RFC3489除了名稱變化外,最大的區(qū)別是支持TCP穿透。
TURN,首先在RFC5766中定義,英文全稱是Traversal Using Relays around NAT:Relay Extensions to Session Traversal Utilities for NAT,即使用中繼穿透NAT:STUN的擴(kuò)展。簡(jiǎn)單的說(shuō),TURN與STURN的共同點(diǎn)都是通過(guò)修改應(yīng)用層中的私網(wǎng)地址達(dá)到NAT穿透的效果,異同點(diǎn)是TURN是通過(guò)兩方通訊的“中間人”方式實(shí)現(xiàn)穿透。
ICE跟STUN和TURN不一樣,ICE不是一種協(xié)議,而是一個(gè)框架(Framework),它整合了STUN和TURN。
2、STUN詳解
了解STUN之前,我們需要了解NAT的種類。
NAT對(duì)待UDP的實(shí)現(xiàn)方式有4種,分別如下:
-
Full Cone NAT:
完全錐形NAT,所有從同一個(gè)內(nèi)網(wǎng)IP和端口號(hào)發(fā)送過(guò)來(lái)的請(qǐng)求都會(huì)被映射成同一個(gè)外網(wǎng)IP和端口號(hào),并且任何一個(gè)外網(wǎng)主機(jī)都可以通過(guò)這個(gè)映射的外網(wǎng)IP和端口號(hào)向這臺(tái)內(nèi)網(wǎng)主機(jī)發(fā)送包。 -
Restricted Cone NAT:
限制錐形NAT,它也是所有從同一個(gè)內(nèi)網(wǎng)IP和端口號(hào)發(fā)送過(guò)來(lái)的請(qǐng)求都會(huì)被映射成同一個(gè)外網(wǎng)IP和端口號(hào)。與完全錐形不同的是,外網(wǎng)主機(jī)只能夠向先前已經(jīng)向它發(fā)送過(guò)數(shù)據(jù)包的內(nèi)網(wǎng)主機(jī)發(fā)送包。 -
Port Restricted Cone NAT:
端口限制錐形NAT,與限制錐形NAT很相似,只不過(guò)它包括端口號(hào)。也就是說(shuō),一臺(tái)IP地址X和端口P的外網(wǎng)主機(jī)想給內(nèi)網(wǎng)主機(jī)發(fā)送包,必須是這臺(tái)內(nèi)網(wǎng)主機(jī)先前已經(jīng)給這個(gè)IP地址X和端口P發(fā)送過(guò)數(shù)據(jù)包。 -
Symmetric NAT:
對(duì)稱NAT,所有從同一個(gè)內(nèi)網(wǎng)IP和端口號(hào)發(fā)送到一個(gè)特定的目的IP和端口號(hào)的請(qǐng)求,都會(huì)被映射到同一個(gè)IP和端口號(hào)。如果同一臺(tái)主機(jī)使用相同的源地址和端口號(hào)發(fā)送包,但是發(fā)往不同的目的地,NAT將會(huì)使用不同的映射。此外,只有收到數(shù)據(jù)的外網(wǎng)主機(jī)才可以反過(guò)來(lái)向內(nèi)網(wǎng)主機(jī)發(fā)送包。
2.1 RFC3489/STUN
STUN(Simple Traversal of User Datagram Protocol Through Network Address Translators),即簡(jiǎn)單的用UDP穿透NAT,是個(gè)輕量級(jí)的協(xié)議,是基于UDP的完整的穿透NAT的解決方案。它允許應(yīng)用程序發(fā)現(xiàn)它們與公共互聯(lián)網(wǎng)之間存在的NAT和防火墻及其他類型。它也可以讓應(yīng)用程序確定NAT分配給它們的公網(wǎng)IP地址和端口號(hào)。STUN是一種Client/Server的協(xié)議,也是一種Request/Response的協(xié)議,默認(rèn)端口號(hào)是3478。(IETF官方文檔RFC3489/STUN點(diǎn)此進(jìn)入)
2.1.1 報(bào)文結(jié)構(gòu)
【? 消息頭】
所有的STUN消息都包含20個(gè)字節(jié)的消息頭,包括16位的消息類型,16位的消息長(zhǎng)度和128位的事務(wù)ID。
<ignore_js_op style="overflow-wrap: break-word;">
</ignore_js_op>
消息類型許可的值如下:
- 0x0001:捆綁請(qǐng)求
- 0x0101:捆綁響應(yīng)
- 0x0111:捆綁錯(cuò)誤響應(yīng)
- 0x0002:共享私密請(qǐng)求
- 0x0102:共享私密響應(yīng)
- 0x0112:共享私密錯(cuò)誤響應(yīng)
消息長(zhǎng)度,是消息大小的字節(jié)數(shù),但不包括20字節(jié)的頭部。事務(wù)ID,128位的標(biāo)識(shí)符,用于隨機(jī)請(qǐng)求和響應(yīng),請(qǐng)求與其相應(yīng)的所有響應(yīng)具有相同的標(biāo)識(shí)符。
【? 消息屬性】
消息頭之后是0或多個(gè)屬性,每個(gè)屬性進(jìn)行TLV編碼,包括16位的屬性類型、16位的屬性長(zhǎng)度和變長(zhǎng)屬性值。
<ignore_js_op style="overflow-wrap: break-word;">
</ignore_js_op>
屬性類型定義如下:
- MAPPED-ADDRESS:MAPPED-ADDRESS屬性表示映射過(guò)的IP地址和端口。它包括8位的地址族,16位的端口號(hào)及長(zhǎng)度固定的IP地址。
- RESPONSE-ADDRESS:RESPONSE-ADDRESS屬性表示響應(yīng)的目的地址
- CHASNGE-REQUEST:客戶使用32位的CHANGE-REQUEST屬性來(lái)請(qǐng)求服務(wù)器使用不同的地址或端口號(hào)來(lái)發(fā)送響應(yīng)。
- SOURCE-ADDRESS:SOURCE-ADDRESS屬性出現(xiàn)在捆綁響應(yīng)中,它表示服務(wù)器發(fā)送響應(yīng)的源IP地址和端口。
- CHANGED-ADDRESS:如果捆綁請(qǐng)求的CHANGE-REQUEST屬性中的“改變IP”和“改變端口”標(biāo)志設(shè)置了,則CHANGED-ADDRESS屬性表示響應(yīng)發(fā)出的IP地址和端口號(hào)。
- USERNAME:USERNAME屬性用于消息的完整性檢查,用于消息完整性檢查中標(biāo)識(shí)共享私密。USERNAME通常出現(xiàn)在共享私密響應(yīng)中,與PASSWORD一起。當(dāng)使用消息完整性檢查時(shí),可有選擇地出現(xiàn)在捆綁請(qǐng)求中。
- PASSWORD:PASSWORD屬性用在共享私密響應(yīng)中,與USERNAME一起。PASSWORD的值是變長(zhǎng)的,用作共享私密,它的長(zhǎng)度必須是4字節(jié)的倍數(shù),以保證屬性與邊界對(duì)齊。
- MESSAGE-INTEGRITY:MESSAGE-INTEGRITY屬性包含STUN消息的HMAC-SHA1,它可以出現(xiàn)在捆綁請(qǐng)求或捆綁響應(yīng)中;MESSAGE-INTEGRITY屬性必須是任何STUN消息的最后一個(gè)屬性。它的內(nèi)容決定了HMAC輸入的Key值。
- ERROR-CODE:ERROR-CODE屬性出現(xiàn)在捆綁錯(cuò)誤響應(yīng)或共享私密錯(cuò)誤響應(yīng)中。它的響應(yīng)號(hào)數(shù)值范圍從100到699。
- UNKNOWN-ATTRIBUTES:UNKNOWN-ATTRIBUTES屬性只存在于其ERROR-CODE屬性中的響應(yīng)號(hào)為420的捆綁錯(cuò)誤響應(yīng)或共享私密錯(cuò)誤響應(yīng)中。
- REFLECTED-FROM:REFLECTED-FROM屬性只存在于其對(duì)應(yīng)的捆綁請(qǐng)求包含RESPONSE-ADDRESS屬性的捆綁響應(yīng)中。屬性包含請(qǐng)求發(fā)出的源IP地址,它的目的是提供跟蹤能力,這樣STUN就不能被用作DOS攻擊的反射器。
具體的ERROR-CODE(響應(yīng)號(hào)),與它們?nèi)笔〉脑蛘Z(yǔ)句一起,目前定義如下:
- 400(錯(cuò)誤請(qǐng)求):請(qǐng)求變形了??蛻粼谛薷南惹暗膰L試前不應(yīng)該重試該請(qǐng)求。
- 401(未授權(quán)):捆綁請(qǐng)求沒(méi)有包含MESSAGE-INTERITY屬性。
- 420(未知屬性):服務(wù)器不認(rèn)識(shí)請(qǐng)求中的強(qiáng)制屬性。
- 430(過(guò)期資格):捆綁請(qǐng)求沒(méi)有包含MESSAGE-INTEGRITY屬性,但它使用過(guò)期
- 的共享私密??蛻魬?yīng)該獲得新的共享私密并再次重試。
- 431(完整性檢查失敗):捆綁請(qǐng)求包含MESSAGE-INTEGRITY屬性,但HMAC驗(yàn)
- 證失敗。這可能是潛在攻擊的表現(xiàn),或者客戶端實(shí)現(xiàn)錯(cuò)誤
- 432(丟失用戶名):捆綁請(qǐng)求包含MESSAGE-INTEGRITY屬性,但沒(méi)有
- USERNAME屬性。完整性檢查中兩項(xiàng)都必須存在。
- 433(使用TLS):共享私密請(qǐng)求已經(jīng)通過(guò)TLS(Transport Layer Security,即安全
- 傳輸層協(xié)議)發(fā)送,但沒(méi)有在TLS上收到。
- 500(服務(wù)器錯(cuò)誤):服務(wù)器遇到臨時(shí)錯(cuò)誤,客戶應(yīng)該再次嘗試。
- 600(全局失敗):服務(wù)器拒絕完成請(qǐng)求,客戶不應(yīng)該重試。
屬性空間分為可選部分與強(qiáng)制部分,值超過(guò)0x7fff的屬性是可選的,即客戶或服務(wù)器即使不認(rèn)識(shí)該屬性也能夠處理該消息;值小于或等于0x7fff的屬性是強(qiáng)制理解的,即除非理解該屬性,否則客戶或服務(wù)器就不能處理該消息。
2.1.2實(shí)現(xiàn)原理
<ignore_js_op style="overflow-wrap: break-word;">
</ignore_js_op>
STUN協(xié)議的完整交互過(guò)程如上,下面我們來(lái)介紹具體實(shí)現(xiàn)步驟。
一般情況下,客戶會(huì)配置STUN服務(wù)器提供者的域名,該域名被解析為IP地址和SRV過(guò)程的端口號(hào)。服務(wù)器名是“stun”,使用UDP協(xié)議發(fā)送捆綁請(qǐng)求,使用TCP協(xié)議發(fā)送共享私密請(qǐng)求。STUN協(xié)議的缺省端口號(hào)為3478。
若要提供完整性檢查,STUN在客戶和服務(wù)器間使用128位的共享私密,作為在捆綁請(qǐng)求和捆綁響應(yīng)中的密匙。
首先,客戶通過(guò)發(fā)現(xiàn)過(guò)程獲得它將與之建立TCP連接的IP地址和端口號(hào)??蛻舸蜷_該地址和端口的連接,開始TLS協(xié)商,驗(yàn)證服務(wù)器的標(biāo)識(shí)??蛻舭l(fā)送共享私密請(qǐng)求。該請(qǐng)求沒(méi)有屬性,只有頭。服務(wù)器生成響應(yīng)。
客戶會(huì)在該連接上生成多個(gè)請(qǐng)求,但在獲得用戶名和密碼后關(guān)閉該連接。
服務(wù)器收到共享私密請(qǐng)求,驗(yàn)證從TLS連接上到達(dá)的該請(qǐng)求;如果不是通過(guò)TLS收到的請(qǐng)求,則生成共享私密錯(cuò)誤響應(yīng),并設(shè)置ERROR-CODE屬性為響應(yīng)號(hào)433;這里區(qū)分兩種情況:若通過(guò)TCP收到請(qǐng)求,則錯(cuò)誤響應(yīng)通過(guò)收到請(qǐng)求的相同連接發(fā)送;若通過(guò)UDP收到請(qǐng)求,則錯(cuò)誤響應(yīng)發(fā)送回請(qǐng)求送出的源IP和端口。
服務(wù)器檢查請(qǐng)求中的任何屬性,當(dāng)其中有不理解的小于或等于0x7fff的值,則生成共享私密錯(cuò)誤響應(yīng),設(shè)置ERROR-CODE屬性為響應(yīng)號(hào)420,并包括UNKNOWN-ATTRIBUTE屬性,列出它不理解的小于或等于0x7fff的屬性的值。該錯(cuò)誤響應(yīng)通過(guò)TLS連接發(fā)送。
若請(qǐng)求正確,服務(wù)器創(chuàng)建共享私密響應(yīng),包含與請(qǐng)求中相同的事務(wù)ID,并包含USERNAME和PASSWORD屬性。用戶名在10分鐘內(nèi)有效。
共享私密響應(yīng)通過(guò)與收到請(qǐng)求的相同的TLS連接發(fā)送,服務(wù)器保持連接打開狀態(tài),由客戶關(guān)閉它。
接著,客戶發(fā)送捆綁請(qǐng)求,攜帶的屬性包括:
- 可選屬性:RESPONSE-ADDRESS屬性和CHANGE-REQUEST屬性;
- 強(qiáng)制屬性:MESSAGE-INTEGRITY屬性和USERNAME屬性。
客戶發(fā)送捆綁請(qǐng)求,通過(guò)客戶重傳來(lái)提供可靠性??蛻糸_始用100ms的間隔重傳,每次重傳間隔加倍,直至1.6秒。之間間隔1.6秒的重傳繼續(xù),直到收到響應(yīng)或總共已經(jīng)發(fā)送了9次。因此,若9500ms后,還未收到響應(yīng),客戶認(rèn)為傳輸已經(jīng)失敗。
服務(wù)器檢查捆綁請(qǐng)求的MESSAGE-INTEGRITY屬性,不存在則生成捆綁錯(cuò)誤響應(yīng),設(shè)置ERROR-CODE屬性為響應(yīng)號(hào)401;若存在,計(jì)算請(qǐng)求的HMACKey值。
服務(wù)器檢查USERNAME屬性,不存在則生成捆綁錯(cuò)誤響應(yīng),設(shè)置ERROR-CODE屬性為響應(yīng)號(hào)432;若存在,但不認(rèn)識(shí)該USERNAME的共享私密(例如,它超時(shí)了),生成捆綁錯(cuò)誤響應(yīng),設(shè)置ERROR-CODE屬性為響應(yīng)號(hào)430。
若服務(wù)器知道該共享私密,但所計(jì)算的HMAC與請(qǐng)求的不同,生成捆綁錯(cuò)誤響應(yīng),設(shè)置ERROR-CODE屬性為響應(yīng)號(hào)431。
假設(shè)消息完整性檢查通過(guò)了,服務(wù)器檢查請(qǐng)求中的任何屬性的值,若遇到不理解的小于或等于0x7fff的值,生成捆綁錯(cuò)誤響應(yīng),設(shè)置ERROR-CODE屬性為響應(yīng)號(hào)420,該響應(yīng)包含UNKNOWN-ATTRIBUTE屬性,并列出不理解的小于或等于0x7fff的屬性。
若請(qǐng)求正確,服務(wù)器生成單個(gè)捆綁響應(yīng),包含與捆綁請(qǐng)求相同的事務(wù)ID。服務(wù)器在捆綁響應(yīng)中加入MAPPED-ADDRESS屬性,該屬性的IP地址和端口號(hào)為捆綁請(qǐng)求的源IP地址和端口號(hào)。
捆綁響應(yīng)的源地址和端口號(hào)取決于捆綁請(qǐng)求中CHANGE-REQUEST屬性的值及捆綁請(qǐng)求收到的地址和端口號(hào)相關(guān)。總結(jié)如下:
<ignore_js_op style="overflow-wrap: break-word;">
</ignore_js_op>
服務(wù)器在捆綁響應(yīng)中加入SOURCE-ADDRESS屬性,包含用于發(fā)送捆綁響應(yīng)的源地址和端口號(hào);加入CHANGED-ADDRESS屬性,包含源IP地址和端口號(hào)。
如果捆綁請(qǐng)求中包含了USERNAME和MESSAGE-INTEGRITY屬性,則服務(wù)器在捆綁響應(yīng)中加入MESSAGE-INTEGRITY屬性。
如果捆綁請(qǐng)求包含RESPONSE-ADDRESS屬性,則服務(wù)器在捆綁響應(yīng)中加入REFLECTED-FROM屬性:如果捆綁請(qǐng)求使用從共享私密請(qǐng)求獲得的用戶名進(jìn)行認(rèn)證,則REFLECTED-FROM屬性包含共享私密請(qǐng)求到達(dá)的源IP地址和端口號(hào);若請(qǐng)求中的用戶名不是使用共享私密分配的,則REFLECTED-FROM屬性包含獲得該用戶名的實(shí)體的源IP地址和端口號(hào);若請(qǐng)求中沒(méi)有用戶名,且服務(wù)器愿意處理該請(qǐng)求,則REFLECTED-FROM屬性包含請(qǐng)求發(fā)出的源IP地址和端口號(hào)。
服務(wù)器不會(huì)重傳響應(yīng),可靠性通過(guò)客戶周期性地重發(fā)請(qǐng)求來(lái)保障,每個(gè)請(qǐng)求都會(huì)觸發(fā)服務(wù)器進(jìn)行響應(yīng)。
客戶端判斷響應(yīng)的類型是捆綁錯(cuò)誤響應(yīng)還是捆綁響應(yīng)。捆綁錯(cuò)誤響應(yīng)通常在請(qǐng)求發(fā)送的源地址和端口收到;捆綁響應(yīng)通常在請(qǐng)求中的RESPONSE-ADDRESS屬性的地址和端口收到,若沒(méi)有該屬性,則捆綁響應(yīng)將在請(qǐng)求發(fā)送的源地址和端口號(hào)收到。
- 若是捆綁錯(cuò)誤響應(yīng),客戶檢查響應(yīng)中的ERROR-CODE屬性的響應(yīng)號(hào):400至499之間的未知屬性按屬性400處理,500至599之間的未知屬性按500處理,600至699之間的未知屬性按600處理。任何100和399之間的響應(yīng)都會(huì)使請(qǐng)求重傳中止,但其他則忽略;若客戶收到響應(yīng)的屬性類型大于0x7fff,則忽略該屬性,若小于或等于0x7fff,則請(qǐng)求重傳停止,并忽略整個(gè)響應(yīng);
- 若是捆綁響應(yīng),客戶檢查響應(yīng)的MESSAGE-INTEGRITY屬性:如果不存在,客戶在請(qǐng)求中加入MESSAGE-INTEGRITY屬性,并放棄該響應(yīng);如果存在,客戶計(jì)算響應(yīng)的HMAC。如果計(jì)算出的HMAC與響應(yīng)中的不同,則放棄該響應(yīng),并警告客戶可能受到了攻擊;若計(jì)算出的HMAC與響應(yīng)中的匹配,則過(guò)程繼續(xù);
- 不論收到捆綁響應(yīng)還是捆綁錯(cuò)誤響應(yīng),都將中止該請(qǐng)求的重傳。客戶在第一次響應(yīng)后繼續(xù)監(jiān)聽捆綁請(qǐng)求的響應(yīng)10秒鐘,如果這期間它收到任何消息類型不同的響應(yīng)或不同的MAPPED-ADDRESS屬性,它將警告用戶可能受到攻擊;并且,如果客戶收到的捆綁響應(yīng)次數(shù)超過(guò)它發(fā)送的捆綁請(qǐng)求數(shù)的兩倍,它將警告用戶可能受到攻擊;若捆綁響應(yīng)經(jīng)過(guò)認(rèn)證,上述攻擊并未導(dǎo)致客戶丟棄MAPPED-ADDRESS,則客戶可以使用該MAPPED-ADDRESS和SOURCE-ADDRESS屬性。
2.1.3STUN功能舉例
客戶通過(guò)帶外方式獲得STUN服務(wù)器信息后,就打開對(duì)應(yīng)的地址和端口的連接,并開始與STUN服務(wù)器進(jìn)行TLS協(xié)商。一旦打開了連接,客戶就通過(guò)TCP協(xié)議發(fā)送共享私密請(qǐng)求,服務(wù)器生成共享私密響應(yīng)。STUN在客戶和服務(wù)器間使用共享私密,用作捆綁請(qǐng)求和捆綁響應(yīng)中的密匙。之后,客戶使用UDP協(xié)議向STUN服務(wù)器發(fā)送捆綁請(qǐng)求,當(dāng)捆綁請(qǐng)求消息到達(dá)服務(wù)器的時(shí)候,它可能經(jīng)過(guò)了一個(gè)或者多個(gè)NAT。結(jié)果是STUN服務(wù)器收到的捆綁請(qǐng)求消息的源IP地址被映射成最靠近STUN服務(wù)器的NAT的IP地址,STUN服務(wù)器把這個(gè)源IP地址和端口號(hào)復(fù)制到一個(gè)捆綁響應(yīng)消息中,發(fā)送回?fù)碛羞@個(gè)IP地址和端口號(hào)的客戶端。
當(dāng)STUN客戶端收到捆綁響應(yīng)消息之后,它會(huì)將自己發(fā)送捆綁請(qǐng)求時(shí)綁定的本地IP地址和端口號(hào)同捆綁響應(yīng)消息中的IP地址和端口號(hào)進(jìn)行比較,如果不匹配,就表示客戶端正處于一個(gè)或者多個(gè)NAT的前面。
在Full-Cone NAT的情況下,在捆綁響應(yīng)消息中的IP地址和端口是屬于公網(wǎng)的,公網(wǎng)上的任何主機(jī)都可以使用這個(gè)IP地址和端口號(hào)向這個(gè)應(yīng)用程序發(fā)送數(shù)據(jù)包,應(yīng)用程序只需要在剛才發(fā)送捆綁請(qǐng)求的IP地址和端口上監(jiān)聽即可。
當(dāng)然,客戶可能并不在一個(gè)Full-Cone NAT的前面,實(shí)際上,它并不知道自己在一個(gè)什么類型的NAT的前面。為了確定NAT的類型,客戶端使用附加的捆綁請(qǐng)求。具體過(guò)程是很靈活的,但一般都會(huì)像下面這樣工作:客戶端再發(fā)送一個(gè)捆綁請(qǐng)求,這次發(fā)往另一個(gè)IP地址,但是使用的是跟上一次同一個(gè)源IP地址和源端口號(hào),如果返回的數(shù)據(jù)包里面的IP地址和端口號(hào)和第一次返回的數(shù)據(jù)包中的不同,客戶端就會(huì)知道它是在一個(gè)對(duì)稱NAT的前面??蛻舳藶榱舜_認(rèn)自己是否在一個(gè)完全錐形NAT的前面,客戶端可以發(fā)送一個(gè)帶有標(biāo)志的捆綁請(qǐng)求,這個(gè)標(biāo)志告訴服務(wù)器使用另一個(gè)IP地址和端口發(fā)送捆綁響應(yīng)。換句話說(shuō),如果客戶端使X/Y的IP地址端口對(duì)向A/B的IP地址端口對(duì)發(fā)送捆綁請(qǐng)求,服務(wù)器就會(huì)使用源IP地址和源端口號(hào)為C/D的地址端口對(duì)向X/Y發(fā)送捆綁響應(yīng)。如果客戶端收到了這個(gè)響應(yīng),它就知道它是在一個(gè)Full-Cone NAT前面。
STUN協(xié)議允許客戶端請(qǐng)求服務(wù)器從收到捆綁請(qǐng)求的IP地址往回發(fā)捆綁響應(yīng),但是要使用不同的端口號(hào)。這可以用來(lái)檢查客戶端是否在Port Restricted Cone NAT的前面還是在Restricted Cone NAT的前面。
2.2 RFC5389/STUN
STUN協(xié)議在RFC5389中被重新命名為Session Traversal Utilities for NAT,即NAT會(huì)話穿透效用。在這里,NAT會(huì)話穿透效用被定位為一個(gè)用于其他解決NAT穿透問(wèn)題協(xié)議的協(xié)議。它可以用于終端設(shè)備檢查由NAT分配給終端的IP地址和端口號(hào)。同時(shí),它也被用來(lái)檢查兩個(gè)終端之間的連接性,好比是一種維持NAT綁定表項(xiàng)的保活協(xié)議。STUN可以用于多種NAT類型,并不需要它們提供特殊的行為。
STUN本身不再是一種完整的NAT穿透解決方案,它相當(dāng)于是一種NAT穿透解決方案中的工具。這是與RFC3489/STUN版本相比最重要的改變。
2.2.1STUN用途
目前定義了三種STUN用途:
- Interactive Connectivity Establishment(ICE)[MMUSIC-ICE],交互式連接建立
- Client-initiated connections for SIP [SIP-OUTBOUND],用于SIP的客戶端初始化連接
- NAT Behavior Discovery [BEHAVE-NAT],NAT行為發(fā)現(xiàn)
2.2.2報(bào)文結(jié)構(gòu)
【? 消息頭】
STUN消息頭為20字節(jié),后面緊跟0或多個(gè)屬性。STUN頭部包含一STUN消息類型、magic cookie、事務(wù)ID和消息長(zhǎng)度。
<ignore_js_op style="overflow-wrap: break-word;">
</ignore_js_op>
每個(gè)STUN消息的最高位前2位必須為0。當(dāng)STUN協(xié)議為多個(gè)協(xié)議多路復(fù)用時(shí)若使用的是同一個(gè)端口,這可以用于與其他協(xié)議區(qū)分STUN數(shù)據(jù)包。消息類型確定消息的類別(如請(qǐng)求、成功回應(yīng)、失敗回應(yīng)、標(biāo)志)。雖然這里有四種消息類型,但可以分為2類事務(wù):請(qǐng)求/響應(yīng)事務(wù)、標(biāo)志事務(wù)。
消息類型字段可進(jìn)一步劃分為下面結(jié)構(gòu):
<ignore_js_op style="overflow-wrap: break-word;">
</ignore_js_op>
消息類型定義如下:
- 0b00,表示請(qǐng)求
- 0b01,表示標(biāo)志
- 0b10,表示成功響應(yīng)
- 0b11,表示錯(cuò)誤響應(yīng)
魔術(shù)字域必須包含固定的值0x2112A442。在RFC3489中,該域是事務(wù)ID的一部分。配置魔術(shù)字允許服務(wù)器檢測(cè)客戶是否理解某些在改進(jìn)的版本中增加的屬性。另外,還可用于STUN多路復(fù)用時(shí)與其他協(xié)議的包進(jìn)行區(qū)分。
96位的事務(wù)ID用于唯一的識(shí)別STUN事務(wù)。對(duì)于請(qǐng)求/響應(yīng)事務(wù),事務(wù)ID由STUN客戶端來(lái)選擇;對(duì)于標(biāo)志事務(wù),由代理(代理指支持STUN的客戶端或服務(wù)器)來(lái)選擇并發(fā)送。它主要服務(wù)于與請(qǐng)求相關(guān)的響應(yīng),因此它也扮演著一個(gè)幫助阻止確定類型的攻擊的角色。服務(wù)器使用事務(wù)ID來(lái)唯一的標(biāo)識(shí)出所有客戶端的每一個(gè)事務(wù)。事務(wù)ID本身必須是唯一的,并且隨機(jī)的從0到2的96-1次方中選擇。重新發(fā)送相同的請(qǐng)求時(shí),也必須使用新的事務(wù)ID。成功或錯(cuò)誤響應(yīng)必須攜帶與相對(duì)應(yīng)的請(qǐng)求相同的事務(wù)ID。
消息長(zhǎng)度字段不包括20字節(jié)的STUN頭部。所有的STUN屬性必須填充為4字節(jié)的倍數(shù)。消息長(zhǎng)度字段的最后2位總是為0,這為區(qū)分STUN包與其他協(xié)議的包提供了另外一種方法。
【? 消息屬性】
STUN頭之后是0或多個(gè)屬性。每個(gè)屬性都采用TLV編碼,16位的類型、16位的長(zhǎng)度及可變長(zhǎng)度的值。每個(gè)STUN屬性必須是4字節(jié)邊界對(duì)齊。
<ignore_js_op style="overflow-wrap: break-word;">
</ignore_js_op>
屬性空間被劃分為2個(gè)范圍。屬性的類型值在0x0000到0x7fff是強(qiáng)制理解屬性,這意味著除非STUN代理能夠理解這些屬性,否則將不能正常處理包含該屬性的消息;屬性的類型值在0x8000到0xffff范圍是可選理解屬性,這意味著如果STUN代理不能理解它們的話這些屬性可以被忽略。
STUN屬性類型集由IANA維護(hù),具體定義詳見IETF官方文檔 RFC5389。
2.3 RFC5389與RFC3489的區(qū)別
RFC5389與RFC3489的不同點(diǎn)如下:
- 去掉STUN是一種完整的NAT穿透方案的概念,現(xiàn)在是一種用于提供NAT穿透解決方案的工具。因而,協(xié)議的名稱變?yōu)镹AT會(huì)話穿透效用;
- 定義了STUN的用途;
- 去掉了STUN關(guān)于NAT類型檢測(cè)和綁定生命期發(fā)現(xiàn)的用法,去掉了RESPONSE-ADDRESS、CHANGED-ADDRESS、CHANGE-REQUEST、SOURCE-ADDRESS和REFLECTED-FROM屬性;
- 增加了一個(gè)固定的32位的魔術(shù)字字段,事務(wù)ID字段減少了32位長(zhǎng)度;
- 增加了XOR-MAPPED-ADDRESS屬性,若魔術(shù)字在捆綁請(qǐng)求中出現(xiàn)時(shí),該屬性包括在捆綁響應(yīng)中。否則,RFC3489中的行為是保留的(換句話說(shuō),捆綁響應(yīng)中包括MAPPED-ADDRESS);
- 介紹了消息類型字段的正式結(jié)構(gòu),帶有一對(duì)明確的位來(lái)標(biāo)識(shí)Request、Response、Error-Response或Indication消息。因此,消息類型字段被劃分為類別和方法兩部分;
- 明確的指出了STUN的最高2位是0b00,當(dāng)用于ICE時(shí)可以簡(jiǎn)單的與RTP包區(qū)分開來(lái);
- 增加指紋屬性來(lái)提供一種明確的方法來(lái)檢測(cè)當(dāng)STUN協(xié)議多路復(fù)用時(shí),STUN與其他協(xié)議之間的差異;
- 增加支持IPv6,IPv4客戶端可以獲取一個(gè)IPv6映射地址,反之亦然;
- 增加一個(gè)long-term-credential-based認(rèn)證機(jī)制;
- 增加了SOFTWARE、REALM、NONCE和ALTERNATE-SERVER屬性;
- 去掉了共享密匙方法,因此PASSWORD屬性也去掉了;
- 去掉了使用連續(xù)10秒偵聽STUN響應(yīng)來(lái)識(shí)別一個(gè)攻擊的做法;
- 改變事務(wù)計(jì)時(shí)器來(lái)增加TCP友好性;
- 去掉了STUN例子如集中分離控制和媒體面,代替的,在使用STUN協(xié)議時(shí)提供了更多的信息;
- 定義了一類填充機(jī)制來(lái)改變長(zhǎng)度屬性的說(shuō)明;
- REALM、SERVER、原因語(yǔ)句和NONCE限制在127個(gè)字符,USERNAME限制在513個(gè)字節(jié)以內(nèi);
- 為TCP和TLS改變了DNS SRV規(guī)程,UDP仍然和以前保持一致。
2.4 新特性介紹
2.4.1指紋機(jī)制
FINGERPRINT機(jī)制是一種可選的用于其他協(xié)議多路復(fù)用STUN時(shí)發(fā)送給相同的傳輸?shù)刂窌r(shí)區(qū)分STUN數(shù)據(jù)包的機(jī)制,該機(jī)制不支持與RFC3489相兼容。
在一些用途中,基于相同的傳輸?shù)刂窌r(shí)多個(gè)協(xié)議會(huì)多路復(fù)用STUN消息,例如RTP協(xié)議。STUN消息必須首先和應(yīng)用報(bào)文分離開。目前,在STUN報(bào)頭中有3種固定的字段可以用于該目的。盡管如此,在一些案例中,三種固定字段仍然不能充分的區(qū)別開。
當(dāng)擴(kuò)展的指紋機(jī)制被使用時(shí),STUN代理在發(fā)送給其他STUN代理的消息中包括FINGERPRINT屬性。當(dāng)其他STUN代理收到時(shí),除基本的檢查之外,還將檢查是否包含F(xiàn)INGERPRINT屬性及它是否包含正確的值,至此,它將相信這是一個(gè)STUN消息。指紋機(jī)制幫助STUN代理檢查其他協(xié)議那些看起來(lái)像是STUN消息的消息。
2.4.2通過(guò)DNS發(fā)現(xiàn)服務(wù)器機(jī)制
STUN客戶端可以使用DNS來(lái)發(fā)現(xiàn)STUN服務(wù)器的IP地址和端口??蛻舳吮仨氈婪?wù)器的域名。
當(dāng)客戶端希望找出服務(wù)器在公網(wǎng)上的位置就采用捆綁請(qǐng)求/響應(yīng)事務(wù),SRV(資源記錄表)中服務(wù)器名稱是“stun”。當(dāng)通過(guò)TLS會(huì)話采用捆綁請(qǐng)求/響應(yīng)事務(wù),SRV中服務(wù)器名稱為“stuns”。STUN用戶可以定義額外的DNS資源記錄服務(wù)名稱。
STUN請(qǐng)求的默認(rèn)端口是3478,用于TCP和UDP。STUN在TLS上的默認(rèn)端口是5349。服務(wù)器能夠在TLS上運(yùn)行STUN與STUN在TCP上時(shí)使用相同的端口,只有服務(wù)器軟件支持決定初始消息是否是TLS或STUN消息。
如果SRV中沒(méi)有記錄可查,客戶端執(zhí)行A或AAAA記錄查找域名。結(jié)果將會(huì)是1張IP地址表,每一個(gè)都可以使用TCP或UDP采用默認(rèn)端口號(hào)連接。通常要求使用TLS,客戶端使用STUN在TLS上的默認(rèn)端口號(hào)連接其中一個(gè)IP地址。
2.4.3認(rèn)證和消息完整性機(jī)制
短期證書機(jī)制
短期證書機(jī)制假設(shè)在STUN事務(wù)之前,客戶端和服務(wù)器已經(jīng)使用了其他協(xié)議來(lái)交換了證書,以u(píng)sername和password形式。這個(gè)證書是有時(shí)間限制的。例如,在ICE用途中,兩個(gè)終端使用帶外方式交換信息來(lái)對(duì)username和password達(dá)成一致,并在媒體會(huì)話期間使用。這個(gè)證書被用來(lái)進(jìn)行消息完整性檢查,用于每個(gè)請(qǐng)求和多個(gè)響應(yīng)中。與長(zhǎng)期證書機(jī)制相比,沒(méi)有挑戰(zhàn)和響應(yīng)方式,因此,這種證書的時(shí)間限制特性的優(yōu)點(diǎn)是可以阻止重播。
長(zhǎng)期證書機(jī)制
長(zhǎng)期證書機(jī)制依賴于一個(gè)長(zhǎng)期證書,username和password在客戶端和服務(wù)器中是共用的。這個(gè)證書從它提供給 用戶開始將一直是有效的,直到該用戶不再是該系統(tǒng)的用戶。這本質(zhì)上是一個(gè)提供給用戶username和password的傳統(tǒng)的登入方式。
客戶端初始發(fā)送一個(gè)請(qǐng)求,沒(méi)有提供任何證書和任何完整性檢測(cè)。服務(wù)器拒絕這個(gè)請(qǐng)求,并提供給用戶一個(gè)范圍(用于指導(dǎo)用戶或代理選擇username和password)和一個(gè)nonce。這個(gè)nonce提供重放保護(hù)。它是一個(gè)cookie,由服務(wù)器選擇,以這樣一種方式來(lái)標(biāo)示有效時(shí)間或客戶端身份是有效的。客戶端重試這個(gè)請(qǐng)求,這次包括它的username和realm和服務(wù)器提供的nonce來(lái)回應(yīng)。服務(wù)器確認(rèn)這個(gè)nonce和檢查這個(gè)message integrity。如果它們匹配,請(qǐng)求則通過(guò)認(rèn)證。如果這個(gè)nonce不再有效,即過(guò)期了,服務(wù)器就拒絕該請(qǐng)求,并提供一個(gè)新的nonce。
在隨后的到同一服務(wù)器的請(qǐng)求,客戶端重新使用這個(gè)nonce、username和realm,和先前使用的password。這樣,隨后的請(qǐng)求不會(huì)被拒絕直到這個(gè)nonce變成無(wú)效的。需要注意的是,長(zhǎng)期證書機(jī)制不能用來(lái)保護(hù)Indications,由于Indications不能被改變,因此,使用Indications時(shí)要么使用短期證書,要么就省略認(rèn)證和消息完整性。因?yàn)殚L(zhǎng)期證書機(jī)制對(duì)離線字典攻擊敏感,部署的時(shí)候應(yīng)該使用很難猜測(cè)的密碼。
2.4.4備份服務(wù)器機(jī)制
服務(wù)器使用增強(qiáng)的重定向功能將一個(gè)客戶端轉(zhuǎn)向另一個(gè)服務(wù)器,通過(guò)回應(yīng)一個(gè)錯(cuò)誤響應(yīng)號(hào)為300(嘗試備份)的錯(cuò)誤響應(yīng)。服務(wù)器在錯(cuò)誤響應(yīng)中攜帶一個(gè)ALTERNATE-SERVER屬性。
客戶端收到錯(cuò)誤響應(yīng)號(hào)為300的錯(cuò)誤響應(yīng)后,在該響應(yīng)中查找ALTERNATE-SERVER屬性。若找到一個(gè),客戶端就會(huì)將當(dāng)前的事務(wù)作廢,并重新嘗試發(fā)送請(qǐng)求到該屬性中列出的服務(wù)器。請(qǐng)求報(bào)文若已經(jīng)通過(guò)認(rèn)證,則必須使用與先前發(fā)送給執(zhí)行重定向操作的服務(wù)器同樣的證書。如果客戶端在最后5分鐘里已經(jīng)重試發(fā)送請(qǐng)求時(shí)已經(jīng)重定向到了一個(gè)服務(wù)器,它必須忽略重定向操作并將當(dāng)前的事務(wù)作廢,這是為了防止無(wú)限的重定向循環(huán)。
2.5 RFC5389與RFC3489的兼容
在RFC3489中:
- UDP是唯一支持的傳輸協(xié)議
- RFC5389中的魔術(shù)字字段是RFC3489中事務(wù)ID的一部分,事務(wù)ID長(zhǎng)128位
- 沒(méi)有XOR-MAPPED-ADDRESS屬性,綁定方法是使用MAPPED-ADDRESS屬性代替
- 有3個(gè)需要強(qiáng)制理解的屬性,分別是:RESPONSE-ADDRESS、CHANGE-REQUEST、CHANGED-ADDRESS屬性,而RFC5389中不再支持這些屬性。
2.5.1客戶端處理的改變
客戶端想要與RFC3489的服務(wù)器互操作,應(yīng)發(fā)送一個(gè)使用綁定方法的請(qǐng)求消息,不包含任何消息,使用UDP協(xié)議發(fā)送給服務(wù)器。如果成功,將收到服務(wù)器發(fā)回的包含MAPPED-ADDRESS屬性而不是XOR-MAPPED-ADDRESS屬性的成功響應(yīng)??蛻舳嗽噲D與基于RFC3489的應(yīng)用服務(wù)器互操作必須準(zhǔn)備好接收任意一個(gè)屬性。此外,客戶端必須忽略任何在響應(yīng)中出現(xiàn)的保留的強(qiáng)制理解的屬性。RFC3489中規(guī)定保留屬性中的0x0002、0x0004、0x0005和0x000B可能出現(xiàn)在綁定響應(yīng)中。
2.5.2服務(wù)器處理的改變
服務(wù)器能夠察覺(jué)由RFC3489中的客戶端發(fā)送的攜帶有不正確的魔術(shù)字的捆綁請(qǐng)求消息。當(dāng)服務(wù)器察覺(jué)到RFC3489中的客戶端,它應(yīng)該將捆綁請(qǐng)消息中魔術(shù)字域中的值拷貝到捆綁響應(yīng)中的魔術(shù)字字段中,并且插入一個(gè)MAPPED-ADDRESS屬性代替XOR-MAPPED-ADDRESS屬性。
客戶端在極少的環(huán)境下可能包括RESPONSE-ADDRESS或CHANGE-REQUEST屬性中的一個(gè)。在這些情況下,服務(wù)器把這些屬性看做是一個(gè)不認(rèn)識(shí)的強(qiáng)制理解的屬性,并回應(yīng)一個(gè)錯(cuò)誤響應(yīng)。RFC3489版本中的STUN缺少魔術(shù)字和指紋屬性這兩種能夠高可靠性的正確標(biāo)識(shí)其他協(xié)議多路復(fù)用時(shí)的STUN消息。因此,STUN執(zhí)行與RFC3489兼容時(shí)不應(yīng)該被用于多個(gè)協(xié)議。
3、TURN詳解
3.1 RFC5766/TURN
TURN,在RFC5766中定義,英文全稱Traversal Using Relays around NAT(TURN):Relay Extensions to Session Traversal Utilities for NAT(STUN),即使用中繼穿透NAT:STUN的中繼擴(kuò)展。簡(jiǎn)單的說(shuō),TURN與STUN的共同點(diǎn)都是通過(guò)修改應(yīng)用層中的私網(wǎng)地址達(dá)到NAT穿透的效果,異同點(diǎn)是TURN是通過(guò)兩方通訊的“中間人”方式實(shí)現(xiàn)穿透。
如果一個(gè)主機(jī)位于NAT的后面,在某些情況下它不能夠與其他主機(jī)點(diǎn)對(duì)點(diǎn)直接連接。在這些情況下,它需要使用中間網(wǎng)點(diǎn)提供的中繼連接服務(wù)。TURN協(xié)議就是用來(lái)允許主機(jī)控制中繼的操作并且使用中繼與對(duì)端交換數(shù)據(jù)。TURN與其他中繼控制協(xié)議不同的是它能夠允許一個(gè)客戶端使用一個(gè)中繼地址與多個(gè)對(duì)端連接。
TURN協(xié)議被設(shè)計(jì)為ICE的一部分,用于NAT穿越,雖然如此,它也可以在沒(méi)有ICE的地方單獨(dú)使用。
3.2 操作概述
<ignore_js_op style="overflow-wrap: break-word;">
</ignore_js_op>
在一個(gè)典型組網(wǎng)中,一個(gè)TURN客戶端連接在一個(gè)私有網(wǎng)絡(luò)中,通過(guò)一個(gè)或多個(gè)NAT來(lái)連接到公網(wǎng)。在公網(wǎng)中有一個(gè)TURN服務(wù)器。在因特網(wǎng)的別處有一個(gè)或多個(gè)對(duì)端是這個(gè)TURN客戶端希望通訊的。這些對(duì)端也有可能是在一個(gè)或多個(gè)NAT的后面。該客戶端使用服務(wù)器作為一個(gè)中繼來(lái)發(fā)送數(shù)據(jù)包 到這些對(duì)端去,并且從這些對(duì)端接收數(shù)據(jù)包。
客戶端通過(guò)一個(gè)IP地址和端口的組合來(lái)與服務(wù)器建立會(huì)話??蛻舳耸褂肨URN命令在服務(wù)器上創(chuàng)建和操作一個(gè)ALLOCATION。一旦這個(gè)allocation創(chuàng)建好了,客戶端能夠在數(shù)據(jù)發(fā)往哪個(gè)對(duì)端的指示下發(fā)送應(yīng)用數(shù)據(jù)到這個(gè)服務(wù)器,服務(wù)器將中繼這些數(shù)據(jù)到合適的對(duì)端??蛻舳税l(fā)送的應(yīng)用數(shù)據(jù)包含在TURN消息中,服務(wù)器將數(shù)據(jù)提取出來(lái),并以UDP數(shù)據(jù)包方式發(fā)送給對(duì)端。反向上,對(duì)端以UDP數(shù)據(jù)包方式發(fā)送應(yīng)用數(shù)據(jù)到這個(gè)allocation提供的中繼傳輸?shù)刂?。因?yàn)門URN消息總是包含客戶端與哪些對(duì)端通訊的指示,客戶端能夠使用單一的allocation來(lái)與多個(gè)對(duì)端通訊。
3.3 術(shù)語(yǔ)
- TURN client:遵循RFC5766的STUN客戶端。
- TURN server:遵循RFC5766的STUN服務(wù)器。
- Peer:TURN客戶端希望連接的主機(jī)。TURN服務(wù)器為TURN客戶端和它的對(duì)端中繼流量,但Peer并不與TURN服務(wù)器使用TURN協(xié)議進(jìn)行交互,它接收從TURN服務(wù)器發(fā)送過(guò)來(lái)的數(shù)據(jù),并向TURN服務(wù)器發(fā)送數(shù)據(jù)。
- Transport Address:IP地址與端口號(hào)的組合。
- Host Transport Address:客戶端或?qū)Χ说膫鬏數(shù)刂贰?/li>
- Server-Reflexive Transport Address:NAT公網(wǎng)側(cè)的傳輸?shù)刂?,該地址由NAT分配,相當(dāng)于一個(gè)特定的主機(jī)傳輸?shù)刂贰?/li>
- Relayed Transport Address:TURN服務(wù)器上的傳輸?shù)刂?,用于客戶端和?duì)端中繼數(shù)據(jù)。
- TURN Server Transport Address:TURN服務(wù)器上的傳輸?shù)刂?,用于客戶端發(fā)送STUN消息給服務(wù)器。
- Peer Transport Address:服務(wù)器看到的對(duì)端的傳輸?shù)刂?,?dāng)對(duì)端是在NAT后面,則是對(duì)端的服務(wù)器反射傳輸?shù)刂贰?/li>
- Allocation:通過(guò)Allocate請(qǐng)求將中繼傳輸?shù)刂诽峁┙o客戶端,除了中繼狀態(tài)外,還有許可和超時(shí)定時(shí)器等。
- 5-tuple:五元組,包括客戶端IP地址和端口,服務(wù)器IP地址和端口和傳輸協(xié)議(包括UDP、TCP、TLS)的組合。
- Channel:通道號(hào)與對(duì)端傳輸?shù)刂返年P(guān)聯(lián),一旦一個(gè)通道號(hào)與一個(gè)對(duì)端的傳輸?shù)刂方壎?,客戶端和服?wù)器就能夠利用帶寬效應(yīng)更大的通道數(shù)據(jù)消息來(lái)交換數(shù)據(jù)。
- Permission:一個(gè)對(duì)端允許使用它的IP地址和傳輸協(xié)議來(lái)發(fā)送數(shù)據(jù)到TURN服務(wù)器,服務(wù)器只為從對(duì)端發(fā)來(lái)的并且匹配一個(gè)已經(jīng)存在的許可的流量中繼到相應(yīng)的客戶端。
- Realm:服務(wù)器內(nèi)用于描述服務(wù)器或內(nèi)容的一個(gè)字符串,這個(gè)realm告訴客戶端哪些用戶名和密碼的組合可用于認(rèn)證請(qǐng)求。
- Nonce:服務(wù)器隨機(jī)選擇的一個(gè)字符串,包含在報(bào)文摘要中。為了防止中繼攻擊,服務(wù)器應(yīng)該有規(guī)律的改變這個(gè)nonce。
具體協(xié)議細(xì)節(jié),詳見IETF官方文檔:RFC5766.
3.4 協(xié)議交互過(guò)程詳細(xì)舉例
以上圖為例進(jìn)行講解,每個(gè)消息中,多個(gè)屬性包含在消息中并顯示它們的值。為了方便閱讀,值以人們可讀的格式來(lái)顯示。
<ignore_js_op style="overflow-wrap: break-word;">
</ignore_js_op>
客戶端使用10.1.1.2:49271作為傳輸?shù)刂废蚍?wù)器的傳輸?shù)刂钒l(fā)送Allocate請(qǐng)求??蛻舳穗S機(jī)選擇一個(gè)96位的事務(wù)ID。該Allocate請(qǐng)求消息包括SOFTWARE屬性來(lái)提供客戶端的軟件版本信息;包括LIFETIME屬性,指明客戶端希望該allocation具有1小時(shí)的生命期而非缺省的10分鐘;包括REQUESTED-TRANSPORT屬性來(lái)告訴服務(wù)器與對(duì)端之間采用UDP協(xié)議來(lái)傳輸;包括DONT-FRAGMENT屬性因?yàn)榭蛻舳讼M陔S后的Send indications中使用DON’T-FRAGMENT屬性。
服務(wù)器需要任何請(qǐng)求必須是經(jīng)過(guò)認(rèn)證的,因此服務(wù)器拒絕了該最初的Allocation請(qǐng)求,并且回應(yīng)了攜帶有錯(cuò)誤響應(yīng)號(hào)為401(未授權(quán))的Allocate錯(cuò)誤響應(yīng);該響應(yīng)包括一個(gè)REALM屬性,指明認(rèn)證的域;還包括一個(gè)NONCE屬性和一個(gè)SOFTWARE屬性。
客戶端收到了錯(cuò)誤響應(yīng)號(hào)為401的Allocate錯(cuò)誤響應(yīng),將重新嘗試發(fā)送Allocate請(qǐng)求,此時(shí)將包括認(rèn)證屬性??蛻舳嗽谛碌恼?qǐng)求中重新選擇一個(gè)新的事務(wù)ID??蛻舳税ㄒ粋€(gè)USERNAME屬性,使用從服務(wù)器那收到的realm值來(lái)幫助它決定使用哪個(gè)值;請(qǐng)求還包括REALM和NONCE屬性,這兩個(gè)屬性是從收到的錯(cuò)誤響應(yīng)中拷貝出來(lái)的。最后,客戶端包括一個(gè)MESSAGE-INTEGRITY屬性。
服務(wù)器收到認(rèn)證的Allocate請(qǐng)求后,檢查每個(gè)屬性是否正確;然后,產(chǎn)生一個(gè)allocation,并給客戶端回應(yīng)Allocate成功響應(yīng)。服務(wù)器在該成功響應(yīng)中攜帶一個(gè)LIFETIME屬性,本例中服務(wù)器將客戶端請(qǐng)求的1小時(shí)生命期減小為20分鐘,這是因?yàn)檫@個(gè)特定的服務(wù)器可能不允許超過(guò)20分鐘的生命期;該響應(yīng)包括XOR-RELAYED-ADDRESS屬性,值為該allocation的中繼傳輸?shù)刂罚辉擁憫?yīng)還包括XOR-MAPPED-ADDRESS屬性,值為客戶端的server-reflexive地址;該響應(yīng)也包含一個(gè)SOFTWARE屬性;最后,包括一個(gè)MESSAGE-INTEGRITY屬性來(lái)證明該響應(yīng),確保它的完整性。
<ignore_js_op style="overflow-wrap: break-word;">
</ignore_js_op>
接著,客戶端為了準(zhǔn)備向?qū)Χ薃發(fā)送一些應(yīng)用數(shù)據(jù)而創(chuàng)建一個(gè)permission。這里通過(guò)一個(gè)CreatePermission請(qǐng)求來(lái)做到。該請(qǐng)求攜帶XOR-PEER-ADDRESS屬性包含有確定的請(qǐng)求的IP地址,這里為對(duì)端A的地址;需要注意的是,屬性中地址的端口號(hào)被設(shè)置為0在CreatePermission請(qǐng)求中,并且客戶端使用的是對(duì)端A的server-reflexive地址而不是它的主機(jī)地址(私網(wǎng)地址);客戶端在該請(qǐng)求中攜帶與之前的Allocate請(qǐng)求中一樣的username、realm和nonce值,因此該請(qǐng)求被服務(wù)器認(rèn)可。此時(shí)在該請(qǐng)求中,客戶端沒(méi)有攜帶SOFTWARE屬性。
服務(wù)器收到該CreatePermission請(qǐng)求,產(chǎn)生一個(gè)相應(yīng)的許可,并以CreatePermission成功響應(yīng)來(lái)回應(yīng)。該響應(yīng)中只包含了Transaction-ID和MESSAGE-INTEGRITY屬性。
<ignore_js_op style="overflow-wrap: break-word;">
</ignore_js_op>
現(xiàn)在客戶端使用Send indication來(lái)發(fā)送應(yīng)用數(shù)據(jù)到對(duì)端A。對(duì)端的server-reflexive傳輸?shù)刂钒赬OR-PEER-ADDRESS屬性中,應(yīng)用數(shù)據(jù)包含在DATA屬性中。客戶端已經(jīng)在應(yīng)用層上執(zhí)行了路徑MTU發(fā)現(xiàn)功能,因此通過(guò)DON’T-FRAGMENT屬性來(lái)告知服務(wù)器當(dāng)通過(guò)UDP方式來(lái)向?qū)Χ税l(fā)送數(shù)據(jù)時(shí)應(yīng)設(shè)置DF位。Indications不能使用長(zhǎng)期證書機(jī)制來(lái)認(rèn)證,所以該消息中沒(méi)有MESSAGE-INTEGRITY屬性。
服務(wù)器收到Send indication后,提取出應(yīng)用數(shù)據(jù)封裝成UDP格式發(fā)給對(duì)端A;UDP報(bào)文的源傳輸?shù)刂窞橹欣^傳輸?shù)刂?,并設(shè)置DF位。
對(duì)端A回應(yīng)它自己的包含有應(yīng)用數(shù)據(jù)的UDP包給服務(wù)器。目的地址為服務(wù)器的中繼傳輸?shù)刂贰.?dāng)服務(wù)器收到后,將生成Data indication消息給客戶端,攜帶有XOR-PEER-ADDRESS屬性。應(yīng)用數(shù)據(jù)包含在DATA屬性中。
<ignore_js_op style="overflow-wrap: break-word;">
</ignore_js_op>
客戶端現(xiàn)在若要綁定一個(gè)通道到對(duì)端B,將指定一個(gè)空閑的通道號(hào)(本例中為0x4000)包含在CHANNEL-NUMBER屬性中,對(duì)端B的傳輸?shù)刂钒赬OR-PEER-ADDRESS屬性中。與以前一樣,客戶端再次利用上次請(qǐng)求中的username、realm和nonce。
當(dāng)服務(wù)器收到該請(qǐng)求后,服務(wù)器綁定這個(gè)對(duì)端的通道號(hào),為對(duì)端B的IP地址安裝一個(gè)permission,然后給客戶端回應(yīng)一個(gè)ChannelBind成功響應(yīng)消息。
<ignore_js_op style="overflow-wrap: break-word;">
</ignore_js_op>
客戶端現(xiàn)在發(fā)送一個(gè)ChannelData消息給服務(wù)器,攜帶有發(fā)送給對(duì)端B的數(shù)據(jù)。這個(gè)消息不是一個(gè)STUN消息,因此沒(méi)有事務(wù)ID。它之有3個(gè)字段:通道號(hào)、數(shù)據(jù)、數(shù)據(jù)長(zhǎng)度;服務(wù)器收到后,檢查通道號(hào)后發(fā)現(xiàn)當(dāng)前已經(jīng)綁定了,就以UDP方式發(fā)送數(shù)據(jù)給對(duì)端B。
接著,對(duì)端B發(fā)送UDP數(shù)據(jù)包回應(yīng)給服務(wù)器的中繼傳輸?shù)刂?。服?wù)器收到后,回應(yīng)給客戶端ChannelData消息,包含UDP數(shù)據(jù)包中的數(shù)據(jù)。服務(wù)器知道是給哪個(gè)客戶端發(fā)送ChannelData消息,這是因?yàn)槭盏降腢DP數(shù)據(jù)包中的目的地址(即服務(wù)器的中繼傳輸?shù)刂罚?,并且知道使用的是哪個(gè)通道號(hào),這是因?yàn)橥ǖ酪呀?jīng)與相應(yīng)的傳輸?shù)刂方壎恕?/p>
<ignore_js_op style="overflow-wrap: break-word;">
</ignore_js_op>
有時(shí)候,20分鐘的生命期已經(jīng)到了,客戶端需要刷新allocation。此時(shí)通過(guò)發(fā)送Refresh請(qǐng)求來(lái)進(jìn)行。該請(qǐng)求包含最后一次使用的username、realm和nonce,還包含SOFTWARE屬性。當(dāng)服務(wù)器收到這個(gè)Refresh請(qǐng)求時(shí),它注意到這個(gè)nonce值已經(jīng)超期了,則給客戶端回應(yīng)一個(gè)錯(cuò)誤響應(yīng)號(hào)為438(過(guò)期Nonce)的Refresh錯(cuò)誤響應(yīng),并提供一個(gè)新的nonce值??勺o(hù)端將重試該請(qǐng)求,此時(shí)攜帶新的nonce值。若第二次嘗試被接受,服務(wù)器將回應(yīng)一個(gè)成功響應(yīng)。需要注意的是,此時(shí)客戶端在請(qǐng)求中沒(méi)有攜帶LIFETIME屬性,所以服務(wù)器刷新客戶端的allocation時(shí)采用缺省的10分鐘生命期。
4、ICE簡(jiǎn)明介紹
4.1 簡(jiǎn)介
ICE的全稱Interactive Connectivity Establishment(互動(dòng)式連接建立),由IETF的MMUSIC工作組開發(fā)出來(lái)的,它所提供的是一種框架,使各種NAT穿透技術(shù)可以實(shí)現(xiàn)統(tǒng)一。ICE跟STUN和TURN不一樣,ICE不是一種協(xié)議,而是一個(gè)框架(Framework),它整合了STUN和TURN。
4.2 應(yīng)用模型
<ignore_js_op style="overflow-wrap: break-word;">
</ignore_js_op>
如上圖所示,如果A想與B通信,那么其過(guò)程如下:
- 1)A收集所有的IP地址,并找出其中可以從STUN服務(wù)器和TURN服務(wù)器收到流量的地址;
- 2)A向STUN服務(wù)器發(fā)送一份地址列表,然后按照排序的地址列表向B發(fā)送啟動(dòng)信息,目的是實(shí)現(xiàn)節(jié)點(diǎn)間的通信;
- 3)B向啟動(dòng)信息中的每一個(gè)地址發(fā)送一條STUN請(qǐng)求;
- 4)A將第一條接收到的STUN請(qǐng)求的回復(fù)信息發(fā)送給B;
- 5)B接到STUN回復(fù)后,從中找出那些可在A和B之間實(shí)現(xiàn)通信的地址;
- 6)利用列表中的排序列最高的地址進(jìn)一步的設(shè)備間通信。
由于該技術(shù)是建立在多種NAT穿透協(xié)議的基礎(chǔ)之上,并且提供了一個(gè)統(tǒng)一的框架,所以ICE具備了所有這些技術(shù)的優(yōu)點(diǎn),同時(shí)還避免了任何單個(gè)協(xié)議可能存在的缺陷。因此,ICE可以實(shí)現(xiàn)在未知網(wǎng)絡(luò)拓?fù)浣Y(jié)構(gòu)中實(shí)現(xiàn)的設(shè)備互連,而且不需要進(jìn)行對(duì)手配置。另外,由于該技術(shù)不需要為VoIP流量手動(dòng)打開防火墻,所以也不會(huì)產(chǎn)生潛在的安全隱患。
5、本文總結(jié)
在現(xiàn)實(shí)Internet網(wǎng)絡(luò)環(huán)境中,大多數(shù)計(jì)算機(jī)主機(jī)都位于防火墻或NAT之后,只有少部分主機(jī)能夠直接接入Internet。很多時(shí)候,我們希望網(wǎng)絡(luò)中的兩臺(tái)主機(jī)能夠直接進(jìn)行通信(即所謂的P2P通信),而不需要其它公共服務(wù)器的中轉(zhuǎn)。由于主機(jī)可能位于防火墻或NAT之后,在進(jìn)行P2P通信之前,我們需要進(jìn)行檢測(cè)以確認(rèn)它們之間能否進(jìn)行P2P通信以及如何通信。這種技術(shù)通常被稱為NAT穿透(NAT Traversal)。
RFC3489中定義的STUN,即簡(jiǎn)單地用UDP穿過(guò)NAT(STUN)是個(gè)輕量級(jí)的協(xié)議。它允許應(yīng)用發(fā)現(xiàn)它們與公共互聯(lián)網(wǎng)之間存在的NAT和防火墻及其他類型。它還為應(yīng)用提供判斷NAT給它們分配的公共網(wǎng)際協(xié)議(IP)地址。STUN可工作在許多現(xiàn)存NAT上,并且不需要它們做任何特別的行為。它允許廣泛的各類的應(yīng)用穿越現(xiàn)存的NAT設(shè)施。
RFC5389中對(duì)STUN協(xié)議進(jìn)行了修訂,將其定位于為穿透NAT提供工具,即NAT會(huì)話穿透效用是一個(gè)用于其他解決NAT穿透問(wèn)題協(xié)議的協(xié)議。它可以用于終端設(shè)備檢查由NAT分配給終端的IP地址和端口號(hào)。同時(shí),它也被用來(lái)檢查兩個(gè)終端之間的連接性,好比是一種維持NAT綁定表項(xiàng)的?;顓f(xié)議。STUN本身并不是一種完整的NAT穿透解決方案。它相當(dāng)于是一種NAT穿透解決方案中的工具。這是與先前的版本相比最重要的改變。之前的RFC3489中定義的STUN是一個(gè)完整的穿透NAT解決方案。此外,最大的區(qū)別是支持TCP穿透。
RFC5766中對(duì)STUN協(xié)議再次進(jìn)行了擴(kuò)展,即中繼穿透NAT:STUN的擴(kuò)展。TURN與STUN的共同點(diǎn)都是通過(guò)修改應(yīng)用層中的私網(wǎng)地址達(dá)到NAT穿透的效用,異同點(diǎn)是TUN采用了兩方通訊的“中間人”方式實(shí)現(xiàn)穿透,突破了原先STUN協(xié)議無(wú)法在兩臺(tái)主機(jī)不能夠點(diǎn)對(duì)點(diǎn)直接連接下提供作用的限制。
技術(shù)無(wú)止境,NAT穿透技術(shù)仍在不斷更新中,這里只對(duì)STUN/TURN協(xié)議作了簡(jiǎn)單的介紹,具體細(xì)節(jié)請(qǐng)參考RFC3489/5389/5766。
(原文鏈接:點(diǎn)此進(jìn)入,內(nèi)容有修改和刪節(jié))
6、更多文章
《TCP/IP詳解 - 第11章·UDP:用戶數(shù)據(jù)報(bào)協(xié)議》
《TCP/IP詳解 - 第17章·TCP:傳輸控制協(xié)議》
《TCP/IP詳解 - 第18章·TCP連接的建立與終止》
《TCP/IP詳解 - 第21章·TCP的超時(shí)與重傳》
《技術(shù)往事:改變世界的TCP/IP協(xié)議(珍貴多圖、手機(jī)慎點(diǎn))》
《通俗易懂-深入理解TCP協(xié)議(上):理論基礎(chǔ)》
《通俗易懂-深入理解TCP協(xié)議(下):RTT、滑動(dòng)窗口、擁塞處理》
《理論經(jīng)典:TCP協(xié)議的3次握手與4次揮手過(guò)程詳解》
《理論聯(lián)系實(shí)際:Wireshark抓包分析TCP 3次握手、4次揮手過(guò)程》
《計(jì)算機(jī)網(wǎng)絡(luò)通訊協(xié)議關(guān)系圖(中文珍藏版)》
《UDP中一個(gè)包的大小最大能多大?》
《Java新一代網(wǎng)絡(luò)編程模型AIO原理及Linux系統(tǒng)AIO介紹》
《NIO框架入門(一):服務(wù)端基于Netty4的UDP雙向通信Demo演示》
《NIO框架入門(二):服務(wù)端基于MINA2的UDP雙向通信Demo演示》
《NIO框架入門(三):iOS與MINA2、Netty4的跨平臺(tái)UDP雙向通信實(shí)戰(zhàn)》
《NIO框架入門(四):Android與MINA2、Netty4的跨平臺(tái)UDP雙向通信實(shí)戰(zhàn)》
《P2P技術(shù)詳解(一):NAT詳解——詳細(xì)原理、P2P簡(jiǎn)介》
《P2P技術(shù)詳解(二):P2P中的NAT穿越(打洞)方案詳解》
《P2P技術(shù)詳解(三):P2P技術(shù)之STUN、TURN、ICE詳解》
《高性能網(wǎng)絡(luò)編程(一):?jiǎn)闻_(tái)服務(wù)器并發(fā)TCP連接數(shù)到底可以有多少》
《高性能網(wǎng)絡(luò)編程(二):上一個(gè)10年,著名的C10K并發(fā)連接問(wèn)題》
《高性能網(wǎng)絡(luò)編程(三):下一個(gè)10年,是時(shí)候考慮C10M并發(fā)問(wèn)題了》