SIP協議詳解


 

1、SIP協議介紹

Internet的許多應用都需要建立和管理一個會話,會話在這里的含義是在參與者之間的數據的交換。由於考慮到參與者的實際情況,這些應用的實現往往是很復雜的:參與者可能是在代理間移動,他們可能可以有多個名字,他們中間的通訊可能是基於不同的媒介(比如文本,多媒體,視頻,音頻等)-有時候是多種媒介一起交互。人們創造了無數種通訊協議應用於實時的多媒體會話數據比如聲音,影像,或者文本。本SIP(會話初始協議)和這些協議一樣,同樣允許使用Internet端點(用戶代理)來尋找參與者並且允許建立一個可共享的會話描述。為了能夠定位精確的會話參與者,並且也為了其他的目的,SIP允許創建基礎的network hosts(叫做代理服務器),並且允許終端用戶注冊上去,發出會話邀請,或者發出其他請求。SIP是一個輕型的,多用途的工具,可以用來創建,修改和終止會話,它獨立運作於通訊協議之下,並且不依賴建立的會話類型。

 

2、SIP協議功能概況

SIP是一個應用層的控制協議,可以用來建立、修改、和終止多媒體會話(或者會議)例如Internet 電話。SIP也可以邀請參與者參加已經存在的會話,比如多方會議。媒體可以在一個已經存在的會話中方便的增加(或者刪除)。SIP顯示的支持名字映射和重定向服務,這個用於支持個人移動業務-用戶可以使用一個唯一的外部標志而不用關系他們的實際網絡地點。SIP在建立和維持終止多媒體會話協議上,支持5個方面:

1. 用戶定位: 檢查終端用戶的位置,用於通訊。

2. 用戶有效性:檢查用戶參與會話的意願程度。

3. 用戶能力:檢查媒體和媒體的參數。

4. 建立會話:”ringing”,建立會話參數在呼叫方和被叫方。

5. 會話管理:包括發送和終止會話,修改會話參數,激活服務等等。

   SIP不是一個垂直集成的通訊系統。SIP可能叫做是一個部件更合適,它可以用作其他IETF協議的一個部分,用來構造完整的多媒體架構。比如,這些架構將會包含實時數據傳輸協議(RTP)(RFC 1889)用來傳輸實時的數據並且提供QoS反饋,實時流協議(RSTP)(RFC 2326)用於控制流媒體的的傳輸,媒體網關控制協議(MEGACO)(RFC 3015)用來控制到公共電話交換網(PSTN)的網關,還有會話描述協議(SDP)(RFC 2327)用於描述多媒體會話。因此,SIP應該和其他的協議一起工作,才能提供完整的對終端用戶的服務。雖然基本的SIP協議的功能組件並不依賴於這些協議。

 

SIP本身並不提供服務。但是,SIP提供了一個基礎,可以用來實現不同的服務。比如,SIP可以定位用戶和傳輸一個封裝好的對象到對方的當前位置。並且如果我們利用這點來通過SDP傳輸會話的描述,立刻,對方的用戶代理可以得到這個會話的參數。如果我們用這個像傳輸會話描述(SESSION DESCRIPTION SD)一樣呼叫方的照片,一個”呼叫ID”服務很容易就建立了。這個簡單的例子說明了,SIP作為一個基礎,可以在其上提供很多不同的服務。

 

SIP並不提供會議控制服務(比如議席控制或者投票系統),並且並沒有建議會議應該則那樣管理。可以通過在SIP上建立其他的會議控制協議來發起一個會議。由於SIP可以管理參與會議的各方的會話,所以會議可以跨異構的網絡,SIP 並不能,也不打算提供任何形式的網絡資源預留管理。

 

 

安全對於提供的服務來說特別重要。要達到理想的安全程度,SIP提供了一套安全服務,包括防止拒絕服務,認證服務(用戶到用戶,代理到用戶),完整性保證,加密和隱私服務。

 

SIP可以基於IPV4也可以基於IPV6

 

3、術語

在這個文檔中,關鍵詞”必須”,”不允許”,”要求”,”可以”,”不可以”,”應該”,”不應該”,”建議”,”不建議”,”可能”,”可選” 是根據BCP14,RFC 2119[2]的規范描述SIP實現需要的不同層次

 

4、實施概覽

這節通過簡單的示例介紹了SIP的基本實現。本節是通過自然的而非正則的示例來介紹的。

 

   第一個例子說明了SIP的基本功能:定位一個斷點,發出通訊請求,通過協商會話參數建立會話,拆卸剛才建立的會話。

   圖一表示一個典型的Alice和Bob兩個用戶間的SIP消息交易交換例子.(每一個消息采用字母”F”和一個用來指向正文的一個數字做標記)在這個例子里,Alice在她的PC上使用一個SIP的應用程序(比如說一個軟的電話),呼叫Bob在Internet上的一個SIP電話。這個例子也掩飾了兩個SIP代理之間,怎樣為Alice和Bob建立會話連接。This typical arrangement is often referred to as the "SIP trapezoid" as shown by the geometric shape of the dotted lines in Figure 1.

 

Alice 通過Bob的SIP標志 “呼叫” Bob,這個SIP標志是統一分配的資源(Uniform Resource Identifier URI)稱作SIP URI。SIP URI在19.1節中定義。它很像一個email地址,典型的SIP URI包括一個用戶名和一個主機名。在這個范例中,SIP URI是sip:bob@biloxi.com,biloxi.com是Bob的SIP服務提供商。Alice有一個SIP URI: sip:alice@atlanta.com。 Alice可以輸入Bob的URI,也可以直接在地址本的一個超級鏈接上點擊一下Bob的URI。SIP也提供保密URI,稱作SIPS URI。例如:sips: bob@biloxi.com。 一個基於SIPS URI的通話保證這個通話是安全的,並且對呼叫者和被叫的所有的SIP消息是加密傳輸的(叫做TLS)。在TLS中,請求是通過加密方式傳輸給被叫方,但是這個加密機制是基於被叫方宿主服務器的實現的。

 

SIP是基於一個類似HTTP協議的請求應答的通訊模式。每一個通訊都包含對某個功能的請求,並且起碼需要一個應答。在這個應答中,Alice的軟電話發送一個含有Bbo的SIP URI地址的INVITE通訊請求。INVITE是一個SIP請求的例子,表示請求方(Alice)希望服務方(Bob)應答。INVTE請求包含一系列的包頭域(Header fields)。包頭中包含很多屬性並且包含了傳輸消息的附加信息。在INVITE中有如下的字段:呼叫的唯一標志,目的地址,Alice的地址,Alice和Bob建立會話的類型。INVITE請求(圖1中的F1)可能看起來像這樣的:

 

INVITE sip:bob@biloxi.com SIP/2.0

Via: SIP/2.0/UDP pc33.atlanta.com;branch=z9hG4bK776asdhds

Max-Forwards: 70

To: Bob <sip:bob@biloxi.com>

From: Alice <sip:alice@atlanta.com>;tag=1928301774

Call-ID: a84b4c76e66710@pc33.atlanta.com

CSeq: 314159 INVITE

Contact: <sip:alice@pc33.atlanta.com>

Content-Type: application/sdp

Content-Length: 142

(Alice’s SDP not shown)

 

 

atlanta.com . . . biloxi.com

.    proxy                 proxy        .

.                                         .

Alice’s . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .. . . . . . . . . . . . . Bob’s

softphone                                             SIP Phone

|                    |                    |                    |

| INVITE F1            |                    |                    |

|--------------->            | INVITE F2            |                    |

| 100 Trying F3        |--------------->            | INVITE F4            |

|<---------------            | 100 Trying F5        |--------------->            |

|                    |<--------------            | 180 Ringing F6        |

|                    | 180 Ringing F7        |<---------------            |

| 180 Ringing F8        |<---------------            | 200 OK F9            |

|<---------------            | 200 OK F10        |<---------------            |

| 200 OK F11        |<---------------            |                    |

|<---------------            |                    |                    |

|                            ACK F12                        |

|                    ------------------------------------------------->        |

|                        Media Session                        |

|<================================================>    |

|                            BYE F13                        |

|                    <-------------------------------------------------        |

|                        200 OK F14                            |

|                    ------------------------------------------------->        |

|                                                            |

圖一:SIP矩形表達的SIP會話建立例子。

 

在文本消息的第一行,包含了請求的類型(INVITE)。在這行之后的是這個請求的頭域。這個例子中包含了最少需要的頭域集合。簡單介紹一下:

 

VIA域包含了Alice接收發送請求的服務器地址(pc33.atlanta.com)。同樣這個包含了一個分支參數來標志Alice和這個服務器的會話事務。

 

TO域包含了顯示姓名(Bob)和一個SIP或者SIPS URI(sip:bob@biloxi.com)請求將首先傳輸到這個URI中。顯示姓名(Display names)在RFC 2822中描述。

From域也同樣包含一個顯示姓名(Alice)和一個SIP或者SIPS URI(sip:alice@atlanta.com)這個URI用來標志請求的原始發起者。

這個域也包含了一個TAG參數,這個TAG參數是一個隨機字串(1928301774),是軟電話(softphone)在URI上增加的一個隨機串。用來做標志用途的。

Call_ID包含一個全局的唯一標志,用來唯一標志這個呼叫,通過隨機字串和softphone的自己名字或者IP地址混和產生的。通過TO TAG, FROM TAG和CALL-ID完整定義了Alice和Bob之間的端到端的SIP關系,並且表示這個是一個對話性質的關系。

CSEQ或者Command Sequence包含了一個整數和一個請求名字。這個Cseq數字是順序遞增的。每當對話中發起一個新的請求都會引起這個數字的順序遞增。

Contact域包含一個SIP或者SIPS URI用來表示訪問Alice的直接方式,通常由用戶名和一個主機的全名(Fully Qualified Domain Name FQDN)組成。當FQDN作為首選的時候,許多終端用戶由於不會由名字登記(而導致不能訪問Alice的主機),所以IP地址是可選的。

VIA域告訴大家本請求發送到哪里並且應答到哪里,Contract域告訴大家將來的請求將發送到哪里(奇怪…不是Alice發起的么,將來的請求應該是Bob才對啊)。

Max-Forwards:最大轉發數量限制了通訊中轉發的數量。它是由一個整數組成,每轉發一次,整數減一。

Content-type包含了消息正文的描述(消息正文在本范例中沒有列出)

Content-length:包含消息正文的長度(字節數)

完整的SIP包頭域的定義在20節。會話的細節,比如媒體的類型,codec,或者采樣速率,沒有通過SIP來描述。這個可以通過SIP的消息正文來描述,可以通過其他定義的協議在正文中進行描述。有一種是會話描述協議(Session Description Protocol SDP)(RFC2327[1])。這個SDP消息(沒有在例子中列出)通過SIP的消息中傳送,就像通過附件發送EMAIL一樣,或者說通過HTTP傳輸的網頁一樣。

 

由於softphone並不知道bob或者bob的sip服務器biloxi.com在哪里,所以softphone發送INVITE請求到Alice的sip服務器,atlanta.com。這個atlanta.com SIP服務器應該已經在Alice的softphone中配置了,或者可以通過DHCP獲得。atlanta.com SIP服務器是一台代理服務器。代理服務器接收SIP請求並且根據請求轉發。在這個例子中,代理服務器接收到INVITE請求,並且回送一個100(Trying)應答給Alice的softphone。100(Trying)應答表示INVITE請求已經收到,並且代理服務器正在轉發INVITE請求。SIP的應答是通過一個三位數的數字表示的。SIP應答同樣包含TO、FROM、Call-ID,CSEQ和在VIA中的分支參數,這個參數使得Alice的softphone可以把請求和應答關聯起來。atlanta.com代理服務器收到INVITE請求之后,就去找biloxi.com可能通過DNS服務來找提供這個biloxi.com的SIP服務器。這在[4]中有描述。最后,轉發INVITE請求到biloxi.com或者能到達biloxi.com的代理服務器。在轉發請求之前,atlanta.com代理服務器會在via頭上增加一個一段包含自己地址的值(INVITE已經包含了Alice的的地址VIA域)。biloxi.com代理服務器收到這個INVITE請求並且返回一個100(Trying)應答給atlanta.com代理服務器標志這它已經收到這個請求並且正在處理這個請求。這個代理服務器通過查詢數據庫,通常叫做地址服務,這個服務中包含了bob的當前ip地址。(我們在下一節可以看到這個數據庫是怎么回事)biloxi.com代理服務增加另一段包含自己地址的VIA頭域並且發送它到bob的sip 電話。

 

Bob的SIP電話接收到INVITE請求並且提醒bob有一個從Alice的呼入,這樣bob可以決定是否響應這個呼入。這個意思就是:bob的電話響了。bob的sip電話發送一個180(Ringing)回應,這個回應將通過兩個代理服務器原路返回給Alice。每一個代理服務器通過via頭域決定該把這個應答發送給哪里,並且在發送之前把自己的地址從頭上拿走。雖然DNS和定位服務在路由最初的INVITE請求,180(ringing)響應可以簡單返回給發起者而不需要查找發起者在哪里,並且不需要在代理服務器保留狀態,同時,每一個轉發INVITE的代理也可以得到INVITE的每一個應答,這樣的特性也非常有用。

 

當Alice的softphone收到180(Ringing)應答的時候,它提示Alice,可能是通過一個回鈴音,或者屏幕上的一個消息提示。

 

在這個例子中,Bob決定響應這個呼叫。當他拿起電話,他的SIP電話發送200(OK)回應給發送者,表示這個電話已經接起來了。這個200(OK)包含了一個消息體,這個消息體包含SDP媒體描述,這個媒體描述包含Bob希望和Alice建立何種媒體連接。同樣,SDP消息也是兩段交換:Alice發送一個給Bob,Bob發送一個回給Alice。這個兩段的交換提供基本的兼容性協商,並且基於簡單的SDP提出/應答交換模型。如果Bob不想響應這個呼叫或者正在響應別的呼叫,一個錯誤的響應會代替正常的200(OK)回送出去,這樣,就不會有連接建立。SIP完整的返回代碼在21節有介紹。Bob發出的200(OK)(圖一的F9消息)可能長得像這樣的:

 

SIP/2.0 200 OK

Via: SIP/2.0/UDP server10.biloxi.com

;branch=z9hG4bKnashds8;received=192.0.2.3

Via: SIP/2.0/UDP bigbox3.site3.atlanta.com

;branch=z9hG4bK77ef4c2312983.1;received=192.0.2.2

Via: SIP/2.0/UDP pc33.atlanta.com

;branch=z9hG4bK776asdhds ;received=192.0.2.1

To: Bob <sip:bob@biloxi.com>;tag=a6c85cf

From: Alice <sip:alice@atlanta.com>;tag=1928301774

Call-ID: a84b4c76e66710@pc33.atlanta.com

CSeq: 314159 INVITE

Contact: <sip:bob@192.0.2.4>

Content-Type: application/sdp

Content-Length: 131

(Bob’s SDP not shown)

 

應答的第一行包含了應答代碼(200)和原因(ok)。剩下的行包含了包頭域。VIA,TO,FROM,CALL-ID,Cseq包頭域是從INVITE請求包中直接拷貝過來的。(有三個VIA域值-一個是Alice SIP電話增加的,一個是atlanta.com代理加的,一個是biloxi.com代理加的)。Bob的SIP電話增加了一個TAG參數。這個TAG參數會被參與對話的各方所使用,並且在以后的對話中被使用。Contract域包含了一個能直接聯系到Bob的URI。Content-type和Content-Length域包含了消息體(沒有在例子中體現),這個消息體里邊是Bob的SDP媒體信息。

 

除了DNS和位置服務之外,代理服務器可以自主決定路由,也就是說自己決定應該向哪里轉發請求。比如,如果Bob的SIP電話返回一個486(電話正忙)信號,biloxi.com這個代理服務器可以轉發這個INVITE請求到Bob的語音郵箱服務器。一個代理服務器可以同時向N個地方發送INVITE請求。這種並發尋找就是傳說中的分流(forking)。

 

 

在這個例子中,200(OK)應答通過兩個代理並且發送到Alice的softphone上,Alice的softphone收到這個應答,停止振鈴,並且標志電話Bob已經接聽。最后,Alice的電話發送一個確認消息,ACK,到Bob的SIP電話來確認接收到了這個最后的200(OK)應答。在這個例子中,ACK信號是直接由Alice的softphone發送到Bob的SIP phone上,跨過了兩個代理服務器。這是因為兩個端點(Alice和Bob)通過INVITE/200(OK)的請求應答包中的Contact包頭域都知道互相之間的地址了,這個地址是最開始發起INVITE請求的時候所不知道的。所以,不需要兩個代理服務器再查找對方的地址了,所以代理服務器不參與接下來的通話流了。這就完成了一個完整的使用INVITE/200/ACK 三方握手來建立SIP會話的過程。會話建立過程中的細節描述再13節由描述。

 

現在,Alice和Bob的媒體會話開始了,他們通過發送剛才建立會話所交換的SDP包中約定的互相明白的媒體包來進行會話。一般情況下,端到端的媒體包和SIP信號控制包通過不同的通訊路徑來發送。

 

在會話中,Alice或者Bob都可以改變他們自己的媒體會話屬性。這個可以通過發送一個包含新媒體屬性描述的re-INVITE請求來完成。這個re-INVITE是捆綁在一個現有的會話的,這樣參與會話的對方可以明白這是要改變現有的會話屬性而不是新建立一個會話。對方收到這個re-INVITE請求后,會發送一個200(OK)應答表示接受這個改變。請求方通過一個ACK來表示接受了對方的這個200(OK)應答。如果對方不同意這個媒體屬性變化,他會發送一個錯誤的應答比如488(暫時不能進行),這個也會收到發起者的一個ACK響應。不管怎樣,就是是re-INVITE的失敗也不會影響到現有的會話-原有的會話還可以用上次的媒體會話屬性繼續。可以在14節找到會話屬性更改的細節說明。

 

在通話結束的時候,Bob首先斷開(掛機hangs up),並且發送一個BYE的消息。這個BYE的消息將直接送到Alice的softphone,同樣是跳過代理的。Alice通過發送200(OK)應答來確認收到了這個BYE消息,這個消息終止了會話並且應答了BYE的請求。ACK在這里不需要發送-一個ACK信號只在響應一個INVITE的響應的時候被發送。我們稍晚一點會討論這個INVITE的特別處理,但是基於SIP的可靠性的機制,一個通話的時間可以認為包含電話振鈴和掛機的時間(but relate to the reliability mechanisms in SIP, the length of time it can take for a ringing phone to be answered, and forking.)基於這樣的原因,SIP請求的處理通常根據是否INVITE請求進行分類,INVITE類和非INVITE類請求分開處理。結束會話的細節可以在15節查到。

 

24.2節描述了圖1中使用的全部消息詳細解釋。在某些情況下,所有會話中的包都繼續通過代理轉發會很有用。比如,如果biloxi.com代理服務器希望在INVITE之后繼續保持SIP消息流,他會在INVITE中增加一個頭域(Record-Route)包含一個URI指向這個代理服務器的hostname或者IP地址。這個消息會被Bob的SIP電話和Alice的softphone所接到(因為Record-Route頭域將在200(OK)應答中被送回),並且在會話中一直保存。那么biloxi.com代理服務器就可以繼續接收和轉發ACK,BYE,給BYE的200(OK)應答。每一個代理都可以單獨決定是否接收INVITE以后的后續消息,並且這些后續消息都可以被發送到那些決定接收后續消息的代理服務器。這種情況通常發生在提供mid-call業務的代理服務器上。

 

登記服務是另一個常用的SIP操作。登記服務是biloxi.com代理服務器知道Bob當前地址的一個方法。在初始化的時候,或者每隔一段時間,Bob的SIP 電話發送REGISTER消息給biloxi.com的一個注冊服務器。REGISTER消息包含了Bob當前登陸服務器的SIP或者SIPS的URI(sip:bob@biloxi.com)(轉換成為Contact域中的SIP或者SIPS URI)。登記服務器登記這個映射,這個叫做綁定(binding),寫到一個數據庫里邊,叫做定位服務(location service),這個數據庫可以被biloxi.com的代理服務器使用。通常登記服務器和代理服務器是做在一起的。一個很重要的概念就是SIP服務器的差別在邏輯上,並非在物理上的差別。

 

Bob並沒有限定非得在一個單個設備上發起注冊。比如,他家里的SIP電話和公司的SIP電話都可以注冊。這些消息在定位服務(location service)中保存,並且允許代理服務器通過不同的手段查找Bob。同樣的,不同的用戶也可以在同一個設備上同時注冊。

 

定位服務(location service)是一個邏輯概念。他是讓代理服務通過輸入一個URI來查詢到底應該向哪里轉發請求。可以簡單通過用戶注冊來建立這個定位服務所需要的資料,也可以通過其他方法。可以通過其他任意的地址映射方式來實現定位服務。

 

最后在SIP中需要注意的是,注冊服務只是用來提供路由收到的SIP請求的,它並不做請求的身份認證的判定。在SIP中授權和認證可以通過建立在基於請求/應答的模式上的上下文相關的請求來實現,也可以使用更底層的方式來實現(具體在26節有描述)。

 

完整的注冊SIP消息描述例子在24.1節。

 

其他SIP的操作,比如檢查SIP服務器的負載,或者使用客戶端使用可選項(OPTIONS),或者用CANCEL取消一個未決的請求,在后續的章節中會介紹。

 

5、協議的結構

SIP是一個分層的協議,意思是說SIP協議由一組相當無關的處理層次組成,這些層次之間只有松散的關系。協議分成不同層次來描述是為了能夠更清晰的表達,在同一個小節里有功能的公共要素的交叉描述。本協議並沒有規定一個具體的實現。當我們說一個要素”包含”某一個層,我們的意思是這個要素復核這個層定義的規則。

 

不是SIP每一個要素都一定包含每一個層。此外,SIP定義的要素是邏輯上的要素,不是物理要素。一個物理的實現可以實現不同的邏輯要素,或許甚至是基於串行事務處理原理。SIP最底層的是它的語法和編碼層。編碼方式是采用擴展的Backus-Naur Form grammar(BNF范式)。完整的BNF描述在25節;第7節有簡要的SIP消息結構描述。

 

第二層是傳輸層。它定義了一個客戶端如何發送請求和接收應答,以及一個服務器如何接收請求和發送應答。所有的SIP要素都包含一個通訊層。第18節有通訊層的描述。

 

第三層是事務層。事務是SIP的基本組成部分。一個事務是客戶發送的一個請求事務(通過通訊層)發送到一個服務器事務,連同服務器事務的所有的該請求的應答發送回客戶端事務。事務層處理應用服務層的重發,匹配請求的應答,以及應用服務層的超時。任何一個用戶代理客戶端(user agent client UAC)完成的事情都是由一組事務構成的。有關事務的討論在第17節有描述。用戶代理包含一個事務層,來實現有狀態的代理服務器。無狀態的代理服務器並不包含事務層。事務層包含一個客戶元素(可以認為是一個客戶事務)和一個服務器元素(可以認為是一個服務器事務),他們都可以用一個有限狀態機來處理特定的請求。

 

在事務層之上是事務用戶(TU)。每一個SIP實體,除了無狀態代理,都是一個事務用戶。當一個TU發出一個請求,它首先創建一個客戶事務實例(client transaction instance)並且和請求一起發送,這包括了目標IP地址、端口號、以及發送請求的設備。TU可以創建客戶事務,也可以取消客戶事務。當客戶取消一個事務,它請求服務器終止正在處理的事務,並且回滾狀態到該事務開始前的狀態,並且產生指定的該事務的錯誤報告。這是由CANCEL請求完成的,這個請求有自己的事務,並且包含一個被取消的事務(第9節)。

 

SIP要素,包含,用戶代理客戶端和服務器,無狀態和有狀態代理服務器和注冊服務器,包含一個可以互相區別的核心(Cores)。Cores,除了無狀態代理服務器,都是事務用戶。UAC(用戶代理客戶端)和UAS(用戶代理服務端)的cores的行為依賴於實現,對所有的實現來說,有幾個公共的原則(第8節)。對UAC來說,這些規則約束請求的建立;對UAS來說,這些規則約束請求的處理和應答。由於注冊服務在SIP中是一個重要的角色,所以UAS處理REGISTER請求有一個特別的名字:登記員(registrar,登記服務器)。第10節描述了UAC和UAS的對REGISTER實現的core(核心)行為。第11節描述了OPTIONS的UAC和UAS的core實現,這個OPTIONS用來檢測UA的處理能力的(UA-user agent)。

 

 

小虎 2006-05-25 00:03

在對話中,有其他的相關會被發送。一個對話是一個持續一定時間的兩個用戶之間的端到端的SIP關系。對話過程要求兩個用戶代理之間的信息是有序的而且請求被正確路由傳輸的。在這個規范中,只有INVITE請求可以用來建立會話。當一個UAC在一個對話中發出請求的時候,它不僅遵循第8節描述的一般UAC規則而且也遵循對話中的請求規則。第12節講述了對話並且討論了對話的創建和維持,以及在對話中創建一個請求。

 

SIP中最重要的方法就是INVITE方法,它用來在不同的參與者中創建會話使用。一個會話由一組參與者,他們之間用於交流的媒體流組成。第13節講述了這些會話的創建初始化過程,以及創建一個或一組對話。第14節講述了在對話中使用INVITE請求來改變會話的屬性。最后,第15節,講述了如何終止會話。

 

第8、10、11、12、13、14、15節講述了完整的UA核心(第9節描述了取消,在UA核心和代理核心中使用)。第16節講數了代理服務器,代理服務器用於在兩個UA之間做消息路由使用。

 

6、協議的定義

以下講述的名次對SIP有着額外的意義:

Address-of-Record: 記錄地址。一個address-of-record(AOR)是一個SIP或者SIPS URI它指向了一個具有定位服務的主機,這個主機可以把URI映射成為用戶真正物理位置的URI。通常情況下,定位服務器是通過登記服務來建立的。一個AOR經常被認為是一個用戶的”公共地址”

Back-to-Back UserAgent:背對背的用戶代理(B2BUA)是一個邏輯實體,它就像用戶代理服務器(UAS)一樣接收和處理請求。為了決定該如何應答一個請求,B2BUA就像UAC一樣工作,並且發出請求。但是它不像代理服務器(proxy),它維持對話狀態,並且參與已經建立的對話中的每一個請求。由於它是直接的UAC和UAS的串連,所以,不需要對他有額外的定義。

 

Call:呼叫,一個呼叫是一個非正式的術語,它是指在端點之間一個一些通訊行為,通常用於建立多媒體對話。

Call Leg: 對話的別名;在本規范中沒有使用。

 

Call Stateful: 如果一個代理服務器(proxy)保存一個對話的狀態(從最開始的INVITE到對話終結的BYE),那么這個代理服務器就是請求有狀態的。一個請求有狀態(call stateful)的代理服務器也一定是事務有狀態的,但是事務有狀態的不一定是請求有狀態的。

 

Client:客戶端。一個客戶端是一個任意的網絡元素,它發出SIP請求和接收SIP應答。客戶端可能會也可能不會和人交互。用戶代理客戶端(UAC)和代理服務器都是客戶端。

 

Conference: 一個包含多個參與方的多媒體會話(見后)。

 

Core:核心。核心定義了SIP實體的特定類別。比如定義了一個有狀態和無狀態的代理服務器,一個用戶代理或者注冊服務器(registrar)。所有的核心,除了無狀態代理服務器,都是事務用戶。

 

Dialog:對話,一個對話是持續一段時間的兩個UA之間的端到端的SIP關系。一個對話由SIP消息建立,就像用2xx響應INVITE請求。我們用Call identifier,local tag(本地tag),remote tag(對方tag)來標志一個對話,一個對話在RFC 2543中被正式叫做CALL LEG.

 

Downstream: 它是事務中的消息傳遞方向。它特指從UAC到UAS的請求流的方向, 

 

Final Response:終結響應。一個響應終端SIP事務的應答,和事務中間的臨時響應相反。所有的2xx,3xx,4xx,5xx,6xx響應都是終結響應。

 

Header:頭。頭域是在SIP消息頭部用來描述這個SIP消息信息的部分。它由一堆頭域字段組成。

 

Header Field:頭域字段。頭域字段是在SIP消息頭域的字段。一個頭域字段可以由多個頭域字段行組成。一個頭域字段由一個頭域名和(零個或多個)頭域值組成。多個頭域值用’,’分割。某些頭域字段只能有單個值,比如結果域(result)就只能有一個值。

 

Header Field Value:頭域值。一個頭域值是一個單個的值,一個頭域字段可以有0個或者多個頭域值。

 

Home Domain:宿主機。一個提供SIP服務的主機。一般指的是在登記服務中指明的記錄地址中的URI的主機。

 

Informational Response:提示應答。和臨時應答一樣。

Initiator, Calling Party, Caller: 用INVITE初始一個會話(和對話)的那方。一個caller從發出INVITE請求建立對話開始,到對話終止都一直是這個角色。

 

Invitation: 一個INVITE請求。

 

Invitee,Invited User,Called Party, Callee:被叫方。收到INVITE請求並且建立會話的那方。一個被叫方從收到INVITE請求起,到終止INVITE建立的對話結束,都稱作被叫方。

 

Location Service: 定位服務。定位服務是用來給SIP轉發或者代理服務器確定被叫方可能的位置使用的。它包含一張綁定了address-of-record的表,被叫方可能有0到多個記錄。綁定的記錄可以通過多種渠道添加和刪除;本規范定義了REGISTER方法來更新綁定表。

 

Loop:環路。當請求抵達一個代理服務器,代理服務器轉發這個請求,當這個請求再次來到同一個代理服務器,就稱之為環路。當第二次抵達的時候,Request-URI中包含了上次抵達的資料,並且由於並沒有什么東西可以改變轉發的策略,這樣就導致這個請求還會再次被轉發回來。環路請求是錯誤的,所以,處理程序需要檢測和防止協議中出現的環路請求。

 

Loose Routing:丟失路由。代理服務器在下述情況下會丟失路由。

A proxy is said to be loose routing if it follows the procedures defined in this specification for processing of the Route header field. These procedures separate the destination of the request (present in the Request-URI) from the set of proxies that need to be visited along the way (present in the Route header field). A proxy compliant to these mechanisms is also known as a loose router.

 

Message:消息。SIP元素之間傳送的協議數據就是消息。SIP消息既可以是請求也可以是應答。

 

Method:方法。方法是在服務器請求處理的主要功能。方法是請求消息自身攜帶的。典型的方法就是INVITE和BYE。

 

Outbound Proxy:對外代理服務器。一個代理服務器接收到客戶的請求,即使它不是由Request_URI所決定的服務器。通常一個UA會手工配置一個對外的代理服務器,或者可以通過一個自動配置的協議自動配置一個。

 

Parallel Search: 並行搜索。並行搜索情況下,代理服務器會向多個用戶可能存在的地方發起請求,並且等待應答。同串行搜索不同的地方是,並行搜索不會等待上一個請求應答回來之后再發起下一個搜索,而是一個接一個的發起搜索請求。

 

Provisional Response: 臨時應答。服務器用來標志自己正在處理的應答,但是本應答並不結束一個SIP事務。1xx應答就是臨時的,其他應答標志着事務的結束。

 

Proxy,Proxy Server:代理、代理服務器。一個中間的實體。它本身即作為客戶端也作為服務端,為其他客戶端提供請求的轉發服務。一個代理服務器首先提供的是路由服務,也就是說保證請求被發到更加”靠近”目標用戶的地方。代理服務器對某些強制政策有用(比如,確認一個用戶是否允許建立一個呼叫等)。一個代理服務器翻譯,並且,如果有需要的話,再轉發前會重寫請求消息。

 

Recursion:回路、遞歸。一個客戶端,在響應請求的時候產生新的到Contract包頭域的URI請求的時候,會在3xx響應中陷入遞歸。A client recurses on a 3xx response when it generates a new request to one or more of the URIs in the Contact header field in the response.

 

Redirect Server:重定向服務器。一個重定向服務器是一個產生3xx應答的UAS服務器,指示客戶端連接別的URI。

 

Registrar: 登記員。一個登記員(登記服務器)是一個接收REGISTER請求得服務器。他把請求得信息放到定位服務器中,這樣可以讓定位服務器很方便得查找位置信息。

 

Regular Transaction:常規事務。凡不包含INVITE,ACK,或者CANCEL方法得事務就是常規事務。

 

Request: 請求。 一個由客戶端發到服務端得SIP信息,用於執行特定得功能。

 

Response:應答。一個由服務端發到客戶端得SIP信息。用來標志從客戶端發往服務端得請求處理得情況得。

 

Ringback: 回鈴音。回鈴音是一個信號音。是給呼叫方得一個信號表示被叫方正在振鈴(Ringing)。

 

Route Set: 路由集。路由集合是一個順序得SIP或者SIPS URI。這些URI描述了傳遞一個請求所必須經歷得代理列表。一個路由集可以是自適應得,因為包頭中包含了Record-Route(記錄路由),也可以是依賴配置得到得。

 

Server:服務器。一個server是一個網絡元素接收請求並且處理請求並且發送回應給請求方。典型得服務器就是代理服務器(proxies),用戶代理服務器(user agent servers),重定向服務器,登記服務器。

 

Sequential Search:順序查找。在順序查找中,代理服務器順序嘗試聯系地址,在處理下一個之前必須等待上一個請求已經有一個結束應答。一個2xx或者6xx系列得最終應答總是結束一個順序查找。

 

Session:會話。根據SDP得描述:”一個多媒體會話是一個由多媒體發送方和接受方組成得集合,並且包括在發送方和接受方之間得數據流。一個多媒體會議是一個典型得多媒體會話。”(RFC 2327[1])(一個session在SDP訂一下可以是一個或者多個RTP sessino)。在定義中,一個被叫方可以被多次邀請,被不同得呼叫方邀請,到同一個會話。在SDP中,一個會話可以被SDP用戶名,session id,網絡類型,地址類型,地址元素得一個集合串所規定。

 

SIP 事務:一個SIP事務是在客戶端和服務端得事件,包括了從第一個由客戶端發送到服務端得請求,到最后一個(非1xx)服務端向客戶端發出得終結應答。如果請求是一個INVITE請求,並且終結應答是一個非2xx得應答,那么事務還包括一個ACK給服務器做應答。給INVITE請求的2xx應答的ACK回應,是一個獨立的事務。

 

Spiral:回溯。一個回溯是指一個SIP請求,路由給一個proxy,並且轉發,但是又被路由回這個proxy,但是不同於回路(遞歸)的是,這次路由回來的請求包的包頭中,包含了不同於原請求的請求包部分,使得本次proxy決定的路由轉發與上次不同。通常,這是說,請求的Request-URI不同於上次的Request_URI。一個回溯不是一個錯誤,不同於回路(環路loop)。通常導致這樣的現象是呼叫轉發(call forwarding)。一個用戶呼叫joe@example.com。example.com代理服務器轉發請求到Joe的PC,並且Joe的pc呼叫轉移到bob@example.com。這個請求被轉發回example.com代理服務器。可是這個並不是一個環路(loop)。因為請求的目的地址變成了另一個用戶,這就是回溯,是一個合法的情況。

 

Stateful Proxy:有狀態的代理服務器。在邏輯上,有狀態的代理服務器就是處理一個請求的過程中,維持的一個本規范所定義的客戶端和服務端的事務狀態機。也是一個事務又狀態代理服務器(transaction stateful proxy)。具體的stateful proxy在第16節定義。一個(事務)有狀態代理服務器和一個call stateful proxy不是一回事。

 

Stateless Proxy:無狀態的代理服務器。在邏輯上,無狀態代理服務器在處理請求中,並不維持客戶和服務端的事務狀態機。一個無狀態的代理服務器直接轉發每一個接收到的請求和每一個接收到的響應。

 

Strict Routing:嚴格路由。路由處理規則如果復核RFC2543協議(and many prior work in progress versions of this RFC) 就是一個嚴格路由。在這個規則下,如果在包頭中包含Route域,那么代理服務器就會刪除Request_URI域內容。本文檔並不要求一定要有嚴格路由,本文檔只要求松散路由就可以了。支持嚴格路由的代理服務器也叫嚴格路由器。

 

Target Refresh Request: 目標刷新請求。一個Target Refresh Request是一個在對話中發出的請求,用來更改對話目標的請求。

 

Transaction User(TU):事務用戶。在transaction 層之上的協議層。TU包括了UAC 核心,UAS core,和proxy core。

 

 

Upstream:上行流。一個在事務中的消息流向方向。它是指由用戶代理服務器(UAS)發出應答到用戶代理客戶端(UAC)的消息流向方向。

 

URL-encoded:一串根據RFC2396-2.4節編碼的字符。

 

User Agent Client(UAC):用戶代理客戶端。用戶代理客戶端是一個邏輯的概念,他創建一個新請求,並且用客戶事務狀態機發送這個請求。UAC角色只在事務中存在。換句話說,UAC就是一小段代碼初始化一個請求,並且在事務中遵循UAC的規則。如果它接下來收到一個請求,那么在那個事務中,它就是作為UAS來處理請求。

 

UAC Core:UAC核心。在transaction和transport層之上得UAC實現的功能集合。

 

User Agent Server(UAS): 用戶代理服務器.UAS是一個邏輯的實體,對SIP請求做響應的。應答接受、拒絕、或者轉發對應的請求。UAS角色在事務中存在。換句話說,是響應請求的一小段軟件,在事務中作為UAS存在。如果他發出請求,那么他就在事務中作為UAC存在。

 

UAS Core:UAS核心。在transaction和transport層智商的UAS實現的功能集合。

 

User Agent(UA)。一個邏輯實體的概念,包含UAC和UAS。

UAC和UAS,就像代理服務器和轉發服務器,是在事務by事務的原理(串行事務處理)上定義的。例如,當發出一個初始化INVITE請求的時候,UA作為UAC初始化一個呼叫動作,當從被叫方接收到一個BYE請求的時候,UA作為UAS響應。類似的,同樣的代碼可以對一個請求做為proxy服務器處理,對另一個請求作為重定向服務器。

 

proxy,location,registrar服務器都是邏輯實體,在它們的實現中,可能是作為單個應用實現的。

 

7、SIP消息:

SIP協議是一個基於文本的協議,使用UTF-8字符集(RFC2279[7])。

一個SIP消息既可以是一個從客戶端到服務器端的請求,也可以是一個從服務器端到客戶端的一個應答。

即使在字符集上和語法細節上有所不同,請求(7.1)還是應答(7.2)消息都基於RFC2822格式的。(SIP允許包頭域不是標准的RFC2822包頭域)。這兩種消息類型都由一個起始行,一個或者多個包頭域,一個可選的消息中文組成。

 

一般消息=         起始行

*消息包頭

CRLF

[消息正文]

起始行=            請求行/狀態行

 

起始行、每一個包頭行,空行、都必須由回車換行組成(CRLF)。即使消息中文沒有,也必須有一個空行跟隨。

除了在字符集上的區別以外,很多SIP的消息和包頭域的格式都同HTTP/1.1一樣。我們在這里就不重復它的語法和語義了,我們用[HX.Y]來標志HTTP/1.1規范(RFC2616[8])的X.Y節的描述。

SIP並非一個HTTP的超集或者擴展。

7.1 請求

SIP請求是根據起始行中的Request-Line來區分的。一個Request_line包含方法名字,Request-URI,用單個空格(SP)間隔開的協議版本。

Request-Line由CRLF結束。除了用作行結束標志以外,不允許CR或者LF出現在其他地方。在其他域中,不允許出現線形的空白(liner whitespace LWS)

 

Request-Line    =    Method SP Request-URI SP SIP-VERSION CRLF

Method: 這個規范規定了6中方法:REGISTER用於登記聯系信息,INVITE,ACK,CANCEL用於建立會話,BYE用於結束會話,OPTIONS用於查詢服務器負載。SIP擴展、標准RFC追加可能包含擴展的方法。

Request-URI: Request-URI是一個SIP或者SIPS URI,他們在19.1節由描述。也可以是一個通用的URI(RFC 2396[5])。它標志了這個請求所用到的用戶或者服務的地址。Request-URI禁止包含空白字符或者控制字符,並且禁止用”<>”括上。

SIP 元素可以支持除了SIP或者SIPS之外所規定的Request-URIs。比如”tel” URI模式(RFC 2806[9])。SIP元素可以用他們自己的機制來轉換non-SIP URIs到SIP URI,SIPS URI或者其他什么格式的URI。

SIP-Version:請求和應答消息都包含當前使用的SIP版本,這個遵循[H3.1](類似HTTP用SIP替代,用SIP/2.0替代HTTP/1.1)中關於版本的規定,版本依賴,升級版本號。一個應用,發出的SIP消息一定包含了SIP-Version “SIP/2.0”。這個SIP版本串是大小寫不銘感的,但是在實現中必須發送大寫。

不像HTTP/1.1,SIP把版本號當作一個文本串處理。在實現上,這個應該不會有區別。

7.2應答

SIP應答和SIP請求的區別在於在START-LINE中是否包含一個STATUS-LINE。一個status-line在由數字表達的status-code之前,是一個協議的版本串,每一個元素之間用一個單個SP(空格)分開。

除了最后用作結束標志以外,CR/LF不允許出現在其他地方。

status-line    = SIP-VERSION SP STATUS-CODE SP Reasong-Phrase CRLF

Status-Code 是一個3位的數字result code,用來標志處理請求的一個結果。Reason-Phrase是一個簡短的Status-Code的說明。Status-Code是為了能自動處理使用的,但是Reason-Phrase是用來給用戶看得。一個客戶端並不要求一定要顯示或者解釋這個Reason-Phrase。本文檔建議輸出這個reason-phrase,實現中可以選擇其他文本,比如用請求包頭中指定的合適語言來顯示。

status-code的第一個數字表示了應答的類型。接下來兩個數字並不作分類使用。基於這個原因,任何status code在100到199的可以統稱位”1xx應答”,類似的,在200到299的可以統稱位”2xx應答”,依此類推。SIP/2.0允許6類應答:

1xx:臨時應答-請求已經接收,正在處理這個請求。

2xx:成功處理-請求已經成功接收,並且正確處理了這個請求。

3xx:重定向-還需要附加的操作才能完成這個請求,本請求轉發到其他的服務器上處理。

4xx:客戶端錯誤--請求包含錯誤的格式或者不能在這個服務器上完成。

5xx:服務器錯誤-服務器不能正確的處理這個顯然合法的請求。

6xx:全局錯誤-請求不能被任何服務器處理。

21節定義了詳細的代碼說明。

7.3 頭域

SIP頭域和HTTP頭域在語法和語義上都比較類似。特別的,SIP頭域遵循[H4.2]關於消息頭的語法的定義,並且遵循多行的擴展頭域的規則。(后者 is specified in HTTP with implicit whitespace and folding)。這個規范遵循RFC2234[10],並且把清晰的空白和封裝作為內在的語法規則。

[H4.2]也定義了具有相同域名的多個頭域,他們的值是用逗號分開的列表,可以合並到一個頭域中。這個也適用於SIP,但是細節上略有不同,因為語法不同。實際上,任何SIP的包頭語法都是基於如下范式的:

header = “ header-name” HCOLON header-value *(COMMA header-value)

這個允許合並在具有同一個域名的多個頭域,到一個用逗號分割的單個頭域中。Contact頭域除了當域值是”*”之外,都允許用逗號分割的列表。

7.3.1 頭域格式。

 

 

頭域遵循在RFC2822的2.2節定義的通用頭域格式。每一個頭域都由一個域名加上冒號(”:”)和域值組成。

       field-name:field-value

消息頭的正則語法在25節中有介紹介紹。

在消息頭中,允許在冒號的左右有任意個數的空白;但是,在實現中,建議避免域名和冒號中間有空格,並且建議在冒號和值之間使用單個空格(SP)。

       Subject:       lunch

       Subject   :   lunch

       Subject       :lunch

       Subject: lunch

所以,上面的都是合法的,也是相等的,但是最后一種是我們所推薦的。

頭域可以擴展成為多行的,只要在每一個附加行前邊用至少一個SP或者水平TAB(HT)打頭就可以了。這種多行的頭域在行結尾並且在下一行之前的空白SP(或者HT)將被認為是一個單個的SP字符。所以,下邊的例子是相等的:

       Subject: I know you’re there, pick up the phone and talk to me!

       Subject: I know you’re there,

           pick up the phone,

           and talk to me!

頭域中的不同域名的相關順序並沒有什么意義。雖然如此,我們還是強烈建議與路由相關的域(VIA,ROUTE,Record-Route,Proxy-Require,Max-Forwards,Proxy-Authorization等等)放在消息頭的最前邊,這樣可以提高處理的速度。相同域名的域之間的順序非常重要。只有當單個頭域的域值是可以用逗號分割的列表的時候,才可以表達成為同一個域名的多個頭域(這就是說,如果遵循7.3定義的語法)。也就是意味着必須可以將同一個域名的多個頭域在不改變消息語義的前提下,改換表達成為一對”域名: 域值”;這個轉換是通過順序增加每一個域的域值,域值之間用逗號分割。這個規則有幾個例外,就是WWW-Authenticate,Authorization,Proxy-Authenticate,和Proxy-Authorization頭域。多個頭域行可以在消息頭中出現,但是由於他們的語法並不遵循7.3中定義的通用格式,所以,他們並不能合並成為單個頭域行。

在實現上,必須能夠既能夠處理多個頭域行的情況,也必須能夠處理用逗號分割的合並的單個頭域行的情況。

下邊的幾組頭域是相等的:

   Route: <sip:alice@atlanta.com>

   Subject: Lunch

   Route: <sip:bob@biloxi.com>

   Route: <sip:carol@chicago.com>

   

Route: <sip:alice@atlanta.com>, <sip:bob@biloxi.com>

   Route: <sip:carol@chicago.com>

   Subject: Lunch

 

   Subject: Lunch

   Route: <sip:alice@atlanta.com>, <sip:bob@biloxi.com>

           <sip:carol@chicago.com>

 

下邊各組是合法的,但是並不相等。

Route: <sip:alice@atlanta.com>

Route: <sip:bob@biloxi.com>

Route: <sip:carol@chicago.com>

 

Route: <sip:bob@biloxi.com>

Route: <sip:alice@atlanta.com>

Route: <sip:carol@chicago.com>

 

Route: <sip:alice@atlanta.com>,<sip:carol@chicago.com>,<sip:bob@biloxi.com>

 

每一個頭域值的格式是依賴於它的頭域名的。他可以是任意順序的TEXT-UTF8字符,也可以是一個空格,標記,分隔符,引號括起來的字串的組合。很多頭域都回附帶一個通用的域值格式。這個域值格式是由分號分開的參數名和參數值的組合:

   field-name: field-value *(;parameter-name=parameter-value)

雖然在域值里邊可以有任意數量的parameter-name/parameter-value對,但是不能允許有相同的parameter-name存在(唯一性)。除了特別指出的頭域之外,頭域中的域名、域值、parameter name parameter-value都是大小寫不敏感的。標記詞始終是大小寫不銘感的。除非有特別的指定,引號串的字符串是大小寫敏感的。例如:

   Contact: <sip:alice@atlanta.com>;expires=3600

   CONTACT: <sip:alice@atlanta.com>; ExPiReS=3600

相同。

   Content-Disposition: session;handling=optional

   content-disposition: Session;HANDLING=OPTIONAL

相同。

下邊的兩個頭域不相同:

   Warning: 370 devnull “Choose a bigger pipe”

   Warning: 370 devnull “CHOOSE A BIGGER PIPE”

 

 

小虎 2006-05-25 00:03

7.3.2 頭域分類。

有一些頭域是僅僅在請求(或者應答)中有效的。這些頭域叫做請求頭域或者應答頭域。如果消息中的頭域與這個消息的類型不匹配(比如在應答消息中出現的請求頭域),這個頭域必須被忽略。20節定義了每一個頭域的分類。

7.3.3 縮寫格式

SIP提供了一個用縮寫格式來表達通用頭域名字的機制。這個有助於避免消息過大而導致通訊層無法傳輸(比如在UDP傳輸的時候超過了最大傳輸單元(MTU))。這個縮寫格式在20節定義。

縮寫格式的消息頭域名字可以在不改變消息語義的情況下替代較大的消息頭域名字。在單個消息中,頭域名字既可以用長的格式,也可以用縮寫格式。在實現中,必須同時支持對長名字和縮寫名字的處理。

7.4包體

請求信息,包括這個規范以后的擴展的新請求,都可以包含一個消息正文體。對消息正文體的解釋依賴域請求的方法(請求類型)。對於應答消息來說,請求方法和應答狀態(response status code)決定了消息正文體的格式。所有的應答消息都可以有一個消息正文體(body)。

7.4.1 消息正文類型(MessageBodyType)

消息中的internet媒體類別必須在Content-Type頭域中指明。如果消息正文(body)通過某種形式的編碼(encoding),比如壓縮等等,都必須在Content-Encoding 頭域中指明,否則Content-Encoding域必須忽略。如果可行,消息體的字符集作為Content-type頭域的值的一部分表達。

 

在RFC2046[11]中定義的多部分”multipart” MIME類型可以在消息體中應用。在由多部分組成的消息體發送的時候,如果接受方的實現中,包頭域的Accept域中,不包含多部分的標記,那么發送方必須發送一個非多部分的session description。 

SIP消息可以包含二進制的包體或者部分包體。如果發送方沒有其他顯示的字符集參數指出,媒體的文本”text”子類型會是缺省的字符集”UTF-8”。

7.4.2 消息體長度

在Content-Length頭域中存放了包體的字節長度。第20.14節講述了本域的詳細解釋。

HTTP/1.1的“chunked”傳輸編碼方式並不適用於SIP。(備注:chuncked編碼傳輸方式是通過把消息正文體分為一系列的塊來傳輸的,每一塊有它自己的大小標記) 

 

7.5 分幀的SIP消息(Framing SIP Messages)

不同於HTTP的是,SIP實現可以使用UDP或者其他非可靠傳輸協議。每一幀包括一個請求或者應答。第18節講述了非可靠傳輸的應用。

在處理以面向流的通訊為基礎的SIP消息的時候,必須忽略在開始行之前的CRLF[H4.1]。

Content-Length頭域用來確定每一個SIP消息在通訊流中的結束位置的。在基於面向流通訊基礎上的SIP消息一定要使用這個頭域。

 

8 一般用戶代理行為

一個用戶代理代表了一個終端系統。它包含一個用戶代理客戶端(UAC),用來產生請求的,它包含一個用戶代理服務端(UAS),用來響應請求的。UAC可以由一些外部的東西來發出請求和處理應答(比如用戶按了一個按鈕,或者按下了一個電話鍵產生了一個音頻信號等等)。UAS是一個能夠接收請求,並且產生應答的東西,它可以根據用戶輸入,外部輸入,程序執行結果或者其他什么機制來產生應答。

 

當一個UAC發送一個請求,這些請求可能通過一些PROXY(代理服務器)傳遞到UAS上。當UAS產生一個應答,那么這個應答就會同樣的被傳送到UAC。UAC和UAS的處理由兩個特點。第一,基於請求或者應答是否在一個對話里,第二,基於請求的方法(method)。會話的徹底描述在第12節;哪里描述了點對點的用戶代理之間的關系,並且通過一些SIP方法建立了會話,比如INVITE方法等。

 

在本節,我們將討論在處理對話外的請求時,UAC和UAS的方法無關的規則。這些當然也包括用於建立會話的請求。在26節講述了對在對話外的請求和應答的安全處理。特別時,UAS和UAC之間的互相認證的機制。通過用S/MIME加密的消息體可以提供有限的隱私保證。

8.1 UAC特性

本節講述UAC在會話外的特性。

8.1.1 產生一個請求

一個合法的SIP請求必須至少包含如下頭域:TO,FROM,Cseq,Call-ID,Max-Forwards, Via;這些字段在所有SIP請求中必須包含。這6個字段是SIP消息的基本組成部分,他們提供了用於路由用的核心信息,包含了消息的地址,響應的路由,消息傳遞次數,詳細的順序,事務的唯一標志。

這些頭域字段是必須包含在請求行之后的,請求行包含了請求的方法,Request-URI,SIP的版本號碼。

有兩個在對話外的發送請求的示例(通過INVITE請求建立連接,第13節),(通過OPTIONS請求查詢負載,第11節)。

8.1.1.1 Request-URI

最開始的Request-URI頭域應該是TO頭域的的值。但是在REGISTER方法中,有一個值得注意的不同;REGISTER方法的Request-URI頭域在第10節中指出。出於隱私的原因而把這些字段的值設置成為同一個值並不太合適(尤其是如果初始的UA期望Request-URI會在傳輸中改變的話)。

在一些特定的情況下,預先設置的路由表(route-set)會影響消息中的Request-URI。一個預置路由表是由一串server的URI組成,這些服務器是UAC往外發送會話外請求所需要經過的。通常,他們是由用戶或者服務提供商手工在UA上設置的,或者通過一些非SIP的方法自動設置。當一個提供商希望配置一個出口proxy給一個UA,我們強烈建議通過一個預置一個單個URI路由表的方式來實現,這個單個路由就是出口proxy。

當要使用預置路由表(route set),必須提供Request-URI和Route頭域(在12.2.1.1節中有詳細描述)(甚至在沒有對話存在的時候也必須提供),並且把Request-URI當作遠端目標URI。

8.1.1.2 TO

To頭域是第一個並且也是最先指定請求的”邏輯”接收地,或者是這個請求的用戶或者資源的address-of-record。這個域內的地址可以是也可以不是請求的最終接收者。TO頭域可以用SIP或者SIPS URI,也可以用其他方式的URI(比如電話URL (RFC2806[9]))。所有的SIP實現必須支持SIP URI的實現。任何支持TLS的實現必須支持SIPS URI的實現。

To頭域允許有一個顯示用的姓名。

UAC可以通過無數的方法來知道在一個給定請求的時候該如何填寫TO頭域。通常用戶會建議采用人工界面中輸入的To頭域,可能手工輸入這個URI或者從地址本中選擇(就好像outlook郵件中的to一樣)。用戶通常不會輸入完整的URI,可能只是一個簡單的字串(比如”bob”)。這就要求UA能夠判斷用戶輸入的這個到底是那個URI。一般使用用戶輸入的字串加上”@”標志和主機的名字組合成為SIP URI(比如sip:bob@example.com)。如果希望通訊在保密機制下進行,那么就用用戶輸入的字串組成SIPS URI的部分,用戶輸入的將加上”@”和主機的名字作為整個SIPS URI。這個主機的名字通常是請求方的主機名字,這個主機允許處理外發請求。這個很像”縮位撥號”的機制,這個機制要求請求者自身的主機能夠解釋這個縮位撥號一樣。

如果UA不希望指定主機,那么就需要將用戶輸入的電話號碼解釋成為一個電話的URL。相當於,每一個請求經過的主機都會有機會來處理這個請求。比如,一個用戶在機場可能登陸機場的代理服務器,並且通過機場的代理服務器發出一個請求。如果他輸入”411”(美國本地電話本查詢服務號碼),這個就需要機場的外發的代理服務器進行解釋和處理,而不是解釋成有主機的用戶。在這里,tel:411是一個正確的解釋。

在會話外的請求中,不能包含To tag字段,在to頭域中的tag是用來在對話中做標志的。既然對話還沒有建立,那么tag就不能存在。

20.39節有進一步的描述。

下邊這個例子是一個To頭域的例子:

To: Carol <sip:carol@chicago.com>

8.1.1.3 From

From頭域包含了請求發起者的邏輯標志,可能是用戶的address-of-record。就像To頭域一樣,From頭域也包含一個URI並且可以包含一個顯示的姓名。SIP可以用這個頭域來實現對請求的檢查和選擇一個規則進行對請求的處理(比如,自動的呼叫拒絕,凡是x人發過來的東西,一律無視)。同樣的,因為From頭域包含的是邏輯名字,所以From URI也可以不包含IP地址或者UA對應的主機名字FQDN。

From頭域可以包含一個顯示姓名。在客戶身份隱藏的情況下,一個UAC應該使用顯示名字”Anonymous”,連通一個語法正確,但是沒有意義的URI(比如:sip:thisis@anonymous.invalid)。通常,用戶或者用戶的本地主機的管理人員會事先規定請求頭域中的From頭域的值。如果給定的UA是多個用戶共同使用的,那么必須有一個URI對應身份的profile,這樣才能夠切用戶的profile。收到請求的服務方可以根據這個用於分辯身份的URI來區分同一個UA上的不同的用戶,並且根據他們的From頭域來判定他們的身份。(22節有更多的驗證說明)。

From域必須包含一個由UAC產生的新的”tag”參數。19.3節有tag的詳細描述。20.20節有更深入的資料。

例子:

From: “Bob” <sips:bob@biloxi.com> ; tag=a48s

From: sip:+12125551212@phone2net.com;tag=887s

From: Anonymous <sip:c8oqz84zk7z@privacy.org>;tag=hyh8

8.1.1.4 Call-ID

Call-ID是一個在一系列消息中,區分一組消息的唯一標志。在對話中的任一UA的所有請求和所有應答的Call-ID必須一致。在UA的每次注冊中,都應該是一樣的。在會話外的時候,UAC發起一個新的請求,這個Call-ID頭域必須由UAC產生一個全局(在時間和空間上都是)唯一的Call-ID, 除非是請求頭的方法(method)指明了別的產生方式。所有的SIP UA都必須保證自己產生的Call-ID不會和其他UA產生的Call-ID重復。注意,如果是請求的重新嘗試,則重新嘗試的請求不被當作一個新的請求,所以不需要新的Call-ID(重新嘗試的請求例如:認證沖突等等)。(見8.1.3.5)

我們強烈建議用密碼亂序隨機串(RFC 1750[12])來產生Call-ID。實現中,可以用類似”localid@host”這樣的格式產生。Call-ID是大小寫敏感的,並且通過簡單字節/字節的來進行比較。

采用密碼亂序隨機串可以降低會話被竊聽的機會,並且降低Call-ID重復的沖突。不規定或者要求使用用戶界面來選擇輸入Call-ID頭域的值。參見20.8節。

例子:

Call-ID: f81d4fae-7dec-11d0-a765-00a0c91e6bf6@foo.bar.com

8.1.1.5 Cseq

Cseq 頭域是用來區分和做位事務的順序使用的。他由一個方法(method)和一系列的順序號碼組成。方法(method)必須和請求的方法一致。對於對話外的非REGISTER請求來說,順序號碼可以是任意的。這個順序號碼必須可以由32位的無符號整數表達,必須小於2^31。只要遵循了上述指導方針,客戶端可以用任意的方法來產生這個Cseq頭域。12.2.1.1節講述了在對話中如何創建Cseq

例子:

Cseq: 4711 INVITE

8.1.1.6 Max-Forwards

Max-Forwards頭域用來限制請求到他的目的地中間的跳轉。它包含一個每隔一個跳轉就自動減一的數字。如果Max-Forwards在到達目的之前就減到0,他會報告一個483(太多的路由)錯誤回應。

一個UAC必須為每一個請求填寫一個Max-Forwards頭域,這個字段的缺省值應該是70。這個數字是保證了請求在沒有環路的SIP網絡中都能夠送達,也保證了在有環路的時候,盡量少消耗proxy的資源。如果這個數字要變小,則必須保證UA知道整個網絡的拓撲結構。

8.1.1.7 Via

Via頭域是標志了用於事務傳輸的傳輸設備,並且也標志了應答送回的地址。只有當需要通過選擇傳輸設備到達下一個節點(hop)的時候,才需要在頭域中包含Via域。當UAC創建一個請求,它必須在頭域中添加一個Via域。protocol 名字和protocol版本必須分別是SIP和2.0。Via頭域必須包含一個分支(branch)參數。這個參數用於區分請求創建的事務。這個參數客戶端和服務器都會使用。除了CANCEL和給非2xx應答的ACK以外,branch參數對UA發出的所有的請求來說,在時間和空間上必須唯一。在下邊的講解中,CANCEL請求的branch參數必須和他所取消的請求的branch參數一致。在17.1.1.3節中講述了給非2xxx(non-2xx)應答的ACK必須和其對應的的INVITE請求有相同的branch ID。

利用branch ID參數的唯一性來作為事務的ID(transaction ID),並非RFC 2543的一部分。根據本標准產生的branch ID必須用”z9h64bK”開頭。這7個字母是一個亂數cookie(定義成為7位的是為了保證舊版本的RFC2543實現不會產生這樣的值),這樣服務器收到請求之后,可以很方便的知道這個branch ID是否由本規范所產生的(就是說,全局唯一的)。在這樣的要求下,精確的branch ID的格式必須事先有實現的定義。

Via頭域中,maddr,ttl,和sent-by字段會在transport層處理請求的時候設置(18節)。Via在proxy的處理在16.6節8段和16.7節3段描述。

8.1.1.8 Contact

Contact頭域提供了訪問后續請求的特定UA實例的聯系方法:SIP或者SIPS URI。在任何會建立一個對話的請求中,Contact頭域必須提供和包含一個SIP或者SIPS URI。在這個規范中定義的方法中,只有INVITE請求會建立一個會話。對這些請求來說,Contact的作用域是全局性的。這就是說,Contact頭域中包含的URI是UA能夠接收請求的,這個URI必須是有效的,甚至在對話外的請求中的Contact頭域中的URI也必須是有效的。

如果Request-URI或者頭上的Route頭域包含了SIPS URI,Contact頭域也必須是一個SIPS URI。在20.10節有更進一步的說明。

8.1.1.9 Supported 和 Require

如果UAC支持服務端響應請求的SIP擴展,UAC應該在請求的時候包含一個Supported頭域說明options tags(19.2節)描述那些SIP擴展。option tags中出現的擴展說明必須是遵循RFCs的標准擴展說明。這樣可以防止服務端支持非標准的客戶端擴展實現。在Support頭域中的對於SIP擴展定義中,嚴格遵守不支持試驗性質的或者說明性質的RFCs擴展,這個也是由於這些擴展是描述提供商定義的擴展說明。如果UAC要求UAS能夠支持擴展,以便UAS能夠處理UAC的特定請求,那么它必須在請求頭中增加一個Require頭域來說明處理本特定請求需要什么樣的一個擴展option tags。如果UAC需要請求經過的所有proxy都支持它發出的某個請求的擴展部分,它必須增加一個Proxy-Require頭域來說明需要Proxy支持何種option tag擴展。

如同在Supported頭域指出的,Require和Proxy-Require頭域中的option字段必須限定於RFCs的標准擴展。

8.1.1.10 附加信息部分

在一個新請求創建以后,以上的頭域都被正確初始化了以后,就可以位這個請求增加它所需要的附加頭域了。SIP請求允許包含一個MIME-encoded消息正文。無論請求包含哪種消息正文,都必須引入頭域來指出這個正文的類型,以及這個正文的一些其他說明。關於這些頭域的詳細說明,請參見20.11節到20.15節。

8.1.2 發送一個請求

於是,我們就開始查找請求發送的目標。除非有其他的特定說明,目標必須是通過DNS來查找的(參見[4]說明)。如果路由表(route set)中的第一個元素表明這是一個嚴格路由(strict router,在12.2.1.1節中講述),那么這些過程必須在請求的Request-URI中說明。否則,這些過程在請求中被應用於第一個Route頭域中(如果存在),或者在請求的Request-URI中(如果Route頭域不存在)。這樣一些過程產生了一系列的地址,端口,和用於傳輸的傳輸器。無論那個URI用在這個[4]中描述的過程的輸入,如果Request-URI指明了SIPS,那么UAC必須按照[4]中描述的說明來認為輸入的URI是SIPS的URI。

本地策略可以指定一套額外的目的地用於發送。如果Request-URI包含一個SIPS URI,任何額外的目的地都必須用TLS來表達。除此之外,如果請求沒有包含Route頭域,那么就沒有對額外的目的地有什么其他的限制了。這個就提供了一個簡單的外發(outbound)proxy的事前路由的選擇。但是,用這樣的方法配置一個外發proxy是不推薦的;應該由單個UPI規定的預先設定的路由集來指定外發proxy。如果請求包含了Route頭域,請求應該發送到Route頭域最上邊的一個位置,但是請求也可能被發給由本文檔約定的Route或者Request-URI所指定的服務器(同RFC2543定義的相反)。特別的,一個配置了外發proxy的UAC應該首先嘗試把請求發送給由第一個Route頭域值指定的位置,而不是采用把所有消息都發給外發proxy的策略。這就保證了外發的proxy通過不增加Record-Route頭域而不參與后續請求的路徑。這個也允許讓不能分析第一個Route URI的終端,把請求交給外發proxy來發送。UAC應該遵循[4]中定義的過程來實現有狀態的元素,嘗試每一個地址直到連接到一個服務器。每一個嘗試都是一個事務,因此,每一個都有一個不同的Via頭域值和一個新的branch參數值。

此外,在Via頭域中的transport的值被設置成為要到目標服務器所必須的transport。

8.1.3 處理應答

應答首先是被transport層處理,並且被transport層發送給上一層transaction層處理。transaction層處理完成之后將應答發送給更上一層TU處理。在TU層進行的對應答的主要處理是方法相關的。但是也有集中通用的處理原則:

8.1.3.1: transaction 層的錯誤

在某些情況下,從transaction層返回的應答不一定是一個SIP消息,而是一個transaction層的錯誤。當從transaction層收到一個timeout錯誤的時候,必須將這個timeout錯誤當作是收到了一個狀態碼是408(請求timeout)的應答。如果transport層報告了一個嚴重錯誤(通常取決於UDP傳輸中的嚴重的ICMP錯誤,或者是TCP連接中的錯誤),必須把這個錯誤當作是狀態碼503(服務未提供)的錯誤。

8.1.3.2 未知的應答

UAC必須把自己不認識的所有最終應答當作是x00的那類應答,當然UAC也必須能夠處理所有的類別應答的x00的應答。比如,如果UAC收到了不認識的應答代碼431,他可以很安全的假設在他發出的請求中有什么地方弄錯了,並且可以很簡單的把這個應答錯誤當作是接收到了一個應答代碼是400(非法請求)的錯誤應答。並且,UAC必須能夠處理所有的不認識的非終結應答響應當作是183(session progress)。一個UAC必須能夠處理100和183應答。

8.1.3.3 Vias

如果在應答中,有不只一個Via頭域值存在,那么UAC應該丟棄這個消息。包含超過一個Via頭域值的消息是因為被錯誤的路由或者消息被破壞。

8.1.3.4 處理3xx應答

由於接收到一個重定向的應答(比如,狀態碼是301的應答),客戶端應該用在Contact頭域中的URI(s)來組織一個或者多個基於重定向以后的新請求,這個處理過程同proxy處理一個3xx類別的應答很類似,相關資料在16.5節和16.6節中有描述。客戶發起請求的時候只有一個目標URI,就是原始請求中的Request-URI。如果客戶端想在這個請求基礎上重構一個基於3xx類別應答的新請求,那么就需要把這個需要嘗試的URIs放到目標集合中去。基於本規范的規定,一個客戶端可以選擇放置那個Contact URIs到目標集合中(target set)。同proxy會遞歸一樣,客戶端處理3xx應答的時候必須不能重復添加任何URI到target set。如果原始請求的Request-URI頭域中包含了一個SIPS URI,那么客戶端可以選擇改乘一個非SIPS URI,但是需要知會客戶轉發到一個非安全的URI去。任何新的請求都可能導致接收到這些請求的3xx應答,並且在Contact中包含原始的URI。可以通過對兩個位置的配置來形成互相重定向。只要保證在target set中任何URI都只出現1次就能避免無窮的重定向循環。當target set增長了,客戶端可

 

 

小虎 2006-05-25 00:04

以對這個URIs,用任意順序來產生新的請求。常見機制是通過在Contact頭域的值中設置”q”參數來指定這個順序。對這些URIs的請求可以是並行產生的也可以是串行產生的。有一個實現是按照q參數值遞減的方法順序處理URIs,並且對相同q參數值的URIs進行並行處理。還有一種就是直接按照順序的方法處理URIs,對於q參數值不同的按照遞減的順序處理,對於q參數值相同的按照隨機順序處理。

如果發送給連接表上的地址失敗了,(在下一段我們有定義),那么就選擇列表中的下一個地址進行發送,直到列表全部遍歷一遍。如果列表遍歷完了還沒有,那么請求就失敗了。

通過響應碼(大於399)我們可以知道請求的失敗;對於網絡錯誤來說,客戶transaction會給transaciton user報告transport層的通訊錯誤錯誤。注意有一部分響應碼(8.1.3.5)表示請求可以被重試;這個時候,請求可以重發而不是簡單的當作一個錯誤。如果某個contact地址發送失敗,那么client應該嘗試下一個contact地址。這個會導致創建一個新的客戶事務來處理這個新的請求。

為了在處理3xx應答中創建一個基於一個contact地址的請求,UAC必須首先從target set中拷貝除了”method-param”和”header”URI參數之外的整個URI到Request-URI(見參數的定義見19.1.1)。通過使用”header”參數來創建新請求的頭域值,按照19.1.5節的指引,根據重定向的請求來重寫頭域的值。

注意在某些情況下,在contact地址中進行通訊所需要的頭域,可能代替添加到現有的在原始轉發的請求的請求頭域中。作為通用的規則,如果頭域可以接受用逗號分割的值列表,那么新的頭域值可以添加到原始轉發的請求的任何值后邊。如果請求頭域不接收多值列表,那么原始轉發請求中的頭域值可以由contact地址中進行通訊所需要的頭域的值所替換。比如,如果contract地址返回了如下的值:

sip:user@host?Subject=foo&Call-Info=Http://www.foo.com

那么在原始轉發請求中的任何Subject頭域都被重寫,但是HTTP URL僅僅添加到現存的Call-info 頭域值中。

我們推薦UAC重用與原始轉發請求相同的To,From,和Call-ID域值,但是也允許UAC為新的請求改變Call-ID頭域。

最后,當一個新的請求構造好以后,這個請求將通過一個新的客戶transaction發送,應此在最開始的Via頭域中必須有一個新的branch ID (8.1.1.7節說明)

在其他的方面,轉發的請求應該重用原始請求的頭域和消息體。

在某些情況下,Contact頭域的值可能在UAC中暫時或者永久保存,這個依賴於接收到的請求碼和過期的時間;參見21.3.2和21.3.3節。

8.1.3.5 處理4xx應答

某個4xx應答碼要求特定的UA處理,和請求的方法無關。

當接收到401(未授權)或者407(Proxy認證需要)應答的時候,UAC應該遵循在22.2和22.3中規定的認證步驟,重新發送帶認證信息的請求。

當收到413(請求過大)應答的時候(21.4.11節),這說明請求包含了一個UAS所不能接收長度的消息體。如果可能,UAC應該嘗試重新retry這個請求,或者去掉包體或者換一個小一點的長度。

如果收到了一個415(不支持的媒體類型)應答(21.4.13節),那么請求中包含的媒體類別是UAS所不支持的。UAC應該重發這個請求,並且這次發出的請求只包含應答中的Accept頭域所指明的媒體類別,並且采用Accept-Encoding頭域中指明的encoding方法,還有Accept-Language指明的語言。

如果收到了一個416(不支持的URI Scheme)應答(21.4.14節),Request-URI使用的URI Scheme(方案)是服務端所不支持的。客戶端應該重新嘗試這個請求,並且換用SIP URI。

如果收到了一個420(非法擴展)應答(21.4.15節),請求的Require或者Proxy-Require頭域包含的option-tag中包含了UAS或者proxy不支持的特性。UAC應該嘗試去掉應答中的Unsupported頭域中列出的擴展以后然后再嘗試。

在上述所有的情況中,所有需要重試的請求,都需要經過適當修正成為一個新的請求。這個新請求采用一個新的transaction並且應該有和上次請求相同的Call-ID,TO,From頭域,Cseq應該有一個新的順序號碼(比原有順序號碼更大)。

其他的4xx應答,包括尚未制定的,是否允許請求重試,依賴於具體的方法和應用。

8.2 UAS特性

UAS在處理對話外的請求的時候,有一組規則需要遵守,這組規則與方法無關。12節指明了一個方法來判定一個請求是否在一個對話里。

注意,請求的處理是原子級別的。如果請求被處理,那么這個請求的相關狀態一定是一起更新的。如果它被拒絕了,那么這個請求的所有相關狀態一定是沒有改變的。

UASs應當遵循本節所規定的順序來處理請求。(就是說,首先是身份認證,然后是方法判定,然后是頭域,然后按照本文規定處理剩余部分)

8.2.1 方法判定

當請求被認證(或者身份認證被忽略),UAS必須首先判定這個請求的方法。如果UAS發現自己不能處理這個請求的方法的時候,它必須給出一個405(方法不支持)的應答。產生應答的步驟在8.2.6節規定,並且UAS必須在給出的405(方法不支持)應答中增加一個Allow頭域。這個Allow頭域必須列明哪些方法UAS支持。Allow頭域的說明在20.5節。

如果請求中的方法是服務器所支持的,那么處理將繼續。

8.2.2 包頭判斷

如果UAS不認識請求中的包頭域(就是說,包頭域不在本規范中定義或者不在任何擴展中定義),那么服務器必須忽略掉這個包頭域並且繼續處理本請求。UAS必須忽略任何處理本請求所不需要的長得畸形的包頭域。

8.2.2.1 TO 和Request-URI

To頭域包含了由From域描述的發送者發出的請求的原始接受者。原始接受者可能是也可能不是正在處理這個請求的UAS,取決於呼叫轉移或者其他的proxy操作。當TO域值和自身不相符的情況下,UAS可以自行決定是否接收這個請求。但是,我們依舊是建議UAS處理這個請求,甚至TO這個頭域是以他們不認識的URI方案表達的(比如一個tel:URI),或者To頭域並非指向這個自身處理的UAS。當然,另外一方面來說,如果UAS決定拒絕這個請求,它應該產生一個403(禁止訪問)的狀態碼,並且交給服務器的transaction層來發送。

但是,Request-URI確定UAS來處理這個請求。如果Request-URI使用了一個UAS所不支持的方案(比如tel:URI),那么UAS應當拒絕這個請求,並且給出拒絕代碼416(不支持的URI方案)。如果Request-URI並沒有指明本UAS來處理這個請求,那么UAS應當給出一個404(未找到)的應答。比如,一個UA使用REGISTER方法來綁定它的address-of-record到一個特定的聯系地址,將會收到Request-URI等於那個特定聯系地址的請求。

其他潛在的Request-URI資源包括建立和刷新對話的UA發出的請求和應答的Contact頭域。

8.2.2.2 合並的請求

如果請求的To頭域中沒有tag標志,UAS的處理核心必須檢查基於正在進行的transactions上的請求。如果接收到的請求和正在處理的transaction的請求中的頭域From tag,Call-ID,CSeq精確匹配了,但是請求並不匹配那個事務(基於事務匹配機制17.2.3節),UAS核心應該產生一個482(檢測到循環)應答並且給服務器的transaction層發送。

這是由於相同的請求通過不同的路徑到達UAS,很多情況下是由於分支的原因。UAS處理了第一個請求並且給其他所有這個請求以482(檢測到循環)應答。

8.2.2.3 Require

如果請求的各項要素通過了UAS的判定,那么如果存在Require頭域,接下來就是檢查Require頭域。Require頭域是UAC用來通知UAS應該用什么樣的SIP擴展來處理本請求的。Require的格式在20.32節中有介紹。如果UAS不支持請求的Require頭域中的option-tag列表,那就必須產生一個420應答(錯誤的擴展)。並且UAS必須添上Unsupported頭域,里邊填上剛才接收到的請求的Require頭域中,哪些options是自己所不支持的。

需要注意的是,Require和Proxy-Require禁止出現在SIP CANCEL請求中,或者回應給非2xx應答的ACK請求中。就算出現了在處理的時候也必須被忽略。並且回應給2xx應答的ACK請求必須只能包含在初始請求(在這個ACK請求之前的請求)中包含的Require和Proxy-Require所規定options,這樣才能保證服務端能夠正確處理。

例子:

UAC->UAS: INVITE sip:watson@bell-telephone.com SIP/2.0

Require: 100rel

UAS->UAC: SIP/2.0 420 Bad Extension

Unsupported: 100rel

這個特性(Unsupported)是為了保證客戶-服務端都能夠無阻礙的交互,除非是options對方不支持(就像上邊的例子說明的一樣)。對於相互匹配的客戶-服務端(相互匹配的意思就是客戶端Require的正好是服務端支持的),那么這些請求、應答將會處理的非常迅速,減少了一個請求的往返協商的浪費。另外,這個也避免了客戶端不知道服務端到底不支持那些特性擴展。

某些特性擴展只對終端(endsystem)有效例如呼叫處理域等等。

 

8.2.3 內容處理

當UAS支持客戶端請求中要求的擴展支持后,UAS要檢查消息頭域描述的消息體部分。如果UAS並不支持消息體部分的類型(Content-Type指明),語言(Content-Language指明),編碼(Content-Encoding指明),並且這個消息體部分並非可選的消息體(Content-Disposition頭域指明),UAS必須回應一個415錯誤應答(不支持的媒體類型)應答。並且如果不支持請求中包含的消息體的正文類型,那么在應答中必須包含UAS所支持的消息體的類型列表(在Accept頭域中指明)。如果不支持請求包含的消息體的encoding方式,那么應答中必須包含Accept-Encoding頭域列明服務端支持的encoding方式。如果請求中的語言部分不支持,那么就必須在應答中包含Accept-Language頭域列明支持的語言。在這些檢查之外,消息體正文的處理依賴於方法和類型(method and type)。關於處理內容相關的頭域的進一步的資料在7.4、20.11到20.15節。

8.2.4 應用擴展

如果UAS希望應用一部分SIP擴展,那么不可以在產生應答的時候做SIP擴展,除非這個擴展是在請求中的Supported頭域中指明了的。如果這個擴展並沒有在本請求的Supported頭域中指明,那么服務端必須基於基准SIP給出應答,或者給出已知客戶端支持的擴展應答。在極少數情況下,如果服務端不用擴展就無法處理請求,那么服務端應該發送421(需要擴展支持)應答。這個應答說明如果沒有適當的擴展就無法給出正確的應答。在應答中需要的擴展必須在應答中的Require頭域中指出。我們並不推薦這個方法,因為它會降低協同工作的能力。

除了421應答之外的其他應答中,如果需要應用擴展,那么這些擴展需要在421的應答中的Require頭域中列明。當然,服務端不允許使用沒有在請求中的Supported頭域中列明的擴展。因此,應答中的Require頭域只會包含標准的RFCs的擴展option tags。

8.2.5 處理請求

當所有的檢查都通過了以后,UAS根據方法決定如何處理請求。第10節講述了REGISTER請求的處理,11節講述了OPTIONS請求的處理,13節講述了INVITE的處理,15節講述了BYE的處理。

8.2.6 產生應答

UAS產生應答,需要遵守接下來的幾個小節中講述的步驟產生一個應答。

在本節沒有描述的應答代碼的其他的行為,也可能會跟據應答代碼的不同而要求。當創建應答的步驟完成之后,UAS將應答交給收到這個請求的服務端的transaction去處理。

8.2.6.1 發送一個臨時應答

很多情況下,在與方法無關的應答規范中,在非INVITE請求的情況下,我們都要求UAS不應該發送臨時應答給請求者。在這種情況下,UAS應該盡快發送一個終結應答給非INVITE請求。

當需要產生一個100(Trying)應答的時候,所有對應請求中包頭的Timestamp域必須也拷貝到這個應答包頭(就是說如果請求中有Timestamp,應答中也必須有timestamp)。而且如果應答有延時,那么UAS應該在這個Timestamp上增加延時的值。這個數值必須包含了接收請求和發送應答的時間,用秒來計數。

8.2.6.2 包頭和Tags

應答中的From頭域必須和請求中的From頭域相等。應答中的Call-ID頭域必須和請求中的Call-ID頭域相等。應答中的Cseq頭域必須和請求中的Cseq頭域相等。應答中的Via頭域必須和請求中的Via頭域相等,而且順序也必須相等。如果請求中包含了To tag,那么應答中的To頭域必須和請求中的To頭域相等。如果請求中的To頭域並不包含Tag,那么應答中的To頭域的URI必須和請求中的TO頭域的URI相等;此外,UAS還必須增加一個Tag到To頭域上(100(trying)應答是一個例外,在100中可能已經存在了一個tag)。這就提供了一個UAS正在應答的標志,也許就是對話ID的一部分。對同一個請求來說,它的應答必須有相同的tag標志,包括終結應答和臨時應答(同樣100(trying)除外)。生成tag的步驟在19.3節。

8.2.7 無狀態UAS行為

無狀態UAS就是說UAS本身不保持事務的狀態。但是它在發送對應請求的應答之后並不保存請求的狀態。如果無狀態UAS接收到了一個重新發送的請求,它會重新產生一個應答並且重新發送應答,就像它接收到的是一個第一個請求一樣(就像是新請求而不是重發的請求一樣)。只有當相同請求的處理會導致相同的應答的時候,這個UAS才會是無狀態的。例如:這條規則排除了無狀態的登記服務。無狀態的UAS並不包含一個transaction層;他們直接從通訊層接收請求和發送應答。

無狀態的UAS通常用於處理不需要身份認證的請求,而且這些請求的應答是可以預期的。如果當不需要身份認證的請求被作為有狀態的UAS來處理,那么大量的不需要身份認證的請求會造成服務器大量的事務狀態,可能會導致UAS的處理非常慢,甚至中斷處理,這樣就導致了DoS(denial of service)狀態。在26.1.5節中有進一步的描述。

無狀態的UAS有下列重要的特性:

o 無狀態的UAS不許發送臨時應答(1xx)

o 無狀態的UAS必須忽略ACK請求

o 無狀態的UAS必須忽略CANCEL請求

o To頭域必須依據無狀態的規則來產生-就是說對於相同的請求,產生相同的tag標記。19.3節有講tag標記。

對於其他情況而言,無狀態的UAS遵循有狀態的UAS的規則。對於一個新請求來說,同一個UAS可以是有狀態的,也可以是無狀態的。

8.3 重定向服務器

在某些架構下,可以透過重定向機制,來降低proxy服務器上的路由請求的壓力,從而提高消息轉發的效率。重定向允許服務器在一個請求的應答中,推送路由信息到客戶端,這樣就可以做到把自己從后續的消息流中脫離出來,但是同時又能提供准確的請求定位服務。當請求的發起者接收到轉發的應答之后,他會重新產生一個基於接收到的URI(s)的請求。從network的中央轉發到最邊緣,轉發機制可以適用於跨度很大的網絡。

轉發服務器在邏輯上是通過一個服務器的transaction層建立的,他作為transaction user可以訪問某些類型的位置服務(參見第10節有關注冊服務器和位置服務的說明)。這個位置服務可以很方便的用數據庫里邊的一個URI對應多個地址URI(可能在這個地址找到對應的客戶)來實現。

一個重定向服務器並不發出任何指向它自己的請求。接收到任何一個非CANCEL的請求之后,服務器要么拒絕這個請求,要么從位置服務器上找到這個請求應該去的其他位置然后返回一個3xx的終結應答。對於合法的CANCEL請求,它應該返回一個2xx的應答。這個應答將會結束掉SIP事務。重定向服務器在整個SIP事務中位置事務的狀態。在重定向服務器之間檢測循環死鎖應該是客戶端的責任。

當重定向服務器給請求方返回一個3xx應答的時候,它會在Contact頭域填寫一組(一個或者多個)其他位置信息。如果需要指明這個Contact數據的生存周期,可以在Contact頭域添加一個”expires”參數。Contact頭域包含指向新位置的URI或者可以試試看的用戶名,或者就是簡單的附加通訊參數等等。301(永久去除)或者302(臨時去除)應答同樣可以指定和原始請求一樣的目的位置和用戶名,但是會附加像不同的服務器或者多點傳送地址、或者SIP由UDP到TCP傳輸等等這樣的通訊參數。

重定向服務器必須不能重定向一個請求到它自己的Request-URI列表中的地址;但是倘若那個URI並沒有指向自己,重定向服務器可以路由這個請求到目的URI,或者可以返回一個404拒絕。

 

如果一個客戶端正在使用外出的proxy,並且這個proxy實際上是重定向請求,那么就可能出現無限重定向循環。

 

注意,Contact頭域的值可能指向一個與原始呼叫者無關的資源。比如,一個SIP對PSTN(本地電話網)網關的呼叫可能被轉遞給一個特別的說明,比如”你所呼叫的電話號碼已經改為….”。Contact應答頭域可以包含任何合適的能處理這個呼叫對方的URI,並不限於SIP URI。比如,它可以是電話,傳真,或者IRC(如果有支持和定義的話)或者一個mailto:(RFC 2368[32]) URL。在26.4.4節講述了對SIPS URI到非SIPS URI的實現和限制。

 

在Contact頭域中的”expires”參數指明了這個URI的生存周期。這個參數的值是以秒作為單位的。如果這個參數沒有提供,那么這個”expires”的缺省是有Expires頭域所指定的。這個參數中如果設置了非法的值,那么都應改更改成為缺省的3600。這提供了對RFC2543的向后兼容,RFC2543允許在這個頭域中填寫絕對時間。在本協議中,如果這個expires填寫了絕對時間,那么就會當作是非法的值,就會被當作是3600。

 

重定向服務器必須忽略自己所不能理解的特性(包括不認識的頭域,不認識的Require中的option tag,甚至是不認識的方法名),並且帶着問題處理這個請求的重定向。

 

9 取消一個請求(Cancel)

前邊幾節講述了對所有方法的處理請求和處理應答的UA的通用處理過程。在本節中,我們討論一個通用的方法,CANCEL。CANCEL請求,就像名字所說的,是用來取消客戶端發起的上一個請求的。特別是,它請求UAS去終止上一個請求並且對上一個請求產生一個錯誤的應答。CANCEL對UAS已經給出終結應答的請求無效。所以,CANCEL請求的最大用處是取消需要服務器長時間處理的請求。也就是說,CANCEL最常用來處理取消INVITE請求,因為INVITE通常需要花費很長時間來產生一個終結應答。在這種使用中,UAS接收到對一個INVITE請求的CANCEL請求,當這個INVITE還沒有得到終結應答的時候,UAS會”停止振鈴”,並且給INVITE請求一個錯誤的應答(487)。

CANCEL可以由proxy或者UAC發起。15節講述了在何種情況下,UAC會CANCEL

 

 

小虎 2006-05-25 00:05

一個INVITE請求,在16.10節講述了proxy對CANCEL的使用。

一個有狀態的proxy需要對CANCEL進行響應,而不是簡單的轉發從下行流中接收到的一個應答。基於這個原因,CANCEL是一個”點對點”(hop-by-hop)的請求,也就是說,CANCEL需要每一個有狀態的proxy節點進行處理和應答。

9.1 客戶行為(Client Behavior)

CANCEL請求不應該取消除了INVITE之外的請求。因為除了INVITE之外的請求的響應都是立即響應的,所以,發送CANCEL來取消一個非INVITE請求總是形成一種賽跑的局面(就是說,cancel先到還是被取消的請求先到)。

下列步驟用於創建一個CANCEL請求。在CANCEL請求中的Request-URI , Call-ID , To , Cseq的數字部分,From這些頭域都必須和被取消的請求頭域一樣,包含這些頭域的tags. 客戶端創建的CANCEL必須只有一個Via頭域值,這個頭域值和被取消的請求的最上一個Via頭域值相同。這些頭域的值和請求的值相同可以讓CANCEL請求和被取消的請求相匹配(9.2節描述了如何匹配)。在Cseq請求頭域的method部分必須是一個CANCEL方法。這個讓這個CANCEL請求被當作自己的事務而被正確的鑒別和處理(參見17節)。

如果被取消的請求包含一個Route頭域,CANCEL請求也必須包含這個Route頭域的值。這個是讓無狀態的proxy能夠正確路由CANCEL請求。

CANCEL頭域必須不能包含任何Require或者Proxy-Require頭域。

一旦CANCEL請求被創建了,客戶端應當檢查是否收到了這個CANCEL請求取消的原始請求的任何應答(臨時的或者終結的應答)。如果沒有任何臨時應答收到,這個CANCEL請求一定不能發送,直到客戶端等到了第一個臨時應答。如果原始請求已經收到一個終結應答,這個CANCEL也不應當發送,因為CANCEL請求對已經產生了終結應答的請求沒有任何作用。當客戶端決定發送一個CANCEL,它會為這個CANCEL創建一個客戶transaction,並且通過目的地址、端口、傳輸層來發送CANCEL請求。這個CANCEL中的目標地址、端口和傳輸層必須和原始請求一樣。

如果允許在接收應答之前發送CANCEL請求,那么服務端必須支持在接收原始請求之前接收到CANCEL請求。

注意,原始請求的事務和CANCEL請求的事務都是互相獨立的。也就是說,UAC判定一個請求的取消不能依賴原始請求的一個487(請求終止)應答,遵循RFC2543協議,UAS不會產生這樣一個應答。如果原始請求經過了64*T1(T1在17.1.1.1節定義)秒還沒有應答,客戶端應當認為原始請求已經取消,並且應當銷毀對應原始請求的客戶端事務。

9.2 服務端行為(Server Behavior)

CANCEL請求要求服務端的TU取消相關的事務(transaction)。TU根據接收到的CANCEL請求,並且假定請求的方法不是CANCEL或者ACK,並且用17.2.3節描述的事務匹配方法來匹配事務,這樣TU就可以決定那個事務需要被取消了,被匹配的事務就是需要被取消的事務。

服務端對CANCEL請求的處理依賴於服務器的類型。一個無狀態的proxy會轉發這個請求,一個有狀態的proxy可能會響應這個請求,並且自己再產生一些CANCEL請求,UAS會響應這個CANCEL請求。16.10節講述了proxy怎樣處理CANCEL請求。

當UAS收到CANCEL請求,首先按照8.2節的UAS通用處理方法進行處理。不過,既然CANCEL請求是基於”點對點”(hop-by-hop)的,也是不能再提交的,他們不能由服務器為了獲得Authorization頭域中正確的認證而反復嘗試。注意,因此CANCEL請求也不能包含Require頭域。

如果根據上邊的步驟,UAS不能找到與CANCEL請求相匹配的事務,它應該給CANCEL一個481應答(調用的Leg/Transaction不存在 會話/事務不存在)。如果對應原始請求的事務存在,那么UAS在接收到CANCEL請求的處理就依賴於是否已經給這個原始請求發出了終結應答。如果已經發出了,不會對CANCEL請求對應的原始請求做任何處理,不會更改任何會話狀態,不會對原始請求的應答做任何處理。如果UAS沒有發出對原始請求的終結應答,它會依賴於CANCEL所取消的原始請求方法。如果原始請求方法是INVITE,UAS應當立刻響應INVITE一個487(請求終止)。本協議中,對CANCEL取消的其他本協議中定義的方法沒有約定。

不管原先請求的方法是什么,只要CANCEL匹配一個事務,UAS就響應CANCEL請求一個200(OK)應答。這個應答根據8.2.6節約定構造。注意,給CANCEL應答的To tag和給原始請求的應答的To tag應該是一樣的。對CANCEL的應答會通過服務端transaction來傳送。

10 注冊(Registrations)

10.1 概覽

SIP提供了一個搜索機制,如果一個用戶希望建立和其他用戶的會話,SIP必須查找能夠找到對方用戶正在使用的當前主機(hosts)。這個搜索機制經常被SIP網絡基本元素使用,比如proxy服務器,重定向服務器等等。他們在接收、以及響應一個請求的時候,會基於這個用戶的位置信息來判定這個消息應該發送到哪里。要實現這個,SIP網絡部件考慮了一個抽象的服務:位置服務;位置服務是通過對特定地區提供地址綁定來實現的。這些地址綁定轉換輸入的SIP或者SIPS URI,比如sip:bob@biloxi.com,轉換到一個或者一組更加”接近”目標用戶的URI,比如sip:bob@engineering.biloxi.com。基本上,一個proxy會從把輸入的URI轉換到用戶實際位置的位置服務中得到最終用戶的位置。

注冊服務為特定地區的位置服務創建綁定關系,這個綁定關系是用來建立包含一個或者多個聯系地址的address-of-record URI。因而,當那個地區的proxy接收到一個請求,這個請求的Request-URI和address-of-record的記錄匹配,那么這個proxy會轉發請求到這個address-of-record中登記的聯系地址中去。通常,只有當對那個address-of-record的請求會被路由到這個區域的時候,登記這個address-of-record 到這個這個區域的位置服務才是有意義的。在大多數情況下,這個就要求登記服務所覆蓋的區域和URI中的address-of-record所覆蓋的區域相同。有很多種方法來建立位置服務。一個方法是administratively(管理)。在上述的例子種,Bob我們通過查詢公司數據庫知道他是一個工程部職員。不過,SIP提供了一個讓UA能夠創建精確綁定的機制。這個機制就是登記服務。

登記服務需要向一個特殊的UAS服務器(注冊服務器registrar)發出REGISTER請求。注冊服務器(registrar)為一個區域的位置服務作為前端接入,根據REGISTER請求的內容讀寫位置對照表。這個位置服務通常為處理這個區域的proxy服務器提供位置服務。

總的登記服務處理流程在圖2中說明。需要注意的是,登記服務器(registrar)和proxy服務器都是邏輯上的角色,可以在網絡中用一個設備來部屬;在例子圖中是為了能夠清楚的表示所以分開描述。還需要注意的是如果他們(登記服務器和proxy)本身是分開的,那么UA可以通過proxy服務器發送注冊請求。

SIP本身對實現位置服務器(location service)沒有特別的要求。唯一的要求是某些區域的注冊服務器(registrar)必須能夠對位置服務的數據進行讀寫,並且這個區域的proxy或者重定向服務器必須能夠兼容讀取相同的數據。注冊服務器(registrar)可能和某一個區域的proxy服務器部署在一起。

10.2 構造一個REGISTER請求

REGISTER請求用來增加、刪除、查詢綁定資料。一個REGISTER請求可以增加一個address-of-record和一個或者多個聯系地址之間的綁定。在合適的第三方認證的情況下,可以做address-of-record的登記。客戶端同樣可以刪除前邊綁定的內容也可以查詢address-of-record的當前綁定地址。除了特別說明以外,REGISTER請求的構造以及客戶端如何發送REGISTER請求和通常的8.1節描述的和17.1節描述的UAC發出請求是一致的。

一個REGISTER請求並不建立一個對話。當基於事先給定路由集(8.1節)的情況下,一個UAC可以在REGISTER請求中包含一個Route頭域。在REGISTER請求和應答包中,Record-Route頭域並沒有任何意義,如果這個頭域存在,必須被忽略。另外,UAC一定不能基於REGISTER請求的應答包中的任何Record-Route頭域來創建新的路由集合。

下面這些頭域,除了Contact,必須在REGISTER頭域中包含。Contact頭域可選。

Request-URI:    這個頭域指明了登記服務所指明的位置服務所在的區域(比如sip:chicago.com)。”userinfo”和”@”元素在SIP URI中不能出現。

To:            這個頭域包含了被查詢、增加、修改的address-of-record。to頭域和Request-URI頭域通常是不同的,因為這個由用戶名組成。這個address-of-record必須是一個SIP URI或者SIPS URI.

From:            這個頭域包含了提交這個注冊信息的用戶的address-of-record資料。這個值和To頭域的值相同,除非這個請求是第三方發起的注冊請求。

Call-ID:            UAC發出的給某個注冊服務器(registrar)的所有注冊請求都應該有相同的Call-ID頭域值。如果相同的客戶端用了不同的Call-ID值,注冊服務器(registrar)就不能檢測是否一個REGISTER請求由於延時的關系導致了故障。

Cseq:            Cseq值保證了REGISTER請求的正確順序。一個UA為每一個具備相同的Call-ID的REGISTER請求順序遞增這個Cseq字段。

Contact:            REGISTER請求可以有一個Contact頭域。這個頭域可以有0個或者多個包含綁定地址信息的值。

UA在沒有收到上一個注冊請求的應答或者上一個REGISTER請求超時之前,禁止發送新的注冊請求(就是說,包含一個新的Contact頭域值,而不是重發)。

 

 

bob

+----+

| UA    |

|      |

+----+

|

|3)INVITE

|    carol@chicago.com

chicago.com            +--------+            V

+---------+ 2)Store    |Location|4)Query +-----+

|Registrar|=======>    | Service|<======= |Proxy|sip.chicago.com

+---------+            +--------+=======> +-----+

A                                5)Resp        |

|                                            |

|                                            |

1)REGISTER|                                        |

|                                            |

+----+                                            |

| UA |       <-------------------------------+

cube2214a| |                                    6)INVITE

+----+                                carol@cube2214a.chicago.com

carol

               圖2:REGISTER例子

下邊的Contact頭域參數在REGISTER請求中有特別的意義:

action: 在RFC2543中的“action”參數已經廢棄了,UAC不能使用”action”參數。

expires: “expires”參數表明UA的綁定的有效時間。以秒為單位的整數。如果本參數沒有制定,那么這個參數的值就是Expires頭域的值。實現中,可以把超過2**32-1的值(4294967295秒或者136年)認為是2**32-1。非法的值應當視同3600。

 

10.2.1 增加綁定

REGISTER請求是向注冊服務器(registrar)發送一個包含對某一個address-of-record的地址的SIP請求應當發送的實際聯系地址。address-of-record包含在REGISTER請求的To頭域中。

請求中的Contact頭域通常包含了SIP或者SIPS的URI,這些URI表明了特定的SIP端點(比如sip:carol@cube2214a.chicago.com),他們也可以使用其他的URI表示方法。一個SIP UA可以選擇注冊一個電話號碼(比如使用tel URL, RFC 2806[9])或者一個email地址(比如用mailto URL, RFC2368[32])來作為address-of-record的聯系地址Contact域。

例如,Carol,有一個address-of-record”sip:carol@chicago.com”,將會在區域chicago.com的注冊服務器上注冊。她的注冊服務信息將會被chicago.com區域的proxy服務器使用,用來路由和轉發到Carol的address-of-record請求到她的SIP終端。

當客戶端在注冊服務器(registrar)上建立好了綁定以后,它可以根據需要發送后續的注冊請求,包含新的綁定信息或者修改以前的綁定信息。給REGISTER請求的2xx應答中,在Contact頭域中是在這個注冊服務器(registrar)上登記的完整的這個address-of-record的綁定列表。

如果REGISTER請求中的To頭域中的address-of-record是一個SIPS URI,那么任何在REGISTER請求中的Contact頭域都應當是SIPS URI。客戶端只有在有其他手段保證非SIPS URI的安全性的情況下,才能在SIPS 的address-of-record的地址上注冊非SIPS URI。這個也可以適用域使用非SIP協議的URI,或者用非TLS來加密的SIP設備。

注冊並不需要更新所有的綁定。一般情況下,UA只更新它現在的聯系地址。

10.2.1.1 設置Contact地址的過期參數

當一個客戶端發出一個REGISTER請求,它可能包含一個過期參數用來表示這個注冊的地址的有效期。(就像在10.3節講述的那樣,注冊服務器(registrar)根據自己的策略選取實際的時間間隔來計算有效期)。

客戶端設置有效期的方法有兩種:一個是通過設置Expires頭域,一個是通過設置”expires”contact頭域的參數來設置。 后一種允許針對同一個REGISTER請求中的多個綁定聯系地址中的每一個聯系地址單獨設定有效期,然后所有沒有帶”expires”參數的Contact頭域的值都使用Expires的設置。

如果REGISTER中沒有兩種有效期都沒有設置,這就表明這個有效期由服務器來決定。

10.2.1.2 Contact Adress的參數選擇

如果在一個REGISTER請求中包含多個Contact,着說明UA想要把這些Contact頭域的內容都和To頭域中制定的address-of-record地址綁定起來。這個列表可以用”q”參數來區分Contact頭域的優先級。”q”參數用來標志特定Contact頭域值和其他綁定的address-of-record的聯系地址之間的優先級。16.6節講述了一個proxy服務器如何使用優先級。

10.2.2 刪除綁定

注冊信息是一個純粹軟件的狀態,並且如果不刷新會過期。如果需要,也可以被刪除。一個客戶端可以設置注冊服務器(registar)的有效期(10.2.1)。一個UA可以通過發出有效期為”0”的REGISTER請求,使某一個聯系地址立刻失效。UAS都需要實現這個機制使得在聯系地址過期前能夠被刪除。

REGISTER規范中的Contact頭域如果設置成為”*”則表示需要操作所有的注冊項。但是也只能在具有一個Expires頭域並且這個頭域為’0’的情況下能夠使用。(這就是說,只能夠在要刪除所有的注冊項的時候使用”*”)。

用”*”來刪除所有的注冊項有一個好處,就是使得UA不需要知道每一個注冊項的精確值。

10.2.3 訪問綁定

無論請求是否包含了Contact頭域,給任何REGISTER請求的成功應答都包含了一個完整的綁定列表。如果REGISTER請求頭域中不包含Contact頭域,那么注冊服務器的綁定列表將不會改變。

10.2.4 刷新綁定

每一個UA都對先前它建立的綁定信息由刷新的義務。禁止對其他UA建立的綁定信息進行刷新。在注冊服務器(registrar)給出的200(OK)應答中,包含了的Contact頭域中列明了所有的當前綁定信息。UA需要挨個比較這些聯系地址,看看是否這個地址是可以建立聯系的,這個比較是通過19.1.4節中的比較規則來進行的。如果是,它通過更新expires參數來更新過期時間(或者Expires頭域)。於是在這些綁定信息過期前,UA為每個綁定發出REGISTER請求來刷新綁定。也可以通過一個REGISTER請求來刷新數個綁定請求。

UA在一個刷新周期中,應該使用相同的Call-ID來進行注冊調用。刷新的注冊信息應該是和原來登記的信息一致。

10.2.5 設置內部時鍾

如果REGISTER請求的應答中包含一個Date頭域,客戶端可以用這個頭域來校正當前內部的時鍾。

10.2.6 尋找注冊服務器

UA有3種方法來決定向哪里發出注冊請求:通過配置,使用address-of-record,廣播方式。一個UA可以用非本文檔規定的方式,配置一個注冊服務器的地址。如果UA沒有配置任何注冊服務器的地址,UA應該用請求的Request-URI部分種的address-of-record的服務器部分(host part),用普通的SIP服務器定位機制[4]。比如,用戶”sip:carol@chicago.com”地址的注冊服務應該是”sip:chicago.com”。

最后,UA可以通過監聽廣播的形式來獲得注冊服務器地址。監聽廣播的注冊服務器是通過監聽著名的”全部SIP 服務器”廣播地址”sip.mcast.net”(224.0.1.74)。沒有Ipv6的廣播地址。SIP 的UA可以監聽這個地址,並且用這個來知道其他本地用戶的地址(見附件[33]);不過他們並不對請求做響應。通過監聽廣播的登記服務可能在某些環境下不能用,比如,基於同一個本地網絡的多個商業應用的情況。

10.2.7 傳送一個請求

當REGISTER請求被構造好以后,並且也有了登記服務起地址,UAC遵循8.1.2節的說明來提交transaction層來發送REGISTER請求。如果transaction層返回一個由於REGISTER請求沒有應答的超時錯誤,UAC不應該立刻重新嘗試對同一個注冊服務器的注冊請求。

立刻重新嘗試很有可能導致再次超時。等待一個合理的時間在嘗試可以降低網絡的負載,在這里並沒有一個約定的等待時間間隔。

10.2.8 錯誤響應

如果UA接收到一個423(間隔太簡略)應答,它可能需要更改REGISTER請求中的所有有效期,使得這些有效期必須大於等於423應答頭中的Min-Expires頭域中的有效期,並且重新嘗試發送這個REGISTER請求。

10.3 處理REGISTER請求

一個注冊服務器(registrar)就是一個UAS,這個UAS用來響應REGISTER的請求,並且維持一個綁定表,這個綁定表用來提供給它所管理的區域中的proxy服務器和重定向服務器的。一個注冊服務器根據8.2和17.2中的規定來處理請求,但是它僅僅處理REGISTER請求。一個注冊服務器禁止產生6xx應答。一個注冊服務器可以適當的轉發REGISTER請求。通常用於一個注冊服務器(registar)監聽一個多點廣播,並且通過302應答(臨時轉移)轉發這個多點廣播的REGISTER請求到它正確的處理位置。

注冊服務器必須忽略在REGISTER請求中的Record-Route頭域,並且也不能在REGISTER請求的應答中包含任何Record-Route頭域。注冊服務器可能收到一個有proxy轉發過來的REGISTER請求,這個請求中由於proxy處理這個請求當作未知請求所以添加了Record-Route頭域。

一個注冊服務器必須知道(例如,通過配置)它所管理的區域。注冊服務器一定需要按照接收到的REGISTER請求順序進行處理。

REGISTER請求必須當作原子請求來處理,意味着給定的REGISTER請求要么就完整處理,要么就完全拒絕。每一個REGISTER信息的處理都和其他的注冊和綁定信息的處理無關。

當接收到一個REGISTER請求,注冊服務器(registrar)按照如下步驟處理:

1、    注冊服務器(registrar)檢查Request-URI來決定是否它屬於本注冊服務器所管理的區域的Request-URI。如果不是,並且如果這個服務器同時也作為一個proxy服務器,那么這個服務器應當轉發這個請求到指定的區域。這個轉發是根據16節所規定的proxy通用信息處理規則來進行的。

2、    為了保證注冊服務器能夠支持所需要的擴展,注冊服務器必須遵循8.2.2節描述的情況來處理Require頭域。

3、    一個注冊服務器應當對UAC進行身份認證。SIP UA的身份認證機制在22節描述。注冊這個動作需要遵循SIP的通用的身份認證框架。如果沒有任何認證機制,注冊服務器可以使用From地址來作為原始請求的信任依據。

4、    注冊服務器應當檢查認證的用戶是否通過認證來更改這個address-of-record的登記權限。比如,一個登記服務起(registrar)可以通過訪問一個認證數據庫,這個認證數據庫映射了用戶名和一個address-of-record列表,這些列表是用戶可以更改綁定信息的列表。如果認證用戶並沒有權利修改綁定信息,注冊服務器應當返回一個403(禁止訪問)並且跳過后續步驟。在支持第三方認證和注冊的架構下,一個實體可以被授權來更新多個address-of-record的注冊信息。

5、    注冊服務器(registrar)從REGISTER請求的To頭域中解出address-of-record。如果這個address-of-record並非在這個Request-URI指明的區域中合法,那么注冊服務器必須發出一個404(沒有找到)的應答,並且跳過后續步驟。接着URI必須轉換成為標准的格式。所有的URI參數都必須刪去(包括用戶參數user-param),並且任何非法(escaped)字符必須轉換成為合法字符(unescaped)格式。最后形成一個可以用於綁定的列表。

6、    注冊服務器(registrar)檢查是否請求包含了一個Contact頭域。如果沒有包含,它跳過到最后一步。如果Contact頭域包含了,注冊服務器檢查是否有一個Contact頭域值是”*”,並且包含了一個Expires頭域。如果請求有其他的Contact頭域或者任何有效期的值是非0的,這個請求就是非法請求,並且服務器必須送回一個400(非法請求)的應答,跳過后續步驟。如果沒有,那么注冊服務器檢查是否Call-ID復核每一個綁定的值。如果不符合,它必須刪除綁定。如果復核,它必須僅僅刪除保存的綁定表中CSeq值小於請求中的Cseq值的記錄。否則,更新必須終止,請求失敗。

7、    現在注冊服務器(registrar)可以依次處理Contact頭域中的聯系地址了。對於每一個地址,它根據下邊的規則進行有效期檢查。

- 如果含有”expires”參數,這個參數值就是最終的有效期。

- 如果沒有這個參數,並且請求有一個Expires頭域,那么這個值就是有效期。

- 如果沒有Expire頭域也沒有參數,那么本地的缺省配置應當作為有效期。

注冊服務器可以選擇一個小於請求中的有效期值作為最后的有效期。當且僅當請求的有效期大於0 並且小於1個小時並且小於一個注冊服務器配置的最小有效期的時候,注冊服務器可以響應一個423(有效期過小)的錯誤來拒絕。這個應答必須包括了一個Min-Expires頭域來表明本注冊服務器所接收的最小有效期,然后跳過所有后續步驟。

允許注冊服務器設置注冊服務器自己的有效期防止了過分頻繁的刷新注冊信息,並且也降低了維持和管理注冊信息的工作量。在服務的創建的時候,注冊信息中的有效期會經常用到。一個例子是follow-me(跟隨我)服務,在這個服務中,一個用戶可能在一個終端上只停留一小會兒。因此,注冊服務器應當接收簡略的注冊;一個請求只有當它的有效期過短的時候(短到可能降低注冊服務器性能的時候)才應當被拒絕。

對於每一個地址,注冊服務器在當前綁定列表中用URI比較規則進行搜索。如果綁定不存在,它會暫時性的添加進去。如果綁定存在,注冊服務器檢查Call-ID值。如果Call-ID值在這個已經存在的綁定中和本次請求的Call-ID值不一樣,那么如果綁定的有效期為0就刪除這個綁定,否則就刷新這個綁定。如果Call-ID值一樣,那么注冊服務器比較Cseq值,如果請求中的這個值比現存的綁定值中的Cseq高,那么必須更新或者刪除這個綁定(如果有效期為0 就刪除,否則就刷新)。如果這個Cseq值比現存的Cseq值小,那么必須終止更新並且請求失敗。

這個規則確保了從同一個UA過來的請求順序處理,對於非順序的請求跳過處理。

每一個綁定記錄都包含了當時請求的Call-ID和Cseq的值。

只有當且僅當所有的綁定更新和綁定添加都完成的時候,綁定才可以做COMMIT處理(就是說,這次修改對proxy和重定向服務器可見)。如果任何更新或者添加失敗了(比如,由於后台的數據庫commit失敗),必須給出一個500(服務器錯誤)的應答,並且中間進行的臨時更新都必須刪除。

8、    注冊服務器(registrar)返回一個200(OK)應答。這個應答必須包含Contact頭域,並且這個頭域的值中列舉了所有當前綁定的注冊信息。每一個Contact值都必須包含一個”expires”參數,用來標志還有多久這個綁定信息就過期了。應答也必須包含一個Date頭域。

 

11 查詢能力

SIP方法OPTIONS允許一個UA來查詢另外一個UA或者proxy服務器的能力。這個提供個客戶端一個手段來查詢服務端支持的方法,內容類型,擴展,codecs等等。這些都不用”ringing”對方。比如,在客戶端試圖在INVITE請求頭中增加一個請求字段選項的時候,它並不知道對方UAS能否支持這個選項,它就可以用OPTIONS來查詢一下UAS,通過檢查OPTIONS返回的Supported頭域,就可以知道是否支持這個選項。所有的UA都必須支持OPTIONS方法。

OPTIONS請求的目標是用Request-URI指明的,這個既可以是一個UA也可以是一個SIP服務器。如果OPTIONS指向一個proxy服務器,Request-URI設置成為一個沒有用戶部分(user part)的,類似REGISTER請求中的Request-URI一樣。或者,一台服務器收到一個OPTIONS請求並且Max-Forwards頭域值是0的時候,它就需要響應這個請求而不需要關心Request-URI的內容。

這個機制就像HTTP/1.1一樣。這個機制可以用來實現類似”traceroute”功能來通過發出一系列的有着增量Max-Forwards頭域的OPTIONS請求來檢查每一個途徑節點的能力。

就像對一般UA機制來說,如果OPTIONS沒有應答,transaction層能夠返回一個超時錯誤。這個可能標志着對方無法到達因此無響應。OPTIONS請求可以作為建立會話的一部分,用來查詢對方的能力使用,這樣在后續對話中可以使用雙方兼容的方式。

11.1 構造OPTIONS請求

一個OPTIONS請求可以根據8.1.1節中的標准構造方法來進行構造。

Contact頭域在OPTIONS請求中可以存在,也可以不存在。

Accept頭域應當包含在請求中,用來標志UAC希望接收應答中的消息體的類型。通常情況下,這個設置成為UA的多媒體兼容能力,比如SDP(應用/SDP)格式。

對於一個OPTIONS請求的應答是假定是在原請求中的Request-URI范圍內的。但是,僅當一個OPTIONS請求作為建立對話的一部分而發送的時候,后續的請求應當由收到並且響應這個OPTIONS請求的服務器進行處理。(就是說如果在建立會話的時候使用OPTIONS請求,那么OPTIONS之后的這些請求都應該由這個OPTIONS查詢的服務器處理,這樣才能保證使用的特性和OPTIONS查詢出來的能力是一樣的)

 

OPTIONS請求的例子:

OPTIONS sip: carol@chicago.com 

Via: SIP/2.0/UDP pc33.atlanta.com;branch=z9Hg4bKhjhs8ass877

Max-Forwards: 70

To: <sip:carol@chicago.com>

From: Alice <sip:alice@atlanta.com>;tag=1928301774

Call-ID: a84b4c76e66710

Cseq : 63104 OPTIONS

Contact: <sip:alice@pc33.atlanta.com>

Accept: application/sdp

Content-Length: 0

11.2 處理OPTIONS請求

給OPTIONS的應答的構造遵循標准的構造規則(8.2.6節描述)。應答碼的選擇必須和處理INVITE請求一樣的應答碼(就像處理INVITE請求一樣的返回)。這就是說,200(OK)應當在UAS能夠接收請求的時候返回,486(忙)應當在UAS如果忙的時候返回。這樣OPTIONS可以用來檢測UAS的基本狀態,這就是說,我們可以用OPTIONS來知道UAS能否接收INVITE請求。

在一個對話中的OPTIONS請求會產生一個200(OK)的應答,這是和在對話外創建的並且對對話沒有任何影響的請求相同。

這個OPTIONS的使用有一定的限制,因為對於proxy處理OPTIONS和INVITE請求的不同。一個分支的INVITE可以有多個200(OK)的應答返回,但是一個分支的OPTIONS只能有單個200(OK)應答返回。因為這是由於proxy處理OPTIONS請求是當作非INVITE的處理。參見16.7節有詳細的說明。

如果OPTIONS請求的應答是由proxy服務器給出的,proxy返回一個200(OK)的應答,列出這個服務器的各種選項和能力。

應答沒有消息體

Allow,Accept,Accept-Encoding,Accept-Language,和Supported頭域應當在200(OK)應答中出現。如果這個是由proxy產生的應答,那么Allow頭域應當忽略,因為proxy是方法無關的(也就是說不知道該如何處理方法的)。Contact頭域可以在200(OK)的應答中出現,並且與3xx應答有相同的語義。這就是說,他們可以列出指向客戶的一串名字和方法的集合(用以轉發請求)。一個Warning頭域是可以存在的。消息體也可以存在,消息體的類型是由OPTIONS請求的Accept頭域指明的(application/sdp是缺省的,如果Accpet頭域不存在的話)。如果Accept頭域中包含了一個類型能描述媒體接收能力,UAS應當在應答中包含一個消息體用於這個用途。詳細的application/sdp包體說明在[13]中描述。

UAS生成的OPTIONS應答例子。(對應11.1節中的請求例子)

SIP/2.0 200 OK

Via: SIP/2.0/UDP pc33.atlanta.com;branch=z9hG4bKhjhs8ass877

; received = 192.0.2.4

To: <sip:carol@chicago.com>;tag=93810874

From: Alice <sip:alice@atlanta.com>;tag=1928301774

Call-ID: a84b4c76e66710

Cseq: 63104 OPTIONS

Contact: <sip:carol@chicago.com>

Contact: mailto:carol@chicago.com

Allow: INVITE,ACK,CANCEL,OPTIONS,BYE

Accept: application/sdp

Accept-Encoding: gzip

Accept-Language: en

Supported: foo

Content-Type: application/sdp

Content-Length:274

(SDP not shown)

12 對話(Dialog)

一個UA的核心概念就是對話。對話是表現為兩個用戶代理(UA)之間的持續一段時間的點對點的SIP關系。對話(Dialog)使得用戶代理之間的消息順序傳遞和兩個用戶代理之間的請求正確路由更加容易。對話(Dialog)可以認為是對SIP消息解釋的上下文關系。第8節講述了方法無關的UA處理和響應對話(Dialog)外的請求。本節將討論如何通過請求和應答來創建一個對話(Dialog),並且在對話(Dialog)中如何發起和響應后續的請求。

一個對話在參與對話的UA中都有一個dialog ID作為標記,這個ID由Call-ID,和一個本地tag和遠程tag組成。各個UA的dialog ID在對話中是不一樣的。特別是,在一邊UA的本地tag,在另外一方就是遠程tag。這些tag都是互相不透明的,並且使得整個dialog ID是唯一的。

dialog ID同樣是和所有的To頭域中包含了tag參數的請求及應答相關。

填寫一個消息中的dialog ID的規則依賴於SIP元素是UAC還是UAS。對於UAC來說,dialog ID中的Call-ID的值會填寫到消息中的Call-ID域中,遠程tag放在消息中的To的tag參數中,本地tag放在From的tag參數中。(這些規則對請求和應答都適用)。對於UAS來說,dialog ID的Call-ID值放在消息的Call-ID頭域中,遠程tag放在From頭域的tag中,本地tag放在To頭域的tag參數中。

一個對話包含一些特定的狀態用於以后的對話中的消息傳送。這個狀態由dialog ID,本地序列號(用來排序UA到對方的請求的序列),遠程序列號(用來排序請求從遠端到本UA),本地URI,遠端URI,remote target,一個布爾類型的標記”secure”,路由集合(一組有序的URI)組成。

路由集合是由發送請求到對方需要途徑的一組服務器列表組成。一個對話可以處於”early”狀態,這是由於當這個對話收到了臨時應答而創建,並且當收到了2xx終結應答的時候轉換到”confirmed”狀態。對於其他應答,或者沒有應答,”early”對話將會終結。

 

12.1 創建一個對話

對話是由對一組特定請求的沒有失敗的應答來創建的。在本規范中,只有包含To tag的2xx和101-199應答,並且請求是INVITE的,會建立一個對話。當收到一個非終結應答的時候,對話會建立成”early”狀態,並且成為early dailog。創建對話的時候可以使用Extension來定義擴展。13節描述了INVITE請求的更多細節。在這里,我們描述與方法無關的對創建對話狀態的處理。

UA必須按照下邊描述的方法對dialog ID進行賦值。

12.1.1 UAS行為

當UAS響應一個請求給出一個應答,並且這個應答會建立一個對話的時候(比如對INVITE的2xx應答),UAS必須拷貝所有的請求中的Record-Route頭域到應答中去(包括URI,URI參數,和其他任何Record-Route頭域的參數,無論UAS是不是認識的參數都需要原樣拷貝),並且必須維持這些參數的順序。UAS必須增加一個Contact頭域給應答。這個Contact頭域包含一個UAS在后續對話請求中接收請求的地址(這個包含了給INVITE請求的2xx應答的ACK請求處理的地址)。通常情況下,UAS會用IP地址或者FQDN形式來發布自己的這個Contact地址。這個在Contact頭域中的URI必須是一個SIP或者SIPS URI。如果創建對話的請求在Request-URI中包含的是SIPS URI,或者在Record-Route頭域的最上的一個值是SIPS URI,或者如果請求中沒有Record-Route頭域但是請求中的Contact頭域是SIPS URI,那么給出的應答中的Contact頭域必須是一個SIPS URI。 這個URI應該是全局有效的(就是說,這個URI可以用於對話外的消息)。同樣的,在請求INVITE中的Contact頭域的URI也不應當僅限於這個對話中使用。因此它可以用於對話外的消息中。

UAS接着創建這個對話的狀態。對話狀態必須維持直到對話結束。

如果請求是通過TLS過來的,並且Request-URI包含一個SIPS URI,”secure”標志將被賦值成為TRUE。

路由集合必須設置成為請求中的Record-Route的URI列表,保留所有的URI參數和順序。如果請求中沒有Record-Route頭域,那么路由集合必須設置成為空。這個路由集合,即便是空的,為了以后的對話中的請求,也要覆蓋任何預先存在(pre-existing)的路由集合。remote taget必須設置成為請求的Contact頭域中的URI。

遠程序列號必須設置成為請求中的Cseq頭域的序列號。本地序列號必須設置成為空。dialog ID中的呼叫標志應該設置成為請求的Call-ID頭域的值。dialog ID的本地tag必須設置成為對請求的應答包中的To頭域的tag,並且dialog ID的遠程tag必須設置成為請求中的From 頭域中的tag。UAS必須能夠處理接收到的請求中的From頭域沒有tag標志,在這種情況下,這個tag就是空值。這是為了兼容RFC2543協議,它並沒有定義From tag。

遠程URI(remote URI)必須設置成為From頭域中的URI,並且本地URI必須設置成為TO頭域中的URI。

12.1.2 UAC行為

當一個UAC發出一個請求,這個請求能夠建立一個對話(比如這個請求是INVITE),它必須在Contact頭域中提供一個基於全局的SIP或者SIPS URI(例如,可以在對話外使用的SIP URI)。如果請求包含一個Request-URI或者最上的Route頭域是SIPS URI,Contact頭域也必須包含的是SIPS URI。

當一個UAC接收到應答,並且這個應答建立對話的時候,它也同樣構造這個對話的狀態。這個狀態必須維持到對話的結束。

如果這個請求是基於TLS發送的,並且Request-URI包含一個SIPS URI,那么”secure”標志被設置成為TRUE。

路由集合必須設置成為應答中的Record-Route頭域的URI列表,保留所有的URI參數和順序。如果在應答中沒有Record-Route頭域,那么這個路由集合必須設置成為空集合。這個路由集合即便是空的,為了以后的對話中的請求,也要覆蓋任何預先存在(pre-existing)的路由集合。remote taget必須設置成為應答中的Contact頭域的URI。

本地序列號必須設置成為請求中的Cseq頭域的序列號。遠程序列號必須設置成為空(他會由遠端的UA在對話中發送請求而建立)。dialog ID中的呼叫標志必須設置成為請求的Call-ID頭域的值。dialog ID的本地tag必須設置成為請求中的From頭域的tag,dialog ID的遠程tag必須設置成為應答中的To頭域的tag。UAC必須能夠處理接收到的應答的To頭域中沒有tag的情況,在這個情況下,tag值取值成為空。這是為了能夠向下兼容RFC2543,它沒有規定To的tag。

remote URI必須設置成為To頭域的URI,local URI必須設置成為From頭域的URI。

 

12.2 對話中的請求

當兩個UA之間的對話建立以后,他們都可以在對話中初始化一個新的事務(transaction)。如果UA發送請求,將遵循UAC的事務規則。UA接收請求將遵循UAS的規則。在建立對話的事務過程中,UA扮演的角色可能是不一樣的。

在對話中的請求可以包含Record-Route和Contact頭域。不過,雖然他們會修改remote target的URI,但是這些請求也不會導致對話的路由集被改變。明確說,如果請求不是刷新target的請求,那么這個請求不會更改對話的remote target URI,如果請求是刷新target的請求,那么這個請求才會更改對話的remote target URI。對於用INVITE建立的對話來說,唯一的能夠刷新target的請求就是re-INVITE(見14節說明)。可能會有其他擴展定義通過其他方法來刷新target的請求。

注意ACK不是一個刷新target的請求。

刷新target請求只會更改對話的remote target URI,並且更改由Record-Route指定的路由集合。如果更新路由集合會帶來嚴重的和RFC2543向后兼容問題。

 

12.2.1 UAC行為

12.1.1.1 產生請求

在對話中的請求是通過用許多對話的狀態部分來構造的。在TO頭域中的URI部分必須設置成為對話狀態中的remote URI。To頭域的tag參數必須設置成為dialog ID中的remote tag部分。請求的From URI必須設置成為對話狀態中的local URI。From頭域的tag參數必須設置成為dialog ID的local tag部分。如果remote或者local tag是空值,那么tag參數必須分別從From或者To頭域中去除。

在請求序列中的原始請求的To和From頭域的URI的使用方法是為了向下兼容RFC2543協議的,在RFC2543協議中,使用URI作為對話的標志。在這個規范中,只有tags用於區分對話。有可能在本協議的后續版本中,在對話中的請求必須強制反應原始請求的To和From頭域的URI將會去除。

請求的Call-ID必須設置成為對話的Call-ID。在對話中的請求必須嚴格遵循單個遞增的Cseq序列號(每次增加1)(當然要除了ACK和CANCEL,這兩個請求中的Cseq必須和原始的請求或者確認請求一樣)。因此,如果本地序列號(local sequence number)不為空,那么本地序列號碼必須依次增加1,並且這個數值要存放到Cseq頭域中。如果本地序列號碼是空的,那么在8.1.1.5節約定的初始值必須填寫進去。在Cseq頭域中的method字段必須和請求的方法(method)一致。

通過使用32位的長整數,使得即使每秒種產生1筆請求,也會要136年才會用完這個整數出現重復。這個序列號的初始值的選取是為了讓對話中后續的請求序列號不會重復。非0的初始值可以考慮采用時間來作為初始的序列號。一個客戶端可以用31位有符號整數或者32位無符號整數來存放時間作為初始化的序列號。

UAC使用remote target和路由集合來構造請求中的Request-URI和Route頭域。如果路由集合是空的,那么UAC必須把remote target URI放到Request-URI中,並且UAC不能添加Route頭域到請求中。

如果路由集合不為空,並且路由集合的第一個URI包含lr參數(見19.1.1),那么UAC必須填寫remote target URI到Request-URI,並且必須包含Route頭域,這個Route頭域按照順序填寫路由集合和路由集合的參數。

如果路由集合不為空,並且路由集合的第一個URI沒有包含lr參數,那么UAC必須把第一個URI放在Request-URI中,並且拆去所有不被Request-URI允許的參數。UAC必須增加一個Route頭域順序包含所有剩下的路由集合元素,及其參數。UAC接着必須把remote target URI放在Route頭域的最后一項。

例如,如果remote targe是: sip:user@remoteua 並且路由集合包括:

<sip:proxy1>,<sip:proxy2>,<sip:proxy3;lr>,<sip:proxy4>

那么請求應該有下列的Request-URI和Route頭域

METHOD sip:proxy1

Route: <sip:proxy2>,<sip:proxy3;lr>,<sip:proxy4>,<sip:user@remoteua>

如果路由集合的第一個URI不包含lr參數,那么對應的說明proxy並不能支持本文檔所約定的路由機制,而是支持RFC2543文檔所約定的路由機制,那么在發送信息的時候需要通過替換Request-URI為接收到的第一個Route頭域的值。將Request-URI的值放在Route頭域的目的是為了保護Request-URI,使得它經過嚴格路由的時候不丟失(當請求遇到一個松散路由的時候會返回到Request-URI中?????。)

在對話內的任何一個刷新target的請求中,都應當包含一個Contact頭域,並且這個URI除非有必要,否則都應當是和對話內上次請求的URI值一樣。如果”secure”標志設置成為TRUE,那么URI也應當是SIPS URI。

如果在12.2.2節討論的那樣,在刷新target請求中的Contact頭域會更新remote target URI。這個允許UA提供一個新的聯系地址(Contact address),表明它在對話中改變了自己的地址。不過,如果請求不是刷新target的請求,那么不會影響對話中的remote target URI。請求中的剩下的部分請按照8.1.1節描述的填寫。一旦請求被創建了,請求將按照對話外請求發送標准步驟(8.1.2節)來解析服務器的地址並且發送請求。

8.1.2節中的步驟一般把請求發送到Route頭域的最上一個地址,或者如果沒有Route頭域,那么就發送到Request-URI地址。由於受到特定的限制,這些步驟也允許把請求發送到另外一個地址(比如在route set中沒有的缺省的外發proxy)

12.2.1.2 處理應答

UAC將會從transaction層收到請求的應答。如果客戶端的事務層返回一個超時,這會等同於一個408(請求超時)的應答。UAC處理3xx應答的時候,在這個應答是在對話內的請求的應答的處理方法和在對話外的處理方法是一樣的。這個方法在8.1.3.4節中描述。需要注意的是,雖然UAC會嘗試新的地址(處理3xx應答的時候),但是它依舊使用對話內的路由集合來構造請求的Route頭域。

當UAC收到一個刷新target請求的2xx應答的時候,如果對話的remote target URI存在,那么它必須用這個應答的Contact頭域的值來替換對話的remote target URI。

如果對話那的請求的應答是481應答(呼叫/事務不存在Call/Transaction Does Not Exits)或者一個408(請求超時),那么UAC應當終止這個對話。並且UAC應當在請求完全沒有應答的時候(客戶端transaciton將會通知TU這個超時)客戶端transaction終止這個對話。

對於INVITE初始化的對話,終止對話需要發送一個BYE。

12.2.2 UAS行為

在對話中發送的請求,就像其他請求一樣,是原子請求。如果UAS收到某個請求,所有的相關狀態要么一起改變,要么就一起不變。在某些請求中,請求會影響好幾個狀態(比如INVITE請求)。

UAS從transaction層收到請求。如果請求的To頭域有tag字段,UAS的處理核心需要校驗對話的ID,拿請求中的tag和現存的對話相比較。如果匹配成功,那么就是一個在對話中的請求。在這種情況下,UAS首先使用8.2節中的對話外請求處理的步驟。如果請求To頭域包括了一個tag字段,但是對話的ID並不匹配現存的對話,UAS可能是因為崩潰而重新啟動,或者收到了一個另外(可能是錯誤的)UAS(UAS可以構造To的tags,這樣UAS在災備恢復下,可以把這個tag看成它自己的)。還有一種簡單的可能是請求發送錯誤了。在這個基礎上,UAS可以選擇接受或者拒絕請求。在允許的情況下,盡量處理這些請求會提供災難恢復的機制。UAS如果希望支持這樣的特性就必須遵循一些原則,比如用始終使用單調遞增的Cseq序列號,甚至是在重新啟動之后也這樣,在重啟動后重建路由集合,處理越界的RTP時間戳和序列號等等。

如果UAS由於不希望重構對話而拒絕這個請求,它必須應答對方一個481(呼叫/事務不存在。Call / Transaction不存在)應答。

對於在對話中接收到的,那些不會用任何形式更改對話狀態的請求,比如OPTIONS請求,他們等同於在對話外的處理請求。

如果遠端的序列號(remote sequence number)是空的,它必須設置成為請求中的Cseq頭域的序列號(sequence number)。如果remote sequence number不是空的,但是請求中的sequence number小於這個remote sequence number,請求就是非順序的,並且必須通過應答500(服務器內部錯誤)打回去。如果remote sequence number不是空的,並且請求中的序列號大於這個remote sequence number,請求就是按照順序的。這個請求中的Cseq的序列號可以比remote sequence number大不止1。在這種情況下,並非是錯誤的,並且UAS應當准備接收和處理比上次處理的請求Cseq值大於1 的請求。UAS必須設置remote sequence number成為請求中的Cseq頭域中的序列號。

如果一個proxy廢棄掉一個UAC產生的請求,並且UAC重新遞交這個請求的時候。這個請求是會具有一個全新的Cseq序列號。UAS是不會收到第一個請求的,這樣,Cseq序列號就會出現間隔,這樣的間隔並非是一種錯誤的情況。

當UAS接收到一個target刷新請求的時候,如果請求中存在Contact頭域,它必須用Contact頭域中的URI來替換對話的remote target URI。

12.3 終止對話

在建立對話中的終結對話,跟請求方法無關,如果對話外的請求產生了一個非2xx終結應答,任何前邊請求創建的”早期對話”(early dialogs)將會終止。在已經建立的對話中,終結對話就是請求方法相關的。在這個定義中,BYE方法將會終結一個對話。15節有細致的討論。

13 初始化一個會話

13.1 概覽

當UAC希望初始化一個會話(比如,audio,video或者游戲),它首先構造一個INVITE請求。這個INVITE請求一個服務器來建立一個會話。這個請求可能會由proxy層層轉發,最后到達一個或者多個可能能夠處理這個邀請的UAS。這些UAS需要看看是否用戶接收這個邀請。然后UAS可以接收這個請求(也就是會話建立了),通過發送2xx應答。如果邀請被拒絕,根據拒絕的原因,3xx,4xx,5xx或者6xx應答將會發送。在發送終結應答之前,UAS可以發送一些臨時應答(1xx)應答給UAC,以便UAC能夠掌握建立會話的進度。

當收到了一個或者多個臨時應答,UAC可能收到一個或者多個2xx應答或者一個非2xx終結應答。由於在INVITE終結應答之前,可能有不少時間,在INVITE事務的可靠性機制和其他的請求不同(比如OPTIONS)。當UAC收到了終結應答,UAC需要給每一個INVITE的終結應答,發送一個ACK請求。發送ACK請求的步驟依賴於應答的類別。對於在300到699的終結應答,ACK是在transaction層處理的,並且遵循一系列規則(17節)。對於2xx應答,ACK是由UAC處理核心產生的。

INVITE的一個2xx應答會建立一個會話,同時也建立了一個基於發送INVITE請求的UA和產生2xx應答的UA之間的對話。因此,當從多個遠程UA收到了多個2xx應答(可能由於INVITE的分支),每一個2xx建立一個不同的對話(dialog)。所有這些對話都是同一個呼叫的組成部分。

本節介紹了INVITE請求建立會話的詳細過程。支持INVITE的UA也一定同時支持ACK,CANCEL和BYE。

13.2 UAC處理

13.2.1 創建一個初始化的INVITE

由於初始化的INVITE請求是一個對話外的請求,它遵循8.1.1節的步驟創建。除此之外還有專門針對INVITE的附加處理步驟。

在INVITE中應當包括一個Allow頭域(20.5節)。它用來標志在這個INVITE建立的這個對話(dialog)中什么樣的方法可以接受。比如,一個UA可以在對話中接收和處理INFO請求[34],那么在INVITE請求的Allow頭域中應當列出這個INFO方法。在INVITE請求中,Supported頭域應當包含。這個頭域包含了所有這個UAC支持的擴展部分。

在INVITE中可以包含一個Accept頭域(20.1節)。這個標志了UA在后續建立的對話中,能兼容的接收和發送的Content-Type。Accept頭域支持不同會話描述格式(session descrioption format)的時候特別有用。

UAC可以通過包含一個Expire頭域(20.19節)來限制邀請的有效期限。如果Expire頭域的時間到了還沒有接收到INVITE的終結應答,UAC處理核心應當像9節描述的那樣產生一個對INVITE請求的CANCEL請求,

UAC還可以根據需要增加Subject(20.36節),Organization(20.25節)和User-Agent(20.41節)頭域。這些頭域都包含了INVITE的相關資料。UAC可以給INVITE增加一個消息體。8.1.1.10節講述了如何構造Content-Type頭域來描述消息體。

對於消息體,有一些特別的規定――他們是基於某種磋商機制的,他們對應的Content-Disposition 是”session”(會話的)。SIP使用一個請求/應答模型,UA發出一個會話描述,稱作是請求,里邊包含了會話的描述。這個請求標志了特定的聯系內涵(比如audio,vidio,game),這些內涵的參數(比如解碼器等等),並且從應答方接收媒體信息的地址。對方UA會回應另外一個會話的描述,稱之為應答,標志了能接受的聯系內涵,這些內涵的參數。這個請求/應答的交換實在對話的上下文進行中的,所以如果一個SIP INVITE請求導致了多個對話,每一個對話都包含自己獨立的請求/應答的交換。請求/應答模型定義了對於請求和應答的限制。(比如在上一個請求尚未處理完成情況下不能發起下一個請求)。這也導致了請求/應答在SIP消息中出現的位置限制。在這個規范中,請求和應答只能出現在INVITE、ACK請求和其應答中。請求和應答的使用中更進一步被限制。在初始化一個INVITE事務中,規則如下:

o 初始化請求必須在INVITE中,如果不在INVITE請求中,就必須在UAS回送給UAC的第一個非失敗的可靠消息中。在這個規范中,這個應答就是2xx應答。

o 如果初始的請求是一個INVITE,那么應答必須是由UAS發送回給對應發出INVITE請求的UAC的可靠的非失敗的消息。在本規范中,只有2xx應答對應這個INVITE請求。同樣相同的應答可能在之前發送的零食應答中存在。UAC必須把它接收到的第一個會話描述當作是應答,並且必須忽略任何在初始INVITE請求中后續的會話描述應答描述。

o 如果初始請求是在第一個可靠的非失敗的UAS回送給UAC的消息中,那么應答必須在這個消息的確認消息中(在本規范中,就是給2xx應答的ACK確認消息)

o 在發送或者接收到第一個請求的應答之后,UAC可以同樣依據這樣的問答方法產生后續的請求。但是只能在收到每一個請求的應答之后才能發起下一個請求。不能在上一個請求尚未收到應答的時候發起下一個請求。

o 當UAS發送或者接收到初始化的請求的時候,禁止在它給初始的INVITE請求的應答中產生后續的請求(協商會話描述請求)。這就意味着基於本規范的UAS在完成初始化的事務之前,不會產生任何會話描述請求。

 

具體來說,根據本規范,上邊的規則分別定義了兩種UA之間交換信息的方法。請求實在INVITE中,應答是在2xx(可能在1xx中也存在,具備相同的值)中,或者請求在2xx中,應答在ACK中。(這個意思是說,兩個UA之間建立連接的時候,首先需要協商一下兩個UA能夠支持的消息體正文,那么這個協商關系也是通過問答形式的,也就是通過請求/應答的,這個媒體磋商的請求既可以在UAC發起的INVITE請求中,也可以在UAS回應的2xx應答中。同樣的,媒體磋商的應答既可以在UAS的2xx應答或者1xx應答中,也可以在ACK確認請求中)。所有的支持INVITE請求的UA都必須支持

 

 

小虎 2006-05-25 00:07

兩種交換方式。會話描述協議(session description protocol sdp)(RFC 2327[1])在所有的UA中都必須得到支持,並且它的用法和請求/應答的構造必須遵循[13]中定義的步驟。

在上邊講述的請求/應答模型中,只能適用於在包頭域Content-Disposition中的值是”session”的包體情況。因此,有可能會INVITE和ACK請求中都包含一個包體信息(比如,INVITE包含一個相片(Content-Disposition:render)並且ACK包含一個會話描述(Content-Disposition:session))。

如果Content-Disposition頭域不存在,Content-Type 是application/sdp的包體實現就等同於Content-Disposition”session”,其他Content-Type的情況就是實現”render”。

當INVITE請求創建以后,UAC遵循對話外請求發送的步驟進行發送(8節)。這也就是創建一個客戶事務並且由這個客戶事務發送請求並且處理應答。

13.2.2 處理INVITE應答

當INVITE請求被傳送給INVITE的客戶事務層進行處理,UAS等待INVITE的應答。如果INVITE客戶事務層返回一個超時而不是收到一個應答,那么這個TU就應當像收到一個408(請求超時)應答(8.1.3節)那樣進行處理。

13.2.2.1 1xx應答

有可能在收到一個或者多個終結應答之前,UAC會收到0個或者1個或者多個臨時應答。INVITE的臨時應答會建立”early dialogs”(早期對話)。如果一個臨時應答在To頭域中有一個tag子頓,並且應答的dialog ID並不是已經存在的對話的ID,那么就應當遵循12.1.2節定義的步驟創建一個對話(早期對話)。

early dialog只會在下邊這個情況中需要:如果一個UAC需要在完成初始的INVITE事務之前,給對方發送一個對話內的請求的時候,就需要early dialog。在臨時應答中的頭域可以在當對話是early state的時候都有效(也就是說,比如一個臨時應答的Allow 頭域包含的方法,在對話狀態是early state的時候都是有效的。[由於Allow是允許的方法集合,所以,當對話狀態是早期對話的時候,這個Allow的集合是不會改變的,但是當創建正式的dialog之后,Allow的集合可能會改變哦]。)

13.2.2.2 3xx應答

一個3xx應答可能包含一個或者多個Contact頭域值,這個頭域值提供了被叫方可能存在的地點。UAC可以根據3xx應答的狀態碼(21.3節)來決定是否嘗試這些新的地址。

13.2.2.3 4xx,5xx,6xx應答

在INVITE請求中,可能會收到單個非2xx終結應答。4xx,5xx,6xx應答如果包含了Contact頭域,那么這個頭域值指示了錯誤的詳細信息的解釋地點。后續的終結應答(只有可能在發生錯誤的情況下),必須被忽略掉。

所有的早期對話都會由於接收到非2xx終結應答而結束。

一旦接收到了非2xx終結應答,UAC處理核心就認為INVITE事務結束了。INVITE客戶事務處理生成對這個應答的ACK(參見17節)。

13.2.2.4 2xx 應答

單個INVITE請求可能會導致多個2xx應答返回給UAC,這是因為proxy可以分支。每一個應答都是由To中的tag參數來進行區分的,並且每一個應答都代表了一個獨立的對話,具備單獨的對話ID。

如果在2xx應答中的對話ID和一個現存的對話匹配,那么這個對話必須切換到”confirmed”狀態,並且對話的路由集合必須基於2xx的應答進行重新計算(參見12.2.1.2)。如果不匹配,那么必須創建一個新的對話,這個對話具備”confirmed”狀態,參見12.1.2的步驟進行創建。

注意在對話狀態中,只有路由集合不需要重新計算。其他部分比如對話內的最大的序列號(遠程的和本地的)等都不需要重新計算。路由集合只是由於需要向后兼容而需要重新計算。RFC 2543並沒有要求在1xx應答中反射Record-Route頭域回來,只在2xx請求中要求了。我們不能更新對話狀態的全部部分,因為在早期對話(early dialog)中可能會存在對話中的請求,比如更改序列號等等。UAC核心必須為每一個2xx應答,產生一個ACK請求。除了在Cseq和身份認證相關的頭域之外,ACK請求的頭域的創建和在對話中的請求創建的方法一樣(12節)。Cseq頭域的序列號部分必須和需要確認的INVITE請求一樣,但是Cseq的方法部分必須是ACK。ACK必須包含和INVITE請求相同的信任狀。如果2xx包含一個媒體磋商請求(基於上述的規則),ACK必須在包體中包含一個媒體磋商應答。如果2xx應答的媒體磋商請求不能被接收,UAC核心必須產生一個有合法的應答ACK,並且立刻發送一個BYE請求。

 

當ACK創建以后,[附件4]中規定的步驟用來檢測對方地址,端口和transport。這個請求是直接交給通訊層進行通訊的,而不是交給一個客戶事務層進行發送。這是由於UAC核心直接處理ACK的重發,而不是事務層進行重發的處理。每次收到一個重發的2xx終結應答的時候都必須發送一個ACK到通訊層。

 

UAC核心認為INVITE事務在接收到第一個2xx應答后的64×T1秒后完成。在這個時間點后,所有沒有轉換成為建立連接狀態的早期對話都會被終止。一旦UAC確認INVITE事務完成了,那么缺省認為不會收到新的2xx應答了。如果,在相應了對INVITE請求的全部應答之后,UAC並不希望創建這個對話,那么UAC必須通過15節描述的那樣發送BYE請求來結束對話。

13.3 UAS處理

13.3.1 處理INVITE

UAS核心從事務層收到INVITE請求。首先根據8.2節定義的步驟進行處理請求,8.2節中定義的是跟對話內外無關的請求的處理。如果處理順利完成(沒有產生應答),UAS核心根據如下步驟進行額外處理:

1、    如果INVITE請求包含一個Expires頭域,UAS核心就設置一個時鍾計數=這個頭域值。如果時鍾到了,這個邀請就過期了。如果在UAS尚未產生終結應答的時候就超時了,那么487(請求終止)應答應當產生給UAC。

2、    如果請求是一個對話中的請求,12.2.2節定義的方法無關的處理步驟將首先進行處理。這個處理可能會影響到會話;14節講述了細節。

3、    如果請求的To頭域包含了一個tag,但是對話的ID與現存的任何一個對話都不匹配,那么UAS可能是由於崩潰而重新啟動的,或者是由於接收到了本應當發送給另外一個UAS的請求(或者就簡單是由於請求填寫錯誤)。12.2.2節提供了這種情況的處理指引。從這開始的處理將假定這個INVITE是在對話外的,並且INVITE請求的目的是建立一個新的會話。INVITE請求可能包含一個會話描述,在這種情況下是希望和UAS進行會話媒體的磋商。即使INVITE請求是對話外發出的,這個INVITE參與的用戶也有可能正是那個會話中的參與方。這個是由於在多方會議中,某個正在會議中的用戶,被其他參與方邀請參加。如果需要鑒別這樣的情況,UAS可以使用會話描述來檢查是否重復邀請。比如,SDP包含了會話的ID和版本號。如果這個用戶本身就是會話中的一方,並且session參數包含的會話描述沒有改變,UAS可能就悄悄接受這個邀請(就是說,在不提示用戶的情況下發送2xx應答)。

如果INVITE並沒有包含某個會話描述(session description),UAS就是被邀請創建一個會話,並且UAC已經希望UAS來提供這個會話offer。UAS必須在它的給UAC的第一個非失敗的可靠消息中提供這個offer。在本規范中,給INVITE請求的2xx應答中就應當提供這個offer。

UAS可以提示進度,接受,轉發,或者拒絕這個邀請。在這些情況下,它通過按照8.2.6節描述的步驟建立應答。

13.3.1.1 提示進度

如果UAS不能馬上接受或者拒絕邀請,那么它可以提示某種形式的進度給UAC(比如提示一個回鈴聲等等)。這是通過一個101到199的臨時應答實現的。這些臨時應答建立了早期對話(early dialog)(通過8.2.6和12.1.1)。如果UAS願意,UAS可以發送多個臨時應答。每一個臨時應答都必須包含相同的dialog ID。這些臨時應答都並非可靠傳送的。

如果UAS打算延長一點時間來響應這個INVITE請求,它需要請求一個”extension”來防止proxy來取消這個事務。proxy有權利來取消超過3分鍾未完成的事務。要防止這個取消,UAS必須每分鍾發送一個非100臨時應答,防止由於1xx臨時應答的非可靠傳輸導致的臨時應答丟失。

如果呼叫出於等待狀態(比如用戶設置成為呼叫等待的)或者這個呼叫正在和PSTN電話系統進行通訊(PSTN系統允許呼叫沒有應答),一個INVITE事務是可以被延長處理時間的。

13.3.1.2 INVITE請求轉發

如果UAS決定轉發這個呼叫,就需要發出3xx的應答。300(多重選擇),301(永久轉移),302(臨時轉移)應答中應當包含一個Contact頭域,這個頭域包含了一個或者多個表明需要重試的URI新地址。這個應答交給INVITE服務端事務層,由服務端事務層負責應答的重發。

13.3.1.3 INVITE請求的拒絕

拒絕INVITE請求的常見情景是被叫方不想或者不能在終端系統上接收這個呼叫。486(用戶忙)應當在這樣的情況下返回。如果UAS知道沒有其他終端系統能夠響應這個呼叫,就應當返回一個600(Busy Everywhere)。不過,通常情況下UAS是不太會知道這個情況的,並且這個應答也是罕見的。這些應答是交給INVITE服務端的事務層進行發送的,由這個事務層來保證應答的重發機制的。如果UAS拒絕的是INVITE請求包含的媒體磋商offer,UAS應當返回一個488(Not Acceptable Here)應答。這個應答應當包含一個Warning頭域來解釋為何offer被拒絕。

13.3.1.4 接受INVITE請求

UAS核心產生一個2xx應答。這個應答建立一個對話,然后遵循8.2.6節和12.1.1節的描述進行處理。

響應INVITE請求的2xx應答包含Allow頭域和Supported頭域,並且可能包含Accept頭域。包含這些頭域的目的是為了讓UAC不需要再次請求就能夠知道UAS的特性以及UAS的擴展支持。

如果INVITE請求包含了一個媒體磋商請求offer,並且UAS還沒有發送應答,2xx應答中必須包含針對這個offer的應答。如果INVITE請求沒有包含這個offer,而且UAS也尚未發出offer,2xx應答必須包含這個媒體磋商offer。

當應答構建好了以后,它會交給INVITE的服務端事務層進行發送。注意,INVITE的服務端事務將會由於收到這個終結應答並且交給通訊層進行發送而銷毀。因此,有必要在沒有收到ACK的時候,每隔一定的時間就直接交給通訊層進行發送。2xx交給通訊層進行發送的時間間隔是從T1秒開始,並且每次發送后就加倍,直到到達T2秒的時間間隔(T1和T2的時間間隔定義在17節)。當收到了針對這個應答的ACK請求之后,重發就終止了。這個是與使用什么通訊協議來發送這個應答是無關的。

由於2xx的重發是端到端的,並且在UAS和UAC之間存在采用UDP通訊的節點。所以要保證通過這些節點進行可靠的傳送,就必須采用間隔時間重發的機制,哪怕UAS本身的通訊機制是可靠的。

如果服務端的對2xx應答的重發經過了64×T1秒還沒有收到ACK請求,那么dialog就認為是confirmed,但是會話卻應當終止。這個是用過15節描述的方法發送BYE請求來結束。

14 更改已經存在的會話

一個成功的INVITE請求(13節)既會創建一個基於兩個用戶之間的對話,也會基於請求/應答模式(offer-answer)創建一個會話。12節講述了如何通過target refresh 請求來修改一個現存的會話(比如,修改對話的remote target URI)。本節描述如何修改實際的會話(session)。

這個修改可以包括修改地址或者端口、增加媒體流、刪除媒體流等等。這是通過發起新的INVITE請求來完成的,並且這個新的INVITE請求是基於建立會話所相同的對話的。在一個現存對話中發出INVITE請求就是re-INVITE.

注意,單個的re-INVITE請求可以同時更改對話和會話的參數。

呼叫方或者被叫方都可以更改現存的會話。

在本協議中,UA檢測本地媒體有效性是基於自身的策略的。但是,我們並不建議自動產生re-INVITE或者BYE請求,因為這樣可能會導致網絡上的阻塞。在任何情況下,如果某些消息將被自動發送,那么他們應當等待一個隨機的時間間隔。

注意,上邊的這些描述是特指自動產生的BYE和re-INVITE。如果用戶由於媒體不兼容而掛機,UA應當正常發出BYE請求,而不視為自動產生的BYE。

14.1 UAC行為

與INVITE相同的會話描述磋商offer-answer模式(13.2.1節)在re-INVITE中也一樣采用。假設UAS希望增進一個媒體流,那么UAC將會創建一個新的offer包含這個媒體流,並且發送INVITE請求給他的對方。特別需要注意的是,這個會話的全描述,而不是變化部分需要傳送。這個支持無狀態的會話處理,並且支持錯誤恢復機制。當然,UAC可以發送一個re-INVITE請求而不包含會話描述,在這樣的情況下,就是在這個re-INVITE的第一個可靠的非失敗的應答中將會包含這個會話描述offer(在這個規范中,就是2xx應答)。

如果會話描述格式具有版本號碼,那么這個磋商的offer應當標志這個變化了的媒體描述版本。

re-INVITE請求中的To,From,Call-ID,Cseq,Request-URI頭域應當和正常的在對話中的請求構造方法一樣(12節)。

在re-INVITE請求中,UAC可以選擇不增加一個Alert-Infor頭域或者具有Content-Disposition=”alert”的消息體。因為UAS通常不會要求提示操作者來響應這個re-INVITE請求。

和INVITE不同的是,INVITE可以分支(分岔成為多份INVITE),re-INVITE是不會分支的,所以,只會由一個單個的終結應答。re-INVITE不會分岔的原因是因為Request-URI標志的是建立對話的UA的目標地址,而不是用戶的address-of-record地址。

需要注意的是,在相同的對話中,UAC不能在上一個INVITE請求完成前(無論是那一方發起的INVITE)再次發起一個新的INVITE。

1、    如果有正在處理的INVITE客戶事務,TU必須等待這個事務終結或者完成,才能初始化一個新的INVITE。

2、    如果有正在處理的INVITE服務事務,TU必須等待這個事務確認或者終結,才能開始處理一個新的INVITE。

不過,UA可以在INVITE事務正在處理的同時,處理一個普通的事務。也可以在一個普通事務正在處理的同時來初始化一個INVITE事務。如果UA接收到一個針對re-INVITE的非2xx終結應答,則會話參數不能改變,應當就像沒有收到過這個re-INVITE請求一樣。注意,就像在12.2.1.2節一開始講的那樣,如果非2xx終結應答是一個481(Call/Transaction Does Not Exists),或者一個408(Request Timeout),或者完全沒有re-INVITE請求的應答(也就是說從INVITE客戶事務端返回一個超時),UAC會終止這個對話。

如果UAC收到一個re-INVITE的491應答,他應當啟動一個值為T的時鍾,這個T的取值如下:

1、    如果UAC是這個dialog ID的Call-ID的擁有者。(也就是說是UAC產生的Call-ID),那么T取值為一個2.1到4秒的隨機數,單位是10毫秒。

2、    如果UAC並非是dialog ID的Call-ID的擁有者,T應當取值是0到2秒的隨機數,單位是10毫秒。

當這個時鍾到了,如果UAC還希望再次嘗試更改會話參數,UAC應當再次嘗試re-INVITE請求一次。這個意思是說,如果這個呼叫已經被BYE所掛掉了,那么re-INVITE請求就沒有再發的必要。

發送re-INVITE請求的規則,以及針對re-INVITE請求產生的2xx應答而產生的ACK請求的發送規則,等同於初始的INVITE請求(13.2.1節)。

14.2 UAS行為

13.3.1節描述了區分初始的INVITE請求和re-INVITE請求的方法,以及處理對現存的對話中處理re-INVITE請求的步驟。

UAS在發送第一個INVITE的終結應答之前,收到第二個INVITE請求,並且這個請求的Cseq序列號大於第一個INVITE請求,那么就應當給第二個INVITE請求返回一個500(服務器內部錯誤)應答,並且必須包含一個Retry-After頭域,這個頭域中應當包含一個0-10秒的隨機數。

如果UAS正在處理一個INVITE請求的時候又收到了一個在同一個對話上的INVITE請求必須返回一個491(Request Pending)應答給接收到的INVITE。

如果UA接收到一個對現存的對話的re-INVITE請求,那么就必須檢查有關會話描述(session description)的版本標志(version identifiers),或者,如果沒有版本標志,那么就需要檢查會話描述的正文看看有沒有變化。如果會話描述改變了,UAS必須由此調整會話參數,在調整參數的時候可能會要求用戶確認。

 

會話描述的版本可以用來提供給會議的新近加入者,增加或者刪除媒體,或者由單點會議更改成為多方會議。

如果新的會話描述是不能被UA接受的,UAS可以用返回一個488(Not Acceptable Here)來拒絕這個re-INVITE請求。這個響應應當包含一個Warning頭域(用來提供給請求方,提供這個拒絕的原因)。如果UAS產生一個2xx應答,但是沒有收到ACK,它應當產生一個BYE來結束這個對話。

UAS可以不產生180(Ringing)應答給re-INVITE,因為UAC一般不展示這個信息給用戶。同樣的,UAS可以增加一個Alert-Info頭域或者一個由Content-Disposition“alert”的消息體來應答給re-INVITE來讓UAC展示這個信息。

如果UAS在2xx應答中提供了一個媒體磋商offer(因為INVITE沒有包含一個offer),那么它應當當作新呼叫一樣的構造這個offer,就像在[13]中SDP描述一樣,遵循發送更改現有的會話的offer的限制。特別需要注意的是,這個意味着它應當包含全部的UA支持的媒體類型和媒體格式。UAS必須確保會話描述在媒體格式,通訊方式,或者其他要求對方支持的參數上,新的會話描述和舊的會話描述一樣。這個可以避免對方拒絕這個會話描述。如果,UAC任然是不能接受這個會話描述,UAC應當產生一個它能夠接受的會話描述應答,並且發送一個BYE來結束這個會話。

 

15 結束一個會話

本節描述了結束由SIP建立的會話的步驟。會話的狀態和對話的狀態是密切相關的。當一個會話由INVITE建立的時候,每一個由不同UAS的1xx或者2xx的應答創建一個對話,並且當完成了會話描述的請求/應答(offer/answer)交互之后,它也就創建了一個會話。這就是說,每一個會話都和單個對話”相關”-會話是對話所創建的。如果初始化的INVITE產生了非2xx的終結應答,它也終結了由本次請求創建的任何會話(如果有的話),並且終結了所有的本次請求創建的對話(如果有的話)。由於事務完整性的保證,一個非2xx的終結應答同樣也防止了本次INVITE以后可能創建的會話。BYE請求用於終結指定的會話或者嘗試建立的會話。在這里,特定的會話是一個和與之相對的對話的對方UA。當在對話中接收到了一個BYE,任何與該對話相關的會話都應當終止。UA禁止在對話外發送BYE請求。請求方UA可以在已經建立好的對話或者早期對話中發起BYE請求;被叫方只能在建立好的對話中發起BYE請求,不能在早期對話中發起BYE請求。

不過,在一個建立好的對話中,被叫方的UA不能在接收到對應2xx應答的ACK請求前發送BYE請求,或者不能在服務器事務超時前發送BYE請求。如果沒有SIP擴展定義了和這個對話相關的其他應用層狀態,這個BYE請求同樣結束了對話。

 

在對話和會話中,給INVITE的非2xx的終結應答,使得使用CANCEL比較有吸引力。CANCEL是嘗試強制給INVITE請求一個非2xx應答(比如,487應答)。因此,如果UAS希望放棄整個呼叫,它可以發送一個CANCEL。如果INVITE會有2xx終結應答,這個意味着UAS在CANCEL正在處理的時候,接收到一個邀請。UAC可以繼續用這個2xx應答建立會話,也可以用BYE終結這個會話。(這個意思是說,一般情況下,如果UAC希望cancel 這個INVITE請求,那么就會發出CANCEL請求,如果接收到了非2xx的終結應答,就意味着CANCEL掉了,但是如果接收到的還是2xx應答,就說明沒有CANCEL掉,沒有CANCEL掉呢,就可以選擇繼續建立會話,或者說發送一個BYE來終結會話)

 

在SIP中,並沒有一個很好的”hangin up”(掛機中)定義。它屬於一個用戶界面的普通常見的細節。通常,當用戶掛機,它意味着結束建立會話的嘗試,並且終止所有已經建立的會話。對於呼叫方的UA來說,如果沒有收到初始INVITE請求的終結應答,這個可能是產生對初始INVITE請求的一個CANCEL請求,並且收到終結應答之后給每一個建立好的對話發出一個BYE。對於被叫方的UA,就是很普通的BYE;粗略來說,當用戶(因為響應振鈴)摘機,就會產生一個2xx應答,於是掛機會在收到ACK請求之后發送一個BYE。這不是說在收到ACK之前用戶不能掛機,這只是表達在用戶的電話中的軟件,需要保持一小會兒狀態,來正確釋放狀態。如果某個UI(用戶界面)允許用戶在不摘機的情況下拒絕呼叫,可以用403(Forbidden)來作為INVITE的應答,在這樣的情況下,BYE就不能發送。

15.1 使用BYE請求終止一個會話

15.1.1 UAC行為

BYE請求就像其他在對話內的請求一樣的構造,參見12節的描述。

當BYE請求創建好了之后,UAC核心處理部分創建一個新的非-INVITE客戶端事務,並且用它來處理BYE請求。UAC必須認為當BYE請求一發送到客戶端事務,會話就結束了(因此也就停止發送或者接收媒體流)。如果BYE請求的應答是481(Call/Transaction Does Not Exists)或者408(Request Timeout)或者BYE請求壓根沒有應答(就是說客戶端事務返回一個超時),UAC必須認為會話和對話都已經結束。

15.1.2 UAS行為

UAS首先按照通用的UAS接收到請求的處理步驟進行BYE請求的處理(8.2節)。UAS核心處理部分接收到BYE請求以后,首先檢查它是否和現存的對話匹配。如果BYE並不匹配現存的任何一個對話,那么UAS應當產生一個481(Call / Transaction Does Not Exists )應答,並且傳送給服務器事務。

這個規則意味着如果UAC發送沒有帶tag標志的BYE請求會被拒絕。這個是一個對RFC2543的改動,RFC2543允許BYE不帶tag標志。

UAS核心處理部分接收到BYE請求,並且發現和現存的對話匹配,那么它必須遵循12.2.2的步驟來處理請求。一旦處理完成,UAS應當終止這個會話(因此停止發送和接收媒體流)。唯一一個可以選擇不終止的情況是多方會話,在多方會話中參與者允許對話中的其他參與方終止他們自己的會話。不管是否終止會話中的參與方,UAS核心處理都必須給BYE產生2xx的應答,並且必須由服務器的通訊層進行傳輸。

UAS必須依舊響應在這個對話中接收到的未決的請求。我們建議用487(請求終止)來給這些未決的請求以應答。

16 proxy行為

16.1 概述

SIP代理服務器是路由SIP請求到UAS的,並且路由SIP應答到UAC的。一個請求可能通過多個proxy到達UAS。每一個都會作出路由決定,在發送給下一個節點前對請求做一點修改。應答會通過和請求相同的proxy路徑,只是順序是逆序的。

proxy是一個SIP邏輯上的概念。當接收到一個請求,在做代理服務器之前,首先應該有一個部件來決定是否自身需要響應這個請求。例如,在作為代理服務器處理請求之前,首先判定請求可能是非法的或者請求需要一個信任狀。這個元素可以使用任何合適的錯誤代碼來響應這個請求。當直接應答請求的時候,這個元素(proxy)將作為UAS角色,並且必須遵循8.2節描述的UAS行為規范。

proxy對於每一個新的請求來說,既可以作為有狀態的也可以作為無狀態的模式來處理。當作為無狀態的處理模式的時候,proxy就是簡單的轉發。它轉發每一個請求下行到一個由請求所決定的目的地。並且簡單轉發從上行流取得的應答。一個無狀態的proxy在處理完一個消息之后就會丟棄這個消息的相關資料。有狀態的proxy會保留這些信息(尤其是事務信息),保留每一個接收的請求和每一個接收請求的應答的相關信息。它保留這些信息用於處理與這個請求相關的后續消息。一個有狀態的proxy可以選擇”分支”一個請求,路由它到多個地點。任何被路由到多個地點的請求都必須當作有狀態的處理。在某些情況下,proxy可以用有狀態的通訊協議(比如TCP)來轉發請求,而不用自身成為事務有狀態的。例如,proxy可以簡單轉發請求從一個TCP連接到另外一個TCP連接,而不用自身作為有狀態的,只要它放置足夠的信息在需要轉發的消息里,使得能夠正確把應答發送到接收到對應請求的連接發送出去。如果在不同傳輸通訊協議之間進行請求的轉發,那么就必須要求proxy的TU采用某種手段來保證可靠的從一個協議有狀態的轉到另一個協議。

有狀態的proxy可以在處理請求中的任何時候轉換成為無狀態的,只要它不作任何可能導致不能無狀態的操作(比如分支,比如產生100應答)。當做這樣的轉換的時候,所有的狀態就只是簡單的廢棄掉。proxy不應當發起一個CANCEL請求。

在作為無狀態的或者有狀態的時候,處理請求的步驟是一樣復雜的。接下來的步驟是從有狀態的proxy角度來些的。最后一節是講述無狀態proxy的區別。

16.2 有狀態的proxy

作為有狀態的proxy,它必須是一個純粹的SIP事務處理引擎。它在這里的定義遵循17節講述的服務端和客戶端的事務處理規定。有狀態的proxy有一個服務端事務,這個事務與一個或者多個客戶端事務相關,這些客戶端事務是由高層proxy處理元素產生的(圖3),這些高層proxy處理元素就是proxy處理核心。一個輸入的請求是通過一個服務端事務來處理的。請求由服務端事務交給proxy處理核心進行處理。proxy處理核心檢查請求應當路由到哪里,選擇一個或者多個下一個節點。每一個發往下一個節點的外發請求都由客戶端事務進行處理。proxy處理核心從客戶端事務中得到請求的應答並且把他們的應答交給服務端事務進行發送。

有狀態的proxy為每一個接收到的新的請求創建一個服務端事務。任何請求的重復都是由這個服務端事務來處理(參見17節)。proxy處理核心必須遵循UAS的模式,發送一個直接臨時應答(比如100 trying)到這個服務端事務上(8.2.6節)。因此,有狀態的proxy不應當給非INVITE請求產生100(trying)應答。

這是一個proxy的模型,並非軟件實現。在實現上可以擴展並且復用用這個模型定義。

對於所有的新請求來說,包括那些未知方法的請求,proxy處理請求必須:

1、    驗證請求(16.3)

2、    預處理路由信息(16.4)

3、    決定請求的目的(targets)(16.5)

 

 

+--------------------+

|                        | +---+

|                        | | C |

|                        | | T |

|                        | +---+

+---+    |            Proxy        | +---+ CT = Client Transaction

| S |    |    "Higher" Layer    | | C |

| T |    |                        | | T | ST = Server Transaction

+---+    |                        | +---+

|                        | +---+

|                        | | C |

|                        | | T |

|                        | +---+

+--------------------+

Figure 3: Stateful Proxy Model

4、    轉發請求到每一個目的地(16.6)

5、    處理所有的應答(16.7)

 

 

小虎 2006-05-25 00:07

16.3 驗證請求

在proxy轉發請求之前,它必須檢查消息的合法性。一個合法的消息必須經過如下的檢查:

1、    合法的語法

2、    URI scheme

3、    最大轉發次數

4、    (可選)循環檢測(loop detection)

5、    proxy-require

6、    proxy-authorization

如果任何一步失敗了,proxy都必須作為UAS(8.2)一樣,應答一個錯誤碼。

注意proxy並不要求檢查合並的請求,並且不能把合並的請求當作一個錯誤的情況。終端接收到合並的請求會根據8.2.2.2節講述的內容進行分解。

1、    合法的語法。

請求要能夠被服務端事務處理,那么請求就必須是語法無誤的。請求中的任何與檢查相關的部分或者與請求轉發節相關的部分都必須語法嚴格無誤。在檢查中,其他部分的嚴格與否,在檢查中都被忽略,並且在轉發消息過程中保持不變。例如,proxy不會由於非法的Date頭域而拒絕請求。同樣的proxy不會在轉發請求的時候移去非法的Date頭域。這個是為了設計成為可擴展的。以后的擴展可能定義新的方法、新的頭域。proxy不能拒絕由於包含了不認識的方法或者不認識的頭域而拒絕轉發這個請求。

2、    URI scheme檢查

如果Request-URI包含一個proxy所不能理解的URI形式,那么proxy應當通過返回一個416來拒絕這個請求(Unsupported URI scheme)。

3、    最大轉發次數檢查

最大轉發次數Max-Forwards頭域(20.22)是用來限制轉發的次數的。如果請求沒有包含Max-Forwards頭域,那么這個檢查將被忽略。如果請求包含了一個Max-Forwards頭域,並且這個頭域大於0,那么這個檢查就跳過。如果請求包含一個Max-Forwards頭域,並且這個頭域為0,那么這個proxy不能轉發這個請求。如果請求是OPTIONS請求,那么proxy可以作為最終響應者來響應這個請求,並且遵循11節講述的產生應答。否則proxy應當返回一個483(too many hops)應答。

4、    可選的Loop Detection檢查

proxy可以檢查看看轉發是否可能導致循環。如果請求包含一個Via頭域,並且這個頭域值,和proxy早先保留的請求的頭域值相同,那么這個請求就是以前經過過本proxy。要么這個請求就是循環處理了,要么就是合法的循環處理。要檢測請求是否循環處理,proxy可以用branch參數,根據16.6節的第8步來計算,並且和接收到的Via頭域做比較。如果參數相等,那么請求就循環了。如果不等,那么請求就是合理的經過,並且繼續處理。如果檢測到了循環,那么proxy應當返回一個482(LoopDetechted)應答。

5、    Proxy-Require檢查

本協議的以后的擴展可能會要求額外的proxy特性。所以終端會在請求中包含一個Proxy-Require頭域來表明會使用到那些特性,這樣proxy就可以根據Proxy-Require判定自己是否能夠支持這些特性。

如果請求包含一個Proxy-Require頭域(20.29)並且有一個或者多個本proxy不能理解的option-tags。那么這個proxy必須返回一個420(Bad Extension)錯誤,並且這個錯誤應答必須包括一個Unsupported(20.40)頭域列明了那些option-tags這個proxy不能支持。

6、    Proxy-Authorization 檢查

如果proxy要求在轉發請求之前進行身份認證,那么必須根據22.3節中描述的那樣進行請求的檢查。22.3節也定義了proxy應當怎樣處理檢查失敗的情況。

16.4 路由信息預處理

proxy必須檢查請求中的Request-URI部分。如果Request-URI包含了一個本proxy早先放在Record-Route頭域中的值(參見16.6,4),proxy必須用Route頭域中的最后一個值來替換Request-URI,並且從Route頭域中刪去這個值。proxy必須接着按照個修改后的請求進行處理。

這個只會在某元素發送請求到proxy(proxy本身可能是一個終端),並且這個請求是基於嚴格路由的。在接收到請求后重寫這個字段是必須的,因為要保持向后兼容性。同時也允許按照本規范實現的元素保護Request-URI通過嚴格路由的proxy(12.2.1.1節)。

這個要求並沒有強制proxy保留狀態來檢查其早先放在Record-Route頭域中的URI。作為替換的方法,proxy只需要保留足夠的信息在那些URI里邊,這樣,在以后出現的時候就能識別了。

如果Request-URI包含了maddr參數,proxy必須檢查這個參數來看看是否在proxy配置的可信任的地址列表或者可信任的區域列表中。如果Request-URI包含一個maddr參數,並且這個參數包含了一個proxy可以信任的地址,並且這個請求是通過Request-URI中(指明或者缺省)的端口和協議接收到的,proxy必須抽掉maddr和其他非缺省的端口和通訊參數,並且繼續處理。

 

proxy可能收到一個帶有匹配maddr的請求,但是不是在URI中指出的端口和transport接收到的。這個請求需要通過指明的port和transport轉發到對應的proxy。

 

如果Route頭域的第一個值就是這個proxy,那么proxy必須從請求中把它移去。

 

16.5 確定請求的目的

接着,proxy計算請求的目的(或者多個目的地)。目的地集合可以由請求的內容決定或者由絕對位置服務提供。目的地集合中的每一個目的地都由URI來表達。

 

如果請求中的Request-URI包含了maddr參數,必須把Request-URI放在目標集合中,並且是作為唯一一個目標URI,並且proxy必須按照16.6節中的約定進行處理。

 

如果Request-URI的區域並非本proxy負責的區域,那么Request-URI必須放在目標集合中,並且作為唯一一個目標URI,並且proxy必須按照16.6節中的約定進行處理。

 

有很多種情況都會導致proxy收到並非本proxy負責的區域的請求。一個防火牆proxy處理外發的請求(就像HTTPproxy處理外發的請求)就是一個典型的例子。

 

如果請求的目標集合沒有像上邊講述的這樣預先設定,那么這就意味着proxy是負責Request-URI所指明的區域的,並且proxy可以用任何機制來決定往哪里發送這個請求。這些機制都可以歸結成為訪問一個絕對位置服務的形式。這個可能由從SIP注冊服務器創建的位置服務器獲得信息、讀取數據庫、查閱服務器、利用其他協議、或者就簡單的替代Request-URI來實現。當訪問由注冊服務器創建的位置服務的時候,在作為索引查詢之前,Request-URI必須首先根據10.3節進行規范處理。這些機制的輸出結果將作為目的地集合。

如果Request-URI沒有提供足夠的信息來讓proxy能夠產生目的地集合,它應當返回一個485(Ambiguous)應答。這個應答應當包含一個Contact頭域包含一些應當嘗試的新位置。比如,一個到sip:John.Smith@company.com的INVITE可能在某一個proxy是不明確的,因為這個proxy有多個JohnSmith。21.4.23節有細節描述。

 

任何與這個請求有關的,或者與proxy當前環境有關的信息都可以用來構造目的地集合。例如,由於請求的內容不同或者包頭域的不同,可以有不同的目的地集合,又或者不同時間到達的請求也可以有不同的目的地集合,或者不同的時間間隔,上一次失敗的請求,甚至是當前proxy的利用率都可以導致目的地集合的不同。

 

通過這些機制,我們可以有一個可能的目的地列表,他們的URI被增加到目的地集合。每一個目的地只能在目的地列表中出現一次。如果目的URI已經在這個集合中存在了(基於URI類型的相等定義),那么它不能再次增加。

 

如果原請求的Reuqest-URI指明的區域並非本proxy所負責的區域,那么本proxy不能增加任何額外的目的地到目的地集合。

 

如果proxy負責Request-URI所指明的區域,那么這個proxy只可以在轉發的時候改變請求的Request-URI。如果proxy並非負責這個URI,那么它不會在3xx或者416應答的時候查生遞歸。

 

如果原始請求的Request-URI是屬於本proxy負責的區域的,那么proxy可以在請求轉發的時候增加目的地。他可以在處理過程中,用任何可以獲得的信息來決定新的目的地。 例如,proxy可以選擇把一個轉發應答(3xx)所包含的聯系地址合並到目的地集合中。如果proxy使用一個動態的信息源來構造目的地集合(例如訪問SIP的注冊服務器),它應當在處理請求的過程中監測這個信息源。當有新的目的地出現的時候,就應當加到這個目的地集合里邊。就像上邊說得,每一個URI只能在集合中出現1次。

 

只能出現1次的原因是可以降低網絡沖突,在合並重定向請求的聯系地址的情況下可以防止無限遞歸的出現。

 

舉例來說,一個簡單的位置服務是一個”no-op”(無操作的),返回的目的URI就是輸入的請求URI。請求將送到一個特定的下一個節點proxy。在16.6節/6小節定義請求轉發中,通過SIP或者SIPS URI表達的,下一個節點的身份, 將在Route的頭域最上一層插入。

如果Request-URI是這個proxy所負責的,但是在本proxy中找不到,那么proxy必須返回404(Not Found)應答。

 

如果目的集合經過上邊的處理依舊是空的,那么proxy必須返回一個錯誤應答,這個錯誤應答應當是408(暫時不可用)。

 

16.6 請求轉發

當目的地集合不是空的時候,proxy可以開始轉發這個請求。有狀態的proxy可以按照任意的順序處理這個目的地集合。它可以順序處理多個目的地,上一個完成前下一個不能開始。也可以采用並行的處理多個目的地。也可以通過分組的形式,每組之間是串行的,組內是並行的。

 

通常的處理順序機制是使用一個Contact頭域的qvalue參數來處理(20.10節)。目的地從最高的qvalue開始處理到最低的qvalue。相同qvalue的目的地可以並行處理。

 

有狀態的proxy必須包含針對目的地集合的一個接收到應答和轉發出去的原始請求進行匹配的機制。為了完成這樣的目的,這個機制是一個由proxy層在轉發第一個請求前創建的”response context”(應答上下文)來保障的。

 

對於每一個目的地,proxy轉發請求都遵循下列步驟:

1、    拷貝一個接收到的請求

2、    更新Request-URI

3、    更新Max-Forwards頭域

4、    可選增加一個Record-Route頭域

5、    可選增加附加的頭域

6、    路由信息后處理

7、    決定下一個節點地址、端口、通訊協議。

8、    增加一個Via頭域值

9、    如果需要,增加一個Content-Length頭域

10、    轉發這個新的請求

11、    設置定時器C

下面詳細介紹每一步。

 

1、    拷貝請求

proxy首先把接收到的請求做一個拷貝。拷貝必須包含接收到的請求的全部頭域。在接下來的處理步驟中未提及的頭域不能刪除。拷貝應當保留接收到的請求的頭域的順序。proxy不能用合並的域名來進行域值的重新排序(參見7.3.1)。proxy不能增加、修改、刪除消息體。

實際上,在實現中並非只是做一個拷貝;首要的事情是為每一個下一個節點准備一個相同的請求。

 

2、    Request-URI

在拷貝好的請求中的Request-URI必須用目的地的URI進行替換。如果這個目的URI包含任何在Request-URI中所不能允許的參數,那么這些參數必須被刪去。

這個步驟是proxy的本質步驟。proxy通過這個機制來把請求轉發到目的地。在某些情況下,接收到的Request-URI會不作更改的添加到目的地集合中。對於這樣的目的地來說,上邊講的替換就等於是沒有任何操作。

 

3、    Max-Forwards

如果拷貝的頭域包含一個Max-Forwards,proxy必須把這個域值減一。

如果拷貝的頭域沒有包含一個Max-Forwards頭域,proxy必須自己增加一個頭域,缺省值是70。現在有一些UA不會在請求中填寫Max-Forwards頭域。

 

4、    Record-Route

(假設proxy接收到的這個請求會創建一個對話的情況下),如果希望保留這個請求創建的對話中,后續的請求依舊是要經過本proxy,那么本proxy必須增加一個Record-Route頭域值在這個拷貝中,並且增加的這個頭域值應當是在其他現存的Record-Route頭域之前。通過請求建立的對話可以包含一個預置的Route頭域。

如果這個請求已經是一個對話的一部分,proxy如果希望以后這個對話的請求依舊經過本proxy,那么proxy應當增加一個Record-Route頭域值。在12節描述的普通的終端操作中,這些Record-Route頭域值不會對終端使用的路由集合造成任何影響。

 

如果請求本身已經在對話中的話,如果proxy不增加一個Record-Route頭域在請求的包頭,后續的請求也會經過本proxy。但是,如果當終端中斷並且重新構造這個對話的時候,本proxy就會從對話所經過的節點中刪去。

 

一個proxy可以在任何請求中增加這個Record-Route頭域值。如果請求並沒有初始化一個對話,終端將會忽略這個頭域值。12節講述了終端如何使用Record-Route頭域來構造Route頭域的。

 

在請求路徑上的每一個proxy都是獨立的決定是否增加一個Record-Route頭域值的-在請求的Record-Route頭域上的值並不影響這個proxy決定增加還是不增加Record-Route頭域值。

 

在Record-Route頭域中防止的URI必須是SIP或者SIPS URI。這個URI必須包含一個lr參數(參見19.1.1)。這個URI可以和請求將被轉發的地方不同。這個URI不應當包含通訊參數,除非該proxy確認在后續請求將會經過的下行節點中,都支持這個通訊參數(比如本地網絡等等)。

 

本proxy提供的這個URI可能會讓其他元素(其他proxy)作出路由決定。本proxy,通常,並不知道其他節點的處理能力,所以,它必須嚴格律己,讓自己遵循規范的SIP實現:SIP URI和TCP或者UDP通訊協議。

 

在Record-Route中的URI必須指向插入它的元素(或者替代元素),這個意思是說通過附件[4]的服務器定位步驟可以順利找到這個元素,這樣后續的請求才能順利到達同一個SIP元素。如果Request-URI包含一個SIPS URI,或者Route頭域的最上的值(經過后續第6步的處理)包含一個SIPS URI,那么插入Record-Route頭域的值必須是一個SIPS URI。而且,如果請求不是基於TLS接受的,那么proxy必須增加一個Record-Route頭域。在相似的情況下,proxy如果從TLS上接收的請求,但是產生的是一個在Record-Route中或者Route頭域最上值中沒有SIPS URI的請求(在第6步后處理之后),必須在Record-Route頭域中增加一個非SIPS URI。

 

在安全范疇內的proxy必須在對話中保持這個安全范疇。

 

當Record-Route頭域的URI在應答中又重新到達的時候,如果這個URI值需要重寫的時候,這個URI必須是能夠唯一確定的URI。(就是說,請求可能會經過這個proxy好幾次,造成一個或者多個Record-Route頭域值的增加)。16.7節的第8步提供了一個能夠讓這個URI唯一的一個機制。

 

這個proxy可以在Record-Route頭域中增加一些參數。這些參數在某些請求的應答中會被反射(echo)回來,比如給INVITE請求的200(OK)應答。通過在消息的參數中保持狀態比在proxy中保持狀態更加有效。

 

如果proxy想在任何類型的對話中都保持在請求的路徑上(比如在跨越防火牆的對話中),它需要給每一個接收到的請求中,都增加Record-Route頭域,即使是它所不能理解的方法的請求也要增加,因為這些方法可能是對話相關的,具有對話語義的方法。

 

在Record-Route頭域中增加的URI只是在當這個請求創建對話的時候有效。舉一個例子,對於一個對話-有狀態的proxy(dialog-stateful proxy),當在對話結束后,如果再收到一個請求,這個請求的Request-URI的值中包含這個URI,那么它就可以選擇拒絕這個請求。一個對話狀態無關的proxy,當然,沒有對話結束的概念,但是他們可以再這個值中填寫足夠多的信息,這樣就可以在以后的請求來到的時候做對話的ID的比較,並且可以選擇拒絕不匹配這個信息的請求。終端不能在對話外使用這個對話中的Record-Route頭域的URI。參見12節描述的終端使用Record-Route頭域的細節。

 

當proxy需要查看所有對話中的消息的時候,我們就需要Record-routeing。但是,他會降低處理性能和影響擴展性,因此proxy應當只在特定情況下使用record-route。任何初始化一個對話的SIP請求都可以適用Record-Route。在本文檔中,只有INVITE請求是可以適用的,以后的擴展文檔可能包含其他的方法。

 

5、    增加附加的頭域

在這一步,proxy可能增加其他適當的頭域。

 

6、    處理路由信息

proxy可以有一個本地的策略,這個策略要求請求在傳遞到目的地之前,必須經歷一個proxy集合。這樣的proxy必須能夠確保所有的類似的proxy都是松路由(loose routers)的。通常,只有當這些proxy都是在相同的區域管理的時候,我們才可能知道這些proxy是否都是松路由的。這個proxy的集合是通過一組URI的集合表示(每一個都包含一個lr參數)。這個集合必須被放置到Route頭域中,並且放置在其他頭域值之前。如果Route頭域不存在,必須增加一個Route頭域,包含這組URI的列表。

如果proxy有一個本地策略要求請求經過一個指定的proxy,在壓棧Route頭域之外的一個方法就是旁路下邊的第10步的邏輯轉發,而是直接發送這個請求到這個指定的proxy的地址,端口,和協議。如果請求有一個Route頭域,這個額外的方法就不能用了,除非它知道下一個節點proxy是一個松路由的節點。否則,使用上邊講的增加Route頭域的方法會更有效,更靈活,適應性更好,並且操作更一致。而且,如果Request-URI包含了一個SIPS URI,這個proxy必須用TLS來進行通訊。

 

如果請求的拷貝中包含了Route頭域,這個proxy必須檢查這個Route頭域的第一個值。如果這個URI並沒有包含lr參數,那么proxy必須根據下列步驟修改這個請求:

 

- proxy必須把Request-URI放在Route頭域中的最后一個值。

- proxy必須把第一個Route頭域的值放在Request-URI中,並且從Route頭域中刪去。

 

把Request-URI添加到Route頭域的最后是為了讓Request-URI的信息能夠通過嚴格路由的proxy。”Popping”彈掉第一個Route頭域值到Request-URI中是為了能夠讓嚴格路由元素能夠接收到這個請求(並且用它自己的在Request-URI中的URI和在Route頂部下一個節點的URI)。

 

7、    確定下一個節點的地址,端口和通訊協議。

proxy可以有自己的策略來決定發送請求到特定的IP地址,端口和transport,可以和Route的值或者Request-URI的值無關。當本proxy不能確定對應ip,端口,transport的服務器是一個松路由(loose router)的時候,這樣的策略就不能使用了。但是,除了Route頭域應當像上邊講的這樣使用,我們並不推薦這樣的發送請求的機制。

 

在沒有這樣一個替代機制的時候,proxy應用附件[4]的步驟來決定應當向哪里發送這個請求。如果proxy重新規格化請求,並且發送到一個像上邊6點講的嚴格路由的元素,proxy必須應用這些步驟到請求中的Request-URI。否則,如果Route頭域存在,proxy必須應用這些步驟到Route頭域的第一個值;如果Route不存在,proxy必須應用這些步驟到Request-URI。這些步驟最終得到一個序列集合(地址,port,transport)。與使用那個URI作為附件[4]處理的輸入,如果Request-URI指定了一個SIPS URI,那么proxy必須把輸入[4]的URI當作是SIPS URI然后遵循[4]的處理步驟進行處理。

 

就像在附件[4]中講述的,proxy必須嘗試序列集合中的第一組元素,並且依次嘗試序列集合中的每一組元素,直到成功為止。

 

對於每一組的嘗試,proxy必須按照這組的通訊要求,對消息進行適當的格式化,並且用一個新的客戶端事務(第8到第10點講述的),進行請求的發送。

 

由於每一組的發送都是使用心得客戶端事務,這就體現了一個新的分支。因而,第8步插入的Via頭域中的分支參數必須每組發送的都不一樣。

 

如果客戶端事務報告發送請求失敗,或者它自身的狀態機超時,proxy就應當繼續處理序列集合中的下一組元素。當遍歷完序列集合之后,請求就不能發送到目的地集合。proxy不需要在應答上下文中放什么應答,然而在別的方面卻需要就像從目的地集合收到一個408(Request Timeout)終結響應一樣的操作。

 

8、    增加一個Via頭域值

proxy必須在請求的拷貝中增加一個Via頭域值,並且在其他Via頭域值之前增加。這個值的構造可以參見8.1.1.7。這意味着proxy需要計算自己的分支參數,並且應當是全局唯一的分支,並且包含必要的magic cookie。注意這意味着如果請求循環經過本proxy的時候(也就是數次經過同一個proxy),每次的分支參數都不同。

 

在proxy構造分支參數的值上,有一個附加的約束,用來進行循環的檢測。一個要檢測循環的proxy應當創建一個由兩部分組成的分支參數。第一部分必須滿足8.1.1.7的約束。第二部分是用來做循環檢測的,並且是從螺旋中判定是否存在循環(請求數次經過同一個proxy是正常的,這是螺旋,但是如果是循環,那就不正常了。假定proxy是X,CàX,XàY,YàZ,ZàX,XàA,Aà目的地是正常的,但是如果CàX, XàY, YàZ, ZàX, XàY, 這就是循環了)。

 

循環檢測是通過這樣的方法檢測的:當請求返回給一個proxy,與處理請求相關的字段並未改變,那么這個就是循環了。這個分支參數的后一部分應當反應所有的這些頭域(包括所有的Route,Proxy-Require和Proxy-Authorization頭域)。這是確保如果請求從別處重新路由回來,而且這些字段改變了,那么這就是一個螺旋而不是循環(參見16.3)。通常建立這個比較值的方法是計算一個hash值,通過基於To tag,From tag,Call-ID頭域,收到請求的Request-URI(而不是經過處理過后的Request-URI),Via頭域的最上一個,Cseq頭域的序列號,任何附加的Proxy-Require或者Proxy-Authorization頭域。具體的hash算法是基於實現相關的,但是MD5(RFC1321[35]),用16進制表達,是一個有道理的選擇。(基於64位表達的是不太合適的)。

 

如果proxy希望檢測循環,那么”branch”參數必須用包含可能影響處理請求的全部信息構成,包括輸入的Request-URI和其他可能會影響proxy處理路由的字段,通過計算得到。這是檢測循環所必須的,因為如果請求在路由相關的字段改變以后,重新發回這個服務器,那么新的處理可能會發送到另外的地方,而不是造成一個循環。

 

在branch參數的計算上,請求的方法不能計算進去。但是作為特例,CANCEL和ACK請求(給非2xx應答的)必須和他們對應的請求有相同的branch值。branch參數用於在服務器處理這些請求的時候體現請求之間的相關性(17.2.3和9.2)

 

9、    如果需要,增加一個Content-Length頭域

如果請求會通過一個基於流的通訊協議發送到下一個節點,並且發送的請求拷貝中沒有包含一個Content-Length頭域,那么proxy必須增加一個正確的請求包體大小在這個頭域(20.14)。

 

10、    轉發請求

一個有狀態的proxy必須為這個請求創建一個新的客戶端事務(根據17.1節描述的那樣),並且指示事務層用第7步指定的地址,端口和協議進行發送。

 

11、    設定時鍾C

為了能夠處理INVITE請求沒有產生終結應答的情況,TU使用一個定時器(稱作定時器C)。當INVITE請求被轉發的時候,必須為客戶端事務設定一個定時器C。這個定時器C必須大於3分鍾。16.7節的2步講述了這個定時器是如何根據臨時應答來更新的,並且16.8節講述了定時器到時的處理步驟。

 

16.7 應答的處理

當proxy收到一個應答的時候,它首先嘗試定位一個與這個應答匹配的客戶端事務(17.1.3)。如果沒有匹配,proxy必須作為無狀態的proxy來處理這個應答(即使這個應答是信息性質的應答)。如果與應答匹配的客戶端事務找到了,那么這個應答將轉給這個客戶端事務進行處理。

 

將應答轉給對應的客戶端事務(或者更通常的說法是發出請求的或者相關的事務),並不是為了更強大的處理能力,它是保證了”晚到”的給INVITE請求的2xx應答能夠正確的轉發。

 

當客戶端事務把應答交給proxy層,將會執行下列步驟:

1、    尋找適當的應答上下文。

2、    用臨時應答來更新定時器C

3、    從最上邊移除Via

4、    在應答上下文中增加應答

5、    檢查這個應答是否需要立刻發送

6、    如果需要,從應答上下文中選擇最好的終結應答。

 

如果在與這個應答上下文相關的每一個客戶端事務都結束的以后,還是沒有終結應答轉發,那么proxy必須選擇從已經收到的應答中,選擇轉發”best”應答回去。

 

下列步驟必須在每一個被轉發的應答上執行。就像每一個請求有超過一個應答被轉發一樣:至少有一個終結應答和0個或者多個臨時應答。

 

7、    需要合並認證頭域值。

8、    可選的重寫Record-Route頭域值

9、    轉發應答

10、    產生合適的CANCEL請求。

 

上述每一步在下邊有詳細的描述:

 

1、    尋找上下文

proxy通過16.6節定義的方法來在尋找轉發原始請求前創建的”應答上下文” 。在這個上下文中進行后續的處理步驟。

 

2、    為臨時應答更新定時器C

對於INVITE事務,如果應答是一個返回碼是101到199的臨時應答(就是說,除了100的臨時應答),proxy必須給這個客戶端事務重新設置定時器C。這個定時器可以設置成為其他的值,和原始值不一樣,但是這個數字必須大於3分鍾。

 

3、    Via

proxy從應答中移去Via頭域中最上的值。

 

如果在這個應答中沒有這個Via頭域值,那么應答的含義就是說這個應答不應當被這個proxy轉發。本節描述的后續處理步驟也不需要繼續處理,而是用8.1.3節定義的UAC處理規則進行處理(傳輸層處理已經進行)。

 

這種情況可能會發生,比如,當某一個元素產生一個第10節規定的CANCEL請求。

 

4、    增加應答到上下文

收到的終結應答都會保存在應答的上下文中,直到收到一個由服務端事務產生的針對這個上下文的終結應答為止。這個應答是從那個服務端事務中收到最佳終結應答的一個候選。即使這個應答不會被選中作為最佳應答,這個應答的信息也需要用來構造成為最佳應答。

 

如果proxy決定嘗試調用3xx應答返回的聯系地址,並且把他們添加到目的地集合,它必須在把這個應答添加到應答上下文之前把聯系地址從應答中移除。不過,proxy不應當在源請求的Request-URI是一個SIPS URI的情況下,嘗試調用非SIPS URI。如果proxy嘗試每一個3xx應答給回的聯系地址,proxy不應當把這個應答添加到應答上下文中。

 

從應答中刪去聯系地址的目的是為了防止下一個節點嘗試本proxy已經嘗試的地址。

 

3xx應答可能包含SIP,SIPS和非SIP URI。proxy可以自行決定自己調用那些SIP或者SIPS URI,並且把剩下的放在應答上下文中返回。

 

如果proxy收到一個對於一個Rquest-URI並非SIP URI的請求的416(不支持的URI scheme)應答,但是原始請求的Request-URI是SIP或者SIPS(就是說,proxy在轉發請求的時候自己調換了請求的SIP或者SIPS為一個什么其他的東西),proxy應當增加一個新的URI到目的地集合。這個URI應當是剛才嘗試的非SIP URI的SIP URI版本。對於電話URL來說,這個就是把電話URL的電話號碼部分放在SIP URI的用戶部分,並且設置SIP URI的主機部分成為當前請求發送者的區域。19.1.6節有電話URL到SIP URI的轉換細節。

 

在3XX應答的情況下,如果proxy在416上會產生”遞歸”(因為嘗試SIP或者SIPS URI而導致遞歸),那么應當在應答上下文中增加這個416應答。

 

5、    檢查轉發的應答

當終結應答到達服務端事務的時候,下列應答包必須立刻轉發。

- 任何非100(trying)的臨時應答

- 任何2xx應答。

如果收到一個6xx應答,那么就不立刻進行轉發,如果是有狀態的proxy,那么還需要cancel所有的依賴於這個事務的客戶端(在10節中描述的那樣),並且不能在上下文中創建新的分支。

 

這個是和RFC 2543的不同之處,2543要求proxy立刻轉發6xx應答。對於一個INVITE事務來說,如果立刻轉發6xx應答,會使得2xx應答到達別的分支。這個結果就是讓UAC在2xx應答之后收到一個6xx應答,這個是不允許發生的。在新的規則下,基於接收到一個6xx應答,proxy應當產生一個CANCEL請求,那么這個會給所有等待的客戶端事務一個487應答,這就是6xx應答應當給上行隊列的一個結果。

 

在服務端事務上發送了終結應答之后,下列的應答應當立刻被發送:

- 任何給INVITE請求的2xx應答。

 

一個有狀態的proxy必須不能立刻轉發其他的應答。特別是,一個有狀態的poxy必須不能轉發任何100(Trying)應答。這些應答是作為后續將被轉發”最佳”應答的候選,通過上邊的”在上下文中增加應答”的步驟增加到應答上下文中。

 

任何將被立刻發送的應答都必須遵照”7、 需要合並認證頭域值。”和”8、可選的重寫Record-Route頭域值”來處理。

 

這一步,合並下一步,確保有狀態的proxy能夠精確轉發一個終結應答到一個非-INVITE請求,或者給一個INVITE請求的非2xx應答或者一個或者多個2xx應答。

 

6、    選擇最佳的應答

對於一個有狀態的proxy來說,如果根據上邊的步驟,沒有任何終結應答被立刻發送,並且在客戶端事務中的所有的客戶端服務都已經終結,那么這個proxy必須發送一個終結應答到一個應答上下文的服務端事務層。

 

那么這個有狀態的proxy就必須從接收到的應答上下文中選擇一個”最佳”的終結應答。如果在上下文中沒有一個終結應答,那么proxy就必須返回一個408(請求超時)的應答到服務端事務層。

 

如果應答上下文中有終結應答,那么proxy就必須從這個應答上下文中取得應答來發送。如果應答上下文中有6xx應答,那么就必須選擇這個6xx應答。如果沒有6xx應答,那么proxy應當選擇最小的應答(應答返回代碼比較小)。proxy可以選擇對應最小應答系列中的任意一個應答(比如2xx系列中的任意一個應答)。proxy應當給那些提供對影響請求的應答更多的機會,比如在4xx系列中,選擇401,407,415,420或者484應該稍稍優先一些。

 

當proxy收到503(Service Unavailable)應答的時候,不應當轉發到上行隊列中,除非它能夠知道這個后續的請求隊列都能產生503的應答。換句話說,就是轉發503就意味着proxy確實不能處理任何請求,不僅僅是Request-URI里邊的這個地址不能處理請

 

 

小虎 2006-05-25 00:08

求。如果只有某個應答會產生503,proxy應當產生500應當到上行隊列中。

 

被轉發的應答都必須遵照”7、 需要合並認證頭域值。”和”8、可選的重寫Record-Route頭域值”來處理。

 

例如,如果一個proxy轉發一個請求到4個地方,並且收到了503,407,501,和404應答,它可能選擇407(Proxy Authentication Required)應答。

 

1xx和2xx應答可能和建立對話有關。當請求沒有包含一個To tag,UAC使用在應答中的To tag來區分請求創建的對話的多個應答。如果請求中沒有包含To的tag,那么proxy必須不能為1xx或者2xx應答增加這個tag到To頭域。一個proxy不能修改1xx或者2xx應答中的To頭域的tag字段。

 

在請求的1xx應答中,如果應答沒有To頭域的tag字段的時候,由於proxy不能添加tag字段到這個To頭域,它就不能產生它自己的非100臨時應答。但是它可以把這個請求分支到其他一個UAS上,這個UAS可以和proxy共享同樣的元素。這個UAS可以返回它自己的臨時應答,進入請求創建早期對話中。這個UAS並沒有必要作為一個proxy的嚴格處理步驟存在。它可以是一個在proxy內部的一個虛擬的UAS實現。

 

3到6xx的應答是節點到節點傳遞的。當產生了一個3-6系列的應答的時候,每一個節點都作為UAS一樣,產生它自己的應答,通常基於下行隊列的應答產生自己的應答。對於每一個節點來說,在轉發3到6系列應答回去的時候,如果這個應答沒有包含To tag,那么這個節點也應當不改變這個to tag。

 

當收到的應當包含了一個To tag,那么這個proxy不能夠修改這個To tag。

 

恩,實際上在proxy轉發3到6系列應答的時候,如果替換了To tag也不會讓上行隊列所經過的節點有影響,保留原始的tag值可以有助於調試。

 

當proxy需要合並多個應答的信息的時候,從這些應答中選取To tag的方式是任意的,並且產生一個新的To tag可能可以使得調試更加容易。舉例來說,當合並401(Unauthorized)和407(Proxy Authentication Required)信息的時候,或者合並一個未加密的Contact值和未通過驗證的3xx應答的時候,產生一個新的To tag就會讓調試比較容易。

 

7、    合並認證頭域值

如果選擇的應答是401(Unauthorized)或者407(Proxy Authentication Required),那么proxy就必須從本應答上下文中的所有其他401(Unauthorized)和407應答中搜集WWWAuthenticate和Proxy-Authenticate 頭域值。並且把這些信息增加到這個應答中。最后的401或者407應答中可能會包含多個WWWAuthenticate和Proxy-Authenticate頭域值。

 

由於這個請求的一個或者多個目的地可能是需要請求身份驗證的,所以這個搜集步驟就是必須的。客戶端需要接收到這些所有目的者的應答並且在下一次嘗試的時候,為每一個目的地提供相關的身份證明。在26節有相關的說明。

 

8、    Record-Route

如果最終發送的應答中包含Record-Route頭域值,並且是這個proxy所原創提供的值,那么在發送這個應答前,proxy可能需要重寫這個值。這提供了一個機制,讓proxy能夠給下一個上行節點或者下行節點提供非本機的一個URI地址。這種情況是很常見的,比如,在多地址主機系統就非常有用。

 

如果proxy是通過TLS收到請求的,並且通過非TLS轉發出去,proxy必須重寫在Record-Route頭域中的URI,重寫成SIPS URI。如果proxy通過非TLS接收到請求,轉發是通過TLS轉發的,那么proxy必須重寫Record-Route請求頭域的URI為一個SIP URI。

 

proxy提供的新的URI必須滿足同樣的Record-Route頭域的URI約束(16.6節的第四步)。並且遵循下列的修改:

 

URI不應當包含通訊參數除非proxy知道下一個上行(同下行隊列對應的)節點,對於后續的請求都支持相關的通訊參數。

 

如果proxy打算修改應答中的Record-Route頭域,要做的一件事情就是定位插入的Record-Route頭域值。如果請求是螺旋經過的,並且proxy在每次螺旋的時候都插入了Record-Route值,在應答中(必須在反向路徑中的正確位置)找到正確的需要修改的值就需要一點技巧。上邊的規則強調proxy在增加Record-Route頭域值的時候是必須增加唯一的URI,這樣才能找到正確的一個能夠重寫。我們推薦proxy為每一個URI的user portion增加一個唯一個proxy實例標志。

 

當應答到達的時候,proxy修改第一個和proxy實例標志匹配的Record-Route。這個修改導致產生一個在user portion部分去掉proxy實例的URI。到下一個循環回來處理的時候,同樣的算法(用參數從上而下的尋找Record-Route頭域值)會更改這個proxy插入的下一個Record-Route頭域值。

 

對於proxy增加Record-Route頭域值的請求來說,並非每一個應答都包含一個Record-Route頭域。如果應答包含一個Record-Route頭域,那么就包含這個proxy增加的值。

 

9、    轉發應答

當”合並認證頭域”和”Record-Route”步驟完成以后,proxy可以對這個應答做其他的附加處理。但是這個proxy不能增加、修改、刪除消息體。並且除非另有指示,除了Via頭域值(在16.7節3步)之外,proxy不能刪除任何頭域值。特別是,proxy不能刪除任何可能增加到與處理和這個應答相關的下一個請求的Via頭域值的”接收到”的參數。proxy必須把應答傳遞到跟這個應答上下文相關的服務端事務。這回導致應答發送到最上的Via頭域值的地方。如果服務端事務不在處理這個發送,這個節點必須作為無狀態proxy轉發這個應答到服務端通訊層。服務端事務可能已經標志這個發送應答失敗或者內部狀態機已經設置成為超時狀態。這些錯誤都應當記錄下來用於診斷錯誤,但是協議並沒有要求proxy做補救措施。

 

proxy必須維持應答上下文直到所有相關事務都已經終結,甚至在發送完成終結應答后還需要維持。

 

10、    產生CANCEL請求

如果轉發的應答是一個終結應答,proxy必須給依賴於這個應答上下文的所有客戶端事務,產生CANCEL請求。在收到6xx應答的時候,proxy同樣應當為所有等待在這個應答上下文的客戶端事務產生CANCEL請求。等待的客戶端事務就是收到了臨時應答,但是沒有收到終結應答(還是出於處理中的狀態),並且沒有任何CANCEL請求與之相關的請求。產生CANCEL請求請參見9.1節。

 

對於要求基於轉發終結應答而CANCEL的客戶端事務並沒有保證終端不會收到給一個INVITE的多個200(OK)應答。基於多余一個分支的200(OK)應答可能會在CANCEL請求處理前到達。進一步說,后續的擴展可能會改掉這個產生CANCEL請求的要求。

 

16.8 處理定時器C

如果定時器C 被出發了,proxy必須要么用另外一個數值重新設定定時器,要么終結客戶端事務。如果客戶端事務已經收到了臨時應答,那么proxy必須產生一個與之匹配的CANCEL請求。如果客戶端事務還沒有收到臨時應答,那么proxy必須就像收到一個408(Request Timeout)一樣的處理。

 

允許proxy重設定定時器就意味着允許proxy基於當前條件(比如服務器利用率等等)動態的擴展事務的生命周期。

 

16.9 處理通訊層的錯誤

如果在轉發請求(參見18.4)的時候,通訊層報告了一個錯誤,那么proxy必須就像收到了一個503(Service Unavailable)應答一樣的處理。

 

如果proxy在轉發應答的時候接收到錯誤,那么他就丟棄應答。proxy不能由於通訊的原因而cancel任何和這個應答上下文相關的客戶端事務。

 

如果proxycancel了這些客戶端事務,那么一個惡意的或者出錯的客戶端可以用一個Via頭域導致所有的事務都失敗。

 

16.10 CANCEL處理

一個有狀態的proxy可以給他自己產生的其他請求在任何時候都產生CANCEL請求(參見9.1遵從接收到對應請求的一個臨時應答)。在接收到一個匹配的CANCEL請求的時候,proxy必須取消任何與應答上下文相關的客戶端事務。

 

當INVITE請求有一個Expires頭域並且這個頭域值已經超時的情況下,一個有狀態的proxy可以對這個處於pending的INVITE客戶端事務發出CANCEL請求。可是,通常來說,這是不必要的,因為相關的終端會發出結束事務的信號。

 

當有狀態的proxy在它自己的服務端事務上處理CANCEL請求的時候,並沒有新的應答上下文會創建。相反,proxy層尋找與這個CANCEL對應請求的現存的應答上下文。如果找到了對應的應答上下文,那么這個節點應當立刻返回一個200(OK)應答給這個CANCEL請求者。在這個情況下,這個節點就像8.2節定義的UAS一樣的工作。進一步說,這個節點應當為每一個依賴於這個上下文的客戶端事務產生一個CANCEL請求(就像在16.7節第10步描述的那樣)。

 

如果一個應答上下文沒有找到,這個節點就無法CANCEL這個請求。它就必須像無狀態proxy一樣轉發這個CANCEL請求(可能這個節點把被CANCEL的請求在先前也當作無狀態的proxy轉發了)。

 

16.11 無狀態的proxy

當作為無狀態的時候,proxy就是一個簡單的消息轉發者。很多無狀態的處理步驟和有狀態的時候很類似。不同的地方在下邊描述。

 

一個無狀態的proxy並沒有事務的概念,或者用於描述有狀態proxy行為的應答上下文。相反的是,無狀態的proxy處理消息,無論是請求還是應答,都是直接從通訊層處理的(參見18節)。當然,無狀態proxy自己也不重發這些消息。他們只是轉發他們收到的任何重發的消息(他們本身並沒有能力來分辯那些消息是重發的,那些消息是原始消息)。進一步說,當無狀態的處理一個請求的時候,這個節點並不產生它自己的100(Trying)或者其他臨時應答。

 

無狀態的proxy必須用16.3節描述的那樣來驗證一個請求。

 

無狀態的proxy必須遵從16.4到16.5節定義的步驟來處理請求,有如下幾點例外:

o 無狀態的proxy必須從目的地集合中,選擇一個並且只能選擇一個目的地。這個選擇必須是根據消息的頭域並且是和服務器時間無關的。特別是,一個重發的請求必須能夠每次都轉發到相同的目的地。進一步說,CANCEL和非路由的ACK請求必須和他們相關的INVITE請求有相同的轉發目的地。

 

一個無狀態的proxy必須遵循16.6節定義的請求處理步驟,並且有下列的不同:

 

o無狀態proxy的branchID來說,必須要求在時間上和空間上都是唯一的。也就是說,無狀態的proxy不能簡單的使用一個隨機數產生器來計算branchID的第一個部分(16.6節8步)。這是由於請求的重發需要相同的值,並且無狀態的proxy不能區分重發的請求和原始請求。因此,branch參數的組成部分要求唯一,這樣使得重發的時候能夠填寫相同的值。對於無狀態的proxy來說,branch參數必須作為一個重發無關的消息處理參數存在。

 

我們沒有規定無狀態proxy采用何種手段保證branchID的唯一性。不過,下列步驟是推薦的方法。proxy檢查在接收到請求的最上Via頭域值的branchID。如果它是由magic cookie打頭的,那么branchID的第一個部分就是當作接收到的branchID的hash值。否則branchID的第一個部分就當作是Via頭域的最上值、To頭域的tag、From頭域的tag,Call-ID頭域,Cseq序列號(除了方法部分),接收到的請求的Request-URI的一個hash值。這些頭域值在不同事務中總是不一樣的。

 

o所有其他的消息轉換(16.6節)必須保證轉發重發的請求的時候能夠轉發到相同的節點。特別是,如果proxy在Record-Route頭域中增加了值,或者在Route頭域中增加了值,proxy必須在轉發重發的請求的時候增加相同的值。至於Via 的branch參數,這就意味着轉發必須是基於時間無關的配置或者請求重發無關的屬性。

 

o一個無狀態proxy決定轉發的地點是像16.6節10步描述的有狀態的proxy一樣。但是請求是直接交給通訊層發送的,而不是交給客戶端事務。

 

由於一個無狀態的proxy必須轉發重發的請求到相同的地方,並且增加標志性的branch參數,它只能用消息中本身的信息和時間無關的配置來計算。如果配置狀態不是時間無關的(比如,如果路由表更新了),這個改變相關的請求,在這個改動開始以后,到在事務超時的時間范圍內,就不能作為無狀態的轉發了。這個處理這段時間的請求是實現相關的。通常處理的方法,是把這些請求當作事務有狀態的進行轉發。

 

無狀態的proxy必須不能對CANCEL做特別的處理。CANCEL的處理就像對其他請求的處理一樣進行。特別是,一個無狀態的proxy使用相同的Route頭域來處理CANCEL請求,就像處理其他請求一樣。

 

對於16.7節中定義的應答處理,對於無狀態proxy來說,並不適用。當一個應答到達一個無狀態proxy,proxy必須檢查最上的Via頭域值的sent-by參數。如果這個地址和這個proxy一樣(就是和proxy插入的先前的請求中的值一樣),那么這個proxy必須從應答中移除這個頭域值,並且轉發這個應答到下一個Via頭域值。這個proxy必須不能增加,修改或者刪除消息體。除非有特別的說明,proxy必須不能移除其他的頭域值。如果地址不匹配本proxy,消息就必須簡單的悄悄扔掉。

 

16.12 Proxy Route處理的總結

在沒有本地策略的情況下,proxy對於包含Route頭域的請求處理可以歸結於如下的步驟:

 

1、    proxy會檢查Request-URI。如果它指向的是本proxy所負責的區域,那么proxy會用位置服務的結果來替換這個URI。否則,proxy不改變這個URI。

2、    proxy會檢查Route頭域的最上URI。如果這個URI指向這個proxy,這個proxy從Route頭域中移除(這個路由節點已經到達)。

3、    proxy會轉發請求到最上的Route頭域值所標志的URI,或者Request-URI(如果沒有Route頭域)。proxy通過附件[4]的步驟來產生地址,端口,通訊協議等等用來轉發請求所必須的參數。

 

如果在請求的路徑中,沒有嚴格路由節點,Request-URI會始終標志着請求的目的地。

 

16.12.1例子

16.12.1.1 基本SIP四邊形

本例子是一個基本的SIP四邊傳送,U1->P1->P2->U2,使用proxy來傳送。下邊是過程。

 

U1 發送:

INVITE sip:callee@domain.com SIP/2.0

Contact: sip:caller@u1.example.com

發給P1,P1是一個外發的proxy。P1並不管轄domain.com,所以它查找DNS並且發送請求到那里。它也增加一個Record-Route頭域值:

INVITE sip:callee@domain.com SIP/2.0

Contact: sip:caller@u1.example.com

Record-Route: <sip:p1.example.com; lr>

 

P2收到這個請求。這是domain.com所以它查找位置服務器並且重寫Request-URI。它也增加一個Record-Route頭域值。請求中沒有Route頭域,所以它解析一個新的Request-URI來決定把請求發送到哪里。

INVITE sip:callee@u2.domain.com SIP/2.0

Contact: sip:caller@u1.example.com

Record-Route: <sip:p2.domain.com; lr>

Record-Route: <sip:p1.example.com; lr>

 

在u2.domain.com的被叫方接收到這個請求並且返回一個200OK應答:

SIP/2.0 200 OK

Contact: sip: callee@u2.domain.com

Record-Route: <sip:p2.domain.com;lr>

Record-Route: <sip:p1.example.com;lr>

 

u2的被叫方並且設置對話的狀態的remote target URI為:

sip: caller@u1.example.com並且它的路由集合是:

(<sip:p2.domain.com;lr>,<sip:p1.example.com;lr>)

 

這個轉發通過P2到P1到U1。現在U1設置它自己的對話狀態的remote target URI為:sip:calle@u2.domain.com並且它的路由集合是:

(<sip:p1.example.com;lr>,<sip:p2.domain.com;lr>)

 

由於所有的路由集合元素都包含了lr參數,那么U1構造最后的BYE請求:

BYE sip:callee@u2.domain.com SIP/2.0

Route: <sip:p1.example.com;lr>,<sip:p2.domain.com;lr>

 

就像其他所有的節點(包括proxy)會做的那樣,它會使用DNS來解析最上的Route頭域的URI值,這樣來決定往哪里發送這個請求。這就發到了P1。P1發現Request-URI中標記的URI不是它負責的域,於是它就不改變這個Request-URI。然后看到它是Route頭域的第一個值,於是就從Route頭域中移去,並且轉發這個請求到P2:

BYE sip:callee@u2.domain.com SIP/2.0

Route: <sip:p2.domain.com;lr>

P2也發現它自己並非負責這個Request-URI的域(P2負責的是domain.com並非u2.domain.com),於是P2並不改變它。它看到自己在Route的第一個值,於是移去這個,並且向u2.domain.com轉發(根據在Request-URI上查找DNS):

BYE sip:callee@u2.domain.com SIP/2.0

 

16.12.1.2 穿越一個嚴格路由proxy

在這個例子中,對話建立通過4個proxy,每一個增加Record-Route頭域值。第三個proxy是由嚴格路由實現的(RFC 2543)。

U1->P1->P2->P3->P4->U2

 

INVITE請求到達U2包括了:

INVITE sip:callee@u2.domain.com SIP/2.0

Contact: sip:caller@u1.example.com

Record-Route: <sip:p4.domain.com;lr>

Record-Route: <sip:p3.middle.com>

Record-Route: <sip:p2.example.com;lr>

Record-Route: <sip:p1.example.com;lr>

 

並且U2返回了一個200 OK。接着,U2根據第一個Route頭域值發送下邊的BYE請求到P4:

BYE sip:caller@u1.example.com SIP/2.0

Route: <sip:p4.domain.com;lr>

Route: <sip:p3.middle.com>

Route: <sip:p2.example.com;lr>

Route: <sip:p1.example.com;lr>

 

P4並不管轄Request-URI指出的域,於是就不更改這個Reqeust-URI。它發現自己在第一個Route頭域中,於是把自己從Route頭域移除。然后准備發送請求到現在的第一個Route頭域值:sip:p3.middle.com,但是它發現這個URI並沒有包含lr參數,於是在發送前,它把這個請求更改成為:

BYE sip:p3.middle.com SIP/2.0

Route: <sip:p2.example.com;lr>

Route: <sip:p1.example.com;lr>

Route: <sip:caller@u1.example.com>

P3是一個嚴格路由,於是它轉發到P2:

BYE sip:p2.example.com;lr SIP/2.0

Route: <sip:p1.example.com;lr>

Route: <sip:caller@u1.example.com>

P2看到Request-URI是它放在Record-Route頭域中的值,於是在進一步處理前,它把這個請求改寫為:

BYE sip:caller@u1.example.com SIP/2.0

Route: <sip:p1.example.com; lr>

P2自己並不管轄u1.example.com,於是它根據Route頭域的值,轉發這個請求到P1。

 

P1發現自己在Route頭域的最上,於是把自己移除,得到:

BYE sip:caller@u1.example.com SIP/2.0

由於P1並不管轄u1.example.com並且沒有其他的Route頭域,P1會基於Request-URI轉發這個請求到u1.example.com。

 

16.12.1.3 重寫Record-Route頭域值。

在這里例子中,U1和U2是在不同的私有域空間中,並且他們通過proxy P1開始一個對話,這個P1作為不同私有namespace的一個網關存在。

U1->P1->U2

U1發送:

INVITE sip:callee@gateway.leftprivatespace.com SIP/2.0

Contact: <sip:caller@u1.leftprivatespace.com>

 

P1使用自己的定位服務並且發送下邊的信息到U2:

INVITE sip:callee@rightprivatespace.com SIP/2.0

Contact: <sip:caller@u1.leftprivatespace.com>

Record-Route: <sip:gateway.rightprivatespace.com;lr>

 

U2發送200 OK應答回給P1:

SIP/2.0 200 OK

Contact: <sip:callee@u2.rightprivatespace.com>

Record-Route: <sip:gateway.rightprivatespace.com;lr>

 

P1重寫它的Record-Route頭域參數,提供成為U1能夠使用的參數,並且發送給P1:

SIP/2.0 200 OK

Contact: <sip:callee@u2.rightprivatespace.com>

Record-Route: <sip:gateway.leftprivatespace.com;lr>

 

稍后,U1發送接下來的BYE到P1:

BYE sip:callee@u2.rightprivatespace.com SIP/2.0

Route: <sip:gateway.leftprivatespace.com;lr>

 

P1轉發到U2:

BYE sip:callee@u2.rightpriatespace.com SIP/2.0

 

17事務

SIP是一個基於事務處理的協議:部件之間的交互是通過一系列無關的消息交換所完成的。特別是,一個SIP 事務由一個單個請求和這個請求的所有應答組成,這些應答包括了零個或者多個臨時應答以及一個或者多個終結應答。在事務中,當請求是一個INVITE(叫做INVITE事務),當終結應答不是一個2xx應答的時候,事務還包括一個ACK。如果應答是一個2xx應答,那么ACK並不認為是事務的一部分。

這個分開的原因是基於傳遞全部200(OK)應答到UAC的INVITE請求的重要性所決定的。要把所有的200應答全部發給UAC,那么UAS獨自負責這些應答的重新傳送(參見13.3.1.4),UAC肚子負責挨個ACK確認(參見13.2.2.4)。由於ACK的重傳只由UAC發起,所以在自己的事務中進行重傳會比較有效。

 

事務分為客戶端和服務端兩方。客戶端的事務是客戶端事務,服務器端的事務就是服務端事務。客戶端事務發出請求,並且服務端事務送回應答。客戶端和服務端事務都是邏輯上的概念,他們可以被無數部件所包含。特別是,他們在UA中和有狀態的proxy服務器中存在。以第四節的例子來說明。在這個例子中,UAC執行客戶端事務,它的外發proxy執行服務端事務。外發proxy同時也執行客戶端事務,把請求發送到一個那發proxy的服務端事務。這個proxy也同時執行一個客戶端事務,把請求發到一個UAS的服務端事務上去。這個在圖四中比較明白:

 

 

小虎 2006-05-25 00:08

+---------+         +---------+            +---------+         +---------+

|        +-+|Request |+-+     +-+|Request    |+-+    +-+|Request |+-+        |

|        |C||------->||S|     |C||------->    ||S|    |C||------->||S|        |

|        |l||         ||e|     |l||            ||e|    |l||         ||e|        |

|        |i||         ||r|     |i||            ||r|    |i||         ||r|        |

|        |e||         ||v|     |e||            ||v|    |e||         ||v|        |

|        |n||         ||e|     |n||            ||e|    |n||         ||e|        |

|        |t||         ||r|     |t||            ||r|    |t||         ||r|        |

|        | ||         || |     | ||            || |    | ||         || |        |

|        |T||         ||T|     |T||            ||T|    |T||         ||T|        |

|        |r||         ||r|     |r||            ||r|    |r||         ||r|        |

|        |a||         ||a|     |a||            ||a|    |a||         ||a|        |

|        |n||         ||n|     |n||            ||n|    |n||         ||n|        |

|        |s||Response||s|     |s||Response    ||s|    |s||Response||s|        |

|        +-+|<-------|+-+     +-+|<-------    |+-+    +-+|<-------|+-+        |

+---------+         +---------+            +---------+         +---------+

UAC                Outbound            Inbound               UAS

Proxy                Proxy

圖4: 事務關系

無狀態的proxy並沒有客戶端或者服務端的事務。事務是一邊基於UA或者有狀態的proxy,另外一邊也基於UA或者有狀態的proxy。在SIP事務范疇下,無狀態的proxy是用作透明轉發很有效。客戶端事務的用處是用於從一個元素中接收一個請求,這個客戶端是內嵌的(這個元素就是”事務用戶”或者TU;它可以是一個UA或者有狀態的proxy),並且可靠的把這個請求傳送到一個服務端事務。

 

客戶端事務也負責接收應答並且把應答轉交TU處理,過濾掉重發的應答或者不允許的應答(比如給ACK的應答)。另外,在INVITE請求的情況下,客戶端事務也負責產生給2xx應答的ACK請求。

 

類似的,服務端事務也負責從通訊層接收請求並且轉發這個請求到TU。服務端事務過濾重發的請求。並且服務端事務從TU接收應答並且轉發到通訊層來發送。在INVITE事務的情況下,它需要接收給非2xx應答的終結應答的ACK請求。

 

2xx應答和它的ACK請求通過特定的方式來接收和處理。這個應答只會被UAS重發,並且它的ACK只由UAC產生。由於呼叫者知道整個已經接收呼叫的用戶集合,所以需要這種端到端的處理。由於這樣的特別處理,2xx應答的重發是基於UA核心的,並非基於通訊層。類似的,給2xx應答的ACK處理也是由UA核心處理的,每個路徑上的proxy僅僅轉發這些INVITE的2xx應答以及他們的ACK。

 

17.1 客戶端事務

客戶端事務是通過維持一個狀態機來提供服務的。

 

TU和客戶端事務通過一個簡單的接口進行通訊。當TU希望初始化一個新的事務,它創建一個客戶端事務並且通過設置ip地址,端口和transport來把一個SIP請求交給它傳送。然后客戶端事務開始執行它自己的狀態機。合乎規格的應答會從客戶端事務傳送給TU。

 

總共有兩種類型的客戶端事務狀態機,根據TU傳遞的請求的方法不同來區分的。一個用於處理INVITE請求。這種狀態機對應的是一個INVITE客戶事務。另外一個是用來處理其他所有的非INVITE請求的。它對應的是非INVITE客戶事務。對於ACK來說,是不存在客戶事務的。如果TU希望送一個ACK請求,它直接交給通訊層進行通訊處理。

 

INVITE事務和其他事務是不同的,因為它的時間周期很長。通常,對於INVITE請求的應答來說,都需要人的參與,這樣會導致在應答INVITE請求之前會有很長的延時。在三方握手(人,兩方機器)的時候也會有很長的延時。在另一方面,其他請求的響應都是很快就完成的。因為其他非INVITE請求事務是雙方的握手,TU能夠立刻對非INVITE請求作出應答。

 

17.1.1 INVITE客戶事務

17.1.1.1 INVITE事務概述

INVITE請求包含了一個三方的握手。客戶端事務發送一個INVITE,服務端事務回送一個應答,客戶端事務發送一個ACK。對於非可靠傳輸(比如UDP),客戶端事務每隔T1重發請求,每次重發后間隔時間加倍。T1是一個估計的循環時間(round-trip time,RTT),缺省設置成為500ms。幾乎所有的事務定時器都以T1為單位,並且調整T1的值也就調整了那些定時器的值。請求不會在可靠的通訊協議上重新發送。在接收到1xx應答以后,重發機制完全停止,並且客戶端等待更進一步的應答。服務端事務可以發送附加的1xx應答,這個應答並非由服務端事務可靠傳輸。最后,服務端事務會發送一個終結應答。對於非可靠的傳輸協議,應答會間隔時間來重發,對於可靠的傳輸協議,它只發送1次。對於客戶端事務所接收的每一個終結應答,客戶端事務都發送一個ACK,用於終止應答的重發送。

 

17.1.1.2 正式的描述

INVITE客戶端事務的狀態機在圖5中展示。初始狀態,”calling”,必須保證TU是用INVITE請求來初始化一個新的客戶端事務。客戶端事務必須把請求發送到通訊層來進行發送(18節)。如果使用的是非可靠傳輸的通訊層,客戶端事務必須啟動一個定時器A並且由缺省值T1組成。如果是一個可靠的通訊協議,那么客戶端事務不應當啟動定時器A(定時器A控制請求的重發送)。對於任何通訊協議來說,客戶端事務必須啟動一個定時器B並且有着64×T1秒的缺省值(定時器B控制事務的超時)。

 

當定時器A觸發了,客戶端事務必須重發這個請求,把請求交給通訊層進行發送,並且重新設置定時器為2*T1。在傳輸層中重傳的定義是指把剛才通過傳輸層發送的消息,再次交給傳輸層重新發送一次。

 

當定時器A在2×T1后觸發了,請求必須再次重傳(如果客戶端事務依舊還是在這個狀態的話)。這個處理必須持續下去,這樣請求才能每重發一次以后定時器延時1倍。重發機制只有當客戶端事務在”calling”狀態的時候才能進行。

 

缺省的T1是500ms。T1是一個RTT的估計時間,是在客戶端和服務端的一個事務處理的估計時間。節點可以(不推薦)使用更小的T1值,比如私有網絡,並不接到INTERNET的網絡可以設置小一點。T1也可以設置成為大一點的值,並且我們建議如果當我們知道RTT值比較大的時候(比如高延時的網絡)應當設置T1成為大一點的值。不管T1如何取值,本節要求的重傳機制要求的指數延時是必須使用的。

 

當定時器B觸發的時候,如果客戶端事務是依舊在”calling”狀態,那么客戶端事務應當通知TU發生了超時。客戶端事務必須不能產生ACK。64×T1是和在不可靠通訊鏈路上傳輸7個請求的時間相同。

 

如果客戶端事務在”calling”狀態接收到一個臨時應答,那么就把狀態切換到”proceeding”狀態,客戶端事務不應當再次重新發送請求了。進一步說,臨時應答必須傳送給TU。在”proceeding”狀態的任何臨時應答都必須傳送給TU。

 

當在”calling”或者”proceeding”狀態的時候,如果接收到一個應答碼是300-699的應答,那么就必須把狀態切換到”Completed”。客戶端事務必須把收到的應答轉給TU,並且客戶端事務必須傳生ACK請求,即使通訊層是可靠傳輸的(在17.1.1.3節中有描述怎樣根據應答創建一個ACK請求)並且把ACK交給傳輸層進行傳送。ACK必須和原始請求發送到相同的地址,端口和用同樣的transport。當客戶端事務進入”Completed”狀態的時候,應當開始一個定時器D,缺省值是在非可靠通訊上是至少32秒,在可靠通訊上是0秒。定時器D反應了服務端事務在非可靠傳輸的情況下,在”completed”狀態維持的時間。這個是和INVITE請求服務端事務定時器H相同的,定時器H的缺省值是64*T1。不過,客戶端事務不知道服務端事務使用的T1值,所以我們用絕對值32秒來代替T1用作定時器D的缺省值。

 

在”completed”狀態下,受到的任何終結應答的重傳都應當產生一個ACK應答到通訊層來重新發送,但是新近收到的應答卻不能傳送給TU。一個應答是否是重傳的定義是根據這個應答是否和客戶端事務按照17.1.3定義的規則匹配。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

圖5: INVITE客戶端事務

 

如果在客戶端事務狀態是”Completed”的時候,定時器D觸發,那么客戶端事務必須轉到終結狀態。當客戶端狀態是”calling”或者”proceeding”狀態的時候,接收到一個2xx應答必須導致客戶端事務進入”terminated”狀態,並且應答必須交給TU處理。處理這個應答的方法依賴於TU是否是一個proxy核心還是是UAC核心。UAC核心會給應答產生ACK,proxy核心會轉發一個200(OK)應答到上行隊列。這個在proxy和UAC之間,對200(OK)的不同的處理是導致對應答的處理不在事務層進行的原因。

 

當客戶端事務進入”terminate”狀態以后,客戶端事務必須立刻銷毀。這樣才能保證正確操作。原因是當給一個INVITE請求的2xx應答的不同處理;對於proxy轉發的時候和對UAC處理ACK的時候是不一樣的。因此,每一個2xx都需要交給proxy 核心(這樣才能被轉發),或者交給UAC核心(這樣才能被ACK確認)。這期間沒有事務層的處理。無論應答是否由通訊層收到,如果通訊層找不到匹配的客戶端事務(用17.1.3的方式),那么應答就應當交給核心處理。這是由於與之匹配的客戶端事務已經被第一個2xx應答所銷毀,后續的2xx應當就匹配不成功了,於是就交給核心來處理。

 

17.1.1.3 構造ACK請求

本節定義了在客戶端事務中構造ACK請求的方法。UAC核心為2xx應答產生ACK請求必須使用13節描述的方法,而不是用下邊的方法。

 

在客戶端事務中構造的ACK請求必須包括與原始請求相同的Call-ID, From, Request-URI頭域值(就是說和在客戶端事務發到通訊層的請求中的這些頭域值相同)。在ACK請求中的To頭域必須和被確認的應答的To頭域值相同,因此通常和原始請求有所不同,不同點在增加了附加的tag參數。ACK必須包含一個單個的Via頭域,並且必須和原始請求的最上邊一個Via頭域值相等。ACK的Cseq頭域必須包含和原始請求的Cseq的序列號相同,但是方法參數應當是”ACK”。

 

如果INVITE請求的應答是有Route頭域的,這些Route頭域必須也在ACK中。這是確保ACK能夠正確路由通過下行隊列的無狀態的proxy。

 

雖然請求可以包含一個包體,但是ACK的包體卻比較特別,因為請求不能因為不能理解包體而拒絕這個請求。因此,我們不建議在給非2xx應答的ACK請求中放置包體,但是如果放置了,並且假設給INVITE的應答不是415應答,那么包體的類型應當嚴格和INVITE請求中定義的那樣。如果是415應答,那么ACK的包體應當和415應答中的Accept列出的類型一致。

 

例如:有如下請求

INVITE sip:bob@biloxi.com SIP/2.0

Via: SIP/2.0/UDP pc33.atlanta.com;branch=z9hG4bKkjshdyff

To: Bob <sip:bob@biloxi.com>

From: Alice <sip:alice@atlanta.com>;tag=88sja8x

Max-Forwards: 70

Call-ID: 987asjd97y7atg

Cseq: 986759 INVITE

 

給非2xx終結應答的ACK請求應當是:

ACK sip:bob@biloxi.com SIP/2.0

Via: SIP/2.0/UDP pc33.atlanta.com;branch=z9hG4bKkjshdyff

To: Bob <sip:bob@biloxi.com>;tag=99sa0xk

From: Alice <sip:alice@atlanta.com>;tag=88sja8x

Max-Forwards: 70

Call-ID: 987asjd97y7atg

Cseq: 986759 ACK

 

17.1.2 非INVITE客戶端事務

17.1.2.1 非INVITE事務概覽

非INVITE事務並不使用ACK。他們只是簡單的請求-應答的交互。對於非可靠的通訊來說,請求是間隔倍增T1的時間重新傳輸(直到間隔時間達到T2)。如果收到了一個臨時應答,在非可靠通訊上,重傳繼續知道達到T2。只有當重傳的請求收到的時候,服務端事務會重傳其發出的最后一個應答,既可以是臨時的應答也可以是終結應答。這就是為什么請求在收到一個臨時應答之后還需要一直重傳的原因;他們能夠確保收到一個終結應答。

 

不像INVITE事務,非INVITE事務不需要對2xx應答做特別處理。UAC對一個非INVITE請求來說,只會產生一個單個的2xx應答。

 

17.1.2.2 正式的描述

在圖6中講述了非INVITE客戶端事務的狀態機。這個狀態機和INVITE客戶端事務的狀態機非常像。

 

當TU用請求來初始化一個新的客戶端事務的時候,首先進入的是“trying”狀態。當進入這個狀態的時候,客戶端事務應當初始化一個定時器F,這個定時器F應當有一個初始值64×T1秒。這個請求必須交給通訊層來發送。如果使用的是非可靠傳輸的通訊協議,客戶端事務必須還設置定時器E,初始值是T1。如果定時器E觸發了,並且還是在”trying”狀態,那么定時器需要設置成為MIN(2*T1,T2),並且重新發送;如果再次觸發了,那么就再設置成為MIN(4*T1,T2),每次都是倍增,知道T2。這個過程會一直繼續,直到重發的間距是T2為止。缺省的T2是4秒,並且它大概是一個在沒有立刻響應的情況下,非INVITE服務端事務處理一個請求的時間。根據缺省的T1和T2,那么間隔就會是:500ms,1s,2s,4s,4s,4s以次類推。

 

如果定時器F觸發了,並且客戶端事務依舊是在”trying”狀態,那么客戶端事務應當通知TU這個超時,並且轉入”terminate”狀態。如果在”trying”狀態的時候收到了一個臨時應答,那么這個應答必須轉給TU處理,並且客戶端事務轉到”proceeding”狀態。如果在”trying”狀態收到了一個終結應答(200-699的應答碼),那么應答必須交給TU,並且客戶端事務必須轉到”Completed”狀態。

 

如果定時器E在”Proceeding”狀態觸發了,那么請求必須交給通訊層進行傳輸,並且定時器E必須重新設置成為T2秒。如果定時器F在”Proceeding”狀態觸發了,那么必須通知TU超時了,並且客戶端事務必須轉到終結狀態。如果在”Proceeding”狀態的時候收到了一個終結應答(狀態碼200-699),這個應答必須發送給TU,並且客戶端事務必須轉到”Completed”狀態。

 

一旦客戶端事務進入”Completed”狀態,對於非可靠傳輸的情況,客戶端事務必須設置一個定時器K=T4秒,對於可靠傳輸的情況,設置定時器K=0秒。這個”Completed”狀態維持的目的是為了緩沖可能會收到的其他重發的應答(這是為什么客戶端事務在這里為非可靠傳輸維持一段時間的原因)。T4代表了網絡在客戶端和服務端事務中傳輸信息可能的時間。缺省的值T4=5秒。當應答具有相同的事務匹配的時候,根據17.1.3的判定,這個應答就是重發的應答。如果定時器K在這個狀態被觸發,客戶端事務必須轉到”Terminate”狀態。

 

當事務進入終結狀態,就必須立刻終止了。

 

17.1.3 客戶端事務匹配應答

當客戶端事務的通訊層收到一個應答,他必須決定是否由客戶端事務來處理這個應答,這樣17.1.1和17.1.2才能夠正確執行。在Via頭域的最上邊的branch參數就是用來做這個的。一個應答和一個客戶端事務匹配的話,就有兩個條件:

1、    如果應答Via最上邊的branch參數和創建這個客戶端事務的請求的Via最上邊的branch參數相同。

2、    如果Cseq頭域的方法參數和創建事務的請求的方法相同。這是因為CANCEL方法的事務和源請求的事務不同,但是卻有相同的branch參數所決定的。

 

如果一個請求是廣播發送的,他可能從不同的服務器上得到不同的應答。這些應答的最上邊的Via都有相同的branch參數,但是在To tag中是不同的。當收到了第一個應答,基於上邊的規則,將會判定是這個客戶端事務的應答,其他的應答將會視同為重發。這並不是錯誤的情況;多點傳送SIP只是提供了一個根本的”尋找最接近的單點”服務的方法,這樣就限定了只需要處理一個單個應答。詳情參見18.1.1。

 

 

 

17.1.4 處理通訊錯誤。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

圖6:非INVITE客戶端事務

當客戶端事務發送一個請求到通訊層發送的時候,如果通訊層報告發送失敗,那么需要執行下列步驟。

 

客戶端事務應當通知TU這個通訊失敗,並且客戶端事務應當直接轉到”Terminate”狀態。TU處理通訊失敗的機制在附件[4]中描述。

 

17.2 服務端事務

服務端事務是用來傳輸請求到TU並且可靠的傳輸應答的。它是通過狀態機來實現的。服務端事務是當請求到達的時候由核心創建的,事務的處理也是主要圍繞着對應請求的(也就是說並非全部都是和對應請求相關)。

 

和客戶端事務對應的,狀態機依賴於是否接收的請求是INVITE請求。

 

17.2.1 INVITE服務端事務

INVITE服務端事務的狀態圖在圖7表達。

 

當為一個請求創建了服務端事務的時候,服務端事務進入”proceeding”狀態。除非服務端事務知道TU在200ms之內會生成臨時或者終結應答(在這種情況下,TU可能會產生100Trying應答),他必須生成100(Trying)應答。這個臨時應答是用來停止客戶端重發請求的,這個可以避免網絡風暴。這個100(Trying)應答是根據8.2.6節描述的步驟構造的,除此之外: 如果接收的請求頭中的To頭域沒有tag標志,那么原來描述的可以增加tag標記,更改成為不應該增加tag標志。這個請求必須交給TU處理。

 

TU可以給服務端事務任意數量個臨時應答。只要服務端事務在”proceeding”狀態,每個臨時應答都應當交給通訊層發送。這些臨時應答並非被通訊層可靠的發送(他們並不重新發送臨時應答)並且臨時應答並不改變服務端事務的狀態。如果在”proceeding”狀態,收到一個請求的重發請求,那么就需要把從TU最近收到的那個臨時應答重新交給通訊層發送一次。請求是否是重發的請求,是基於17.2.3來判定的匹配相同服務端事務的請求。

 

如果,在”proceeding”狀態,TU發送了一個2xx應答給服務端事務,服務端事務必須把這個應答交給通訊層進行發送。這個並非由服務端事務進行重發;對於2xx應答的重發是由TU處理的。服務端事務必須轉到”Terminated”狀態。

 

當在”Proceeding”狀態的時候,如果TU交給服務端事務一個300到699的應答,那么應答必須交給通訊層進行發送,並且狀態機必須進入”Completed”狀態。對於非可靠傳輸的情況,必須設置定時器G=T1秒,對於可靠傳輸的情況,不設置定時器G(=0的情況就是不設置)

這個是和RFC2543所不同的,2543要求應答都要重發,甚至在可靠傳輸的情況下。

 

當進入了”Completed”狀態,必須為所有的傳輸,設置一個定時器H=64×T1秒。定時器H決定何時服務端事務取消重發應答。這個值和定時器B的取值一樣,是等同於客戶端事務會重試發送請求的時間。如果定時器G觸發了,那么應答會交給通訊層再次發送,並且定時器設置成為MIN(2*T1,T2)秒。依此類推,當定時器G再次觸發,那么定時器G的值會翻倍,直到T2。這個和非INVITE客戶端事務的”trying”請求的重發機制是一樣的。進一步說,當在”Completed”狀態的時候,如果接收到重發的請求,服務端事務應當把應答交給通訊層再次發送。

 

當服務端事務在”Completed”狀態的時候,如果收到了一個ACK請求,服務端事務必須轉到”Confirmed”狀態。因為定時器G會在這個狀態被忽略,所有的應答重發都會被終止。

 

如果在”completed”狀態的時候,定時器H觸發了,就意味着沒有收到ACK請求。在這個情況下,服務端事務必須轉到”Terminated”狀態,並且必須通知TU事務失敗。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

圖7:INVITE服務端事務

 

設定”Confirmed”狀態的目的是為了處理任何附加的ACK消息,這是由重發的終結應答所觸發的。當進入這個狀態,如果是在不可靠傳輸協議,那么就要設定一個定時器I=T4秒,如果是可靠傳輸協議,那么就設定I=0。當定時器I觸發了,服務端事務必須轉到”Terminated”狀態。

 

當服務端事務狀態處於”Terminated”狀態,這個事務必須立刻銷毀。和客戶端事務一樣,這是為了保證給INVITE的2xx應答的可靠性。

 

17.2.2 非INVITE服務端事務

對非INVITE服務端事務的狀態機是在圖8中表示。

 

 

小虎 2006-05-25 00:09

當收到一個不是INVITE或者ACK的請求的時候,狀態機會初始化成為”trying”狀態。並且這個請求會交給TU處理。當在”trying”狀態,任何重發的請求會被忽略。一個請求在通過17.2.3節的步驟,匹配現有的服務端事務,將被認為是重發的請求。

 

當處於”trying”狀態,如果TU交給服務端事務一個臨時應答,服務端事務應當進入”Proceeding”狀態。這個應答必須交給通訊層進行發送。在”Proceeding”狀態下從TU收到的任何應答都必須交給通訊層進行發送。如果一個重發的請求在”proceeding”狀態下收到了,那么最近發出的一個臨時應答應當再次交給通訊層進行重發。如果在”Proceeding”狀態下,TU交給服務端事務一個終結應答(應答碼是200-699),那么服務端事務必須進入”Completed”狀態,並且應答必須交給通訊層進行發送。

 

當服務端事務進入了”Completed”狀態,對於不可靠傳輸協議來說,必須設定一個定時器J=64×T1秒,對於可靠傳輸來說,設定為0秒(就是不設定定時器)。當在”Completed”狀態下,當服務端事務收到了一個重發的請求的時候,服務端事務必須交給通訊層終結應答來重新發送。在”Completed”狀態下,任何其他TU傳遞下來給服務端事務的終結應答都必須被拋棄。服務端事務保持這個狀態直到定時器J觸發,當定時器J觸發了以后,服務端事務必須進入”Terminated”狀態。

 

17.2.3 為服務端事務匹配請求。

當服務端從網絡上收到一個請求以后,他必須和現有的事務進行判定。這個是根據下邊的規則來判定的。

 

首先要檢查請求中的Via頭域的最上一個branch參數。如果他以”z9hG4bk”開頭,那么這個請求一定是由客戶端事務根據本規范產生的。因此,branch參數在該客戶端發出的所有的事務中都是唯一的。根據下列規則我們可以判定請求是否和事務匹配:

 

1、    請求中的最上的Via頭域的branch參數和創建本事務的請求的最上的Via頭域的branch參數一樣,並且:

2、    請求的最上的Via頭域的sent-by參數和創建本事務的請求的最上的Via頭域的send-by參數一樣,並且:

3、    請求的方法和創建本事務的方法一樣。這有一個例外,就是ACK,ACK對應的創建本事務的請求方法是INVITE。

這個匹配規則用於INVITE和非INVITE事務。

 

send-by參數被用於匹配過程,這是因為有可能存在無意/惡意的相同的不同客戶端傳來的branch參數。

 

如果最上的Via頭域的branch參數不存在,或者沒有包含那個”z9hG4bk”,那么就用下列步驟進行判定。這是為了和RFC2543進行兼容的。

 

如果是INVITE請求,並且這個INVITE請求的Request-URI,To tag,From tag,Call-ID,Cseq,和最上的Via頭域都和創建事務的INVITE請求的這些字段匹配,那么這個INVITE請求就是匹配這個事務的INVITE請求。在這個情況下,INVITE就是創建這個事務的INVITE請求的一個重發。ACK請求在匹配創建事務的INVITE請求的Request-URI, From tag, Call-ID ,Cseq序列號(非方法字段), 最上的Via頭域,並且To tag和服務端事務發出的應答的To tag相同,這個ACK就是這個事務的ACK。當這些頭域比較完成,那么這個匹配也就完成了。在ACK比較中包含To tag的比較是為了在proxy上能夠區別給2xx的ACK和給其他應答的ACK,這個proxy可能會轉發全部的應答(這個會在某種罕見的情況下發生。特別是,當一個proxy分支一個請求,接着宕機了,應答會轉發到別的proxy,這個proxy可能會終止轉發多重應答到上行隊列)。一個匹配INVITE請求事務的ACK請求,如果這個INVITE請求已經被前一個ACK請求所匹配,那么這個ACK請求就是上一個ACK請求的重發。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

圖8: 非INVITE 服務端事務

 

對於所有的其他請求方法,如果請求的Request-URI,To tag,From tag,Call-ID, Cseq(包括Cseq中的方法字段),以及Via頭域的最上值,都和創建服務端事務的請求想匹配,那么這個請求就是這個事務的匹配請求。匹配是基於針對每一個頭域值的判定進行的。當非INVITE請求和現有事務匹配了,那么它就是創建這個事務的請求的一個重發。

 

由於匹配規則中包含了Request-URI,服務器不能匹配應答對應到事務。所以當TU傳送了一個應答到服務端事務,它必須為這個應答指定傳送到那個服務端事務。

 

17.2.4 處理通訊錯誤

當服務端事務發送一個應答到通訊層要發送的時候,如果通訊層報告發送失敗,那么就需要執行下列的步驟:

 

首先,附件[4]的步驟需要執行,這就是說需要把應答發送一個備份的地點。如果這個也失敗了,基於[4]中對失敗的定義,服務端事務應當通知TU發送失敗,並且把狀態切換到終止狀態。

 

18 通訊(transport)

通訊層負責請求和應答在網絡上的實際傳輸。這包括了在面向連接的通訊方式下的請求和應答所使用的連接管理。

 

通訊層負責管理像TCP/SCTP之類通訊協議的長連接,或者在這些協議上的TLS連接,並且包括管理打開這些連接的使用者的管理。這包括了客戶端或者服務端通訊層打開的連接,這樣在客戶端服務端通訊函數可以共享這些連接。這些連接采用一組用遠端的地址,端口,通訊協議標志的索引來進行管理。當通訊層打開了一個連接,這個連接的索引就設置成為遠端的IP,端口,還有打開這個連接的通訊層的實例 。當通訊層接收了一個連接,那么這個連接的索引就被設置成為連接方的源IP地址,port,還有通訊層的實例transport。注意,由於源端口port通常是臨時創建的,但是由於通過附件[4]的步驟不能知道它是臨時創建的還是配置的,所以通訊層被動接收的連接通常是不被重復使用的。這就是說,如果兩個proxy再一個”peering”(點對點)的關系中,使用一個面向連接的通訊協議通常有兩個連接要使用,每個都是自己作為主動方連接的。

 

我們建議在實現中,當發送(或者接收)完成最后一個消息之后,依舊維持這個連接一段時間(這段時間可以是實現自己定義的時間)。這段時間應當是至少等於本節點的事務從創建到結束的最長時間。這是為了讓事務能夠在他們所創建的同一個連接上完成(比如,在這個連接上完成請求,應答的處理,在INVITE的情況下的給非2xx的ACK應答等等)。這通常意味着至少64×T1秒(參見17.1.1.1中關於T1的定義)。不過,如果當本程序的TU使用的是一個比較大的定時器C(參見16.6節11步)的時候,也可以選取一個比較大的值。

 

所有的SIP元素都必須實現基於UDP和TCP的通訊。SIP元素還可以實現其他的協議。

 

要求UA支持TCP是對RFC2543的一個重要改進。這是由於需要處理更大的消息,就像接下來講到的那樣,必須使用到TCP協議。因此,即使是SIP元素不要發送大的消息,但是由於它可能收到大消息並且處理這些消息,所以,要求支持TCP。

 

18.1 客戶Clients

18.1.1 發送請求

通訊層的客戶端負責發送請求和接收應答。通訊層的用戶把請求交給通訊層的實例進行處理,包括IP地址端口,通訊層實例,還有可能有多點廣播的TTL。

 

如果請求的大小和MTU差是在200個字節以內的,或者它是大於1300字節的,並且路徑MTU的大小是未知的,那么請求必須遵循RFC2914[43]控制阻塞的傳輸協議,比如使用TCP。如果這導致了Via最上邊指定的通訊協議的改變,那么Via最上邊的值就必須也隨之改變。這使得在UDP傳輸上的消息的分割,並且也提供了大消息的傳輸阻塞控制。不過,在實現上,必須能夠支持達到最大包大小的消息的處理。對於UDP來說,包含了IP和UDP頭的大小是65535個字節。

 

在消息的大小和MTU之間的200個字節的”buffer”,提供了一個機制使得在SIP的應答中,可以超過請求的大小。比如在INVITE請求的應答中,增加了Record-Route頭域值。有了這個額外的buffer,應答可以大概比請求大170個字節,而且在Ipv4上不用進行分塊傳輸(假設沒有IPSec,大概IP/UDP會使用30個字節)。當MTU是未知的時候,選取1300是基於假設Ethernet的MTU是1500字節的基礎上。

 

如果SIP元素是因為消息大小的限制,所以基於TCP發送一個請求,並且消息如果不是因為大小的限制,會使用UDP來發送,並且如果建立連接產生一個ICMP 協議不支持的錯誤,或者導致TCP reset,那么這個元素就應當用UDP重試這個請求。這只是為了向后兼容RFC 2543針對不支持TCP的實現。在本規范以后的改動中,這部分內容會有修訂。

 

如果客戶端向多個地址發送請求,那么必須增加”maddr”參數到Via頭域值上,並且這個參數值指定多個目的地址,對於Ipv4來說,應當增加”ttl”參數=1,IPV6的多點傳送在本規范中沒有定義,會在后續的標准中描述。

 

這些規則定義了SIP的多點傳送。首要的目的是為了提供”尋找最接近的單點”服務(”single-hop-discovery-like”),這個服務將請求轉發到一組類似的服務器,並且只需要處理其中任意一個服務器的應答。這個功能主要用於注冊服務。實際上,基於17.1.3的事務處理規則,客戶端事務會接收第一個應答,並且因為其他應答包含同樣的Via的branch參數,而視這些應答為重發應答。

 

在請求發送嵌,客戶端通訊層必須在Via頭域中增加一個”sent-by”欄。這個字段包含了一個IP地址或者主機名,端口。我們推薦使用FQDN方法描述這個主機名。這個字段在某些特定情況下,用於發送應答。如果端口不存在,缺省的值依賴於通訊協議。對於UDP,TCP和SCTP來說是5060,TLS是5061。

 

對於可靠傳輸協議,應答通常簡單的通過連接發送,並且這個連接是收到對應請求的連接。因此,客戶端傳輸層必須准備在發出請求的同一個連接上接收應答。在出現錯誤的情況下,服務端可能會嘗試新建立一個連接來發送應答。為了能夠處理這種情況,通訊層必須准備接收一個從源IP建立的新連接,這個連接的IP是請求發起的源IP,port是在”sent-by”字段中指定的port。這也同樣要求准備接收從任意地址和端口來得新連接上接收應答,這個端口是由服務器根據附件[4]的5節所講述的步驟來選取的。

 

對於非可靠的傳輸協議,客戶端通訊層必須准備從發送請求的那個原始IP地址上接收應答。(因為應答會送到原始地址去),並且端口號是在”sent-by”字段的端口好。進一步說,和可靠傳輸一樣,早某些情況下,應答會發往不同的地方。客戶端必須能夠准備從其他地址和端口上接收應答,這個端口是由服務器根據附件[4]的5節所講述的步驟來選取的。

 

對於多點傳送的情況來說,客戶端通訊層必須准備從相同的多點傳輸組上接收應答,這個組的地址和端口和發出請求的組相同(就是說,它必須是發送請求的那個多點傳輸組的一個成員)。

 

如果請求發送的目的IP地址,端口和transport都和現有的一個連接相同,那么建議使用這個連接來發送請求,同時也允許新建立一個連接來發送。

 

如果請求通過多點發送,那么它發送的一組地址,端口和TTL都是由通訊層的用戶提供。如果請求是通過不可靠通訊協議發送,那么發送的IP地址和端口也是由通訊層的用戶提供。

 

18.1.2 接收應答

當應答接收到的時候,客戶端通訊層檢查最上的Via頭域值。如果”sent-by”參數不符合客戶端通訊層在請求中插入的值,那么這個應答必須悄悄丟棄。

 

如果由任何客戶端事務存在,客戶端通訊層使用17.1.3的步驟來匹配現存的事務和這個接收到的應答。如果匹配到了,應答必須交給事務層進行處理。否則,應答必須交給核心去處理(無論是有狀態的proxy,還是無狀態的proxy,還是UA的核心)。處理這些”stray”(迷路)的應答是基於核心的策略的(如果是proxy就會轉發,如果是UA就會忽略,等等)。

 

18.2 服務端

18.2.1 接收請求

一個服務器應當能夠接收從任何IP地址、端口和協議上過來的請求。他們是通過對這個服務器的SIP或者SIPS URI(附件[4])的DNS查找,得到這個服務器的地址然后連接和發送的請求的。在這里,”handing out”(發布)包含了在REGISTER請求或者轉發應答的Contact頭域中放一個URI,或者在請求或者應答的”Record-Route”頭域中放一個URI。這個URI可以通過放在網頁或者名片上被”handing out”(發布)。同樣的我們也建議服務器在公網上監聽缺省的SIP端口(TCP/UDP是5060,5061是在TCP上的TLS)。如果是在局域網上,或者私有網上,或者一個物理服務器上運行好幾個服務實例,那就很自然的可以設置成不同的。對於服務器監聽UDP的任何端口和界面,都必須在TCP上也進行同樣的監聽。這是因為可能消息還需要通過TCP進行傳輸,比如消息過大的情況。所以,在相反的情況下就不需要了。如果一個服務器在TCP監聽了,那么它不一定需要在UDP上也進行相應的監聽。當然服務器也可以因為某些原因在特定地址和端口上監聽UDP。當服務端事務從任意一個通訊層上接收到一個請求的時候,它必須檢查最上的Via頭域的”sent-by”參數。如果”sent-by”參數的主機部分包含了一個主機名,或者它包含的IP地址和包的源地址不同,服務器必須增加一個”received”參數到這個Via頭域值中。這個參數必須包含收到的包的原地址。由於服務端必須把應答發送給收到請求的那個源IP地址,所以這個可以用來幫助服務端通訊層發送應答。

 

一個服務端通訊層收到的請求可能是這樣的(部分):

 

INVITE sip:bob@Biloxi.com SIP/2.0

Via: SIP/2.0/UDP bobspc.biloxi.com:5060

 

請求是從源IP:192.0.2.4收到的。在請求轉交到上層之前,通訊層增加了一個”received”參數,這樣請求的部分就是:

INVITE sip:bob@Biloxi.com SIP/2.0

Via: SIP/2.0/UDP bobspc.biloxi.com:5060;received=192.0.2.4

 

接着,服務端通訊層嘗試和服務端事務做匹配。這個使用的是17.2.3節定義的規則。如果匹配上一個服務端事務,那么請求就交給那個事務去處理。如果沒有匹配到事務,請求就交給核心去處理,可能會創建一個新的服務端事務來處理。注意當UAS核心給INVITE請求發送一個2xx應答的時候,服務端事務已經銷毀了。這就是說,當ACK收到的時候,不會有匹配的服務端事務,並且基於這個規則,ACK回交給UAS核心來處理。

 

18.2.2 發送應答

服務端事務使用最上邊的Via頭域值來決定把應答發送到哪里。它必須遵從如下步驟來發送:

o 如果”sent-protocol”是一個可靠的傳輸協議比如TCP或者SCTP,或者在其上的TLS,應答必須用現存的到原始請求(創建這個事務的請求)的連接進行發送(如果連接還存在的情況下)。這個要求服務端通訊層保留服務端事務和通訊層連接的相關性。如果連接不存在了,服務端應當創建一個新的連接,如果存在”received”參數,就用對應的在”received”參數中指定的IP地址。如果存

 

 

小虎 2006-05-25 00:10

在”sent-by”參數,那么就用”sent-by”指定的port,如果不存在,那么就用缺省的port。如果對應的連接已經失效,那么服務器應當采用附件[4]的步驟來決定使用那個IP地址和端口來建立連接並且發送應答。

 

o 否則,如果Via頭域包含一個”maddr”參數,就必須把應答轉發到maddr所指明的地址,並且使用”sent-by”所指定的端口,如果沒有sent-by參數,那么就使用5060缺省參數。如果地址是一個多點地址,應答應當使用”ttl”參數所指定的TTL,或者如果沒有指定”ttl”參數,則使用TTL=1的參數。

 

o 否則(對於非可靠傳輸),如果Via的最上頭域包含一個”received”參數,那么應答必須發送到”received”參數所指定的地址,並且使用”sent-by”所指定的端口,如果沒有sent-by參數,那么就使用5060缺省參數。如果這步失敗了,比如,如果得到一個ICMP”端口不能到達”的錯誤,那么就應當根據附件[4]的第5節的步驟來決定應當把應答發送到哪里。

 

o 否則,如果沒有receiver-標記,那么應答應當使用附件[4]的第5節指定的步驟,送到”sent-by”參數指定的地址。

 

18.3 分塊

在面向消息的通訊協議中(比如UDP),如果消息有一個Content-Length頭域,那么消息體就有可能包含很多字節。並且收到的包中除了這個消息體的Content-Length字節意外,還有通訊層附加的通訊包字節,那么這部分額外的字節應當被丟棄。如果通訊包在沒有收到完整的Content-Length字節的消息體就終止了,這就意味着出錯了。如果這個消息是一個應答,那么這個消息必須被丟棄。如果消息是一個請求,那么本程序應當給出一個400(Bad Request)應答。如果消息沒有包含一個Content-Length頭域,消息體的結束點就是消息體的結束點。

 

在面向流的通訊協議中(比如TCP),Content-Length頭域標志這包體的大小。在面向流的通訊協議中,必須使用Content-Length字段。

 

18.4 錯誤處理

錯誤的處理取決於出現錯誤的消息是請求還是應答。

 

如果通訊層的用戶要求在一個非可靠傳輸協議上發送一個消息,並且結果是一個ICMP錯誤,那么錯誤處理的方法依賴於ICMP錯誤類型。當通訊層遇到主機、網絡、端口或者協議無法到達的錯誤,或者參數錯誤的時候,應當通知通訊層的用戶發送失敗。Source quench和TTL exceeded ICMP錯誤應當被忽略。

 

如果通訊層用戶要求在一個可靠傳輸協議上發送一個請求,並且結果是一個連接錯誤,通訊層應當通知通訊層用戶這個發送錯誤

 

19 常見消息部件(Common Message Components)

在SIP消息中,有一些很長用的部件。(甚至在SIP消息外這些部件也存在)。這些部件值得我們單獨討論一下。

 

19.1 SIP和SIPS統一資源標記

SIP或者SIPS 的URI用來標記一個通訊用的資源。就像其他所有的URI一樣,SIP和SIPS URI可以放在網頁上,email消息里,或者打印出來的名片上等等。在這些URI里邊包含了足夠的信息來發起和維持到這個資源的一個通訊會話。

 

一個通訊資源的例子包含下列內容:

 

o 一個在線服務的用戶

o 一個多線電話

o 消息系統中的郵箱

o 網關服務的PSTN電話號碼

o 一個組織中的一個部門(比如”銷售”,或者”helpdesk”)

 

SIPS URI定義了對資源的訪問是安全的。這就意味着,特別是,在UAC和這個資源的主機之間的通訊是基於TLS的。從資源的主機到用戶之間的通訊是加密安全的,這個安全機制是依賴於主機的實現的。任何用SIP URI描述的資源,只要想通過加密的形式進行通訊,都可以通過簡單改變一下資源描述府就可以”升級”成為一個SIPS URI。

 

19.1.1 SIP和SIPS部件

“sip:”和”sips:”描述符是遵循RFC2396[5]的規范定義的。他們使用類似mailto URL的格式定義,允許有SIP請求頭域字段和SIP消息體的規范。這使得在網頁上或者email中,可以用URI來初始化一個會話,這個會話有特定的主題,媒體類別,緊急類型。這個SIP或者SIPS URI的格式規范在25節定義。一個SIP URI的通常格式是這樣的:

 

sip: user:password@host:port;uri-parameters?headers

 

這個和SIPS URI的格式是相同的,只是SIPS用”sips”來代替sip。這些符號,和符號的擴展,具有下列意義:

 

user: 這是在主機的特定資源地址。”主機”(host)在這里通常指的是一個域名。URI中的”userinfo”包含了這個用戶域,口令域,並且包含其后的一個@。URI的用戶信息部分是可選的,或者說是可以沒有的;當目的主機沒有用戶的概念或者主機本身就是資源的目標,那么這個URI的用戶信息部分就是可以沒有的。如果在SIP或者SIPS URI中有@,那么用戶部分必須不能為空的。如果主機部分可以處理電話號碼地址,比如說是一個internet電話網關,那么根據RFC2806[9]定義的電話號碼域應當出現在用戶信息部分。在19.1.2節有關於在SIP或者SIPS URI中的電話號碼描述域的額外說明

 

password:password字段是和用戶相關的。SIP或者SIPS URI語法允許增加password這個字段,這種用法我們是不推薦的,因為把身份認證信息放在明碼表示的地方(比如URI)會帶來很大的安全風險。比如,通訊層在這個字段用了一個PIN碼,那么就會暴露這個PIN碼而帶來安全隱患。

 

注意密碼字段只是一個用戶信息擴展的一部分。實現上並沒有標記一個特別的密碼部分,可以簡單的把”user:password”當作一個簡單的用戶串來對待。

 

host:主機提供了SIP資源。host部分包含了一個完整的主機名字或者IPV4/IPV6的地址。我們強烈建議如果可能,就使用完整格式的主機名字。

 

port: 端口號是請求將被送出的端口。

 

URI 參數:請求將使用這個URI來構造。

 

URI參數在hostport部件之后增加,用分號分開。

URI參數有如下格式:

 

參數名’=’參數值

 

雖然在同一個URI中允許有任意多個URI的參數,但是同一個參數名只能出現1次。

 

這個擴展機制包括了transport,maddr,ttl,user,method和lr參數

 

transport參數決定在[4]中定義的發送SIP消息的通訊機制。SIP可以使用任何網絡通訊協議。參數名字是為UDP(RFC 768[14]),TCP(RFC 761 [15])和SCTP(RFC2960[16])定義的。對於一個SIPS URI,transport參數必須指向一個可靠的通訊協議。

 

maddr參數指明了聯系這個用戶的服務器的地址,它會覆蓋在host域中的地址。當給定了一個maddr參數,URI中的port和transport部件將會在maddr中指出。[4]描述了正確的transport,maddr,hostport規范,用於獲得發送請求到目的地所需要的目的地址,端口,通訊協議。

 

maddr字段用作簡單的去掉源路由的方法來使用的。它允許一個URI指定一個必須經過的proxy來到達目的地。我們強烈建議不要把maddr參數用於這個目的(我們反對把maddr用於這個機制)。在實現上應當使用本文中描述的Route機制,如果有需要,則建立一個pre-existing(預先設置的)路由集合(參見8.1.1.1)。它提供了一個完整的URI來描述需要經過的節點。

 

ttl參數決定了UDP多點報文的生存周期,並且只能用於maddr是一個多點地址並且通訊協議是UDP的情況。例如,為了指定一個到alice@atlanta.com的呼叫,使用多點廣播到239.255.255.1,並且ttl=15,那么應該使用下邊的一個URI:

 

sip:alice@atlanta.com;maddr=239.255.255.1;ttl=15

 

有效的電話描述(telephone-subscriber)的字串一個集合是有效的用戶字串的子集。我們用用戶URI參數來區別電話號碼和用戶名(長得像電話號碼的用戶名)。如果用戶串使用了電話號碼描述的字串,用戶參數值”phone”應當增加。即使沒有這個參數,如果本地用戶名的命名限制機制允許的情況下,SIP和SIPS URI的接受方也可以把這個@以前的部分解釋為電話號碼。

 

從URI中構建SIP請求所需要的method域,可以由method參數指定。

 

如果指定了lr參數,就標志着這個資源的擁有者是根據本規范來實現的路由機制。這個參數回用於proxy放在Record-Route頭域的URI中,也可以出現在pre-existing(預先設置)的路由集合中。

 

這個參數是用來和RFC2543定義中的嚴格路由機制向后兼容所使用的,並且rfc2543bis 改變為bis-05。如果一個元素准備發送一個基於沒有包含這個參數的URI請求,那么我們可以假定這個請求的接受方是根據嚴格路由的規范實現的,並且會重新規格化這個消息來保護在Request-URI中的內容。

 

由於URI參數機制是可以擴展的,SIP元素應當悄悄跳過那些不認識的uri參數。

Headers:頭域是從給定URI創造的請求的頭域部分。

在SIP請求中的頭域可以在URI中用”?”來給出。頭域名(hname)和頭域值(hvalue)都是用&符號間隔的頭域名=頭域值的格式。特定的頭域名”body”的頭域值就是SIP請求的消息體。

 

表1總結了在URI的不同情況下SIP和SIPS URI的部件用法。擴展的列描述了在SIP消息歪的URI,例如在網頁上或者名片上的情況。項目中的’m’是強制必須的意思,’o’是可選的,’-‘是不允許的。處理URI的元素應當忽略掉URI中出現的任何不允許的部件。在表格中的第二列是如果該元素不存在的時候的缺省值。’--'表示本元素不是可選的,或者沒有缺省值的意思。

 

在Contact頭域中的URI在頭域出現的不同地方有着不同的約束。一個是在消息建立和維持一個對話的時候(INVITE請求以及它對應的200(OK)應答),一個是在注冊和轉發消息的時候(REGISTER,以及對應的200(ok)應答,以及給任何方法的3xx系列的應答)

19.1.2 Character Escaping Requirements(字符轉碼要求)

   default    Req-URI    To    From    reg./redir.Contact    dialogContactR-R/Route    external

user    - -    o    o    o    o    o    o

password    - -    o    o    o    o    o    o

host    - -    m    m    m    m    m    m

port    (1)    o    -    -    o    o    o

user-param    ip    o    o    o    o    o    o

method    INVITE    -    -    -    -    -    o

maddr-param    - -    o    -    -    o    o    o

ttl-param    1    o    -    -    o    -    o

transp.-param    (2)    o    -    -    o    o    o

lr-param    - -    o    -    -    -    o    o

other-param    - -    o    o    o    o    o    o

headers    - -    -    -    -    o    -    o

 

(1):缺省的通訊端口是依賴於通訊協議的。對於使用UDP,TCP,SCTP的sip來說,是5060,對於使用基於TCP的TLS來說,是5061。

(2)缺省的通訊協議是和sip/sips相關的,對於sip來說,是UDP,對於sips來說,是TCP。

 

表1:對於SIP頭域值,Request-URI及其引用的使用和缺省值。

 

基於RFC2396[5]的要求和指引,當需要把字符串封裝到SIP URI的時候,使用””%” HEX HEX”機制來進行轉碼。根據RFC2396[5]:

 

任何指定URI部件保留的字符集都是由這個部件定義的。通常,如果URI的語義由於組成字符被它的US-ACII編碼[5]的escape碼替代而改變的時候,這個字符就是保留字符。除了USASCII字符(RFC2396[5])之外,比如空格和控制字符,以及URI所使用的分隔符,必須進行轉碼。URI必須不能包含任何未經轉碼的空白和控制字符。

 

對於每一個部件來說,由BNF擴展的合法字符集合規定了那些字符是可以不經轉碼的。其他字符都必須經過轉碼。

 

比如,”@”不是user部件的字符集中的字符,所以,userj@sOn,必須把@符號進行編碼,成為”j%40sOn”

 

在25節中的hname和hvalue的符號展示了在URI的保留字符中,在頭域名和頭域值中所有需要被轉碼的字符集合。

 

user部件的電話描述(telephone-subscriber)部分由特別的轉碼考慮。在RFC2806[9]關於電話描述部分中未被保留的字符集,由很多字符組成,他們在SIP URI中的不同語法部分的時候,都需要做轉碼。在電話描述中出現的任何字符,只要不在BNF針對user部分的擴展規則中出現的,都需要做轉碼。

 

注意在SIP或者SIPS URI中,host部分不允許做字符的轉碼(%不在它的擴展部分中)。在以后的Inernationalized Domain Names完成以后,這個限制可能就會改了。當前實現中不允許把在host部分收到的轉碼字符進行轉碼處理。因為這個轉碼處理和IDN要求的處理不一樣。

 

19.1.3 SIP和SIPS URI例子

sip:alice@atlanta.com

sip:alice:secretword@atlanta.com;transport=tcp

sip:alice@atlanta.com?subject=project%20x&priority=urgent

sip:+1-212-555-1212:1234@gateway.com;user=phone

sips:1212@gateway.com

sip:alice@192.0.2.4

sip:atlanta.com;method=REGISTER?to=alice%40atlanta.com

sip:alice;day=Tuesday@atlanta.com

 

最后一個URI例子有一個user域”alice;day=Tuesday”。上邊定義的轉碼規則中允許”;”在這個字段中不進行轉碼。在本協議的設計概念中,這個字段是不透明的。這個字段的值只對負責這個資源的SIP元素有用。

 

19.1.4 URI比較

在本規范中,部分操作需要比較兩個SIP或者SIPS URI是否相等。比如,在這個規范中,注冊服務器需要比較在REGISTER 請求中綁定的Contact URI(參見10.3)。SIP和SIPS URI根據如下步驟進行比較:

 

o SIP和SIPS URI永遠不等。

o SIP/SIPS URI的userinfo是大小寫敏感的。這包括了含有password或者按照電話描述格式的userinfo的比較。對於URI的其他部分的比較,除了有特別指出之外,都是大小寫不敏感的。

o 參數的順序和頭域的順序對於比較SIP/SIPS URI不起作用。

o 在保留字符集之外的字符(參見RFC2396[5]),等同於他們的””%” HEX HEX”格式。

o IP地址就算是等同於通過DNS查找到的主機名對應的IP地址,IP地址也不能和主機名等同。

o 兩個URI如果相同,那么user,password,host,port部分必須相同。

 

有user部分的URI和沒有user部分的URI是不相等的。有password部分的URI和沒有password部分的URI也是不同的。

 

一個不帶可選部件的URI和帶了這些部件但是值是缺省值的URI是不等的。例如,如果一個URI省略了port部件,並不等於一個定義了5060port部件的URI。同樣的規則適用域transport-參數,ttl-參數,user-參數,method部件等等。

 

定義sip:user@host,和定義sip:user@host:5060(根據RFC2543的變體)不相等。當從URI中取得地址的時候,相同的URI可以取得相同的地址。sip:user@host:5060始終可以得到端口5060。URI: sip:user@host根據[4]所定義的DNS SRV機制,可能可以得出其他的端口來。

 

o URI uri參數部件按照如下規則進行比較

-    任何在兩個URI中出現的uri參數都必須一樣

-    user,ttl,或者方法uri參數如果只在一方出現,即使和缺省值相等,也判定為兩個URI不相等。

-    包含maddr參數的URI和沒有包含maddr參數的不相等。

-    其他uri參數,如果在一方出現,則在比較的時候忽略。

 

o URI頭部件的比較是不能忽略的。任何在header部分出現的域都必須在雙方URI中進行匹配和比較。比較規則參見20節。

 

下列URI是相等的:

sip:%61lice@atlanta.com;transport=TCP

sip:alice@AtLanTa.CoM;Transport=tcp

 

sip:carol@chicago.com

sip:carol@chicago.com;newparam=5

sip:carol@chicago.com;security=on

 

sip:biloxi.com;transport=tcp;method=REGISTER?to=sip:bob%40biloxi.com

sip:biloxi.com;method=REGISTER;transport=tcp?to=sip:bob%40biloxi.com

 

sip:alice@atlanta.com?subject=project%20x&priority=urgent

sip:alice@atlanta.com?priority=urgent&subject=project%20x

 

下列URI是不相等的:

 

SIP:ALICE@AtLanTa.CoM;Transport=udp         (用戶名不同)

sip:alice@AtLanTa.CoM;Transport=UDP

 

 

sip:bob@biloxi.com                (端口不同)

sip:bob@biloxi.com:5060

 

sip:bob@biloxi.com            (通訊協議不同)

sip:bob@biloxi.com;transport=udp

 

sip:bob@biloxi.com            (通訊協議和端口不同)

sip:bob@biloxi.com:6000;transport=tcp

 

sip:carol@chicago.com        (header部件不同)

sip:carol@chicago.com?Subject=next%20meeting

 

sip:bob@phone21.boxesbybob.com        (就算是phone21.boxesbybob.com

sip:bob@192.0.2.4            解析到192.0.2.4也不能算相等的。)

 

注意相等性是不能傳遞的。

比如 sip:carol@chicago.com 和sip:carol@chicago.com;security=on相等

sip:carol@chicago.com 和 sip:carol@chicago.com;security=off相等

但是:

sip:carol@chicago.com;security=on和sip:carol@chicago.com;security=off不等。

 

19.1.5 從URI中產生請求

對於實現而言,需要能夠直接從一個URI來構造請求。URI可以是從名片,網頁,或者甚至從某些協議內部得到(比如登記的聯系信息等等)。

 

協議的實現必須包括構造請求的Request-URI中的transport,maddr,ttl,或者user參數。如果URI包含了method參數,那么它的值必須和構造的請求的方法一樣。並且method參數不能放在Request-URI中。不認識的URI參數必須放在消息的Request-URI中。

 

實現中應當把URI中出現的header或者包體部分包含入消息本身,並且當作是請求自己的組成部分。

 

在實現中,不應當保留那些明顯危險的頭域字段:From,Call-ID,Cseq,Via和Record-Route。

 

並且實現中,也不應當保留任何請求的Route頭域值,這樣可以避免無知的客戶端進行惡意攻擊。

 

實現中也不應當保留那些可能會導致錯誤登記地址或者誤導能力的頭域字段,這些包括:Accept,Accept-Encoding,Accept-Language,Allow,Contact(在對話中使用),Organization,Supported,和User-Agent。

 

實現上應當檢查每一個請求中所描述的頭域的正確性,包括:Content-Disposition, Content-Encoding,Content-Language, Content-Length, Content-Type, Date, Mime-Version, Timestamp。

 

如果從給定URI構造的請求不是一個合法的SIP請求,那么這個URI就是非法的URI。實現上禁止處理和傳送非法的SIP請求。它應當嘗試追查為何會有一個非法的URI。

 

很多情況都可以得到一個非法的請求。這包括但是不限於,頭域的語法錯誤,非法的URI參數合並,或者錯誤的消息體描述等等。

 

發送從URI構造的請求可能會導致實現上的能力不夠。比如:URI可能指定了尚未實現的通訊協議或者通訊擴展。那個這個具體的實現上來說,應當拒絕發送這些請求,而不是修改這個請求來適應具體實現的處理能力。對於具體實現來說,它不能發送包含它自己不能理解的擴展部分的請求。

 

比如,從一個包含了未知的或者擺明了不支持的Request頭域參數或者method參數的URI中,構造的請求就是不能發送的。

 

19.1.6 關聯SIP URI和tel URL

如果tel URL(RFC 2806[9])轉換成為一個SIP或者SIPS URI,那么tel URL的整個電話描述(telephone-subscriber),機器參數,都需要放在SIP或者SIPS URI的userinfo部分。

 

因此:tel:+358-555-1234567;postd=pp22 會變成:

sip:+358-555-1234567;postd=pp22@foo.com;user=phone

或者

sips:+358-555-1234567;postd=pp22@foo.com;user=phone

而不是

sip:+358-555-1234567@foo.com;postd=pp22;user=phone

或者

sips:+358-555-1234567@foo.com;postd=pp22;user=phone

 

通常來說,相等的”tel”URL轉換成為SIP或者SIPS URI以后,不一定能得到相同的SIP或者SIPS URI。因為SIP和SIPS URI的userinfo部分是根據大小寫敏感的字串。由大小寫不敏感的tel URL以及重新排序的tel URL參數並不改變tel URL的相等性,但是在轉換成為SIP或者SIPS URI之后,卻影響了他們的相等性。

 

例如:

tel:+358-555-1234567;postd=pp22

tel:+358-555-1234567;POSTD=PP22

是等價的,但是

sip:+358-555-1234567;postd=pp22@foo.com;user=phone

sip:+358-555-1234567;POSTD=PP22@foo.com;user=phone

卻是不等價的。

 

類似的:

tel:+358-555-1234567;postd=pp22;isub=1411

tel:+358-555-1234567;isub=1411;postd=pp22

是等價的,但是

sip:+358-555-1234567;postd=pp22;isub=1411@foo.com;user=phone

sip:+358-555-1234567;isub=1411;postd=pp22@foo.com;user=phone

卻不等價

 

為了避免這個問題,在構造放在SIP或者SIPS URI中的userinfo部分的電話描述域的時候,應當轉換大小寫不敏感的電話描述域為小寫,並且除了isdn-subaddress和post-dial,把電話描述的參數按照參數名進行排序, 因為他們需要按順序出現在參數的第一個。(在下邊是除了未來擴展參數意外的全部tel URL大小寫不敏感的部分)。

 

根據上邊的描述,全部:

tel:+358-555-1234567;postd=pp22

tel:+358-555-1234567;POSTD=PP22

 

轉換成為

sip:+358-555-1234567;postd=pp22@foo.com;user=phone

 

並且全部:

tel:+358-555-1234567;tsp=a.b;phone-context=5

tel:+358-555-1234567;phone-context=5;tsp=a.b

轉換成為:

sip:+358-555-1234567;phone-context=5;tsp=a.b@foo.com;user=phone

 

19.2 Option Tags

Option tags是一個唯一標志,用來指明SIP中的新options(擴展)的。這些tags在Require(20.32節),Proxy-Require(20.29節),Supported(20.37節)和Unsupported(20.40節)頭域中使用。注意這些options是以option-tag=的形式作為這些頭域的參數存在的(25節有關定義符號)。

 

Option tags是根據標准的RFC擴展定義的。這是和過去的試驗有所不同,這是協會為了保證多個廠商之間能夠持續互相協作(20.32節、20.37節的討論)。option tags的IANA注冊可以保證查找很容易。

 

19.3 Tags

“tag”參數用於SIP消息中的To和From頭域。它作為一個通用的機制的一部分來唯一標志一個對話,這個機制用Call-ID和兩個從對話參與者的tag來標志一個對話。當UA在對話外發出一個請求時,它只包含了From tag,提供了對話ID的”一半”。對話根據應答創建完成,這個應答在To頭域中提供了對話ID的另一半。SIP請求的分支意味着一個單個請求可以創建多個對話。這個也解釋了為何需要對話兩方的標志;如果沒有被叫方的標志,呼叫方不能分辯和消除由單個請求創建的多個對話。

 

當UA產生一個tag並且增加進一個請求或者應答的時候,它必須是一個全局唯一的,並且是密碼隨機數起碼是32位的隨機數。這個要求是為了讓UA能夠在同一個INVITE請求中,在給這個INVITE的應答中,在To頭域產生一個不同的tag,和原始INVITE請求在From頭域中產生的tag不同。這是因為UA可以邀請自己到一個會話,常見的是在PSTN網關的”hairpinning”(發夾)呼叫。類似的,對不同呼叫的兩個INVITE也有不同的From tag,並且給這兩個呼叫的兩個應答也有不同的To tag。

 

在全局唯一要求之外,產生tag的算法是實現相關的。Tag對於容錯系統比較有用,在容錯系統下,當主服務器故障的時候,對話會在另外一個服務器上進行恢復。UAS可以產生一個tag,讓備用服務器能夠認識到這個請求是在故障服務器上的對話,並且能夠決定是否恢復對話和對話相關的狀態。

 

20 頭域

頭域的語法描述在7.3節。本節列出了頭域的全部列表,包括了語法注釋,含義,和用法。通過本節,我們使用[HX.Y]指當前HTTP/1.1 的RFC2616[8]的規范的X.Y節。每個頭域都有示例給出。

 

關於與方法和proxy處理有關的頭域字段在表2和表3中有處理。

 

“where”列描述了在頭域中能夠使用的請求和應答的類型。這列的值是:

R:頭域只能在請求中出現;

r:頭域只能在應答中出現;

2xx,4xx,等等:一個數字的值區間表示頭域能夠使用的應答代碼。

c:頭域是從請求拷貝到應答的。

如果”where”欄目是空白,表示頭域可以在所有的請求和應答中出現。

 

“proxy”列描述了proxy在頭域上的操作

a:如果頭域不存在,proxy可以增加或者連接頭域

m:proxy可以修改現存的頭域值

d:proxy可以刪除頭域值

r:proxy必須能讀取這個頭域,因此這個頭域不能加密。

 

接下來6個欄目與在某一個方法中出現的頭域有關:

c:條件;對頭域的要求依賴於消息的內容

m:頭域是強制要有的。

m*:頭域應當被發送,但是客戶端/服務端都需要准備接收沒有這個頭域的消息。

o:頭域是可選的。

t:頭域應當被發送,但是客戶端/服務端都需要准備接收沒有這個頭域的消息。客戶端/服務端都需要准備接收沒有這個頭域的消息。如果通訊的協議是基於面向流的協議(比如TCP),那么頭域值必須被發送。

*:如果消息體不為空,那么頭域值就緒要的。(細節請參見20.14,20.15和7.4節)

-:這個頭域是不適用的。

 

“Optional”意味着這個元素可以在請求或者應答中包含這個頭域,並且UA可以忽略在請求或者應答中存在的這個頭域(這條規則有一個例外,就是Require頭域,在20.32節有描述)。”mandatory”(強制)頭域是必須在請求中存在的頭域,並且也必須是UAS接收到一個請求時能夠理解的頭域。一個強制頭域必須也在應答中出現,並且UAC也能處理這個頭域。”Not applicable”(不適用)意味着頭域不能在請求中出現。如果一個UAC錯誤的把這個頭域放在請求中,在UAS收到的時候必須被忽略。同樣的,如果應答中的”不適用”的頭域,也就是說UAS不能在應答中放置的頭域,如果出現了,那么UAC也必須在應答中忽略掉這個頭域。

 

一個UA必須忽略他們所不能處理的擴展的頭參數。

 

本規范也定義了常用的頭域名的縮寫,用於縮小消息的大小。

 

在Contact,From,To頭域中都包含一個URI。如果這個URI包含一個逗號,問號或者分毫,那么這個URI必須使用尖括號括起來(<和>)。所有的URI參數都必須在這些括號內。如果URI並非用尖括號括起來的,那么用分號分開的參數將被視同與header參數而不是URI參數。

 

20.1 Accept

Accept頭域的語法定義遵從[H14.1]。除了如果沒有Accept頭域,服務器應當認為Accept缺省值是application/sdp以外,語義也是和HTTP/1.1類似的語義。

 

空的Accept頭域意味着不接受任何格式。

 

 

小虎 2006-05-25 00:10

Header field    where    proxy    ACK    BYE    CAN    INV    OPT    REG

Accept    R        -    o    -    o    m*    o

Accept    2xx        -    -    -    o    m*    o

Accept    415        -    c    -    c    c    c

Accept-Encoding    R        -    o    -    o    o    o

Accept-Encoding    2xx        -    -    -    o    m*    o

Accept-Encoding    415        -    c    -    c    c    c

Accept-Language    R        -    o    -    o    o    o

Accept-Language    2xx        -    -    -    o    m*    o

Accept-Language    415        -    c    -    c    c    c

Alert-Info    R    ar    -    -    -    o    -    -

Alter-Info    180    ar    -    -    -    o    -    -

Allow    R        -    o    -    o    o    o

Allow    2xx        -    o    -    m*    m*    o

Allow    r        -    o    -    o    o    o

Allow    405        -    m    -    m    m    m

Authentication-Info    2xx        -    o    -    o    o    o

Authorization    R        o    o    o    o    o    o

Call-ID    c    r    m    m    m    m    m    m

Call-Info        ar    -    -    -    o    o    o

Contact    R        o    -    -    m    o    o

Contact    1xx        -    -    -    o    -    -

Contact    2xx        -    -    -    m    o    o

Contact    3xx    d    -    o    -    o    o    o

Contact    485        -    o    -    o    o    o

Content-Disposition            o    o    -    o    o    o

Content-Encoding            o    o    -    o    o    o

Content-Language            o    o    -    o    o    o

Content-Length        ar    t    t    t    t    t    t

Content-Type            *    *    -    *    *    *

Cseq    c    r    m    m    m    m    m    m

Date        a    o    o    o    o    o    o

Error-Info    300-699    a    -    o    o    o    o    o

Expires            -    -    -    o    -    o

From    c    r    m    m    m    m    m    m

In-Reply-To    R        -    -    -    o    -    -

Max-Forwards    R    amr    m    m    m    m    m    m

Min-Expires    423        -    -    -    -    -    m

MIME-Version            o    o    -    o    o    o

Organization        ar    -    -    -    o    o    o

表2: 頭域概覽,A-O

 

 

Header field    where    proxy    ACK    BYE    CAN    INV    OPT    REG

Priority    R    ar    -    -    -    o    -    -

Proxy-Authenticate    407    ar    -    m    -    m    m    m

Proxy-Authenticate    401    ar    -    o    o    o    o    o

Proxy-Authorization    R    dr    o    o    -    o    o    o

Proxy-Require    R    ar    -    o    -    o    o    o

Record-Route    R    ar    o    o    o    o    o    o

Record-Route    2xx,18x    mr    -    o    o    o    o    -

Reply-To            -    -    -    o    -    -

Require        ar    -    c    -    c    c    c

Retry-After    404,413,480,486        -    o    o    o    o    o

Retry-After    500,503600,603        -    o    o    o    o    o

Route    R    adr    c    c    c    c    c    c

Server    r        -    o    o    o    o    o

Subject    R        -    -    -    o    -    -

Supported    R        -    o    o    m*    o    o

Supported    2xx        -    o    o    m*    m*    o

Timestamp            o    o    o    o    o    o

To    c(1)    r    m    m    m    m    m    m

Unsupported    420        -    m    -    m    m    m

User-Agent            o    o    o    o    o    o

Via    R    amr    m    m    m    m    m    m

Via    rc    dr    m    m    m    m    m    m

Warning    r        -    o    o    o    o    o

WWW-Authenticate    401    ar    -    m    -    m    m    m

WWW-Authenticate    407    ar    -    o    -    o    o    o

表3:頭域概覽,P-Z (1)和可能的附加tag一起拷貝。

 

例子:

Accept: application/sdp;level=1,application/x-private,text/html

 

20.2 Accept-Encoding

Accept-Encoding頭域類似Accept,但是限定了接收應答中的內容的編碼[H3.5]。參見[H14.3]。在SIP中的語義和在[H14.3]中的定義是一致的。

 

一個空的Accept-Encoding頭域是允許的。他等同於Accept-Encoding:identity,這就是說,只有identity編碼,也就是說沒有編碼的情況,是允許的。

 

如果沒有Accept-Encoding頭域存在,那么服務端應當使用缺省值:identity。

 

這個和HTTP的定義略有不同,HTTP指出如果本頭域不存在,那么任何編碼形式都可以使用,只是推薦identity編碼而已。

 

例如:

Accept-Encoding:gzip

 

20.3 Accept-Language

Accept-Language頭域用來在請求中指定首選的的語言的,這個首選的語言是在應答中的消息體中的的原因分析,會話描述,或者狀態報告的。如果沒有Accept-Language存在,那么服務端應當假設所有的語言客戶端都可以接受。

 

Accept-Language頭域遵從[H14.4]節定義的語法。對於SIP來說,也同樣支持對語言通過”q”參數來進行排序。

 

例如:

 

Accept-Language: da, en-gb; q= 0.8, en;q=0.7

 

20.4 Alert-Info

當INVITE請求有一個Alert-Info頭域的時候,Alert-Info頭域就包含的是給UAS的一個額外的信息。當在180(Ringing)應答中出現的時候,Alter-Info頭域給出了UAC一個額外的回鈴信息。這個頭域的一個典型用法就是讓proxy增加這個頭域用來體哦你嘎一個與眾不同的振鈴效果。

 

Alter-Info頭域可能會帶來潛在的安全隱患。這個隱患以及相應的處理在20.9節有講述,這個隱患和Call-Info頭域的隱患是相同的。

 

另外,用戶應當可以有選擇的屏蔽這個特定。

 

這個可以保護用戶不因為使用了未受信任節點發送過來的這個頭域而導致的破壞。

例如:

Alter-Info: <http://www.example.com/sounds/moo.wav>

20.5 Allow

Allow頭域列出了UA支持的方法列表。

 

如果要提供UA頭域,那么所有只要是UA支持的方法,包括ACK和CANCEL都必須列在這個Allow頭域中。如果沒有Allow頭域出現,一定不能以為UA什么方法都不支持。應當解釋成為發送這個消息的UA並沒有告訴大家它支持什么方法。

 

在應答中提供Allow頭域比在OPTIONS請求/應答中會減小所需要的消息數量。

 

例如:

Allow: INVITE,ACK,OPTIONS,CANCEL,BYE

 

20.6 Authentication-Info

Authentication-Info 頭域提供了和HTTP類別相同的認證方法。UAS可以在給一個順利通過認證的請求的2xx應答中包含這個頭域,並且是使用基於Authorization頭域的分類。

 

這個頭域的語法和語義遵循RFC2617[17]的規范。

 

例如:

 

Authentication-Info: nextnonce=”47364c23432d2e131a5fb210812c”

 

20.7 Authorization

Authorization頭域包含了了UA進行認證的信任書。22.2節概述了對Authorization頭域的用法,22.4節講述了和HTTP 認證一起使用的時候的語法和語義。

 

這個頭域,和Proxy-Authorization,並不遵循通常的多頭域值的規則。雖然它不是由逗號分割的列表,這個頭域名可以出現多次,並且不能應用7.3節的規則合並成為單個頭域。

 

在下邊的例子中,在分類參數兩邊沒有引號括起來。

 

Authorization:Digest username=”Alice”, realm=”atlanta.com”, 

nonce = ”84a4cc6f3082121f32b42a2187831a94”,

response=”7587245234b3434cc3412213e5f113a5432”

 

20.8 Call-ID

Call-ID頭域用來唯一區別一個特定的邀請或者一個特定客戶端的所有注冊項。單個多媒體會議可以分解成為多個不同Call-ID的呼叫,例如,當一個用戶數次邀請單個個體加入同一個會議的時候。Call-ID是大小寫敏感的並且是字節/字節比較的。

 

Call-ID頭域的簡寫就是i

 

例子:

Call-ID: f81d4fae-7dec-11d0-a765-00a0c91e6bf6@biloxi.com

i:f81d4fae-7dec-11d0-a765-00a0c91e6bf6@192.0.2.4

 

20.9 Call-Info

Call-Info頭域提供了對呼叫方或者被叫方的附加信息,如果出現在請求中則是呼叫方的信息,如果出現在應答中則是被叫方的。”purpose”參數中存放了效果圖URI。”icon”參數包含了一個呼叫方或者被叫方的圖標。”info”參數描述了簡要的呼叫方或者被叫方的信息,例如,通過放置一個網頁進行介紹等。”card”參數提供了一個名片,比如,基於vCard[36]或者LDIF[37]格式。如果附加新的標記,那么可以通過27節描述的步驟通過在IANA注冊來附加。

 

對Call-Info的使用可能會帶來一些安全隱患。如果一個被叫方接到一個惡意呼叫方提供的URI,被叫方可能會由顯示一個不合適的內容,或者危險的或者非法的內容,等等。因此,我們建議UA只顯示那些它能夠檢驗並且信任發送方身份的Call-Info頭域中的內容。這個對於對方UA來說不需要。proxy可以在請求中加入這個頭域。

 

例如:

Call-Info:  ;purpose=icon,

http://www.example.com/alice/;purpose=info

 

20.10 Contact

Contact頭域提供了一個URI,這個URI的含義取決於是在請求還是在應答中。

 

Contact頭域包含了一個顯示的名字,一個包含參數的URI,還有header參數組成。

 

本文檔定義了一個Contact參數”q”和”expires”。這些參數只有當Contact頭域在REGISTER的請求或者應答,或者3xx的應答中才有效。在其他規范中可能會定義一個附加的參數。當頭域值包含一個顯示的名字,那么帶參數的URI應當用”<”和”>”括起來。如果沒有”<”,”>”括起來,所有URI后邊的參數都將視為header參數,而不是URI參數。顯示姓名可以是符號,或者引號引起來的字符串(如果很長的話)。

 

即使”display-name”是空的,如果”addr-spec”包含一個逗號或者分號,或者?的話,也必須使用”name-addr”的格式。這在display-name和”<”之間可以有也可以沒有LWS(線性空白)

 

這些關於顯示名字,URI和URI的參數,header參數的規則同樣對To和From頭域適用。Contact頭域的的角色很像HTTP中的Location頭域的角色。但是HTTP頭域只允許1個地址,沒有其他說明。由於URI中可以包含逗號和分號,所以他們在header或者參數分隔符上是錯誤的。

 

Contact頭域的縮寫是m(“moved”)。

例子:

 

Contact: “Mr.Watson” <sip:watson@worcester.bell-telephone.com>;q=0.7;

expires=3600,

“Mr. Watson” mailto:watson@bell-telephone.com ;q=0.1

 

m: <sips:bob@192.0.2.4>;expires=60

 

20.11 Content-Disposition

Content-Disposition頭域描述了消息體,或者消息的多個部分,或者消息體的一個部分應被UAC或者UAS怎樣解釋。這個SIP頭域擴展了MIME Content-Type(RFC 2183[18])。

 

SIP定義了Content-Disposition幾個新的”disposition-types”。如果取值”session”意味着消息體位呼叫(calls)或者早期(pre-call)媒體,描述了一個會話。取值”render”表示了消息體可是被顯示或者展示給用戶。注意”render”比”inline”更適合避免MIME消息體作為一個大的消息的一部分做展示(由於SIP消息的MIME消息體經常不被展示給用戶)。出於向后兼容的考慮,如果Content-Disposition頭域不存在,服務器應當假設Content-Type為application/sdp的部屬方式是”session”,為其他方式的時候是”render”。

 

部屬方式“icon”表示消息體部分包含了一個用於表示呼叫者或者被叫者的icon圖像,當UA收到這個消息,就可以展示一下,或者在對話過程中一致展示。”alert”意味着消息體部分包含了信息,比如是一段聲音,應當由UA展示給用戶提示用戶這個請求,通常是初始化對話的請求;這個altering消息體可以是一個在180Ringing臨時應答發出后的一個鈴聲。

 

所有需要展示給客戶的具有”disposition-type”的MIME消息體,都應當只在這個消息有適當的安全認證的時候展示。

 

處理參數,handling-param,描述了UAS在接收到這個內容類型或者部屬類型是它所不支持的消息體的時候,應當如何操作。這個參數定了了”optional”和”required”兩個值。如果處理參數沒有,那么這個處理參數缺省值就是”required”。處理參數在RFC3204[19]中定義和描述的。

 

如果這個頭域不存在,那么MIME類型決定了缺省的內容部屬。如果沒有MIME類型,那么缺省值就是”render”

例如:

 

Content-Disposition: session

 

20.12 Content-Encoding

Content-Encoding頭域是對”media-type”(媒體類型)的一個修正。當存在這個頭域的時候,它的值就是對包體內容編碼的附加說明,並且因此必須根據本字段應用正確的解碼機制,這樣才能得到正確的Content-Type頭域指出的媒體類型的解碼。Content-Encoding首要應用於在不丟失媒體類型標記的情況下對消息體進行壓縮處理。

 

如果包體應用了多個編碼,那么包體編碼必須按順序在這個字段中進行列出。

 

所有的Content-Encoding的值都是大小寫不敏感的。IANA是這個編碼方式的注冊機構。參見[H3.5]獲得Content-coding的語法定義。

 

客戶端可以在請求中進行包體的內容編碼。服務端也可以在應答中進行內容編碼。服務端必須只能應用客戶端在請求中的Accept-Encoding頭域中列出的編碼類型。

 

Content-Encoding簡寫是e。

例如:

Content-Encoding:gzip

e: tar

 

20.13 Content-Language

參見[H14.12].例如:

Content-Language: fr

 

20.14 Content-Length

Content-Length頭域標志了消息體的大小,給消息的接受者,以10進制表示的數字。應用程序應當使用這個字段標志的大小來傳送消息體,而不關心消息體的媒體類型是什么。如果是基於流的通訊協議(比如TCP),那么本頭域必須提供。

 

消息的大小並不包含CRLF分開的頭域和包體。任何大於或者等於0 的Content-Length都是合法的長度。如果消息中不包含包體,那么Content-Length必須設置成為0

 

對Content-Length的忽略能夠簡化創建一個類似cgi一樣動態生成應答的腳本。(???)

 

這個頭域的簡寫是l

 

例如:

Content-Length:349

l:173

 

20.15 Content-Type

Content-Type頭域標志了發給對方的消息體的媒體類型。”media-type”是在[H3.7]中定義的。如果消息體不為空,那么Content-Type頭域就必須存在。如果消息體是空的,並且笨頭域存在,那么就表示了特定類型的媒體的包體是0長度(比如空的音頻文件)。

 

本頭域的簡寫是c

例如:

Content-Type: application/sdp

c: text/html;charset=ISO-8859-4

 

20.16 Cseq

請求中的Cseq頭域包含了一個單個的數字序列號和請求的方法。這個序列號必須是表示成為一個32位的無符號整數。在Cseq的請求方法部分是大小寫敏感的。Cseq頭域是為了在會話中對事務進行排序的,提供事務的唯一標志,並且區分請求和請求的重發。如果序列號相等,並且請求的方法相等,那么兩個Cseq頭域就是相等的。

例如:

Cseq:4711 INVITE

 

20.17 Date

Date頭域包含了日期和時間。和HTTP/1.1不同,SIP只支持最近的RFC1123[20]格式的日期。如同在[H3.3]中,SIP限制了在SIP-date中的時區是”GMT”,但是在RFC1123中支持任意的市區。RFC1123的日期是大小寫敏感的。Date頭域反應的時間是請求或者應答被發送的那一刻的時間。

 

Date頭域可以用來簡化沒有后備電池的終端系統,讓他們能夠獲得當前的時間。但是由於是GMT格式的,所以,它要求客戶端知道和GMT的時差。

 

例如:

Date:Sate,13 Nov 2010 23:29:00 GMT

 

20.18 Error-Info

Error-Info頭域提供了對有錯誤應答碼的應答的附加信息。

 

SIP UAC具有從彈出的窗口PC界面,到只有聲音的電話或者網關過來的終端界面。與其強制服務器產生一個錯誤來選擇是發送一個帶有詳細原因說明的錯誤代碼應答,還是播放一段聲音,不如使用Error-Info頭域把兩個都發送。讓UAC來決定用什么來展示給呼叫方。

 

UAC可以把在Error-Info中的一個SIP或者SIPS URI當作是轉發的一個Contact地址,並且據此產生一個新的INVITE,這樣可以建立預先錄制的聲明會話。如果是非SIP URI,那么可以展示給用戶。

 

例如:

 

SIP/2.0 404 The Number you have dialed is not in service

Error-Info: <sip:not-in-service-recording@atlanta.com>

 

20.19 Expires

Expires頭域給定了消息(或者內容)過期的相關時間。這個字段的精確定義是方法相關的。對於一個INVITE的超時時間並不影響這個INVITE請求建立的實際的會話。不過,會話描述協議可以描述在一個會話上的的時間限制。

 

這個頭域的值是一個以秒計數的整數,從0到(2**32)-1,從收到請求開始計數。

 

例如:

Expires:5

 

20.20 From

From頭域表示了請求的來源地。這個可能和對話的來源的不同,被叫方到呼叫方的請求會在From頭域使用被叫方的地址。

 

選項”display-name”是展示給界面的。如果客戶標志停留在隱藏狀態,那么系統應當使用”Anonymous”作為顯示名字。即使是”displayname”是空的,如果”addr-spec” 包含一個逗號,?,或者分毫,那么就必須使用”name-addr”格式。相關的格式在7.3.1節描述。

 

如果From的URI相等,並且參數也相等,那么這兩個頭域就是相等的。如果擴展參數在一個頭域中存在,但是在另外一個頭域中不存在,那么當這兩個頭域做比較的時候,這個參數將被忽略。這意味着顯示名字的存在與否不影響比較的結果。

 

參見20.10處理顯示名字,URI和URI參數,以及頭域參數的規則。

 

From頭域的簡寫是f

例子:

From: “A. G. Bell” <sip:agb@bell-telephone.com> ; tag=a48s

From: sip:+12125551212@server.phone2net.com;tag=887s

f: Anonymous <sip:c8oqz84zk7z@privacy.org>;tag=hyh8

 

20.21 In-Reply-To

In-Reply-To頭域列舉了本次呼叫相關的或者返回的Call-ID。這些Call-ID可以備客戶端cache起來,這樣可以在這個頭域中返回。

 

這允許自動呼叫奮發系統來路由這些返回的呼叫到第一個呼叫的原始請求地點。這也允許被叫方過濾呼叫,這樣只有在呼叫中原始請求建立的呼叫才會被接受。這個字段不是對請求驗證的一個替代。

例如:

 

In-Reply-To: 70710@saturn.bell-tel.com,17320@saturn.bell-tel.com

 

20.22 Max-Forwards

Max-Forwards頭域必須在任何一個SIP請求中使用,來限制中間轉發請求到下一個節點的proxy或者gateway的個數。這個在客戶端trace一個請求,如果路由失敗或者在中間出現循環的時候特別有用。

 

Max-Forwards是一個0-255的整數,表明了在這個請求消息中允許被轉發的剩余次

 

 

小虎 2006-05-25 00:11

數。每當服務器轉發這個請求一次,這個數字就減一。建議的初始值是70。當不能確定有無循環路由的時候,必須在頭域中增加本頭域。比如,一個B2BUA應當增加這個頭域。

 

例如:

Max-Forwards:6

 

20.23 Min-Expires

Min-Expires頭域包含了一個服務器所支持的內部狀態(soft-state)的最小的刷新時間間隔。這個包括被登記服務器所登記的Contact頭域。這個頭域包含了一個以秒計數的整數,從0到(2**32)-1。在423(Interval Too Brief)應答中,本頭域的用法在10.28,10.3,和21.4.17中有描述。

例如:

Min-Expires:60

 

20.24 MIME-Version

參見[H19.4.1]

例如:

MIME-Version: 1.0

 

20.25 Organization

Organization頭域包含了發出請求或者應答的SIP節點所屬的組織名字。這個字段可以用來讓客戶端軟件過濾呼叫。

例如:

 

Organization: Boxes by Bob

 

20.26 Priority

Priority頭域標志了客戶端評價的請求緊急程度。Priority頭域描述了SIP應當處理人工或者UA發過來的請求的優先級。舉例來說,這可能是決定呼叫轉發和處理的優先要素。對於判定優先級來說,如果消息沒有包含Priority字段,那么處理的時候應當當作”normal”優先級處理。Priority頭域不影響通訊資源的優先順序,比如路由上的包轉發的優先級或者訪問PSTN網關的優先級。本頭域有”non-urgent”,”normal”,”urgent”,和”emergency”取值,另外的取值可以在別處定義。我們強烈建議”emergency”只用於影響到生命、身體、或者財產危急時候才使用。其他情況下, 本頭域沒有額外的語義。在RFC2076[38]中,定義了”emergency”。

 

例如:

Subject: A tornado is heading our way!

Priority: emergency。

 

或者

Subject: Weekend plans

Priority: non-urgent.

 

20.27 Proxy-Authenticate

Proxy-Authenticate頭域用來進行認證使用的。這個頭域的用法在[H14.33]中定義。參見22.3節關於本字段的細節討論。

 

例如:

Proxy-Authenticate: Digest realm=”atlanta.com”,

domain=”sip:ss1.carrier.com”,qop=”auth”,

nonce=”f84f1cec41e6cbe5aea9c8e88d359”,

opaque=””,stale=FALSE,algorithm=MD5

 

20.28 Proxy-Authorization

Proxy-Authorization頭域允許客戶端向一個要求認證的proxy證明自己(或者證明它的使用者)的身份。一個Proxy-Authorization頭域包含了與UA認證信息相關的信任書,這個信任書是給proxy和/或者本請求相關的域的。

 

參見22.3節關於這個頭域的定義。

 

本頭域,連通Authorization頭域,並不遵循常用的多頭域名(多個相同頭域名的合並)的規則。雖然不是用逗號分割的列表,這個頭域名可以出現多次,並且不能用7.3.1描述的通常規則合並成為一個頭域。

 

例如:

Proxy-Authorization: Digest username=”Alice”,realm=”atlanta.com”,

nonce=”c60f3082ee1212b402a21831ae”,

response=”245f23415f11432b3434341c022”

 

20.29 Proxy-Require

Proxy-Require頭域用來表示請求中一定要求proxy支持的相關的特性。參見20.32關於這個頭域的使用。

 

例子:

Proxy-Require:foo

 

20.30 Record-Route

Record-Route頭域是proxy在請求中增加的,用來強制會話中的后續請求經過本proxy的。本頭域的用法在16.12.1節有描述。

 

例子:

Record-Route: <sip:server10.biloxi.com;lr>,

<sip:bigbox3.site3.atlanta.com;lr>

 

20.31 Reply-To

Reply-To頭域包含了邏輯上返回目的地URI,這個可以和From頭域不同。比如,URI可以用來返回未接電話或者未建立的會話。如果用戶希望保留匿名,那么這個頭域應當從請求中去除或者改變,這樣可以避免透露個人隱私信息。

 

即使”display-name”是空的如果”addr-spec”包含了逗號、問號、或者分號,那么就需要使用”name-addr”的格式來填寫。這個語法在7.3.1中定義。

 

例如:

Replay-To: Bob <sip:bob@biloxi.com>

 

20.32 Require

Require頭域用於UAC告訴UAS關於要求UAS支持那些特性。雖然這是一個可選的頭域,但是如果Require頭域存在,那就一定不能掠過不處理。

 

頭域包含一個option tag的列表,這個列表在19.2節中描述。每一個option tag定了一個要處理請求要求UAS必須支持的SIP擴展。通常,這用於定義一個需要支持的擴展頭域的集合。復核本規范的UAC應當值包含規范的RFC擴展。

例如:

Require:100rel

 

20.33 Retry-After

Retry-After頭域可以用於500(Server Internal Error)或者503(Service Unavailable)應答,用來標志大約本服務還會處於不可用狀態多久。在404(Not Found),413(Request Entity Too Large), 480(Temporarily Unavailable),486(Busy Here), 600 (Busy), 或者603(Decline)應答中用於標志何時被叫方會恢復正常。這個字段的值是一個秒為單位的正整數(十進制),從應答生成開始的一個正整數。

 

對於回叫的時間,可以有一個附加的說明。”duration”參數標志了被叫方變成正常狀態的時間長度。如果沒有定義,那么服務可以被看作是永遠有效。

 

例如:

Retry-After: 18000;duration=3600

Retry-After:120 (I’m in a meeting)

 

20.34 Route

Route頭域用於強制一個請求經過一個proxy路由列表。Route頭域的使用在16.12.1節定義:

例如:

Route: <sip:bigbox3.site3.atlanta.com;lr>,

<sip:server10.biloxi.com;lr>

 

20.35 Server

Server頭域包含了關於UAS處理請求所使用的軟件信息。

 

服務器的特定軟件版本可能會使服務器由於特定軟件安全漏洞導致服務器收到攻擊。實現上應當使得Server頭域是一個可以配置的選項。

 

例如:

 

Server:HomeServer v2

 

20.36 Subject

Subject頭域提供了呼叫的一個概覽,允許呼叫不用分析會話描述就可以大致過濾。會話描述並不需要和INVITE邀請使用相同的主題標志。

 

Subject的縮寫是s

例如:

Subject: Need more boxes

s: Tech Support

 

20.37 Supported

Supported頭域列舉了UAC或者UAS支持的擴展。

 

Supported頭域包含了一個option tag的列表,在19.2節描述的option tag,他們是這個UAS或者UAC所支持的。遵循本規范的UA必須只包含遵循標准RFC擴展的option tag。如果本字段是空的,意味着不支持任何擴展。

 

Supported頭域的縮寫是k

例如:

 

Supported: 100rel

 

20.38 Timestamp

Timestamp頭域描述了當UAC發送請求到UAS的時間戳。

 

參見8.2.6節關於如何給請求產生一個包含這個頭域的應答。雖然沒有定義本字段的標准行為,我們允許對擴展應用或者SIP應用獲得RTT預計時間。

例如:

Timestamp:54

 

20.39 To

To頭域定義了邏輯上請求的接收者。選項”display-name”意味着展示給客戶的界面。”tag”參數提供了對話識別機制。

 

參見19.3節關於”tag”參數的些界描述。

 

對於To頭域的比較是和對From頭域的比較相同的。參見20.10節的比較規則來比較display name,URI和URI參數,以及頭域的參數。

 

To頭域的縮寫是t。

下邊是一個To頭域的例子:

To: The Operator <sip:operator@cs.columbia.edu>;tag=287447

t: sip:+12125551212@server.phone2net.com

 

20.40 Unsupported

Unsupported頭域列出了不被UAS支持的特性列表。參見20.32。

例如:

Unsupported:foo

 

20.41 User-Agent

User-Agent頭域包含了發起請求的UAC信息。本頭域的語義在[H14.43]定義。

 

UA所使用的版本號情況可能會導致由於這個版本的安全漏洞二遭受攻擊。所以在實現上應當使得User-Agent頭域是可以配置的。

例如:

User-Agent:Softphone Beta1.5

 

20.42 Via

Via頭域是用來描述請求當前經歷的路徑的,並且標志了應答所應當經過的路徑。Via頭域的branch ID參數提供了事務的標志,並且用於proxy來檢查循環路由。

 

Via頭域包含了用於發送消息的通訊協議,客戶端主機名或者網絡地址,可能還有接收應答所用的端口號碼。Via頭域還可以包含參數”maddr”,”ttl”,”received”和”branch”,這些定義在其他節中描述。對於遵循本規范的實現,這個branch參數的值必須用magic cookie”z9hG4bK”打頭(8.1.1.7節)。

 

這里定義的通訊協議是”UDP”,”TCP”,”TLS”,和”SCTP”,”TLS”意思是基於TCP的TLS。當請求發送到一個SIPS URI上時,協議依舊標記着時”SIP”,但是通訊協議是TLS。

 

Via: SIP/2.0/UDP erlang.bell-telephone.com:5060;branch=z9hG4bK87asdks7

Via: SIP/2.0/UDP 192.0.2.1:5060 ;received=192.0.2.207

;branch=z9hG4bK77asjd

 

Via頭域的縮寫是v

 

在這個例子中,從多源(multi-homed)主機的消息有兩個地址,192.0.2.1和192.0.2.207。發送者猜錯了發送的網絡界面(以為是在192.0.2.1上發送的)。Erlang.belltelephone.com發現了這個不匹配,並且給這個節點的Via增加了一個參數,包含了實際包接收到的地址。

 

在SIP URI語法下,並不要求填寫主機名或者網絡地址和端口號。特別是,允許在”:”或者”/”兩遍的LWS(線形空白)。例如:

 

Via: SIP / 2.0 / UDP first.example.com: 4000;ttl=16

;maddr=224.2.0.1 ;branch=z9hG4bKa7c6a8dlze.1

 

即使本規范要求所有的請求中都包含branch參數,本頭域的BNF描述中,branch參數是可選的。這就和RFC2543元素可以進行互操作,因為RFC2543沒有添加branch參數。

 

如果他們的發送協議和sent-by域相等,都有相同的參數集合,並且參數都相等,那么兩個Via頭域就是相同的。

 

20.43 警告

Warning頭域用來給應答的狀態添加附加說明使用的。Warning頭域值是在應答中包含的,並且包括了一個3位的警告代碼,主機名,和警告正文。

 

“warn-text”應當是一個自然語言,給個人用戶接收應答時候來響應的。這可以通過現有的各種信息來決定這個warn-text,比如用戶的位置,Accept-Language域,或者應答重的Content-Language等等。缺省語言是idefault[21]。

 

下邊列出了當前定義的”warn-code”,並且有英文描述的推薦的warn-text。這些井蓋描述了會話描述中的各種可能的失敗情況。第一個warn-code的數字是”3”表示這是一個SIP規范的警告信息。警告信息300到329是保留用於標志在會話描述中的保留字錯誤的,330到339是會話描述中基本網絡服務相關警告,370到379是關於會話描述重的QoS參數數量相關的警告,390到399是上邊未列除的雜項警告信息。

 

300 Incompatible network protocol:(不兼容的網絡協議),One or more network protocols contained in the session description are not available.(在會話描述中的一個或者多個網絡協議不適用)

 

301 Incompatible network address formats(不兼容的網絡地址格式):One or more network address formats contained in the session description are not available. (會話描述中的一個或者多個網絡地址格式不合法)

 

302 Incompatible transport portocol(不兼容的通訊協議):One or more transport protocols described in the session description are not available. (會話描述中的一個或者多個通訊協議不存在)。

 

303:Incompatible bandwidth units(不兼容的帶寬單位): One or more bandwidth measurement units contained in the session description were not understood.(會話描述中的一個或者多個帶寬單位不支持)。

 

304 Media type not available(媒體類型不存在): One or more media types contained in the session description are not available. (會話描述中的一個或者多個媒體類型不存在)。

 

305 Incompatible media format(媒體格式不兼容): One or more media formats contained in the session description are not available.(會話描述中的一個或者多個媒體格式不兼容)。

 

306 Attribute not understood(媒體屬性不支持): One or more of the media attributes in the session description are not supported.(會話描述中的一個或者多個媒體屬性不支持)。

 

307 Session description parameter not understood(會話描述參數不支持): A parameter other than those listed above was not understood.(列出的會話描述參數不支持)。

 

330 Multicast not available(多點傳輸不允許): The site where the user is located does not support multicast.(用戶定位的這個服務器不支持多點傳送)。

 

331 Unicast not available(Unicast不支持): The site where the user is located does not support unicast communication (usually due to the presence of a firewall)。(用戶定位的節點不支持unicast通訊(通常由於在防火牆之后))。

 

370 Insufficient bandwidth(帶寬不足): The bandwidth specified in the session description or defined by the media exceeds that known to be available.(會話描述的帶寬要求或者媒體要求的帶寬超過限制)。

 

399 Miscellaneous warning(雜項警告): The warning text can include arbitrary information to be presented to a human user or logged. A system receiving this warning MUST NOT take any automated action.(這個警告信息可以包含給用戶的任意信息或者做日志記錄。接收到這個警告的系統禁止做任何自動操作)。

 

1xx和2xx消息是HTTP/1.1使用的。

附加的”warn-code”是IANA定義的,在27.2節有附加說明。

例如:

Warning: 307 isi.edu "Session parameter ’foo’ not understood"

Warning: 301 isi.edu "Incompatible network address type ’E.164’"

 

20.44 WWW-Authenticate

WWW-Authenticate頭域包含了認證信息,參見22.2節有關的詳細說明。

例如:

WWW-Authenticate:Digest realm=”atlanta.com”,

domain=”sip:boxesbybob.com”,qop=”auth”,

nonce="f84f1cec41e6cbe5aea9c8e88d359",

opaque="", stale=FALSE, algorithm=MD5

 

21 應答代碼

應答碼是包含了,並且擴展了HTTP/1.1應答碼。並不是所有的HTTP/1.1應答碼都適當應用,只有在這里指出的是適當的。其他HTTP/1.1應答碼不應當使用。並且,SIP也定義了新的應答碼系列,6xx。

 

21.1 臨時應答1xx

臨時應答,也就是消息性質的應答,標志了對方服務器正在處理請求,並且還沒有決定最后的應答。如果服務器處理請求需要花200ms以上才能產生終結應答的時候,它應當發送一個1xx應答。

 

注意1xx應答並不是可靠傳輸的。他們不會導致客戶端傳送一個ACK應答。臨時性質的(1xx)應答可以包含消息體,包含會話描述。

 

21.1.1 100 Trying

這個應答表示下一個節點的服務器已經接收到了這個請求並且還沒有執行這個請求的特定動作(比如,正在打開數據庫的時候)。這個應答,就像其他臨時應答一樣,種植了UAC重新傳送INVITE請求。100(Trying)應答和其他臨時應答不同的是,在這里,它永遠不會被有狀態proxy轉發到上行流中。

 

21.1.2 180 Ringing

UA收到INVITE請求並且試圖提示給用戶。這個應答應當出世化一個本地回鈴。

 

21.1.3 818 Call is Being Forwarded(呼叫被轉發)

服務器可以用這個應答代碼來表示呼叫正在轉發到另一個目的地集合。

 

21.1.4 182 Queued

當呼叫的對方暫時不能接收呼叫的時候,並且服務器決定將呼叫排隊等候,而不是拒絕呼叫的時候,那么就應當發出這個應答。當被叫方一旦恢復接收呼叫,他會返回合適的終結應答。對於這個呼叫狀態,可以有一個表示原因的短語,比如:”5 calls queued;expected waiting time is 15minutes”。服務器可以給出好幾個182(Queued)應答告訴呼叫方排隊的情況(比如排隊靠前了等等)。

 

21.1.5 183 會話進度

183(Session Progress)應答用於提示建立對話的進度信息。Reason-Phrase(表達原因的句子)、頭域或者消息體可以用於提示呼叫進度的更消息的信息。

 

21.2 成功信息2xx

這個應答表示請求是成功的。

 

21.2.1 200 OK

請求已經處理成功。這個信息取決於不同方法的請求的應答。

 

21.3 轉發請求3XX

3xx系列的應答是用於提示用戶的新位置信息的,或者為了滿足呼叫而轉發的額外服務地點。

 

21.3.1 300 Multiple Choices

請求的地址有多個選擇,每個選擇都有自己的地址,用戶或者(UA)可以選擇合適的通訊終端,並且轉發這個請求到這個地址。

 

應答可以包含一個具有每一個地點的在Accept請求頭域中允許的資源特性,這樣用戶或者UA可以選擇一個最合適的地址來轉發請求。沒有未這個應答的消息體定義MIME類型。

 

這些地址選擇也應當在Contact頭域中列出(20.10節)。不同於HTTP,SIP應答可以包含多個Contact頭域或者一個Contact頭域中具有一個地址列表。UA可以使用Contact頭域來自動轉發或者要求用戶確認轉發。不過,本規范沒有定義自動轉發的標准。

 

如果被叫方可以在多個地址被找到,並且服務器不能或者不願意轉發請求的時候,可以使用這個應答來給呼叫方。

 

21.3.2 301 Moved Permently

當不能在Request-URI指定的地址找到用戶的時候,請求的客戶端應當使用Contact頭域(20.10)所指出的新的地址重新嘗試。請求者應當用這個新的值來更新本地的目錄,地址本,和用戶地址cache,並且在后續請求中,發送到這個/這些列出的地址。

 

21.3.3 302 Moved Temporarily

請求方應當把請求重新發到這個Contact頭域所指出的新地址(20.10)。新請求的Request-URI應當用這個應答的Contact頭域所指出的值。

 

在應答中的Expires(20.19節)或者Contact頭域的expires參數定義了這個Contact URI的生存周期。UA或者proxy在這個生存周期內cache這個URI。如果沒有嚴格的有效時見,那么這個地址僅僅本次有效,並且不能在以后的事務中保存。

 

如果cache的Contact頭域的值失敗了,那么被轉發請求的Request-URI應當再次嘗試一次。臨時URI可以比超時時間更快的失效,並且可以有一個新的臨時URI。

 

21.3.4 305 Use Proxy

請求的資源必須通過Contact頭域中指出的proxy來訪問。Contact頭域指定了一個proxy的URI。接收到這個應答的對象應當通過這個proxy重新發送這個單個請求。305(UseProxy)必須是UAS產生的。

 

21.3.5 380 Alternative Service

呼叫不成工,但是可以嘗試另外的服務。另外的服務在應答的消息體中定義。消息體的格式在這里沒有定義,可能在以后的規范中定義。

 

21.4 請求失敗4xx

4xx應答定義了特定服務器響應的請求失敗的情況。客戶端不應當在不更改請求的情況下重新嘗試同一個請求。(例如,增加合適的認證信息)。不過,同一個請求交給不同服務器也許就會成功。

 

21.4.1 400 Bad Request

請求中的語法錯誤。Reason-Phrase應當標志這個詳細的語法錯誤,比如”Missing Call-ID header field”。

 

21.4.2 401 Unauthorized

請求需要用戶認證。這個應答是由UAS和注冊服務器產生的,當407(Proxy Authentication Required)是proxy服務器產生的。

 

21.4.3 402 Payment Required

保留/以后使用

 

 

小虎 2006-05-25 00:11

21.4.4 403 Forbidden

服務端支持這個請求,但是拒絕執行請求。增加驗證信息是沒有必要的,並且請求應當不被重試。

 

21.4.5 404 Not Found

服務器返回最終信息:用戶在Request-URI指定的域上不存在。當Request-URI的domain和接收這個請求的domain不匹配的情況下, 也會產生這個應答。

 

21.4.6 405 Method Not Allowed

服務器支持Request-Line中的方法,但是對於這個Request-URI中的地址來說,是不允許應用這個方法的。

 

應答必須包括一個Allow頭域,這個頭域包含了指定地址允許的方法列表。

 

21.4.7 Not Acceptable

請求中的資源只會導致產生一個在請求中的Accept頭域外的,內容無法接收的錯誤。

 

21.4.8 407 Proxy Authentication Required

這個返回碼和401(Unauthorized)很類四,但是標志了客戶端應當首先在proxy上通過認證。SIP對認證的訪問請參見26節和22.3節。

 

這個返回碼用於應用程序訪問通訊網關(比如,電話網關),而很少用於被叫方要求認證。

 

21.4.9 408 Request Timeout

在一段時間內,服務器不能產生一個終結應答,例如,如果它無法及時決定用戶的位置。客戶端可以在稍后不更改請求的內容然后重新嘗試請求。

 

 

21.4.10 410 Gone

請求的資源在本服務器上已經不存在了,並且不知道應當把請求轉發到哪里。這個問題將會使永久性的。如果服務器不知道,或者不容易檢測,這個資源消失是臨時性質的還是永久性質的,那么應當返回一個404(Not Found)。

 

21.4.11 413請求實體過大。

服務器拒絕處理請求,因為這個請求的實體超過了服務器希望或者能夠處理的大小。這個服務器應當關閉連接避免客戶端重發這個請求。

 

如果這個情況是暫時的,那么服務端應當包含一個Retry-After頭域來表明這是一個暫時的故障,並且客戶端可以過一段時間再次嘗試。

 

21.4.12 414 Request-URI Too Long

服務器拒絕這個請求,因為Request-URI超過了服務器能夠處理的長度。

 

21.4.13 415 Unsupported Media Type

服務器由於請求的消息體的格式本服務器不支持,所以拒絕處理這個請求。這個服務器必須根據內容的故障類型,返回一個Accept,Accpet-Encoding,或者Accept-Language頭域列表。UAC根據8.1.3.5節定義的方法處理這個應答。

 

21.4.14 416 Unsupported URI Scheme

服務器由於不支持Request-URI中的URI方案而終止處理這個請求。客戶端處理這個應答參照8.1.3.5。

 

21.4.15 Bad Extension

服務器不知道在請求中的Proxy-Require(20.29)或者Require(20.32)頭域所指出的協議擴展。服務器必須在Unsupported頭域中列出不支持的擴展。UAC處理這個應答請參見8.1.3.5

 

21.4.16 421Extension Required

UAS需要特定的擴展來處理這個請求,但是這個擴展並沒有在請求的Supported頭域中列出。具有這個應答碼的應答必須包含一個Require頭域列出所需要的擴展。

 

UAS不應當使用這個應答除非它真的不能給客戶端提供有效的服務。相反,如果在Support頭域中沒有列出需要的擴展,服務器應當根據基准的SIP兼容的方法和客戶端支持的擴展來進行處理。

 

21.4.17 423 Interval Too Brief

服務器因為在請求中設置的資源刷新時間(或者有效時間)過短而拒絕請求。這個應答可以用於注冊服務器來拒絕那些Contact頭域有效期過短的注冊請求。這個應答的用法和相關的Min-Expires頭域在10.2.8,10.3,20.23節中介紹和說明。

 

21.4.18 480 Temporarily Unavailable

請求成功到達被叫方的終端系統,但是被叫方當前不可用(例如,沒有登陸,或者登陸了但是狀態是不能通訊,或者有”請勿打擾”的標記)。應答應當在Retry-After中標志一個合適的重發時間。這個用戶也有可能在其他地方是有效的(在本服務器中不知道)。Reason-Phrase(原因短句)應當提示更詳細的原因,為什么被叫方暫時不可用。這個值應當是可以被UA設置的。狀態碼486(Busy Here)可以用來更精確的表示本請求失敗的特定原因。

 

這個狀態碼也可以是轉發服務或者proxy服務器返回的,因為他們發現Request-URI指定的用戶存在,但是沒有一個給這個用戶的合適的當前轉發的地址。

 

 

21.4.19 481 Call/Transaction Does Not Exist

這個狀態表示了UAS接收到請求,但是沒有和現存的對話或者事務匹配。

 

21.4.20 482 Loop Detected

服務器檢測到了一個循環(16.3/4)

 

21.4.21 483 Too Many Hops

服務器接收到了一個請求包含的Max-Forwards(20.22)頭域是0

 

21.4.22 484 Address InComplete

服務器接收到了一個請求,它的Request-URI是不完整的。在原因短語中應當有附加的信息說明。這個狀態碼可以和撥號交疊。在和撥號交疊中,客戶端不知道撥號串的長度。它發送增加長度的字串,並且提示用戶輸入更多的字串,直到不在出現484(Address Incomplete)應答為止。

 

21.4.23 485 Ambiguous

Request-URI是不明確的。應答可以在Contact頭域中包含一個可能的明確的地址列表。這個提示列表肯囊個在安全性和隱私性對用戶或者組織造成破壞。必須能夠由配置決定是否以404(NotFound)代替這個應答,又或者禁止對不明確的地址使用可能的選擇列表。

給帶有Request-URI的請求的一個應答例子:

sip: lee@example.com:

SIP/2.0 485 Ambiguous

Contact: Carol Lee <sip:carol.lee@example.com>

Contact: Ping Lee <sip:p.lee@example.com>

Contact: Lee M.Foote <sips:lee.foote@example.com>

 

部分email和語音郵箱系統提供了這個功能。這個狀態碼和3xx狀態碼不同:對於300來說,它是假定同一個人或者服務有不同的地址選擇。所以對3xx來說,自動選擇系統或者連續查找就有效,但是對485(Ambiguous)應答來說,一定要用戶的干預。

 

21.4.24 486 Busy Here

當成功聯系到被叫方的終端系統,但是被叫方當前在這個終端系統上不能接聽這個電話,那么應答應當回給呼叫方一個更合適的時間在Retry-After頭域重試。這個用戶也許在其他地方有效,比如電話郵箱系統等等。如果我們知道沒有其他終端系統能夠接聽這個呼叫,那么應當返回一個狀態碼600(Busy Everywhere)。

 

21.4.25 487 Request Terminated

請求被BYE或者CANCEL所終止。這個應答永遠不會給CANCEL請求本身回復。

 

21.4.26 488 Not Acceptable Here

這個應答和606(Not Acceptable)有相同的含義,但是只是應用於Request-URI所指出的特定資源不能接受,在其他地方請求可能可以接受。

 

包含了媒體兼容性描述的消息體可以出現在應答中,並且根據INVITE請求中的Accept頭域進行規格化(如果沒有Accept頭域,那么就是application/sdp)。這個應答就像給OPTIONS請求的200(OK)應答的消息體一樣。

 

21.4.27 491 Request Pending

在同一個對話中,UAS接收到的請求有一個依賴的請求正在處理。14.2描述了這種情況應當怎樣解決。

 

21.4.28 493 Undecipherable

UAS接收到了一個請求,包含了一個加密的MIME,並且不知道或者沒有提供合適的解密密鑰。這個應答可以包含單個包體,這個包體包含了合適的公鑰,這個公鑰用於給這個UAS通訊中加密包體使用的。細節描述在23.2節。

 

21.5 Server Failure 5xx

5xx應答是當服務器本身故障的時候給出的失敗應答。

 

21.5.1 500 Server Internal Error

服務器遇到了未知的情況,並且不能繼續處理請求。客戶端可以顯示特定的錯誤情況,並且可以在幾秒種以后重新嘗試這個請求。

 

如果這個情況是臨時的,服務器應當在Retry-After頭域標志客戶端過多少秒鍾之后重新嘗試這個請求。

 

21.5.2 501 Not Implemented

服務器沒有實現相關的請求功能。當UAS不認識請求的方法的時候,並且對每一個用戶都無法支持這個方法的時候,應當返回這個應答。(proxy不考慮請求的方法而轉發請求)。

 

注意405(Method Not Allowed)是因為服務器實現了這個請求方法,但是這個請求方法在特定請求中不被支持。

 

21.5.3 502 Bad Gateway

如果服務器,作為gateway或者proxy存在,從下行服務器上接收到了一個非法的應答(這個應答對應的請求是本服務器為了完成請求而轉發給下行服務器的)。

 

21.5.4 503 Service Unavailable

由於臨時的過載或者服務器管理導致的服務器暫時不可用。這個服務器可以在應答中增加一個Retry-After來讓客戶端重試這個請求。如果沒有Retry-After指出,客戶端必須就像收到了一個500(Server Internal Error)應答一樣處理。

 

客戶端(proxy或者UAC)收到503(Service Unavailable)應當嘗試轉發這個請求到另外一個服務器處理。並且在Retry-After頭域中指定的時間內,不應當轉發其他請求到這個服務器。

 

作為503(Service Unavaliable)的替代,服務器可以拒絕連接或者把請求扔掉。

 

21.5.5 504 Server Time-out

服務器在一個外部服務器上沒有收到一個及時的應答。這個外部服務器是本服務器用來訪問處理這個請求所需要的。如果從上行服務器上收到的請求中的Expires頭域超時,那么應當返回一個408(Request TimeOut)錯誤。

 

21.5.6 505 Version Not Supported

服務器不支持對應的SIP版本。服務器是無法處理具有客戶端提供的相同主版本號的請求,就會導致這樣的錯誤信息。

 

21.5.7 Message To Large

服務器無法處理請求,因為消息長度超過了處理的長度。

 

21.6 Global Failures 6xx

6xx應答意味這服務器給特定用戶有一個最終的信息,並不只是在Request-URI的特定實例有最終信息。

 

21.6.1 600 Busy Everywhere

成功聯系到被叫方的終端系統,但是被叫方處於忙的狀態,並不打算接聽電話。這個應答可以通過增加一個Retry-After頭域更明確的告訴呼叫方多久以后可以繼續呼叫。如果被叫方不希望提示拒絕的原因,被叫方應當使用603(Decline)。只有當終端系統知道沒有其他終端節點(比如語音郵箱系統)能夠訪問到這個用戶的時候才能使用這個應答。否則應當返回一個486(Busy Here)的應答。

 

21.6.2 603 Decline

當成功訪問到被叫方的設備,但是用戶明確的不想應答。這個應答可以通過增加一個Retry-After頭域更明確的告訴呼叫方多久以后可以繼續呼叫。只有當終端知道沒有其他任何終端設備能夠響應這個呼叫的勢能才能給出這個應答。

 

21.6.3 604 Does Not Exists Anywhere

服務器驗證了在請求中Request-URI的用戶信息,哪里都不存在

 

21.6.4 606 Not Acceptable

當成功聯系到一個UA,但是會話描述的一些部分比如請求的媒體,帶寬,或者地址類型不被接收。

 

606(NotAcceptable)應答意味着用戶希望通訊,但是不能充分支持會話描述。606(Not Acceptable)應答可以在Warning頭域中包含一個原因列表,用於解釋為何會話描述不能被支持。警告原因代碼在20.43節中列出。

 

在應答中,可以出現一個包含媒體兼容性描述的消息體,這個消息體的格式根據INVITE請求中的Accept頭域指出的格式進行規格化(如果沒有Accept頭域,那么就是application/sdp),就像給OPTIONS親求的200(OK)應答中的消息一樣。

 

我們希望這些媒體協商不要經常需要,並且當一個新用戶被邀請加入已經存在的會話的時候,這個媒體協商可能不需要。這取決於邀請的初始化者是否需要對606(Not Acceptable)進行處理。

 

這個應答只有當客戶端知道沒有其他終端能夠處理這個請求的時候才能發出。

 

 

22 使用HTTP認證

SIP為認證系統提供了一個無狀態的,試錯機制,這個認證機制式基於HTTP的認證機制的。任何時候proxy服務器或者UA接收到一個請求(22.1節例外),它嘗試檢查請求發起者提供的身份確認。當發起方身份確認了,請求的接受方應當確認這個用戶是否式通過認證的。在本文檔中,沒有建議或者討論認證系統。

 

本節描述的“Digest”認證機制,只提供了消息認證和復查保護,沒有提供消息完整性或者機密性的保證。上述的保護級別和基於這些Digest提供的保護,可以防止SIP攻擊者改變SIP請求和應答。

 

注意由於這個脆弱的安全性,我們不贊成”Basic”(基本的)認證方法。服務器必須不能接收驗證方法式”Basic”類型的信任書,並且服務器必須拒絕”Basic”。這是和RFC2543的改變。

 

 

22.1 框架

SIP認證的框架和HTTP非常接近(RFC2617[17])。特別式,auth-scheme的BNF范式,auth-param,challenge,realm,realm-value,以及信任書都是一樣的(雖然對”Basic”認證方案是不允許的)。在SIP,UAS使用401(Unauthorized)應答來拒絕UAC的身份(或者講是考驗UAC的身份,如果不通過,就是401)。另外,注冊服務器,轉發服務器可以使用401(Unauthorized)來應答身份認證,但是proxy必須不能用401,只能用407(Proxy Authentication Required)應答。對於Proxy-Authenticate的包含要求,Proxy-Authroization,WWW-Authenticate,Authorization在不同的消息中是相同的,如同在RFC2617[17]中講述的一樣。

 

由於SIP並沒有一個規范的root URL的概念,所以,需要保護的空間的概念在SIP中的解釋也不一樣。realm字串單獨定義被保護的區域。這個是和RFC2543的改變,在2543中Request-URI和realm一起定義了被保護的區域。

 

這個先前定義的被保護的區域回導致一定程度的混亂,因為Request-URI是UAC發送的,並且接收到Request-URI的認證服務器可能是不同的,並且真正的最終的Request-URI的格式可能對UAC並不知道。同樣,早先的定義依賴於一個Request-URI中的SIP URI,並且看起來不允許其他的URI 方案(比如tel URL)

 

需要鑒別接收到的請求的UA使用者或者proxy服務器,必須根據下邊的指導來為他們的服務器創建一個realm字串。

 

o Realm字串必須是全局唯一的。我們強調這個realm字串必須包含一個主機名或者域名,遵循3.2.1節或者RFC2617[17]的推薦

 

o Realm字串應當是一個可讀的能夠展示給用戶的字串。

例如: 

INVITE sip:bob@biloxi.com SIP/2.0

Authorization: Digest realm=”biloxi.com”,<…>

通常,SIP認證對於特定realm(一個保護區域)是有意義的。因此,對於Digest認證來說,每一個類似的保護區域都有自己的用戶名和密碼集合。如果服務器對特定請求沒有要求認證,那么它可以接收缺省的用戶名,”anonymous”,並且這個用戶名沒有密碼(密碼是””)。類似的,代表多個用戶的UAC,比如PSTN網關,可以有他們自己的設備相關的用戶名和密碼,而不是每一個用戶名一個用戶名密碼(這就是說,比如網關,有一個網關的用戶和密碼,而不是說通過網關的每一個實際用戶和密碼)。

 

當服務器可以正確處理絕大部分SIP請求,有本文檔約定了兩類請求要求特別的認證處理:ACK和CANCEL。在某一個認證方案下,並且這個認證方案是使用應答來放置計算nonces(比如Digest),那么對於某些沒有應答的情況,就會出現問題,比如ACK。所以,基於這個原因,一個服務器接受在INVITE請求中的信任書,也必須同樣接收對應ACK的信任書。UAC通過賦值所有的INVITE請求中的Authorization和Proxy-Authorization頭域值來創建一個相關的ACK消息。服務器必須接收這個ACK請求。

 

雖然CANCEL方法具有應答(2xx),服務器必須不能拒絕CANCEL請求,因為這些請求不能被重新提交。通常,如果CANCEL請求和被CANCEL的請求來自同一個節點(假設某種通訊協議,或者網絡層有安全關系26.2.1節描述),服務器應當接收CANCEL請求。

 

當UAC接收到驗證拒絕,並且UAC設備並不知道realm驗證失敗的具體原因,它必須展示給用戶,驗證失敗的”realm”參數內容(既可以在WWW-Authenticate頭域或者Proxy-Authenticate頭域)。對於給自己的realm預先配置信任狀的UA服務提供商來說,應當注意到這樣一點:當被一個預先配置信任狀的設備拒絕的時候,用戶不會有機會在這個realm中展示他們自己的信任狀。

 

最后,注意即使一個UAC能夠定位與相關realm匹配的信任書,也有可能存在這個信任書可能不在有效,或者某個服務器會用什么原因不接受這個信任書(特別是當提供的是沒有口令的”anonymous”用戶時)。在這種情況下,服務器可能會繼續拒絕,或者返回一個403 Forbidden。UAC必須不能再次使用剛才被拒絕的信任書進行嘗試(如果當前環境沒有改變,那么請求可以再次嘗試)。

 

22.2 用戶到用戶的認證。

當UAS收到一個UAC發起的請求,UAS在請求被處理之前進行身份認證。如果請求中沒有信任書(在Authorization頭域),UAS可以使用401(Unauthorized)拒絕認證,並且讓客戶端提供一個認證書。

 

WWW-Authenticate應答頭域必須在401(Unauthorized)應答消息中出現。這個頭域值包含了至少一個表明認證方式和適用realm的參數的拒絕原因。

 

在401中的WWW-Authenticate頭域例子:

WWW-Authenticate:Digest,

realm=”biloxi.com”,

qop=”auth,auth-int”,

nonce=”dcd98b7102dd2f0e8b11d0f600bfb0c093",

opaque="5ccc069c403ebaf9f0171e9517f40e41"

 

當原始請求的UAC接收到這個401(Unauthorized)應答的時候,如果可能的話,他應當重新組織這個請求,並且填寫正確的信任書。在繼續處理之前,UAC可以要求原始用戶輸入信任書。一旦信任書(不管是用戶輸入的,還是內部密鑰)提供了,UA應當把這個給特定To頭域和”realm”字段的信任書cache起來,以備給這個地址下一個請求時候使用。UA可以用任何方式來cache這個信任書。

 

如果沒有找到對應realm的信任書,UAC應當嘗試用用戶”anonymous”和空口令來重新嘗試這個請求。

 

一旦找到了一個信任書,那么UA應當要求在UAS或者注冊服務器上認證自己,這是通常的情況,但是並非一定要求的,在接收到一個401(Unauthorized)應答后-可以在請求中增加一個Authorization頭域然后再認證。Authorization頭域包含了具有這個UA到請求的資源所在的realm(區域)的信任書和所需要的認證支持的參數和重現保護的參數。

 

Authorization頭域例子:

Authorization: Digest username=”bob”,

   realm=”biloxi.com”,

   nonce=”dcd98b7102dd2f0e8b11d0f600bfb0c093",

   uri=”sip:bob@biloxi.com”,

qop=auth,

nc=00000001,

   cnonce=”0a4f113b”,

   response=”6629fae49393a05397450978507c4ef1",

   opaque=”5ccc069c403ebaf9f0171e9517f40e41"

 

當UAC在接收到401(Unauthorized)或者407(ProxyAuthenticationRequired)應答之后,重新用它的信任書來提交請求,它必須增加Cseq頭域的值,就像發送一個正常的新請求一樣。

 

22.3 Proxy到用戶的認證

類似的,當UAC發送一個請求到proxy服務器,proxy服務器可以在處理請求之前,驗證原始請求的認證。如果請求中沒有信任書(在Proxy-Authorization頭域),proxy可以用407(Proxy Authentication Required)拒絕這個原始請求,並且要求客戶端提供適當的信任書。proxy必須在407(ProxyAuthenticationRequired)應答中增加一個Proxy-Authenticate頭域,並且在這個頭域中給出適用於本proxy的認證資源。

 

對於Proxy-Authenticate和Proxy-Authorization一起在[17]中描述,兩者有一個不同。Proxy不能在Proxy-Authorization頭域中增加值。所有的407(ProxyAuthenticationRequired)應答必須轉發到上行隊列,遵循發送應答的步驟發送到UAC。UAC負責在Proxy-Authorization頭域值增加適用於這個proxy要求認證的這個proxy的realm的信任書。

 

如果proxy要求UAC在請求中增加Proxy-Authorization頭域並且重新提交請求,那么UAC應當增加Cseq頭域的值,就像一個新請求一樣。不過,這樣就導致提交原始請求的UAC需要忽略UAS的應答,因為Cseq的值可能是不一樣的。

 

當原始請求的UAC接收到一個407(Proxy Authentication Required)的時候,如果可能,

 

 

小虎 2006-05-25 00:11

它應當使用正確的信任書重新組織請求。它應當和對前邊講述的401應答的處理步驟一樣顯示和處理”realm”參數。

 

如果沒有找到對應realm的信任書,那么UAC應當嘗試用用戶”anonymous”和空口令重新嘗試請求。

 

UAC也應當cache這個在重新發送請求中的信任書。

 

我們建議使用下列步驟來cache一個proxy的信任書:

 

如果UA在給特定Call-ID的請求的401/407應答中,接收到一個Proxy-Authenticate頭域,它應當合並對這個realm的信任書,並且為以后具有相同Call-ID的請求發送這個信任書。這些信任書必須在對話中被cache住;不過如果UA配置的是它自己的本地外發proxy,那么如果出現要求認證的情況,那么UA應當cache住跨對話的信任書。注意,這個意味着在一個對話中的請求可以包含在Route頭域中所經過proxy都不需要的信任書。

 

任何希望在proxy服務器上認證的UA――通常,但是並非必須,在接收到407(Proxy Authentication Required)應答之后――可以在請求中增加一個Proxy-Authorization頭域然后再次嘗試。Proxy-Authorization請求頭域允許客戶端像proxy來證明自己(或者使用者)的身份。Proxy-Authorization頭域包含了UA提供給proxy和/或者請求資源所在的realm的身份認證信息的信任書。

 

一個Proxy-Authorization頭域值只提供給指定proxy驗證的,這個proxy的realm是在”realm”參數中指明的(這個proxy可以事先通過Proxy-Authenticat頭域提出認證要求)。當多個proxy組成一個鏈路的時候,如果proxy的realm和請求中的Proxy-Authorization頭域的”realm”參數不匹配,那么這個proxy就不能使用本Proxy-Authorization頭域值來驗證。

 

注意,如果一個認證機制不支持Proxy-Authorization頭域的realm,porxy服務器必須嘗試分析所有的Proxy-Authorization頭域值來決定是否其中之一有這個proxy認為合適的信任書。因為這個方法在大型網絡上很耗時間,proxy服務器應當使用一個一個支持Proxy-Authorization頭域的realm的認證方案。

 

如果一個請求被分支(參見16.7節),可能對同一個UAC有不同的proxy服務器和/或者UA希望要求認證。在這種情況下,分支的proxy服務器有責任把這些被拒絕的認證合並成為一個應答。每一個分支請求的應答中接收到WWW-Authenticate和Proxy-Authenticate頭域值必須由這個分支proxy放置在同一個應答中發送給UA;這些頭域值的順序並沒有影響。

 

當proxy服務器給一個請求發出拒絕認證的應答,在UAC用正確的信任書重新發請求過來之前,不會轉發這個請求。分支proxy可以同時向多個要求認證的proxy服務器轉發請求。每一個proxy在沒有接收到UAC在他們各自的realm的認證之前,都不會轉發這個請求。如果UAC沒有給這些失敗的驗證提供信任書,那些發出拒絕通過認證的proxy是不會把請求轉發給UA的目標用戶的,因此,分支的優點就少了很多。

 

當針對包含多個拒絕認證的401(Unauthorized)或者407(Proxy Authentication Required)應答重新提交請求時,UAC應當對每一個WWW-Authenticate和Proxy-Authorization頭域值提供一個信任書。根據上邊的說明,一個請求的多個認證書應當用”realm”參數分開。

 

不過,在同一個401(Unauthorized)或者407(Proxy Authentication Required)應答中,可能包含對同一個realm的多個驗證拒絕。例如,當在相同域的多個proxy,使用共同的realm,接收到了一個分支請求,並且認證拒絕了的時候,就會有這樣的情況。當UAC重新嘗試這個請求的時候,UAC因此會提供多個Authorization或者Proxy-Authorization頭域,包含相同的”realm”參數。並且對於同一個realm,應當有相同的信任書。

 

22.4 Digest 認證方案

奔進誒描述了對HTTP Digest 認證方案的SIP修改和簡化。SIP使用了和HTTP[17]幾乎完全一樣的方案。

 

由於RFC 2543是基於RFC2069[39]定義的HTTP Digest的,支持RFC2617的SIP服務器也必須確保他們和RFC2069兼容。RFC2617定義了保證兼容性的步驟。注意,SIP服務器必須不能接收或者發出Basic認證請求。

 

Digest認證的規則在[17]中定義,只是使用”SIP/2.0”替換”HTTP/1.1”,並且有如下的不同:

 

1、    URI有着如下的BNF:

URI=SIP-URI/SIPS-URI

2、    在RFC 2617定義中,有一個HTTP Digest認證的Authorization頭域”uri”參數的錯誤,是沒有括號配對的錯誤。(在RFC2617的3.5節的例子是正確的)。對於SIP來說,’uri’參數必須在引號中引起來。

3、    digest-uri-value的BNF是:

digest-uri-value=Request-URI; 在25節定義。

4、    對SIP來說,產生基於Etag的nonce的步驟例子不適用。

5、    對SIP來說,RFC2617[17]關於chache操作不適用。

6、    RFC2617[17]要求服務器檢查請求行的URI,並且在Authorization頭域的URI要指向相同的資源。在SIP中,這兩個URI可以指向不同的用戶,因為是同一個proxy轉發的。因此,在SIP,一個服務器應當檢查在Authorization頭域值的Request-URI和服務器希望接收請求的用戶是否一致,但是如果兩者不一致,並無必要展示成為錯誤。

7、    在Digest認證方案中,關於計算消息完整性保證的A2值的一個澄清,實現着應當假定,當包體是空的(也就是說,當SIP消息沒有包體)應當對包體的hash值產生一個M5hash空串,或者:

H(entity-body)=MD5(“”)=

"d41d8cd98f00b204e9800998ecf8427e"

8、    RFC2617指出了在Authorization(以及擴展的Proxy-Authorization)頭域中,如果沒有qop指示參數,就不能出現cnonce值。因此,任何基於cnonce(包括”MD5-Sess”)的運算都要求qop指數先發送。在RFC2617中的”qop”參數是可選的,這是為了向后兼容RFC2069;由於RFC2543是基於RFC2069的,”qop”參數必須被客戶和服務器依舊是當作可選參數存在。不過,服務器必須始終在WWW-Authentication和Proxy-Authenticate頭域值中傳送”qop”參數。如果一個客戶端在一個拒絕認證的應答中收到一個”qop”參數,他必須把這個”qop”參數放在后續的認證頭域中。

 

RFC 2543不允許使用Authentication-Info頭域(在RFC2069中使用)。不過我們現在允許使用這個頭域,因為他提供了對包體的完整性檢測以及提供了相互認證。RFC2617[17]定義了在請求中使用qop屬性的向后兼容機制。這些機制必須在服務器使用,用來檢測是否客戶支持RFC2617的,沒有在RFC2069中定義的新機制,

 

23 S/MIME

SIP消息可以加載一個MIME 消息體,並且MIME標准包括了MIME內容的保密機制,確保完整性和機密性(包括”multipart/signed”和”application/pkcs7-mime”的MIME類別,參見RFC 1847[22],RFC 2630[23],RFC2633[24])。實現中應當注意,不管怎樣,也會有很少的網絡節點(不是典型的proxy服務器),會依賴於查看修改SIP消息(特別是SDP),采用加密的MIME可以防止這類網絡節點操作。

 

這個實現特別對某些類型防火牆有效。

 

在RFC2543描述的對於SIP頭域和包體的PGP加密機制,已經不建議使用了。

 

23.1 S/MIME 認證

用於區別服務器使用的S/MIME而進行的最終用戶的鑒定,有一種重要的特點-這些信任狀的持有者是被最終用戶地址鑒定的,而不是由一個特定的hostname所鑒定的。這個地址是由SIP或者SIPS URI的”userinfo””@”和”domainname”組成的(換句話說,就是用的email格式,類似bob@biloxi.com),最常見的就是和用戶的address-of-record對應的地址。

 

這些認證也同樣和用於標記或者加密SIP消息包體的密鑰相關聯。包體由發送方的私鑰所標記(這個發送方在適當情況下,可以包含他們的公鑰),並且包體是由被接受方的公鑰所加密。顯然,發送方必須事先知道接受方的公鑰,這樣才能加密包體。公鑰可以在UA的一個虛擬的鑰匙環上保存。

 

每一個支持S/MIME的UA都必須包含用於終端用戶驗證的一個特定密鑰組。這個密鑰組應當由address-of-record和相應的認證信息的映射組成。在時間上,當用戶產生具有相同address-of-record的原始的信號URI(From頭域)時,用戶應當使用相同的認證信息。

 

任何依賴於現存的終端用戶認證的機制都是嚴格受限於:事實上,目前沒有一個統一的權威認證機構為終端用戶應用提供認證。不過,用戶應當從已知的公共認證機構獲得認證。作為替代,用戶可以創建自標記(self-signed)的信任書。self-signed信任書在26.4.2節中有介紹。在實現中,也可以采用在配置中存放預先配置的信任書,這個配置中存放的時上次SIP實體之間的信任關系。

 

在獲得終端用戶的認證問題上,很少有廣為人知的集中發布終端用戶認證的目錄機構。信任書的擁有者應當在一些公共的目錄機構中,適當的發布自己的信任書。類似的,UAC應當支持從與SIP請求的目標URI有關的公共目錄機構導入信任書(手工或者自動的)。

 

23.2 S/MIME 密鑰交換

SIP自身可以根據下列的方法發布公鑰。

 

無論何時SIP的S/MIME使用了CMS SignedData消息,他必須包含由公鑰所加密的信任書,用於檢查簽名。

 

當UAC發送一個創建對話的請求,包含了一個S/MIME包體,或者在對話外發送一個非INVITE請求,UAC應當創建一個S/MIME ‘multipart/signed’ CMS SignedData包體結構的包體。

 

如果特定的CMS服務時EnvelopedData(並且知道目標用戶的公鑰),UAC應當在一個SignedData消息中發送EnvelopedData消息。

 

當UAS接收到一個包含S/MIME CMS包體的請求,並且這個包體包含一個信任書,UAS應當首先檢查這個信任書,如果可能,使用以后的root信任書來進行信任書檢查。UAS也應當檢查信任書的主題(subject)(對於S/MIME,SubjectAltName應當包含了適當的標記)並且,把這個值和請求From頭域進行比較。如果信任書不被通過,因為它是self-signed(自簽發),或者被位置的信任機構所頒發的,或者它是可信任的,但是他的主題和請求的From頭域不匹配,UAS必須把這個通知用戶(包括信任書的主題,簽發者,和密鑰指紋信息),並且在繼續處理之前,要求用戶確認。如果信任書成功通過認證,並且信任書的主題(subject)和SIP請求的From頭域相同,或者如果用戶(在知會之后)批准了這個信任書的使用,那么UAS應當增加這個信任書到本地密鑰組中(keyring),並且用信任書擁有者的address-of-record作為索引。

 

當UAS要發送一個包含S/MIME消息體的應答,這個應答回應了在對話中的前一個請求,或者是給對話外的一個非INVITE請求的應答,UAS應當構造一個S/MIME ‘multipart/signed’ CMS SignedData包體。如果給定的CMS服務要求EnvelopedData,UAS應當發送一個EnvelopedData的SignedData消息

 

當UAC收到一個包含S/MIME CMS包體的應答,這個應答中包含了一個信任書,UAC應當首先檢查這個信任書,如果可能,使用任何適當的root信任書來進行檢查。UAC應當同樣檢查信任書的主題(subject),並且和應答的To頭域進行比較;雖然兩個可能完全不同,這個也沒有必要當作是不安全的。如果因為self-signed,或者由未知認證機關頒發而導致信任書不能通過認證,那么UAC應當通知它的使用者這個狀態(包含信任書的主題,它的簽發者,以及密鑰的指紋信息),並且在繼續操作之前,要求使用者確認。如果信任書通過了認證,並且信任書的主題和應答的To頭域相同,或者用戶(在提示確認后)明確的確認這個信任書以后,UAC應當把這個信任書放在本地密鑰組中,用這個信任書的擁有者的address-of-record作為索引。如果UAC沒有把自己的信任書事先傳送給UAS,它在下一個請求或者應答時應當使用一個CMS SignedData包體。

 

在以后,當UA接收到包伙那一個From頭域的請求或者應答,並且這個From頭域和本地的一個密鑰組的索引匹配,那么UA應當比較消息中的信任書和這個密鑰組對應索引的信任書。如果他們不匹配,那么UA必須通知用戶信任書改變了(更合適的是提示這個可能是一個潛在的安全沖突),並且在繼續處理前要求用戶的確認。如果用戶確認了這個信任書,它應當在本地密鑰組的這個address-of-record項的前一個值旁邊附加這個信任書。

 

注意,這個值的改變機制,在self-signed信任書的情況或者未知機關頒發的信任書情況下,並不保證密鑰變更的安全性,並且這個缺陷被廣為人知的攻擊。在本文的作者看來,它提供的安全性當然比什么也沒有要強;實際上可以和廣泛應用的SSH應用相比。這些限制在26.4.2節中有詳細描述。

 

如果UA接收到了一個S/MIME包體,並且是由本接受方所不知道的公鑰加密的,它必須用493(Undecipherable)拒絕這個請求。這個應答應當回答一個合法的信任書(相應的,如果可能,給被拒絕請求的To頭域指出的全部地址),這個信任書包含一個’certs-only’ “smime-type”參數的MIME包體。。

 

一個沒有任何信任書信息的493(Undecipherable)應答表示回答者不能或者不會利用S/MIME加密信息,雖然他們可以依舊支持S/MIME標記。

 

注意UA接收到一個包含一個S/MIME包體的請求(這個請求包含了Content-Disposition頭域和”required”的”handling”參數),如果這個MIME類型是不被支持的,那么就必須用415 Unsupported MediaType來拒絕這個請求。當UA接收到這個應答,並且這個應答表示的是對方不支持剛才發送的S/MIME類型的請求,那么UA應當通知它的使用者對方不支持這個S/MIME類型,並且如果可以,他可以在隨后的請求中不包含S/MIME部分;另外,這個415應答可以制造一個下級的攻擊。

 

如果一個UA在請求中發送了一個S/MIME包體,但是接收到的應答包含了一個未加密的MIME包體,UAC應當提示用戶,這個會話可能是不安全的。可是,如果一個支持S/MIME的UA接收到一個包含未加密的包體的請求,它不應當給出一個包含加密包體的應答,但是如果它希望從發送方獲得一個S/MIME(比如,因為發送方的From頭域值在它的密鑰組中存在關聯),UAS應當通知它的使用者這個會話可能是不安全的。

 

當信任書管理不正常的情況下,許多條件都可以導致前邊講的要給用戶提示以及確認的情況。使用者可能會問在這個情況下應當怎樣做。首先,一個信任書的異常改變,或者在需要安全保護的時候不安全,是導致這個警告出現的原因,但是並非表示正在遭受攻擊。使用者可以中斷現有的連接或者拒絕連接請求;在電話談話中,他們可以掛機並且回叫。使用者可能會使用另外的方式來聯系對方並且確認他們的密鑰是否合法的更改了。注意用戶有時候需要強制更改他們的信任書,例如當他們懷疑他們的私鑰不夠安全的情況。當他們的私鑰不再私鑰,用戶必須合法的產生一個新的密鑰並且重新和其他持有舊密鑰的用戶建立信任關系。

 

最后,如果在對話中UA收到了在一個CMS SignedData Message中的信任書,並且和早先對話中交換的信任書不一致,那么UA必須提示它的使用者這個變化,更好的方法是展示成為一個安全隱患。

 

23.3 加密MIME 包體

SIP中,總共由兩種類型的加密MIME包體:對他們的使用應當遵循S/MIME規范[24],並且有幾點不同:

 

l    multipart/signed”只在CMS 分離簽名的時候有用。這是為了和非S/MIME規范接受者兼容。

l    S/MIME包體應當包含一個Content-Disposition頭域,並且他的”handling”參數的值應當是”required”

l    如果UAC在它的密鑰組中沒有對應要發送請求的接受者的address-of-record的信任書,它不能發送加密的”application/pkcs7-mime” MIME消息。UAC可以發送一個初始化的請求(比如OPTIONS消息),包含一個為了方便對方處理的CMS分離的簽名(簽名應當基於一個”message/sip”包體,類型描述在23.4節)。

注意以后的S/MIME標准工作可以定義基於非信任書的密鑰。

l    S/MIME的發送方應當使用”SMIMECapabilities”(參見2.5.2 [24])屬性來為以后的通訊介紹他們自己的能力和參數。注意,特別是那些可能用”preferSignedData”的發送方希望接受方應答一個CMS SignedData消息(例如,當發送一個OPTIONS請求的時候)。

l    S/MIME實現必須至少支持SHA1作為數字簽名算法,並且3DES作為加密算法。其他的簽名和加密算法也可以支持,實現上應當可以用”SMIMECapabilities”屬性進行這些算法的協商。

l    在SIP中的每一個S/MIME包體應當只有一個信任書的簽名。如果UA接收到的消息有多個信任書,最外邊的信任書應當當作這個包體的信任書。並行的簽名應當不能使用。

下邊是一個SIP中的加密的S/MIME SDP包體的例子:

INVITE sip:bob@biloxi.com SIP/2.0

Via: SIP/2.0/UDP pc33.atlanta.com;branch=z9hG4bKnashds8

To: Bob <sip:bob@biloxi.com>

From: Alice <sip:alice@atlanta.com>;tag=1928301774

Call-ID: a84b4c76e66710

CSeq: 314159 INVITE

Max-Forwards: 70

Contact: <sip:alice@pc33.atlanta.com>

Content-Type: application/pkcs7-mime; smime-type=enveloped-data;

name=smime.p7m

Content-Disposition: attachment; filename=smime.p7m

handling=required

********************************************************************

* Content-Type: application/sdp                        *

*                                                    *

* v=0                                                *

* o=alice 53655765 2353687637 IN IP4 pc33.atlanta.com    *

* s=-                                                *

* t=0 0                                                *

* c=IN IP4 pc33.atlanta.com                            *

* m=audio 3456 RTP/AVP 0 1 3 99                        *

* a=rtpmap:0 PCMU/8000                                *

********************************************************************

 

 

23.4 SIP頭隱私和用S/MIME的完整性:SIP地道

作為提供端到端的某種程度認證來說,SIP頭域的完整性或者機密性,SI/MIME可以把SIP消息通過”message/sip”的包體類型,整個裝入一個MIME包體,並且接着就像處理普通SIP包體一樣,對這個大的包體用MIME安全性來保護。這些被封裝的SIP請求和應答並不產生另外的對話或者事務,他們只是一個用來檢驗完整性的或者提供附加信息的”outer”外部包裝消息。

 

如果UAS接收到一個請求,包含一個”message/sip”隧道的S/MIME包體,它應當把應答用同樣的SMIME-TYPE封裝成一個”message/sip”隧道包體。

 

任何傳統的MIME包體(比如SDP),應當附加為”inner”內部消息,這樣他們可以使用到S/MIME安全性。注意,如果在請求中需要傳送未加密的MIME類型的內容,那么”message/sip”包體可以作為MIME “multipart/mixed”包體的一部分存在,

 

23.4.1 SIP頭的完整性和機密屬性

當應用了S/MIME完整性或者機密性機制,那么在”inner”消息和”outer”消息中的值可能會有差異。對於所有頭域來說,處理這些差異的規則在本節指出。

 

注意,由於松散的時間戳,所有的在”message/sip”隧道中的SIP消息應當在”inner”和”outer”消息中都包含一個Date頭域。

 

23.4.1.1 完整性

無論何時,當完整性被檢查,頭域的完整性應當通過檢查被封裝包體中的頭域值和”outer”消息的頭域值,使用20節描述的SIP比較規則。

 

頭域可以被proxy服務器合法修改的是:Request-URI, Via, Record-Route, Route, Max-Forwards, 和Proxy-Authorization。如果這些頭域不完全相等,實現中不應當認為是一個安全錯誤。對於其他頭域的修改就是破壞了完整性,必須通知使用者關於這個差異。

 

23.4.1.2 機密性

當消息加密以后,頭域可以在加密的包體中存在,而不用在”outer”消息中存在。

 

有一些頭域必須有一個明碼格式的版本,因為他們是請求和應答中必須要求的頭域-他們是:To, From, Call-ID, Cseq, Contact。 雖然提供一個額外的Call-ID,Cseq,或者Contact的加密版本沒有什么用處,我們允許把To/From 放一份在”outer”消息的To或者From頭域中。注意這些加密包體中的值並不用作鑒別事務或者對話――加密包體中的這些值僅僅作為提示信息使用。如果在加密包體中的From頭域和在”outer”消息中的值不一樣,那么這個加密包體中的值應當顯示給用戶,但是不能用於后續”outer”消息的頭域中。

 

首先,UA應當希望加密有着端到端語義的頭域,包括: Subject,Replay-To,Organization, Accept,Accept-Encoding,Accept-Language,Alter-Info,Error-Info, Authentication-Info , Expires, In-Replay-To, Require, Supported, Unsupported, Retry-After, User-Agent, Server, 和Warning。 如果這些頭域在加密包體中出現,那么就應當替換在”outer”消息中的頭域,而不管這個頭域是顯示給用戶的還是設置UA內部狀態的。他們應當在后續消息中不在在”outer”消息的頭域中使用。

 

如果存在Date頭域,那么Date頭域必須在”inner”和”outer”頭域中的值一樣。

 

由於MIME包體是在”inner”消息中的,實現中通常會加密MIME指定的頭域,包括:MIME-Version,Content-Type,Content-Length, Content-Language, Content-Encoding, 和Content-Disposition。”outer”消息會為S/MIME包體使用適當的MIME頭域。這些頭域(和他們開始的任何MIME包體)在SIP消息中,應當作為普通的MIME頭域和包體

 

 

小虎 2006-05-25 00:11

接收。

 

對下邊這些頭域的加密並不是特別有用: Min-Expires, Timestamp, Authorization, Priority, 和 WWWAuthenticate。這類頭域包含了那些能夠被proxy服務器所更改的頭域(在前邊章節有講述)。如果這些頭域不出現在”outer”消息中,那么UA應當不在”inner”消息中包含這些頭域。如果UA在加密的包體中接收到這些頭域,應當忽略到這些加密的值。

 

注意,SIP的擴展可能會定義附加的頭域;那么這些擴展的作者應當描述這些頭域的完整性和機密性特性。如果一個SIP UA遇到了一個不認識的頭域,並且產生了一個完整性沖突,它必須忽略掉這個頭域。

 

23.4.2 隧道的完整性和身份認證

通過S/MIME包體的SIP消息隧道可以提供SIP頭域的完整性保證,只要發送方把這個包整個打包放在用CMS分離簽名的”message/sip” MIME包體中。

 

假設那個”message/sip”包體包含了最基本的對話要素(最小集合)(To, From, Call-ID, CSEq),並且一個簽名的MIME包體可以提供優先的身份認證。在這個特別的最小集合上,如果接受方不認識用於簽名MIME包體的信任狀,並且不能被檢驗,那么在同一個信任狀擁有者所初始化的會話中,這個信任狀擁有者可以稍后發送一個在會話中的請求,包含這個簽名來進行確認。如果簽名MIME包體的接受方選擇信任這個信任書(他們可以檢驗信任書,他們已經從信任列表中檢查了,或者他們經常使用這個信任書),那么這個簽名就和這個信任書的主題有着相同的身份一致性。

 

為了排出可能的對實體包頭域增加減少的相關困惑,發送方應當把請求的所有頭域放在簽名的包體中。任何需要完整性保護的包體都必須添加到”inner”消息中。

 

如果有一個簽名包體的消息中包含一個Date頭域,接受方應當比較它自己的內部時鍾和這個頭域值。如果檢測到了時差(比如超過1個小時或者更多),UA應當警告使用者,並且提示這個可能是安全隱患。

 

如果接受方檢測到消息的完整性破壞了,如果是這個消息是請求,那么應當使用403(Forbidden)來拒絕這個請求,或者終止現存的對話。UA應當提示用戶這個情況,並且要求明確的操作指示。

 

下邊是一個使用隧道”message/sip”的例子:

 

INVITE sip:bob@biloxi.com SIP/2.0

Via: SIP/2.0/UDP pc33.atlanta.com;branch=z9hG4bKnashds8

To: Bob <sip:bob@biloxi.com>

From: Alice <sip:alice@atlanta.com>;tag=1928301774

Call-ID: a84b4c76e66710

CSeq: 314159 INVITE

Max-Forwards: 70

Date: Thu, 21 Feb 2002 13:02:03 GMT

Contact: <sip:alice@pc33.atlanta.com>

Content-Type: multipart/signed;

protocol="application/pkcs7-signature";

micalg=sha1; boundary=boundary42

Content-Length: 568

 

--boundary42

Content-Type: message/sip

 

INVITE sip:bob@biloxi.com SIP/2.0

Via: SIP/2.0/UDP pc33.atlanta.com;branch=z9hG4bKnashds8

To: Bob <bob@biloxi.com>

From: Alice <alice@atlanta.com>;tag=1928301774

Call-ID: a84b4c76e66710

CSeq: 314159 INVITE

Max-Forwards: 70

Date: Thu, 21 Feb 2002 13:02:03 GMT

Contact: <sip:alice@pc33.atlanta.com>

Content-Type: application/sdp

Content-Length: 147

 

v=0

o=UserA 2890844526 2890844526 IN IP4 here.com

s=Session SDP

c=IN IP4 pc33.atlanta.com

t=0 0

m=audio 49172 RTP/AVP 0

a=rtpmap:0 PCMU/8000

 

--boundary42

Content-Type: application/pkcs7-signature; name=smime.p7s

Content-Transfer-Encoding: base64

Content-Disposition: attachment; filename=smime.p7s;

handling=required

ghyHhHUujhJhjH77n8HHGTrfvbnj756tbB9HG4VQpfyF467GhIGfHfYT6

4VQpfyF467GhIGfHfYT6jH77n8HHGghyHhHUujhJh756tbB9HGTrfvbnj

n8HHGTrfvhJhjH776tbB9HG4VQbnj7567GhIGfHfYT6ghyHhHUujpfyF4

7GhIGfHfYT64VQbnj756

 

--boundary42-

 

 

23.4.3 隧道加密

把”message/sip”MIME包體用CMS EnvelopedData消息S/MIME包體進行加密是值得的,但是在實踐中,大部分頭域都是用於網絡的;而通常使用S/MIME進行的加密都是加密類似SDP的消息體的,而不是消息頭的。有一部分頭域的信息,比如Subject或者Organization或許需要端到端的安全保證。以后的SIP應用可能會定義其他的頭域,這些頭域也或許需要端到端的安全保證。

 

另一個加密頭域的可能的應用是選擇性匿名。可以構造一個沒有個人信息的From頭域(比如sip:anonymous@anonymizer.invalid)。不過,第二個From頭域包含了真實的請求者的address-of-record信息,並且加密存放在”message/sip”MIME包體中,並且只會在對話的對方節點被看到。

 

注意如果這個機制用於匿名,那么將接受方將不再用From頭域來作為密鑰組的索引,並且也不用於從密鑰組查詢合適的發送方的S/MIME密鑰。這個消息必須首先被解密,並且”inner”From頭域必須當作一個索引。

 

為了提供端到端的完整性,加密的”message/sip”MIME包體應當由發送方簽名。這個創建了一個包含一個加密包體和一個簽名的”multipart/signed” MIME包體,包體類型都是”application/pkcs7-mime”.。

 

在下邊這個例子中,是一個加密和簽名的消息,在*括起來的文字是加密的:

 

INVITE sip:bob@biloxi.com SIP/2.0

Via: SIP/2.0/UDP pc33.atlanta.com;branch=z9hG4bKnashds8

To: Bob <sip:bob@biloxi.com>

From: Anonymous <sip:anonymous@atlanta.com>;tag=1928301774

Call-ID: a84b4c76e66710

CSeq: 314159 INVITE

Max-Forwards: 70

Date: Thu, 21 Feb 2002 13:02:03 GMT

Contact: <sip:pc33.atlanta.com>

Content-Type: multipart/signed;

protocol="application/pkcs7-signature";

micalg=sha1; boundary=boundary42

Content-Length: 568

 

--boundary42

Content-Type: application/pkcs7-mime; smime-type=enveloped-data;

name=smime.p7m

Content-Transfer-Encoding: base64

Content-Disposition: attachment; filename=smime.p7m

handling=required

Content-Length: 231

 

******************************************************************************

* Content-Type: message/sip                                    *

*                                                            *

* INVITE sip:bob@biloxi.com SIP/2.0                            *

* Via: SIP/2.0/UDP pc33.atlanta.com;branch=z9hG4bKnashds8    *

* To: Bob <bob@biloxi.com>                                    *

* From: Alice <alice@atlanta.com>;tag=1928301774                *

* Call-ID: a84b4c76e66710                                    *

* CSeq: 314159 INVITE                                        *

* Max-Forwards: 70                                            *

* Date: Thu, 21 Feb 2002 13:02:03 GMT                        *

* Contact: <sip:alice@pc33.atlanta.com>                        *

*                                                            *

* Content-Type: application/sdp                                *

*                                                            *

* v=0                                                        *

* o=alice 53655765 2353687637 IN IP4 pc33.atlanta.com            *

* s=Session SDP                                            *

* t=0 0                                                        *

* c=IN IP4 pc33.atlanta.com                                    *

* m=audio 3456 RTP/AVP 0 1 3 99                                *

* a=rtpmap:0 PCMU/8000                                        *

******************************************************************************

 

--boundary42

Content-Type: application/pkcs7-signature; name=smime.p7s

Content-Transfer-Encoding: base64

Content-Disposition: attachment; filename=smime.p7s;

handling=required

 

ghyHhHUujhJhjH77n8HHGTrfvbnj756tbB9HG4VQpfyF467GhIGfHfYT6

4VQpfyF467GhIGfHfYT6jH77n8HHGghyHhHUujhJh756tbB9HGTrfvbnj

n8HHGTrfvhJhjH776tbB9HG4VQbnj7567GhIGfHfYT6ghyHhHUujpfyF4

7GhIGfHfYT64VQbnj756

 

--boundary42-

 

24 例子

 

在下邊這個例子中,由於簡略介紹的關系我們經常省略消息體和對應的Content-Length和Content-Type頭域。

 

24.1 注冊

Bob在啟動的時候進行注冊。這個消息流在圖9中展示。注意對於注冊服務來說,通常需要認證,而且不像下邊描述的這樣簡單。

 

 

 

 

 

 

 

 

 

 

 

圖9: SIP 注冊例子

 

F1 REGISTER Bob -> Registrar

REGISTER sip:registrar.biloxi.com SIP/2.0

Via: SIP/2.0/UDP bobspc.biloxi.com:5060;branch=z9hG4bKnashds7

Max-Forwards: 70

To: Bob <sip:bob@biloxi.com>

From: Bob <sip:bob@biloxi.com>;tag=456248

Call-ID: 843817637684230@998sdasdh09

CSeq: 1826 REGISTER

Contact: <sip:bob@192.0.2.4>

Expires: 7200

Content-Length: 0

 

注冊會在2小時后超時。注冊服務器回應一個200OK:

F2 200 OK Registar -> Bob

SIP/2.0 200 OK

Via: SIP/2.0/UDP bobspc.biloxi.com:5060;branch=z9hG4bKnashds7

;received=192.0.2.4

To: Bob <sip:bob@biloxi.com>;tag=2493k59kd

From: Bob <sip:bob@biloxi.com>;tag=456248

Call-ID: 843817637684230@998sdasdh09

CSeq: 1826 REGISTER

Contact: <sip:bob@192.0.2.4>

Expires: 7200

Content-Length: 0

 

24.2 建立會話

這個例子包括了4節描述的建立會話的細節。消息流在圖1中展示了。注意這些消息流展示了頭域的最小集合--一般來說還需要包含一些其他頭域,比如Allow和Supported頭域。

 

F1 INVITE Alice -> atlanta.com proxy

 

INVITE sip:bob@biloxi.com SIP/2.0

Via: SIP/2.0/UDP pc33.atlanta.com;branch=z9hG4bKnashds8

Max-Forwards: 70

To: Bob <sip:bob@biloxi.com>

From: Alice <sip:alice@atlanta.com>;tag=1928301774

Call-ID: a84b4c76e66710

CSeq: 314159 INVITE

Contact: <sip:alice@pc33.atlanta.com>

Content-Type: application/sdp

Content-Length: 142

 

(Alice’s SDP not shown)

 

F2 100 Trying atlanta.com proxy -> Alice

 

SIP/2.0 100 Trying

Via: SIP/2.0/UDP pc33.atlanta.com;branch=z9hG4bKnashds8

;received=192.0.2.1

To: Bob <sip:bob@biloxi.com>

From: Alice <sip:alice@atlanta.com>;tag=1928301774

Call-ID: a84b4c76e66710

CSeq: 314159 INVITE

Content-Length: 0

 

F3 INVITE atlanta.com proxy -> biloxi.com proxy

 

INVITE sip:bob@biloxi.com SIP/2.0

Via: SIP/2.0/UDP bigbox3.site3.atlanta.com;branch=z9hG4bK77ef4c2312983.1

Via: SIP/2.0/UDP pc33.atlanta.com;branch=z9hG4bKnashds8

;received=192.0.2.1

Max-Forwards: 69

To: Bob <sip:bob@biloxi.com>

From: Alice <sip:alice@atlanta.com>;tag=1928301774

Call-ID: a84b4c76e66710

CSeq: 314159 INVITE

Contact: <sip:alice@pc33.atlanta.com>

Content-Type: application/sdp

Content-Length: 142

 

(Alice’s SDP not shown)

 

F4 100 Trying biloxi.com proxy -> atlanta.com proxy

 

SIP/2.0 100 Trying

Via: SIP/2.0/UDP bigbox3.site3.atlanta.com;branch=z9hG4bK77ef4c2312983.1

;received=192.0.2.2

Via: SIP/2.0/UDP pc33.atlanta.com;branch=z9hG4bKnashds8

;received=192.0.2.1

To: Bob <sip:bob@biloxi.com>

From: Alice <sip:alice@atlanta.com>;tag=1928301774

Call-ID: a84b4c76e66710

CSeq: 314159 INVITE

Content-Length: 0

 

F5 INVITE biloxi.com proxy -> Bob

 

INVITE sip:bob@192.0.2.4 SIP/2.0

Via: SIP/2.0/UDP server10.biloxi.com;branch=z9hG4bK4b43c2ff8.1

Via: SIP/2.0/UDP bigbox3.site3.atlanta.com;branch=z9hG4bK77ef4c2312983.1

;received=192.0.2.2

Via: SIP/2.0/UDP pc33.atlanta.com;branch=z9hG4bKnashds8

;received=192.0.2.1

Max-Forwards: 68

To: Bob <sip:bob@biloxi.com>

From: Alice <sip:alice@atlanta.com>;tag=1928301774

Call-ID: a84b4c76e66710

CSeq: 314159 INVITE

Contact: <sip:alice@pc33.atlanta.com>

Content-Type: application/sdp

Content-Length: 142

 

(Alice’s SDP not shown)

 

F6 180 Ringing Bob -> biloxi.com proxy

 

SIP/2.0 180 Ringing

Via: SIP/2.0/UDP server10.biloxi.com;branch=z9hG4bK4b43c2ff8.1

;received=192.0.2.3

Via: SIP/2.0/UDP bigbox3.site3.atlanta.com;branch=z9hG4bK77ef4c2312983.1

;received=192.0.2.2

Via: SIP/2.0/UDP pc33.atlanta.com;branch=z9hG4bKnashds8

;received=192.0.2.1

To: Bob <sip:bob@biloxi.com>;tag=a6c85cf

From: Alice <sip:alice@atlanta.com>;tag=1928301774

Call-ID: a84b4c76e66710

Contact: <sip:bob@192.0.2.4>

CSeq: 314159 INVITE

Content-Length: 0

 

F7 180 Ringing biloxi.com proxy -> atlanta.com proxy

 

SIP/2.0 180 Ringing

Via: SIP/2.0/UDP bigbox3.site3.atlanta.com;branch=z9hG4bK77ef4c2312983.1

;received=192.0.2.2

Via: SIP/2.0/UDP pc33.atlanta.com;branch=z9hG4bKnashds8

;received=192.0.2.1

To: Bob <sip:bob@biloxi.com>;tag=a6c85cf

From: Alice <sip:alice@atlanta.com>;tag=1928301774

Call-ID: a84b4c76e66710

Contact: <sip:bob@192.0.2.4>

CSeq: 314159 INVITE

Content-Length: 0

 

F8 180 Ringing atlanta.com proxy -> Alice

 

SIP/2.0 180 Ringing

Via: SIP/2.0/UDP pc33.atlanta.com;branch=z9hG4bKnashds8

;received=192.0.2.1

To: Bob <sip:bob@biloxi.com>;tag=a6c85cf

From: Alice <sip:alice@atlanta.com>;tag=1928301774

Call-ID: a84b4c76e66710

Contact: <sip:bob@192.0.2.4>

CSeq: 314159 INVITE

Content-Length: 0

 

F9 200 OK Bob -> biloxi.com proxy

 

SIP/2.0 200 OK

Via: SIP/2.0/UDP server10.biloxi.com;branch=z9hG4bK4b43c2ff8.1

;received=192.0.2.3

Via: SIP/2.0/UDP bigbox3.site3.atlanta.com;branch=z9hG4bK77ef4c2312983.1

;received=192.0.2.2

Via: SIP/2.0/UDP pc33.atlanta.com;branch=z9hG4bKnashds8

;received=192.0.2.1

To: Bob <sip:bob@biloxi.com>;tag=a6c85cf

From: Alice <sip:alice@atlanta.com>;tag=1928301774

Call-ID: a84b4c76e66710

CSeq: 314159 INVITE

Contact: <sip:bob@192.0.2.4>

Content-Type: application/sdp

Content-Length: 131

 

(Bob’s SDP not shown)

 

F10 200 OK biloxi.com proxy -> atlanta.com proxy

 

SIP/2.0 200 OK

Via: SIP/2.0/UDP bigbox3.site3.atlanta.com;branch=z9hG4bK77ef4c2312983.1

;received=192.0.2.2

Via: SIP/2.0/UDP pc33.atlanta.com;branch=z9hG4bKnashds8

;received=192.0.2.1

To: Bob <sip:bob@biloxi.com>;tag=a6c85cf

From: Alice <sip:alice@atlanta.com>;tag=1928301774

Call-ID: a84b4c76e66710

CSeq: 314159 INVITE

Contact: <sip:bob@192.0.2.4>

Content-Type: application/sdp

Content-Length: 131

 

(Bob’s SDP not shown)

 

F11 200 OK atlanta.com proxy -> Alice

 

SIP/2.0 200 OK

Via: SIP/2.0/UDP pc33.atlanta.com;branch=z9hG4bKnashds8

;received=192.0.2.1

To: Bob <sip:bob@biloxi.com>;tag=a6c85cf

From: Alice <sip:alice@atlanta.com>;tag=1928301774

Call-ID: a84b4c76e66710

CSeq: 314159 INVITE

Contact: <sip:bob@192.0.2.4>

Content-Type: application/sdp

Content-Length: 131

 

(Bob’s SDP not shown)

 

F12 ACK Alice -> Bob

 

ACK sip:bob@192.0.2.4 SIP/2.0

Via: SIP/2.0/UDP pc33.atlanta.com;branch=z9hG4bKnashds9

Max-Forwards: 70

To: Bob <sip:bob@biloxi.com>;tag=a6c85cf

From: Alice <sip:alice@atlanta.com>;tag=1928301774

Call-ID: a84b4c76e66710

CSeq: 314159 ACK

Content-Length: 0

 

在Alice和Bob之間的媒體會話現在建立了。Bob首先掛機。注意Bob的SIP電話維持它自己的Cseq號碼空間,在這里,是231開始的。由於Bob發起請求,那么To和From URI和tags交換了。

 

F13 BYE Bob -> Alice

 

BYE sip:alice@pc33.atlanta.com SIP/2.0

Via: SIP/2.0/UDP 192.0.2.4;branch=z9hG4bKnashds10

Max-Forwards: 70

From: Bob <sip:bob@biloxi.com>;tag=a6c85cf

To: Alice <sip:alice@atlanta.com>;tag=1928301774

Call-ID: a84b4c76e66710

CSeq: 231 BYE

Content-Length: 0

 

F14 200 OK Alice -> Bob

 

SIP/2.0 200 OK

Via: SIP/2.0/UDP 192.0.2.4;branch=z9hG4bKnashds10

From: Bob <sip:bob@biloxi.com>;tag=a6c85cf

To: Alice <sip:alice@atlanta.com>;tag=1928301774

Call-ID: a84b4c76e66710

CSeq: 231 BYE

Content-Length: 0

 

這個SIP呼叫流程文檔[40]包含了SIP消息的更多例子。

 

25 SIP協議的BNF范式

本協議中定義的機制都用文本和Backus-Naur Form(BNF)范式定義(RFC2234[10])。6.1節和RFC2234定義了一個本文檔使用的基本核心規則,這里就不贅述了。實現者需要熟悉RFC2234協議,這樣才能理解整理定義的規范。某些基本規則是用大寫字母表示的,比如SP,LWS,HTAB,CRLF,DIGIT,ALPHA,等等。尖括號定義了規則的名字。

 

方括號的使用是在語法上可選的。在這里用於特定參數是可選的語義提示。

 

25.1 基本規則

下列貫穿本規范的規則是用於描述基本的語法結構。US-ASCII碼字符集是在ANSI X3.4-1986中定義的。

 

alphanum = ALPHA/DIGIT

 

部分規則是和RFC2396[5]中合並使用的,但是依據RFC2234[10]做了更新,這些包括:

 

reserved = ";" / "/" / "?" / ":" / "@" / "&" / "=" / "+" / "$" / ","

 

unreserved = alphanum / mark

mark = "-" / "_" / "." / "!" / "?" / "*" / "’" / "(" / ")"

 

escaped = "%" HEXDIG HEXDIG

 

SIP頭域值可以折疊成為多行,如果每行的開始是一個空格或者一個水平制表符(就是Tab鍵啦)。所有的線形空白,包含折疊的空白,和SP有着同樣的語義(SP就是空格啦)。一個接受方在處理頭域值之前或者轉發消息到下行隊列之前,可以把任何線形空白當作一個單個SP處理。這個和RFC2616[8]中描述的HTTP/1.1處理方法完全一樣。當線形空白是可選的時候,SWS構造就需要了,通常在兩個符號和分隔符之間存在:

 

LWS = [*WSP CRLF] 1*WSP ; linear whitespace

SWS = [LWS] ; sep whitespace

 

為了把頭域名和頭域值分開,就需要使用冒號,根據上邊的規則,允許在冒號之前有空白,但是不允許有行分隔符,並且允許在冒號之后有空白,或者行分隔符。HCOLON有如下定義:

 

HCOLON = *( SP / HTAB ) ":" SWS

 

TEXT-UTF8規則只用於描述不被消息分析所分析的域內容和域值。*TEXT-UTF8包含了UTF-8字符集的字符(RFC2279[7])。TEXT-UTF8-TRIM規則是用於描述一個n t引號引起來的字符串,其前后的空白是無意義的。在這里,SIP和HTTP不同,HTTP使用的是ISO 8859-1字符集。

 

TEXT-UTF8-TRIM    = 1*TEXT-UTF8char *(*LWS TEXT-UTF8char)

TEXT-UTF8char    = %x21-7E / UTF8-NONASCII

UTF8-NONASCII    =        %xC0-DF    1UTF8-CONT

/    %xE0-EF 2UTF8-CONT

/    %xF0-F7 3UTF8-CONT

/    %xF8-Fb 4UTF8-CONT

/    %xFC-FD 5UTF8-CONT

UTF8-CONT = %x80-BF

 

在TEXT-UTF8-TRIM的定義中,CRLF只允許作為頭域的延長部分存在。當LWS(空格)折疊的時候,在分析TEXTUTF8-TRIM之前,會使用單個SP代替。

 

部分協議要素使用了十六進制數字字符。有一些要素(authentication)強制十六進制數字使用小寫字母。

 

LHEX = DIGIT / %x61-66 ;lowercase a-f

 

許多SIP頭域值都包含用LWS或者特殊符號分開的詞。除非有額外的說明,這些符號是大小寫不銘感的。當特殊字符作為參數值存在的時候,這些特殊字符必須通過引號引起來。Call-ID中建立的詞組允許使用絕大部分分隔符。

 

token        =    1 * ( alphanum / "-" / "." / "!" / "%" / "*"

/ "_" / "+" / "‘" / "’" / "?" )

separators    =    "(" / ")" / "<" / ">" / "@" /

"," / ";" / ":" / "\" / DQUOTE /

"/" / "[" / "]" / "?" / "=" /

"{" / "}" / SP / HTAB

word        =    1*(alphanum / "-" / "." / "!" / "%" / "*" /

"_" / "+" / "‘" / "’" / "?" /

"(" / ")" / "<" / ">" /

":" / "\" / DQUOTE /

"/" / "[" / "]" / "?" /

"{" / "}" )

當標志符號或者分隔符用在要素之間是,空白通常允許在這些字符之前或者之后。

 

STAR                = SWS "*" SWS ; asterisk

SLASH            = SWS "/" SWS ; slash

EQUAL            = SWS "=" SWS ; equal

LPAREN            = SWS "(" SWS ; left parenthesis

RPAREN            = SWS ")" SWS ; right parenthesis

RAQUOT            = ">" SWS ; right angle quote

LAQUOT            = SWS "<"; left angle quote

COMMA            = SWS "," SWS ; comma

SEMI                = SWS ";" SWS ; semicolon

COLON            = SWS ":" SWS ; colon

LDQUOT            = SWS DQUOTE; open double quotation mark

RDQUOT            = DQUOTE SWS ; close double quotation mark

 

在SIP頭域中可以使用注釋,通過把注釋放在圓括號中就可以了。只有在頭域的定義中允許”comment”作為他們的頭域值的一部分才可以使用注釋。在其他頭域中,圓括號被視同為頭域值的一部分。

 

comment    =    LPAREN *(ctext / quoted-pair / comment) RPAREN

ctext        =    %x21-27 / %x2A-5B / %x5D-7E / UTF8-NONASCII

/ LWS

 

ctext包含了除了左右括號和反斜線之外的所有的字符。在雙引號引起來的字符串中的字串,被視為單個詞。在引起來的字串中,引號(“)和反斜線需要轉碼。

 

quoted-string    =    SWS DQUOTE *(qdtext / quoted-pair ) DQUOTE

qdtext            =    LWS / %x21 / %x23-5B / %x5D-7E

/ UTF8-NONASCII

 

反斜線(“\”)可以當作單個字符使用,轉義機制只有在引號引起來的字符串中或者注釋結構中有效。和HTTP/1.1不同的是,CR和LF不能通過這個機制來轉義,這樣可以避免同頭的折疊的沖突。

 

quoted-pair        =    "\" (%x00-09 / %x0B-0C

/ %x0E-7F)

 

SIP-URI            =    "sip:" [ userinfo ] hostport

uri-parameters [ headers ]

SIPS-URI            =    "sips:" [ userinfo ] hostport

uri-parameters [ headers ]

userinfo            =    ( user / telephone-subscriber ) [ ":" password ] "@"

user                =    1*( unreserved / escaped / user-unreserved )

user-unreserved    =    "&" / "=" / "+" / "$" / "," / ";" / "?" / "/"

password            =    *( unreserved / escaped /

"&" / "=" / "+" / "$" / "," )

hostport            =    host [ ":" port ]

host                =    hostname / IPv4address / IPv6reference

hostname            =    *( domainlabel "." ) toplabel [ "." ]

domainlabel        =    alphanum

/ alphanum *( alphanum / "-" ) alphanum

toplabel            =    ALPHA / ALPHA *( alphanum / "-" ) alphanum

IPv4address        =    1*3DIGIT "." 1*3DIGIT "." 1*3DIGIT "." 1*3DIGIT

IPv6reference        =    "[" IPv6address "]"

IPv6address        =    hexpart [ ":" IPv4address ]

hexpart            =    hexseq / hexseq "::" [ hexseq ] / "::" [ hexseq ]

hexseq            =    hex4 *( ":" hex4)

 

 

小虎 2006-05-25 00:12

hex4                =    1*4HEXDIG

port                =    1*DIGIT

 

對於電話描述(telephone-subscriber)的BNF說明在RFC2806[9]中。注意,無論如何,如果在這里允許的字符,如果在SIP URI中的user部分不許可,那么就一定要用轉義。

 

uri-parameters        =    *( ";" uri-parameter)

uri-parameter        =    transport-param / user-param / method-param

/ ttl-param / maddr-param / lr-param / other-param

transport-param    =    "transport="

( "udp" / "tcp" / "sctp" / "tls"

/ other-transport)

other-transport        =    token

user-param        =    "user=" ( "phone" / "ip" / other-user)

other-user            =    token

method-param        =    "method=" Method

ttl-param            =    "ttl=" ttl

maddr-param        =    "maddr=" host

lr-param            =    "lr"

other-param        =    pname [ "=" pvalue ]

pname            =    1*paramchar

pvalue            =    1*paramchar

paramchar            =    param-unreserved / unreserved / escaped

param-unreserved    =    "[" / "]" / "/" / ":" / "&" / "+" / "$"

 

headers            =    "?" header *( "&" header )

header            =    hname "=" hvalue

hname            =    1*( hnv-unreserved / unreserved / escaped )

hvalue            =    *( hnv-unreserved / unreserved / escaped )

hnv-unreserved    =    "[" / "]" / "/" / "?" / ":" / "+" / "$"

 

SIP-message        =    Request / Response

Request            =    Request-Line

*( message-header )

CRLF

[ message-body ]

Request-Line        =    Method SP Request-URI SP SIP-Version CRLF

Request-URI        =    SIP-URI / SIPS-URI / absoluteURI

absoluteURI        =    scheme ":" ( hier-part / opaque-part )

hier-part            =    ( net-path / abs-path ) [ "?" query ]

net-path            =    "//" authority [ abs-path ]

abs-path            =    "/" path-segments

opaque-part        =    uric-no-slash *uric

uric                =    reserved / unreserved / escaped

uric-no-slash        =    unreserved / escaped / ";" / "?" / ":" / "@"

/ "&" / "=" / "+" / "$" / ","

path-segments        =    segment *( "/" segment )

segment            =    *pchar *( ";" param )

param                =    *pchar

pchar                =    unreserved / escaped /

":" / "@" / "&" / "=" / "+" / "$" / ","

scheme            =    ALPHA *( ALPHA / DIGIT / "+" / "-" / "." )

authority            =    srvr / reg-name

srvr                =    [ [ userinfo "@" ] hostport ]

reg-name            =    1*( unreserved / escaped / "$" / ","

/ ";" / ":" / "@" / "&" / "=" / "+" )

query                =    *uric

SIP-Version        =    "SIP" "/" 1*DIGIT "." 1*DIGIT

 

message-header    =    (Accept

/ Accept-Encoding

/ Accept-Language

/ Alert-Info

/ Allow

/ Authentication-Info

/ Authorization

/ Call-ID

/ Call-Info

/ Contact

/ Content-Disposition

/ Content-Encoding

/ Content-Language

/ Content-Length

/ Content-Type

/ CSeq

/ Date

/ Error-Info

/ Expires

/ From

/ In-Reply-To

/ Max-Forwards

/ MIME-Version

/ Min-Expires

/ Organization

/ Priority

/ Proxy-Authenticate

/ Proxy-Authorization

/ Proxy-Require

/ Record-Route

/ Reply-To

/ Require

/ Retry-After

/ Route

/ Server

/ Subject

/ Supported

/ Timestamp

/ To

/ Unsupported

/ User-Agent

/ Via

/ Warning

/ WWW-Authenticate

/ extension-header) CRLF

 

INVITEm            =    %x49.4E.56.49.54.45 ; INVITE in caps

ACKm                =    %x41.43.4B ; ACK in caps

OPTIONSm        =    %x4F.50.54.49.4F.4E.53 ; OPTIONS in caps

BYEm                =    %x42.59.45 ; BYE in caps

CANCELm            =    %x43.41.4E.43.45.4C ; CANCEL in caps

REGISTERm        =    %x52.45.47.49.53.54.45.52 ; REGISTER in caps

Method            =    INVITEm / ACKm / OPTIONSm / BYEm

/ CANCELm / REGISTERm

/ extension-method

extension-method    =    token

Response            =    Status-Line

*( message-header )

CRLF

[ message-body ]

 

Status-Line        =    SIP-Version SP Status-Code SP Reason-Phrase CRLF

Status-Code        =    Informational

/ Redirection

/ Success

/ Client-Error

/ Server-Error

/ Global-Failure

/ extension-code

extension-code        =    3DIGIT

Reason-Phrase    =    *(reserved / unreserved / escaped

/ UTF8-NONASCII / UTF8-CONT / SP / HTAB)

 

Informational        =    "100" ; Trying

/ "180" ; Ringing

/ "181" ; Call Is Being Forwarded

/ "182" ; Queued

/ "183" ; Session Progress

 

Success            =    "200" ; OK

 

Redirection        =    "300" ; Multiple Choices

/ "301" ; Moved Permanently

/ "302" ; Moved Temporarily

/ "305" ; Use Proxy

/ "380" ; Alternative Service

Client-Error = "400" ; Bad Request

/ "401" ; Unauthorized

/ "402" ; Payment Required

/ "403" ; Forbidden

/ "404" ; Not Found

/ "405" ; Method Not Allowed

/ "406" ; Not Acceptable

/ "407" ; Proxy Authentication Required

/ "408" ; Request Timeout

/ "410" ; Gone

/ "413" ; Request Entity Too Large

/ "414" ; Request-URI Too Large

/ "415" ; Unsupported Media Type

/ "416" ; Unsupported URI Scheme

/ "420" ; Bad Extension

/ "421" ; Extension Required

/ "423" ; Interval Too Brief

/ "480" ; Temporarily not available

/ "481" ; Call Leg/Transaction Does Not Exist

/ "482" ; Loop Detected

/ "483" ; Too Many Hops

/ "484" ; Address Incomplete

/ "485" ; Ambiguous

/ "486" ; Busy Here

/ "487" ; Request Terminated

/ "488" ; Not Acceptable Here

/ "491" ; Request Pending

/ "493" ; Undecipherable

 

Server-Error        =    "500" ; Internal Server Error

/ "501" ; Not Implemented

/ "502" ; Bad Gateway

/ "503" ; Service Unavailable

/ "504" ; Server Time-out

/ "505" ; SIP Version not supported

/ "513" ; Message Too Large

 

 

Global-Failure        =    "600" ; Busy Everywhere

/ "603" ; Decline

/ "604" ; Does not exist anywhere

/ "606" ; Not Acceptable

 

Accept            =    "Accept" HCOLON

[ accept-range *(COMMA accept-range) ]

accept-range        =    media-range *(SEMI accept-param)

media-range        =    ( "*/*"

/ ( m-type SLASH "*" )

/ ( m-type SLASH m-subtype )

) *( SEMI m-parameter )

accept-param        =    ("q" EQUAL qvalue) / generic-param

qvalue            =    ( "0" [ "." 0*3DIGIT ] )

/ ( "1" [ "." 0*3("0") ] )

generic-param        =    token [ EQUAL gen-value ]

gen-value            =    token / host / quoted-string

 

Accept-Encoding    =    "Accept-Encoding" HCOLON

[ encoding *(COMMA encoding) ]

encoding            =    codings *(SEMI accept-param)

codings            =    content-coding / "*"

content-coding        =    token

 

Accept-Language    =    "Accept-Language" HCOLON

[ language *(COMMA language) ]

language            =    language-range *(SEMI accept-param)

language-range    =    ( ( 1*8ALPHA *( "-" 1*8ALPHA ) ) / "*" )

 

Alert-Info            = "Alert-Info" HCOLON alert-param *(COMMA alert-param)

alert-param        = LAQUOT absoluteURI RAQUOT *( SEMI generic-param )

 

Allow                =    "Allow" HCOLON [Method *(COMMA Method)]

Authorization        =    "Authorization" HCOLON credentials

credentials            =    ("Digest" LWS digest-response)

/ other-response

digest-response    =    dig-resp *(COMMA dig-resp)

dig-resp            =    username / realm / nonce / digest-uri

/ dresponse / algorithm / cnonce

/ opaque / message-qop

/ nonce-count / auth-param

username            =    "username" EQUAL username-value

username-value    =    quoted-string

digest-uri            =    "uri" EQUAL LDQUOT digest-uri-value RDQUOT

digest-uri-value        =    rquest-uri ; Equal to request-uri as specified

by HTTP/1.1

message-qop        =    "qop" EQUAL qop-value

 

 

cnonce            =    "cnonce" EQUAL cnonce-value

cnonce-value        =    nonce-value

nonce-count        =    "nc" EQUAL nc-value

nc-value            =    8LHEX

dresponse            =    "response" EQUAL request-digest

request-digest        =    LDQUOT 32LHEX RDQUOT

auth-param        =    auth-param-name EQUAL

( token / quoted-string )

auth-param-name    =    token

other-response        =    auth-scheme LWS auth-param

*(COMMA auth-param)

auth-scheme        =    token

 

Authentication-Info    =    "Authentication-Info" HCOLON ainfo

*(COMMA ainfo)

ainfo                =    nextnonce / message-qop

/ response-auth / cnonce

/ nonce-count

nextnonce            =    "nextnonce" EQUAL nonce-value

response-auth        =    "rspauth" EQUAL response-digest

response-digest    =    LDQUOT *LHEX RDQUOT

 

Call-ID            =    ( "Call-ID" / "i" ) HCOLON callid

callid                =    word [ "@" word ]

 

Call-Info            =    "Call-Info" HCOLON info *(COMMA info)

info                =    LAQUOT absoluteURI RAQUOT *( SEMI info-param)

info-param            =    ( "purpose" EQUAL ( "icon" / "info"

/ "card" / token ) ) / generic-param

Contact            =    ("Contact" / "m" ) HCOLON

( STAR / (contact-param *(COMMA contact-param)))

contact-param        =    (name-addr / addr-spec) *(SEMI contact-params)

name-addr            =    [ display-name ] LAQUOT addr-spec RAQUOT

addr-spec            =    SIP-URI / SIPS-URI / absoluteURI

display-name        =    *(token LWS)/ quoted-string

 

contact-params        =    c-p-q / c-p-expires

/ contact-extension

c-p-q                =    "q" EQUAL qvalue

c-p-expires        =    "expires" EQUAL delta-seconds

contact-extension    =    generic-param

delta-seconds        =    1*DIGIT

 

Content-Disposition    =    "Content-Disposition" HCOLON

disp-type *( SEMI disp-param )

disp-type            =    "render" / "session" / "icon" / "alert"

/ disp-extension-token

disp-param        =    handling-param / generic-param

handling-param    =    "handling" EQUAL

( "optional" / "required"

/ other-handling )

other-handling        =    token

disp-extension-token =    token

 

Content-Encoding    =    ( "Content-Encoding" / "e" ) HCOLON

content-coding *(COMMA content-coding)

 

Content-Language    =    "Content-Language" HCOLON

language-tag *(COMMA language-tag)

language-tag        =    primary-tag *( "-" subtag )

primary-tag        =    1*8ALPHA

subtag            =    1*8ALPHA

 

Content-Length        =    ( "Content-Length" / "l" ) HCOLON 1*DIGIT

Content-Type        =    ( "Content-Type" / "c" ) HCOLON media-type

media-type        =    m-type SLASH m-subtype *(SEMI m-parameter)

m-type            =    discrete-type / composite-type

discrete-type        =    "text" / "image" / "audio" / "video"

/ "application" / extension-token

composite-type        =    "message" / "multipart" / extension-token

extension-token    =    ietf-token / x-token

ietf-token            =    token

x-token            =    "x-" token

m-subtype            =    extension-token / iana-token

iana-token            =    token

m-parameter        =    m-attribute EQUAL m-value

m-attribute            =    token

m-value            =    token / quoted-string

 

CSeq                =    "CSeq" HCOLON 1*DIGIT LWS Method

 

Date                =    "Date" HCOLON SIP-date

SIP-date            =    rfc1123-date

rfc1123-date        =    wkday "," SP date1 SP time SP "GMT"

date1                =    2DIGIT SP month SP 4DIGIT

; day month year (e.g., 02 Jun 1982)

time                =    2DIGIT ":" 2DIGIT ":" 2DIGIT

; 00:00:00 - 23:59:59

wkday                =    "Mon" / "Tue" / "Wed"

/ "Thu" / "Fri" / "Sat" / "Sun"

month                =    "Jan" / "Feb" / "Mar" / "Apr"

/ "May" / "Jun" / "Jul" / "Aug"

/ "Sep" / "Oct" / "Nov" / "Dec"

 

Error-Info            =    "Error-Info" HCOLON error-uri *(COMMA error-uri)

 

error-uri            = LAQUOT absoluteURI RAQUOT *( SEMI generic-param )

 

Expires            =    "Expires" HCOLON delta-seconds

From                =    ( "From" / "f" ) HCOLON from-spec

from-spec            =    ( name-addr / addr-spec )

*( SEMI from-param )

from-param        =    tag-param / generic-param

tag-param            =    "tag" EQUAL token

 

In-Reply-To        =    "In-Reply-To" HCOLON callid *(COMMA callid)

 

Max-Forwards        =    "Max-Forwards" HCOLON 1*DIGIT

 

MIME-Version        =    "MIME-Version" HCOLON 1*DIGIT "." 1*DIGIT

 

Min-Expires        =    "Min-Expires" HCOLON delta-seconds

 

Organization        =    "Organization" HCOLON [TEXT-UTF8-TRIM]

 

Priority            =    "Priority" HCOLON priority-value

priority-value        =    "emergency" / "urgent" / "normal"

/ "non-urgent" / other-priority

other-priority        =    token

 

Proxy-Authenticate    =    "Proxy-Authenticate" HCOLON challenge

challenge            =    ("Digest" LWS digest-cln *(COMMA digest-cln))

/ other-challenge

other-challenge        =    auth-scheme LWS auth-param

*(COMMA auth-param)

digest-cln            =    realm / domain / nonce

/ opaque / stale / algorithm

/ qop-options / auth-param

realm                =    "realm" EQUAL realm-value

realm-value        =    quoted-string

domain            =    "domain" EQUAL LDQUOT URI

*( 1*SP URI ) RDQUOT

URI                =    absoluteURI / abs-path

nonce                =    "nonce" EQUAL nonce-value

nonce-value        =    quoted-string

opaque            =    "opaque" EQUAL quoted-string

stale                =    "stale" EQUAL ( "true" / "false" )

algorithm            =    "algorithm" EQUAL ( "MD5" / "MD5-sess"

/ token )

qop-options        =    "qop" EQUAL LDQUOT qop-value

*("," qop-value) RDQUOT

qop-value            =    "auth" / "auth-int" / token

 

Proxy-Authorization    =    "Proxy-Authorization" HCOLON credentials

 

Proxy-Require        =    "Proxy-Require" HCOLON option-tag

*(COMMA option-tag)

option-tag            =    token

 

Record-Route        = "Record-Route" HCOLON rec-route *(COMMA rec-route)

rec-route            =    name-addr *( SEMI rr-param )

rr-param            =    generic-param

 

Reply-To            =    "Reply-To" HCOLON rplyto-spec

rplyto-spec            =    ( name-addr / addr-spec )

*( SEMI rplyto-param )

rplyto-param        =    generic-param

 

Require            =    "Require" HCOLON option-tag *(COMMA option-tag)

 

Retry-After            =    "Retry-After" HCOLON delta-seconds

[ comment ] *( SEMI retry-param )

 

retry-param        =    ("duration" EQUAL delta-seconds)

/ generic-param

 

Route                =    "Route" HCOLON route-param *(COMMA route-param)

route-param        =    name-addr *( SEMI rr-param )

 

Server            =    "Server" HCOLON server-val *(LWS server-val)

server-val            =    product / comment

product            =    token [SLASH product-version]

product-version        =    token

 

Subject            =    ( "Subject" / "s" ) HCOLON [TEXT-UTF8-TRIM]

 

Supported            =    ( "Supported" / "k" ) HCOLON

[option-tag *(COMMA option-tag)]

 

Timestamp            =    "Timestamp" HCOLON 1*(DIGIT)

[ "." *(DIGIT) ] [ LWS delay ]

delay                =    *(DIGIT) [ "." *(DIGIT) ]

 

To                =    ( "To" / "t" ) HCOLON ( name-addr

/ addr-spec ) *( SEMI to-param )

to-param            =    tag-param / generic-param

 

Unsupported        = "Unsupported" HCOLON option-tag *(COMMA option-tag)

User-Agent        =    "User-Agent" HCOLON server-val *(LWS server-val)

 

Via                =    ( "Via" / "v" ) HCOLON via-parm *(COMMA via-parm)

via-parm            =    sent-protocol LWS sent-by *( SEMI via-params )

via-params        =    via-ttl / via-maddr

/ via-received / via-branch

/ via-extension

via-ttl                =    "ttl" EQUAL ttl

via-maddr            =    "maddr" EQUAL host

via-received        =    "received" EQUAL (IPv4address / IPv6address)

via-branch            =    "branch" EQUAL token

via-extension        =    generic-param

sent-protocol        =    protocol-name SLASH protocol-version

SLASH transport

protocol-name        =    "SIP" / token

protocol-version    =    token

transport            =    "UDP" / "TCP" / "TLS" / "SCTP"

/ other-transport

sent-by            =    host [ COLON port ]

ttl                    =    1*3DIGIT ; 0 to 255

 

Warning            = "Warning" HCOLON warning-value *(COMMA warning-value)

warning-value        =    warn-code SP warn-agent SP warn-text

warn-code            =    3DIGIT

warn-agent        =    hostport / pseudonym

; the name or pseudonym of the server adding

; the Warning header, for use in debugging

warn-text            =    quoted-string

pseudonym        =    token

 

WWW-Authenticate    =    "WWW-Authenticate" HCOLON challenge

 

extension-header    =    header-name HCOLON header-value

header-name        =    token

header-value        =    *(TEXT-UTF8char / UTF8-CONT / LWS)

message-body        =    *OCTET

 

26 安全考慮:威脅模式和安全應用建議。

 

SIP不是一個容易進行安全保護的協議。它使用的中間媒體,以及它的多面信任關系,它希望的節點之間交互基於互不信任的關系,它的用戶到用戶的操作使得安全保證非常重要。今天,我們需要找到基於廣泛環境和使用方法的很好的安全解決方案。為了達到這一目標,我們需要建立對SIP的不同使用的幾種安全機制。

 

注意SIP的安全性本身同SIP使用的傳輸協議比如RTP的安全性或者和SIP包體的實現的安全性本身沒有繼承關系(雖然MIME安全體系是SIP安全體系的一個基石)。任何和一個會話相關的媒介都可以被端到端的加密,並且這個和相關的SIP信令無關。媒體加密是在本文檔討論范疇之外的。

 

首先對一系列的經典的威脅模式的分析可以在很大程度上描繪了SIP所需要的安全需要。這些威脅模式所針對的地址需要一些安全保護,我們接下來通過對集中安全集中的詳細分析,來講述如何這些安全機制能夠提供對這個地址的安全保護。接着我們就可以定義SIP實現者的需求,並且通過提供一個安全配置樣例來描述應用於提高SIP安全性的這些安全機制。本節也標注了一些隱私相關的注解。

 

26.1 攻擊和威脅模式

本節講述了對SIP部屬來說常見的威脅模式。這些威脅模式是經過特別篩選的,用來體現各個SIP所需要的安全保衛服務。接下來的例子並不是完整的針對SIP的威脅模式列表;不過他們是”經典”的例子,用來代表各類對SIP的攻擊。

 

這些攻擊假設攻擊者可以從網絡上讀取任何報文-這是因為SIP會通常基於公共網絡Internet。在網絡上的攻擊者通常可以更改報文內容(可能基於中間某個節點來更改)。攻擊者可以希望盜取服務,竊聽通訊,或者干擾會話。

 

26.1.1 注冊服務 Hijacking。

SIP注冊機制是提供一個用戶UA把自己的信息到一個注冊服務器上,在這個信息中,可以用address-of-record找到這個用戶的地址。注冊服務器會檢查在REGISTER消息中的From頭域所提供的身份說明,來決定是否這個請求可以修改由To頭域所包含的address-of-record的相關聯系地址。這兩個頭域通常是相同的,也會有很多第三方代替用戶注冊聯系地址的情況。

 

SIP請求的From頭域,可以被一個UA的擁有者任意修改,這就給惡意注冊信息打開了方便之門。一個成功模擬一個UA,通過檢查來修改一個address-of-record的相關聯系地址,可以,例如先注銷某個URI的所有聯系地址,然后把自己的設備地址注冊上去,這樣所有對原來URI的地址的請求將發往攻擊者的設備。

 

這種攻擊屬於很常見的對沒有請求發前方數字簽名的攻擊。任何有意義的SIP UAS(比如對傳統電話呼叫的SIP 網關等等),也許希望通過對收到的請求做認證來控制對自己資源的訪問。就算是最終終端UA,例如SIP電話,也會有對原始請求身份的驗證要求。這個威脅表明了SIP實體需要對原是請求做安全認證的安全服務需要。

 

26.1.2 模仿一個服務器

對於請求發送到的區域,一般是用Request-URI來標志的。UA通常直接聯系這個區域中的服務器來發送請求。不過總會存在一個可能,就是攻擊者把自己模仿成為遠端的服務器,這樣UA的請求可能會被其他人中間截獲。

 

例如,我們考慮這樣一個情況:一個轉發服務器在一個區域:chicago.com,它模擬的轉發服務器在另外一個區域: biloxi.com。用戶UA要發送一個請求到biloxi.com,但是chicago.com的轉發服務器回答了一個偽造的應答,並且有偽造的頭域就好像應答是從biloxi.com回來的一樣。在轉發應答中的偽造的聯系地址可以引導原始UA到一個不合適的或者不安全的資源,或者簡單的阻止發送請求到biloxi.com。

 

這個常見的威脅有着許多的成員,並且相當嚴重。作為和注冊服務hijacking相反的威脅,我們考慮這個情況: 當注冊服務信息發送給biloxi.com的被chicago.com截獲,並且回應給注冊者一個偽造的301(Moved Permanently)應答。這個應答可能看起來是從biloxi.com來得,並且指明了chicago.com作為新的注冊服務。那么這個原始UA的所有REGISTER請求就會轉發到chicago.com了。

 

要想防止這個威脅,那么就需要UA能夠對接收他們請求的服務器進行身份鑒定。

 

26.1.3 修改消息包體

當然,SIP UA路由請求通過信任的proxy服務器是一件必然的事情。不管這個信任關系是如何建立的(在本節的其他地方有講proxy的認證),UA可以信任proxy來轉發請求,而不是檢查懷疑請求中的包體被修改了。

 

考慮UA使用SIP消息體來進行媒體會話的會話密鑰通訊的情況。雖然它信任本域的proxy服務器,但是它也不希望域的管理者能夠解密后續的媒體通訊(就是不希望proxy得到這個會話密鑰)。更糟糕的是,如果這個proxy服務器是有惡意的,他可以修改這個會話密鑰,就像中間人一樣,或者改變原始請求UA的安全特性。

 

這個類型的威脅不僅僅是對會話密鑰,對所有SIP端到端的內容都有威脅。這可能包含對需要展示給用戶的MIME包體,SDP或者電話信令等內容的威脅。攻擊者可能試圖修改SDP包體,例如,給RTP媒體流增加一個竊聽設備來偷聽語音通話信息。

 

同樣需要注意的是,有一些SIP頭域是對端到端有一定含義的,比如Subject。UA可以決定保護這些頭域和包體(比如中間惡意的攻擊者可以把Subject頭域更改成為看起來好像是一個惡意郵件)。不過,因為很多頭域都是proxy服務器在處理請求轉發的時候需要合法檢查或者更改的,所以不是所有的頭域都需要端到端的保護。

 

基於這些原因,UA可以加密SIP包體,並且對端到端的頭域做一定的限制。對包體的安全服務要求包含了機密性,完整性和身份認證。這些端到端的安全服務應當與用於和中間節點交互的安全機制無關或者不依賴。

 

26.1.4 破壞會話

當對話被初始消息所建立,后續的請求可以用於修改對話並且/或者會話的狀態。對於會話的負責者來說,非常重要的事情是確定請求不是由攻擊者偽造的。

 

我們考慮這樣一個情況,一個第三者攻擊者截獲了一些初始信息,這些初始信息是對話的雙方在建立會話是交換的參數等等(To tag,From tag,等等),並且這個攻擊者在會話中插入了一個BYE請求。攻擊者可以選擇假造一個從會話的任意方的請求。當一個BYE被對方接收到,會話就會被提前終止。

 

類似會話中的威脅,有偽造re-INVITE修改會話(可能減少會話的安全性,或者作為竊聽攻擊轉發媒體流)。

 

對於這種威脅,最有效的對策就是對BYE的發送方做身份認證。在這個例子中,接受方只需要確認BYE是從建立對話的對方發起的就可以了。同樣的,如果攻擊者不能夠知道會話的參數,他也沒有辦法偽造BYE。但是,有些中間節點(比如proxy服務器)需要這些參數來判定是否會話已經建立連接。

 

26.1.5 拒絕服務和擴展。

DOS(拒絕服務)攻擊主要是使得一個特定網絡節點無法工作,通常是通過轉發超大量的網絡通訊阻塞它的網絡接口。分布式的拒絕服務攻擊允許一個網絡用戶導致大量的網絡服務器來對一個目標做洪水攻擊。

 

在很多架構下,SIP proxy服務器是基於公網的,因為它需要處理全球的IP終端的請求。這樣SIP就給這些分布式拒絕服務攻擊者提供了很多機會,這樣就必須要求SIP系統的設計者和管理者能夠識別這樣的攻擊並且定位這樣的攻擊者。

 

攻擊者可以發出包含假IP地址及其相關的Via頭域的請求,這個Via頭域標志了被攻擊的主機地址,就像這個請求是從這個主機地址來的一樣。然后把這個請求發給大量的SIP節點,這樣不幸的SIP UA或者proxy就會給被攻擊的主機產生大量的垃圾應答,從而形成拒絕服務攻擊。

 

類似的,攻擊者可以用在請求中偽造的Route頭域值來標志被攻擊的目的主機,並且把這個消息發送到分支proxy,這些分支proxy會放大請求數量發送給目標主機。

 

Record-Route頭域也可以產生類似的效果。當一個攻擊者確定一個被請求初始化的SIP對話會向回產生很多事務的時候,那么Record-Route頭域也可以被用於攻擊。

 

如果REGISTER請求沒有經過注冊服務器進行適當的認證,那么就會有很多拒絕服務的攻擊的機會。攻擊者可以在一個域中,首先把一些或者全部的用戶都注銷,從而防止這些用戶被加入新的會話。接着一個攻擊者可以在注冊服務器上注冊大量的聯系地址,這些聯系地址都指向同一個被攻擊的服務器,這樣可以使得這個注冊服務器和其他相關的proxy服務器對分布式DOS攻擊進行放大。攻擊者也會嘗試通過注冊大量的垃圾來耗盡注冊服務器可能的內存或者硬盤。

 

多點傳送的SIP請求可以非常明顯的增大拒絕服務攻擊的可能性。

 

 

小虎 2006-05-25 00:13

這些展示的問題需要定義一個架構來把拒絕服務攻擊造成的影響最小化,並且需要在安全機制中對這類攻擊特別留意。

 

26.2 安全機制

從上邊講述的威脅來看,我們得到了SIP協議所需要的基本安全服務,他們是:保護消息的隱私性和完整性,保護重現(replay)攻擊或者消息欺騙,提供會話參與者的身份認證和隱私保護,保護拒絕服務攻擊。SIP消息中的包體分別要求機密性,完整性和身份認證。

 

比為SIP重新定義新的安全機制更好的是,SIP可以重用已經存在的HTTP或者SMTP的安全機制。

 

全加密的消息提供了最好的機密保護-它也可以保證消息不被惡心的中間節點更改。不過SIP請求和應答不能簡單的進行端到端的加密,因為在大多數網絡架構下,消息的頭域比如Request-URI,Route,Via在中間經過的proxy中需要可見,這樣SIP請求才能被正確路由。注意,proxy服務器需要修改一些消息的特定屬性(比如增加Via頭域),這樣才能保證SIP正常工作。那么proxy服務器就必須被UA信任,至少在某種程度上信任。為了這個目標,我們建議為SIP提供低層次的安全機制,他們是基於節點到節點的整個SIP請求和應答的在線加密,並且語序終端節點校驗他們發出請求的proxy服務器的身份。

 

SIP實體也可以為安全保證,需要驗證對方的身份。當SIP終端向對方UA或者proxy服務器,聲明它的用戶的身份時,這個身份應當是可以通過某種方法驗證的。SIP中的數字簽名機制就是為了這個需要的。

 

SIP消息體的一個獨立的安全(加密)機制提供了另一個端到端的相互認證方式,也降低了UA必須信任中間節點的程度。

 

26.2.1 通訊和網絡層的安全

通訊或者網絡層的安全機制是加密信號通訊,保證消息機密的和完整的傳送。

 

很多情況下,低層次的安全是通過證書實現的,這些證書可以在很多架構下用於提供身份認證使用。

 

兩個通常使用的方法,提供了通訊層和網絡層的安全,他們是TLS[25]和IPSec[26]。

 

IPSec是一組網絡層的協議工具,他們可以一起使用來作為傳統IP通訊的安全替代。IPSec最常用於一組主機或者管理的域有一個現存的互相信任關系的架構下。Ipsec通常由主機的操作系統級別實現,或者在一個提供機密通訊和完整性保證的通訊安全網關上實現(比如VPN結構),IPSec也可以用於點到點的結構。

 

在很多結構下,IPSec並不要求和SIP應用一起使用;IPSec可能是最適合於部屬在那種難於直接在SIP服務器上增加安全性的情況。具有預先共享密鑰關系的UA和他們的第一個節點的proxy服務器很適合使用IPSec。為SIP部屬的IPSec要求一個IPSec 描述了協議工具的profile。這個profile在本文檔中沒有提供。

 

TLS提供了通訊層的安全性,基於連接相關的協議(TCP)。可以通過在Via頭域或者在一個SIP URI中列明”tls” (表示基於TCP的TLS)指定通訊協議為TLS。TLS最適合沒有事先定義的信任關系的點到點的結構。例如Alice 信任她的本地proxy服務器,這個服務器在進行證書交換后信任Bob的本地proxy服務器,這個Bob的本地proxy服務器是Bob信任的,因此Bob可以和Alice安全的通訊。

 

TLS必須和SIP應用緊緊聯系在一起。注意在SIP中的通訊機制是點到點的,因此一個基於TLS發送請求到proxy服務器的UA並不能保證這個TLS會在端到端的應用。

 

當實現者在SIP應用中使用TLS的時候,實現者必須支持最小集合的TLS_RSA_WITH_AES_128_CBC_SHA密碼套件[6]。並且為了向后兼容,proxy服務器,重定向服務器和注冊服務器應當支持TLS_RSA_WITH3DES_EDE_CBC_SHA。實現者也可以支持其他密碼套件。

 

26.2.2 SIPS URI方案

SIPS URI方案是SIP URI(19節)語法的一個附加,雖然這個方案串是”sips”不同於”sip”。SIPS的語義和SIP URI的語義十分不同。SIPS 允許指定希望通過安全訪問的資源。SIPS URI可以當作一個特定用戶的address-of-record使用-這個用戶是已知的(根據他們的名片,在他盟請求的From頭域,在REGISTER請求的To頭域)。當在請求中使用Request-URI,SIPS 方案指出請求經過的每一個節點,知道請求到達目的這個Request-URI指明的SIP元素,必須通過TLS進行加密;當請求抵達目標的域,他會根據目標域的本地安全策略和轉發策略,很有可能最后一部也是用TLS到達UAS。當用在請求的發起方(就像這種情況,當他們使用SIPS URI當作目標的address-of-record一樣),SIPS只是這個實體請求,到目的主機的所有路徑都應當加密。

 

SIPS方案適用於很多在SIP中應用的SIP URI,比如附加域Request-Uri,包含在address-of-record,聯系地址(Contact的內容,包含REGISTER方法的頭域等等),還有Route頭域等等。在每個用法中,SIPS URI方案允許這些存在的URI來指明需要安全訪問的資源。這些由SIPS URI所替換的東西,有他們自己的安全屬性([4]中詳細介紹)。

 

對SIPS的使用在細節上要求必須具備TLS互相的認證,並且要求支持密碼套件TLS_RSA_WITH_AES_128_CBC_SHA。在認證過程中接收到的信任書應當從客戶端持有的信任書跟節點開始驗證;對信任書驗證失敗應當導致請求的失敗。

 

注意在SIPS URI方案中,通訊層是和TLS沒有依賴關系的,並且因此”sips:alice@atlanta.com;transport=tcp”和”sips:alice@atlanta.com;transport=sctp”都是合法的(雖然注意到UDP不能用於SIPS的傳送)。我們不建議使用類似”transport=tls”的方式,部分原因是因為這是用於請求的單個節點之間的通訊。這是從RFC2543的一個變化。

 

將address-of-record用SIPS URI發出的用戶,如果在非可靠通訊協議上收到的請求,可以操作設備來拒絕這個請求。

 

26.2.3 HTTP Authentication

SIP提供了認證機制,基於HTTP認證的身份認證機制,他們依賴於401倒407應答碼和相關頭域來提供拒絕不信任的信任書。對於SIP使用的HTTP Digest認證機制,並沒有做重大的修改,它提供了replay攻擊的保護和單向認證關系。

 

對SIP的Digest 認證使用在22節有描述。

 

26.2.4 S/MIME

就像上邊講述的,在端到端的過程中加密整個SIP消息體,可以提供機密性的保護,但是並非所有的字段都能使用這個機制進行保護,因為中間的網絡節點(比如proxy服務器),需要根據讀取適當的頭域然后決定這個消息應當轉發倒哪里,並且如果這些中間節點由於安全原因被排出在外,那么SIP消息從本質上就是不能路由的。

 

不過,S/MIME允許SIP 的UA在SIP中加密MIME包體,在不影響消息頭的情況下,在端到端的通訊中加密這些MIME包體。S/MIME可以提供消息體的端到端的完整性和機密性,同樣也提供了雙向的認證機制。使用S/MIME也可以通過SIP消息隧道,為SIP頭域提供一個完整性和機密性的方案。

 

對SIP的S/MIME使用在23節講述。

 

26.3 安全機制的實現

26.3.1 對SIP實現者的要求

proxy服務器,重定向服務器,和注冊服務器必須實現TLS,並且必須支持雙向的和單向的認證關系。強烈建議UA可以初始化TLS;UA同樣可以作為一個TLS服務器。proxy服務器,重定向服務器和注冊服務器應當有一個站點信任書,這個信任書的主題和他們的規范主機名相關。對於TLS的雙向認證,UA可以有他們自己的信任書,但是本文檔中,沒有規定他們的具體用法。所有的支持TLS的SIP元素必須具備在TLS協商中,驗證信任書的機制;這個使得證書機關(可能是有名的類似web瀏覽器證書發行機構的發行機構)發布的一個或者多個根信任書成為必須。所有支持TLS的SIP元素必須同樣支持SIPS URI方案。

 

Proxy服務器,重定向服務器,注冊服務器,和UA可以實現IPSec或者其他底層的安全協議。

 

當UA試圖聯系一個proxy服務器,重定向服務器或者主阿服務器,UAC應當初始化一個TLS連接,在這個連接上發起SIP消息。在某些結構嚇,UAS可以同樣在這些TLS接收請求

 

Proxy服務器,重定向服務器,注冊服務器,和UA必須實現Digest身份認證,包括所有的22節要求的要點。Proxy服務器,重定向服務器,注冊服務器應當配置成為至少有一個Digest realm,並且對於給定服務器來說,必須支持至少有一個”realm”字符串和這個服務器的主機名或者hostname相關聯。

 

UA可以支持MIME包體的加密,並且通過23節描述的那樣使用S/MIME傳送信任書。如果UA具有一個或者多個根身份認證的信任書,用來鑒定TLS或者IPSec的信任書,它應當適當的可以用這些來鑒定S/MIME的信任書。UA可以為S/MIME身份認證而具有特定的根信任書。

 

注意,隨着S/MIME實現,將來會有安全擴展,來提高S/MIME的強度。

 

26.3.2 安全解決方案

這些安全機制的操作,可以在某種程度上和現存的WEB和EMAIL安全模式一致。在高一點的級別來看,UA通過Digest 用戶名和口令把他們自己的身份向服務器(proxy服務器,重定向服務器,注冊服務器)認證;服務器把他們自己向UA單節點認證,或者向另外一個服務器進行單節點(one hop)認證(反之亦然),並且是通過TLS來傳送服務器節點信任書。

 

在點對點的級別,UA一般信任網絡來進行對方身份的鑒別;不過,如果網絡不能夠鑒定對方身份,或者網絡本身不被信任的情況下,也可以使用S/MIME來提供直接的身份認證。

 

接下來是一個例子,在這個例子中,不同的UA和服務器使用這些安全機制防止26.1節描述的攻擊威脅。實現者和網絡管理員可以遵循本節末尾給出的指示來防止攻擊威脅,這些指示是作為實現例子提供的。

 

 

26.3.2.1 注冊

當UA上線,並且注冊到它自己的域上,它應當和它的注冊服務器建立一個TLS連接(10節描述了UA怎樣找到它的注冊服務器)。注冊服務器應當提供一個信任書給UA,並且這個信任書的節點必須是這個UA想要注冊的域相關的信任書節點;例如,如果UA向注冊alice@atlanta.com這個address-of-record,這個信任書節點必須是一個atlanta.com域的主機(比如sip.atlanta.com)。如果它收到了TLS信任書消息,UA應當校驗這個信任書,並且檢查這個信任書的節點。如果信任書是非法的,作廢的,或者它和持有者不符,UA必須不能發送REGISTER消息和進行注冊處理。

 

當UA收到注冊服務器提供的一個有效的信任書,UA知道注冊服務器並非一個攻擊者(可能重定向、竊取口令、或者試圖做類似攻擊的攻擊者)。

 

於是UA創建了一個REGISTER請求,並且Request-URI應當指向從注冊服務器所接收到的信任書站點。當UA通過剛才建立的TLS連接發送REGISTER請求,注冊服務器應當給出一個401(Proxy Authentication Required)應答。在這個應答中,Proxy-Authenticate頭域的”realm”參數,應當和前邊給出的信任書節點的域相同。當UAC收到這個拒絕,它應當提示給用戶要求信任書,或者根據應答中的”realm”參數,從現有的密鑰組中查找對應的信任書。這個信任書的用戶名應當和REGISTER請求的To頭域的URI的”userinfo”部分相關。當在一個合適的Proxy-Authorization頭域中插入和這個信任書,REGISTER應當重新發送到注冊服務器。

 

由於注冊服務器要求UA認證它自己,對於攻擊者來說,偽造一個用戶的address-of-record的REGISTER請求是很困難的。同樣注意到由於REGISTER是通過機密的TLS連接發送的,攻擊者不能通過截取REGISTER來記錄信任書來進行重放攻擊。

 

當注冊請求被注冊服務器接收,UA應當繼續保持TLS連接,這樣使得注冊服務器可以既當作proxy服務器,這個proxy服務器可以作為管理這個域的proxy服務器。剛完成注冊的TLS連接會繼續保留用於接收UA后續發起的請求。

 

由於UA已經通過在TLS連接的對方的服務器的認證,所有在這個連接上的請求都是經過proxy 服務器的(由於保留了TLS連接,也就是說,剛才的注冊服務器更換了角色,變成一個proxy服務器)--攻擊者不能偽造好像是剛才從這個proxy服務器發送的請求。

 

26.3.2.2 在域之間的請求

現在我們說,Alice的UA希望和遠端管理的域的一個用戶,這個用戶叫做”bob@biloxi.com”,初始化一個會話。我們講會說本地管理的域(atlanta.com)有一個本地外發proxy。

 

對於一個管理域的Proxy服務器處理那發請求,可以同樣作為本地的外發proxy;基於簡單的原則,我們假定這就是atlanta.com(否則這時候UA將要初始化一個新的TLS連接到一個獨立的服務器)。假定這時候,這個客戶端已經完成了注冊(參見前邊的步驟),當它發出INVITE請求邀請另外一個用戶的時候,它將重用這個TLS連接到本地proxy服務器(剛才的注冊服務器)。這個UA在INVITE請求中,將會重用剛才cache的信任書,這樣可以避免不必要的要求用戶輸入信任書。

 

當本地的發外服務器驗證了這個UA在INVITE請求中的信任書,它應當檢查Request-URI來決定這個消息應當如何路由(參見[4])。如果這個Request-URI的”domainname”部分和一個本地域相關聯(atlanta.com)而不是和biloxi.com相關,那么proxy服務器會向本地服務查詢來決定怎樣最好的訪問到被呼叫的用戶。

 

“alice@atlanta.com”正在嘗試聯系呼叫”alex@atlanta.com”,本地proxy將會轉發這個請求到Alex和它的注冊服務器所建立的TLS連接上。由於Alex將會通過它的已經通過認證的連接上收到這個請求,它就確定這個Alice的請求是通過了本地管理域的proxy的身份驗證的。

 

不過,在這個例子中,Request-URI指向的是一個遠程域。在atlanta.com的本地外發服務器應當因此而建立一個和在biloxi.com的遠程proxy服務器的TLS連接。由於這個TLS連接的兩端都是服務器,並且都有服務器的信任書,那么應當使用雙向的TLS身份認證。連接的雙方應當驗證和檢查對方的信任書,把在信任書中的域名同SIP消息中的頭域做比較。例如,atlanta.com 這個proxy服務器,在這步,應當檢查從對方接收到的關於biloxi.com域的信任書。當檢查完畢,並且TLS協商完成,就建立了基於兩個proxy的安全通道,atlanta.com proxy於是可以把INVITE請求轉發給biloxi.com了。

 

在biloxi.com的proxy服務器應當檢查atlanta.com的信任書,並且比較信任書提供的域名和INVITE請求的From頭域的”domainname”部分。這個biloxi proxy可以執行嚴格的安全機制,拒絕那些他們被轉發的域和本proxy所管理的域不匹配的請求(這個不太明白)。

 

這些安全機制可以用來防止SIP和SMTP ‘open relays’一樣經常被用於產生垃圾郵件一樣的信息。

 

這個政策,只是保證了請求聲明的來源確實是它自己;他並不允許biloxi.com確知如何atlanta.com認證的Alice。只有當biloxi.com由其他方法知道atlanta.com的身份認證機制,他才可能確知Alice如何證明她的身份的。biloxi.com可以接着使用更嚴格的方法,禁止來自未確定和biloxi.com相同的認證策略的域的請求(就是說所有biloxi.com接受的請求,都必須來自biloxi.com所知道身份認證方式的域)。

 

當INVITE請求被biloxi.com核准,proxy服務器應當鑒別現存的TLS通道,如果存在現存的TLS通道,並且是和這個請求中的被叫用戶(在這個例子中是bob@biloxi.com)相關聯的。那么這個INVITE請求應當通過這個TLS通道發送給Bob。由於通過這個TLS連接收到的請求,這個TLS連接是剛才已經在biloxi proxy上通過了身份認證,Bob於是知道From頭域沒有被篡改,並且atlanta.com已經認證了Alice,所以就沒有必要猶豫是否信任Alice的身份。

 

在他們轉發請求錢,兩個proxy服務器應當在請求中增加Record-Route頭域,這樣所有在這個對話中的后續的請求講過通過這兩個proxy服務器。proxy服務器因此可以在對話生存周期中,繼續提供安全服務。如果proxy服務器並不在Record-Route頭域增加他們自己,以后的消息將會直接端到端的在Alice和Bob中發送,而沒有任何安全措施(除非兩個端點使用了某種獨立的端到端的安全措施,比如S/MIME)。在這個考慮上,SIP梯形模式可以提供一個精美的結構來在proxy服務器節點之間進行磋商,以提供一個Alice和Bob之間的有道理的安全通道。

 

例如,一個攻擊這個結構的攻擊者將會不能偽造BYE請求並且把他插入Bob和Alice的信令流,因為攻擊者無從探聽會話的參數,這也是由於通訊的完整性機制保證了Alice和Bob之間的通訊是機密的完整的。

 

26.3.2.3 點對點請求

另外,考慮這樣一個情況,UA聲稱carol@chicago.com沒有一個本地外發proxy。當Carol希望發送INVITE到bob@biloxi.com,她的UA應當直接初始化一個TLS連接到biloxi proxy(使用附件[4]中描述的機制來檢查怎樣到達指定的Request-URI)。當她的UA收到一個biloxi proxy發送的信任書,她應當在通過TLS連接發送她的INVITE請求前,正常校驗這個信任書。不過,Carol並沒有義務相biloxi proxy提供她自己的身份,但是她在INVITE請求的”message/sip”包體中,有一個CMS-detached(分離的)簽名。如果Carol在biloxi.com realm有其他信任書的話,就不一定提供這個簽名,但是她在biloxi.com上沒有正式關系,所以她沒有信任書,也就必須提供這個簽名。biloxi proxy可以有嚴格的機制直接把這個請求踢掉,甚至不用麻煩被叫方來驗證這個請求,因為在From頭域的”domainname”部分,並沒有biloxi.com-它可以被當作這個用戶是未認證的。

 

biloxi proxy對於Bob用戶有一個政策,就是所有未認證的請求都應當轉發到一個適當的地址,對於bob@biloxic.om,就是<sipo:bob@192.0.2.4>。Carol在和biloxi proxy建立的TLS連接上,收到這個轉發請求的應答,於是它信任這個轉發地址的真實性。

 

Carol應當和目標地址建立一個TCP連接,並且發送一個新的INVITE請求,在這個請求中,Request-URI包含剛才接收到的聯系地址(需要重新計算包體中的簽名,因為請求重新構造了)。Bob從不安全的界面上接收到這個INVITE請求,但是這個UA是特意預留一個不安全的界面,在這個情況下,認可請求中的From頭域並且隨后把INVITE包體中的簽名和一個本地cache的信任書進行匹配。Bob用類似的方法應答,把他自己相Carol進行認證,這樣一個安全的對話就開始了。

 

某些情況下,在一個域的NAT或者防火牆會阻止UA之間直接建立TCP連接。在這個情況下,如果本地策略許可,proxy服務器可以隱含的做UA之間的請求轉發,並且這個轉發是基於沒有信任關系的(比如不用現存的TLS連接,而是通過TCP明碼的請求轉發)

 

 

26.3.2.4 DoS 防護

基於這些安全解決方案,為了使得拒絕服務(DoS)攻擊造成的影響最小,實現者應當注意如下的指引:

 

當SIP proxy服務器所在的主機是基於公共internte做路由的,他應當部署在一個具有防護操作策略的管理域中(比如block源路由,過濾ping包等等)。TLS和IPSec都可以在管理域的邊界的防護主機,一起參與安全系統的構家,來提高安全性。這些防護主機可以防護拒絕服務攻擊,確保在管理域中的SIP主機不會被大量的消息阻塞。

 

不管使用什么樣的安全措施,對proxy服務器的洪水消息攻擊可以耗盡proxy服務器的資源,並且阻止發送到目的地的正確的請求。對於proxy服務器來說,每一個SIP事務都會好用一定的資源,對於有狀態的服務器來說這個消耗會更大。因此,有狀態的proxy比無狀態的proxy更容易受到洪水攻擊的影響。

 

UA和proxy服務器應當用一個401(Unauthorized)或者407(Proxy Authentication Required)拒絕有問題的請求,並且對這些有問題的請求不使用正常的應答重發機制,並且對這些未認證的請求把自己當作無狀態的服務器使用。如果攻擊者使用假的頭域值(例如Via)指向一個第三方的被攻擊的服務器,那么對於401(Unauthorized)或者407(Proxy Authentication Required)應答的重發可能正中攻擊者的下懷。

 

總得來說,基於TLS簽名的proxy服務器之間的雙向認證機制,降低了中間節點潛在的風險,並且減少了可以用作拒絕服務攻擊的偽造的請求或者應答。這也同樣提高了攻擊者利用無知的SIP節點進行放大攻擊的難度。

 

 

26.4 限制

雖然有這些安全機制,在正確應用的時候,可以阻止很多攻擊,但是對於網絡管理者和開發者來說,也必須明白他們也會有很多限制的地方。

 

26.4.1 HTTP Digest

在SIP中對於HTTP Digest一個限制是Digest的完整性機制在SIP下運作的不是很完美。尤其是,他們提供了對消息中的Request-URI和方法的保護,但是並沒有對UA希望提供加密的所有頭域進行了保護。

 

 

RFC2617所提供的回放攻擊保護對於SIP來說也有一些限制。例如,next-nonce機制,並不支持通過管道傳送的請求。防止回放攻擊應當使用nonce-count機制。

 

另一個HTTP Digest限制是realm的范圍。當用戶希望把他們自己的身份向一個資源(這個資源和他們有着預先存在的關系)進行認證的時候,Digest是有用的,它就像一個服務提供者,用戶是一個客戶端(這是十分常見的場景,因此Digest提供了一個十分有用的功能)。與之對應的,TLS的范圍是基於域之間的,或者multirealm的,因為信任書通常是全局可驗證的,所以UA可以在不需要預先存在關系的服務器上進行身份驗證。

 

26.4.2 S/MIME

對於S/MIME的一個最大的限制是對終端用戶來說,缺少廣泛的公共密鑰機構。如果使用的是自簽名(selfsigned)信任書(或者信任書不能被對話的對方所驗證),23.2節描述的基於SIP的密鑰交換機制就容易遭受中間人攻擊(man-in-the-middle),在這個中間人攻擊中,攻擊者可以悄悄檢查和修改S/MIME包體。攻擊者需要截取對話中,雙方的第一個密鑰交換,在請求和應答中移去現有的CMS-detached簽名,並且插入另外的包含攻擊者提供的信任書(但是看起來像是正確地address-of-record的信任書)的CMS-detached 簽名。通訊的雙方都回以為他們和對方交換了密鑰,實際上他們互相有的只是攻擊者的公鑰。

 

必須明確注意到攻擊者只能攻擊雙方的第一次的密鑰交換-在后續的情況,密鑰的變更對於UA來說就是不可見的了。同樣使得攻擊者難以竊聽對話雙方的以后的對話中(比如一天內的對話,周內的,或者一年的對話)。

 

SSH在第一次密鑰交換的時候,也同樣容易受到這個中間人攻擊;但是,雖然眾所周知SSH不完美,但是它確實提高了連接的安全性。對於SIP來說,使用密鑰的指紋可以有些幫助,就像在SSH中一樣。例如,如果SIP的雙方建立了一個語音通訊會話,每一個都可以讀取對方傳送的密鑰指紋,並且可以根據這個指紋和原始指紋做比較。這使得中間人更加難以在語音中模擬通話雙方的密鑰指紋(實際上這個在Clipper 基於芯片的保密電話中使用)。

 

在S/MIME機制下,如果UA在他們的密鑰組中持有對方address-of-record的信任書,允許UA不用導言來發送加密的請求。不過,存在這個樣的情況,如果某個設備注冊了這個address-of-record,並且沒有持有持有設備當前用戶先前使用的信任書。並且它將因此不能正常處理加密的請求,於是可能會導致某些原本可以避免的錯誤信號。這很像加密的請求被分支的情況。

 

S/MIME相關的密鑰通常用於和特定用戶(一個address-of-record)關聯起來使用,而不是和一個設備(UA)一起使用。當用戶在設備之間移動,很難把私鑰安全的在UA之間傳遞;一個設備如何獲得這些密鑰不在本文討論。

 

另外,使用S/MIME更常見的困難是,它可以導致很大的消息,特別是當采用23.4節的SIP隧道戒指的時候。基於這個原因,當使用S/MIME隧道機制的時候,一定要使用TCP通訊協議。

 

26.4.3 TLS

TLS最大的問題是,它不能基於UDP;TLS要求面向連接的底層通訊協議,對於本文來說,就是TCP。

 

對於TLS來說,在本地外發proxy服務器和/或者主車服務器上和大量UA,維持很多並發TLS長連接,是一件費力的事情。這導致某些容量問題,特別是在使用加強密鑰套件的時候更容易出現容量問題。維持冗余的TLS長連接,特別是當UA獨立負責他們的連接,會很耗資源。

 

TLS只允許SIP實體到他們臨近的認證服務器的連接;TLS提供了嚴格的節點到節點的安全性(hop-by-hop)。無論TLS,還是其他本文檔規定的安全機制,當不能直接建立TCP連接的情況下,都允許客戶通過驗證自己到proxy服務器來到達目的地。

 

26.4.4 SIPS URI

實際上在請求經過的每一段都使用TLS,要求了最終的UAS必須能夠通過TLS到達(也許是通過SIPS URI作為一個聯系地址注冊的)。通常這個目的地是用SIPS描述的。在很多結構下,使用TLS保護一部分請求路徑,最后一部確實依賴於其他的安全機制到UAS。因此SIPS不能保證TLS是真正的端到端的使用。注意,這是由於許多UA不接受進來的TLS連接,甚至那些支持TLS的UA也可能要求要維持一個永久的TLS連接(就像前邊的TLS限制節講述的一樣),目的是為了作為UAS從TLS上接收請求。

 

位置服務,對於SIPS Request-URI來說,是不要求提供SIPS綁定的。雖然位置服務通常由用戶注冊(10.2.1節描述)組成,但是對於一個address-of-record來說,可以由不同的協議和界面都可以提供聯系地址,並且這些工具都可以用來映射SIPS URI到適當的SIP URI。當對綁定信息查詢的時候,位置服務返回它的聯系地址,而不關心這個是否是從一個SIPS的Request-URI上收到的請求。如果是轉發服務器訪問位置服務,那就是說取決於處理轉發的Contact頭域的SIP實體來決定聯系地址的屬性。

 

如果要求請求經過的全部路徑都使用TLS通訊,那么對目的域來說稍稍有點麻煩。如果實現困難,也允許在請求傳送節點中的基於密碼認證的proxy服務器,不兼容或者選擇一個折中方案來略過SIPS的轉發機制(在16.6節定義的通常轉發規則)。但是,惡意的中間節點就有可能把一個基於SIPS URI的請求,重新降級成為SIP URI。

 

另外,中間節點也可以正常的把一個基於SIP的請求更改成基於SIPS URI的請求。請求的接受方發現請求的Request-URI是基於SIPS URI方案的並不能假設原始請求的Request-URI也是基於SIPS通過中間節點傳送的(從客戶端往后)。

 

為了解決這個問題,我們建議請求的收件人,在請求的Request-URI包含一個SIP或者SIPS URI的情況下,檢查To頭域值,看看是否包含一個SIPS URI(雖然注意到Request-URI可以和To頭域URI有相同的URI方案,但是他們不相等並不意味者是一個安全隱患)。雖然客戶端可以在一個請求中把Request-URI和To頭域做成不一樣的,但是當兩個字段的SIPS不一樣的話,那就是可能的安全隱患,並且請求因此應當被接受方拒絕。接受方也可以檢查Via頭域鏈來雙重檢查是否TLS在整個請求路徑中被使用,一直到最近的本地管理域。源UAC也可以用S/MIME來幫助確保原始格式的To頭域被端到端的發送。

 

如果UAS有原因來相信Request-URI的URI方案在通訊中被非法修改,UA應當提示用戶這個潛在的安全隱患。

 

作為更深遠的考量,為了防止底層的攻擊,如果SIP實體只接受SIPS請求,那么可以拒絕在非安全端口的連接。

 

終端用戶應當完全清楚SIPS和SIP URI的區別,他們可以在應答中手工更改這個URI方案。這可以增加或者降低安全性。例如,如果一個攻擊者攻陷了一個DNS的cache,插入了一個假的記錄集,這個記錄集有效刪去了一個proxy服務器的所有SIPS記錄,接着經過這個proxy服務器的SIPS請求就會失效。這時候,一個用戶,看見這個SIPS address-of-record總是失敗,他可以手工改掉URI從SIPS改成SIP,並且重試。當然,這也有一些安全機制防止這類事情發生(如果目標UA真是有神經病拒絕所有非SIPS請求的話)。但是這個限制毫無價值。往好了想,用戶也可以把SIP URI變成SIPS URI。

 

26.5 Privacy(隱私)

SIP消息經常包含發送者的敏感信息-不只是他們將說些什么,也包括了他們和誰在通訊,以及他們通訊了多久,以及從那里到哪里的通訊等等。很多應用和他們的用戶都要求這類隱私信息對於沒有必要知道的方面來說都必須保持隱秘。

 

注意,也有隱私信息也有少數直接泄漏的方式。如果用戶或者服務,位於一個容易猜到的地址,比如通過用戶的名字或者機構的聯系(這是構成Address-of-record的最經常的情形),傳統保證隱私性的方式是通過一個未列出的”電話號碼表”來實現的。在呼叫方發起的會話邀請中,用戶位置服務可以提供精確的被叫方的位置從而破壞了隱私;在實現上因此應當更嚴格,基於每用戶的原則,根據不同的呼叫方給出不同的位置信息和可用性的信息。這是一個SIP需要解決的完整的問題。

 

在某些情況下,用戶可能希望在通過身份驗證時,在頭域中隱藏個人信息。這可以應用於不僅是From和相關表現請求發起方信息的頭域,也應用於To頭域-他可能不能由快速撥號的nick-name轉換成為最終的目的地信息,或者一個為擴展的一組目標的標志,當請求被路由的時候,每一個都可以從Request-URI上被移去,但是如果和To頭域初始值相等的時候,不能更改To頭域。因此,他可能由於隱私的原因創建和Request-URI不同的To頭域。

 

27 IANA 認證

SIP應用中的所有的方法名字,頭域名字,狀態碼,和option tags,都是通過RFC中關於IANA認證節的說明在IANA注冊的。

 

本規范指示IANA創建了4個新的子注冊項目在:http://www.iana.org/assignments/sip-parameters: Option Tags,Warning Codes(warn-codes),Methods和Response Codes,頭域的子項也在那里。

 

27.1 Option Tags

這個規范在http://www.iana.org/assignments/sip-parameters建立了Option Tags注冊項.

 

Option tags用於類似Require,Supported,Proxy-Require,Unsupoprted這類的頭域,用來支持SIP的擴展兼容性機制(19.2節)。Option tag自身時一個字符串,和特定的SIP選項相關(也就是和特定的SIP擴展相關)。它為SIP終端確定這個選項。Option tags當被標准的RFC軌跡擴展,它就在IANA注冊了。RFC的IANA認證節必須包含如下內容,這些內容與RFC出版號碼一起在IANA注冊表登記。

 

o option tag的名字。名字可以是任意長度的,但是應當不要超過20個字母。丙子必須是alphanum(25節)字符。

 

o 描述擴展的描述信息

 

 

27.2 Warn-Codes

本規范在http://www.iana.org/assignments/sip-parameters建立了Warn-Codes注冊項。並且初始發布的warn-codes值在20.43節列出。附加的warn-codes通過RFC出版物注冊發表。

 

對於warn-codes表的描述信息如下:

 

當一個會話描述協議(SDP)(RFC 2327[1])出現問題導致事務失敗的時候,Warning codes在SIP應答信息中提供了對狀態碼的補充信息。

“warn-code”包含了3位數字。第一個數字”3”表示warning是SIP的警告信息。除非以后另有描述,只有3xx警告信息可以被注冊。

 

300到329的警告信息為會話描述保留字錯誤保留,330到339為會話要求基本網絡服務錯誤保留,370到379是為會話描述要求的QoS參數數量錯誤保留,390到399是無法歸類的雜項警告信息。

 

27.3 頭域名

本規范在http://www.iana.org/assignments/sip-parameters建立了頭域的注冊項。

 

為了注冊一個新的頭域名,下列信息需要在RFC出版物中提供,

 

o 頭域注冊的RFC號碼

o 注冊的頭域名

o 如果有縮寫,頭域的縮寫版本。

 

部分常用的頭域可以縮寫成1個字母的簡寫形式(7.3.3節)。簡寫形式只能在SIP工作組復查的時候被登記在RFC出版物上。

 

27.4 方法和應答碼

本規范在http://www.iana.org/assignments/sip-parameters建立了Method和Response-code的注冊項。並且下邊列出了他們的初始值。初始的方法表如下:

 

INVITE            [RFC3261]

ACK            [RFC3261]

BYE            [RFC3261]

CANCEL        [RFC3261]

REGISTER        [RFC3261]

OPTIONS        [RFC3261]

INFO            [RFC2976]

 

應答碼的初始值在21節列出,分為信息部分(Informational),成功(Success),轉發(Redirection),客戶端錯誤(Client-Error),服務端錯誤(Server-Error),全局錯誤(Global-Failure)幾個部分。這個表格有如下格式:

 

類型(Type,就是Informational), Number, Default Reason Phrase [RFC3261]

 

如下信息需要提供給RFC出版機構來注冊新的應答碼或者方法:

 

 

o 應答碼和方法需要注冊的RFC號碼

o 應答碼號碼或者方法名

o 缺省應答碼的原因說明

 

27.5 “message/sip” MIME類型

本文檔注冊了”message/sip”MIME媒體類型,目的是允許SIP可以通過SIP包體隧道,首要用於端到端的安全保障。這個媒體類型由如下部分組成:

 

媒體類型名字:    message

媒體子類型:    sip

要求的參數:    none

可選的參數:    version/Encoding scheme/Security considerations

version: 這個打包的消息的SIP-version號碼(比如”2.0”)。如果沒有提供,version的缺省值就是”2.0”。

Encoding scheme: SIP消息包含一個8位的頭,在二進制的MIME數據對象后邊可選。同樣的,SIP消息必須把這個當作一個二進制值。在通常情況下,SIP消息通過二進制兼容的通訊協議傳送,不需要特別的編碼方式。

Security considerations: 本參數用於同23.4給出的S/MIME安全機制一致,請參見下邊的例子和說明。

 

27.6 新Content-Disposition 參數注冊

本文檔也注冊了4個新的Content-Disposition頭”disposition-types”: altert,icon,session和render。作者要求這些值在IANA中為Content-Disposition登記。

 

對於”disposition-types”的描述,包括例子和說明,在20.11中說明。

 

在IANA注冊中的簡單描述是:

 

alter            包體是一個客戶定義的鈴聲用於提醒用戶。

icon            包體是一個顯示給用戶的icon

render        包體應當顯示給用戶

session        包體描述了一個通訊會話,例如RFC2327 SDP包體

 

28 同RFC 2543的改變

這個RFC修訂了RFC 2543。它主要和RFC2543向后兼容。這里描述的變更主要修訂了RFC2543中發現的錯誤,並且提供了在RFC2543中沒有提及的相關情節信息。在本文中,協議以更清晰的層次結構描述。

 

我們把和RFC2543的永久改變,分成按功能行為划分這些區別。比如在互操作上不兼容,或者修正了某些情況下的錯誤操作,以及和RFC2543功能行為不同但是不是一個潛在的兼容性問題。以及有數不清楚的澄清,在本文中沒有提及。

 

28.1 主要的功能改變

o 在UAC還沒有給出應答之前,UAC希望終止一個會話,它發送CANCEL來終止。如果原始INVITE依舊給出2xx應答,那么UAC接着發送BYE。BYE只能發給現存的呼叫對話(leg)中(在這個RFC中成為對話(dialog)),但是在RFC2543中可以在任何時間發送BYE。

o SIP的BNF改成RFC2234兼容的了。

o SIP URL的BNF更改的更加通用,在user部分,允許一個很大的字符集。此外,比較規則也簡化為首先大小寫不敏感,接着詳細比較各個參數。最主要的變化是帶參數的URI和不帶這個參數(但是缺省值相等)的URI 判定是不相等的。

o 刪除了隱藏的Via。這要求發出絕對的信任,由於它依賴於下一個節點來執行這個不明確的操作。作為替代,Via隱藏可以通過在proxy服務器上的本地實現選擇完成,於是在這里不在說明。

o 在RFC2543,CANCEL和INVITE請求是混和的。他們現在分開了。當用戶發出一個INVITE請求接着CANCEL掉,INVITE事務將正常終結。UAS應當對原始INVITE請求給出一個487應答。

o類似的,CANCEL和BYE事務也是混和的;RFC2543允許UAS在收到BYE的時候,不給INVTE請求發送應答。本文檔不允許這種情況出現。原始的INVITE請求需要一個應答。

o 在RFC2543中,UA需要支持只有UDP的情況。在這個RFC中,UA應當支持UDP和TCP。

o 在RFC2543中,forking 分支proxy在收到多個拒絕應答的時候,只向下行元素發出一個拒絕身份認證應答,在這個RFC中,proxy應當搜集所有的拒絕並且把他們放在應答中發送。

o 在Digest信任書中,URI需要被引號引起來;這個在RFC2617和RFC2069是不一致的。

o SDP處理被分為獨立的規范[13],並且更全面的描述了正式的通過SIP包體隧道進行的offer/answer交換過程。作為SIP實現基線,SIP允許在INVITE/200或者200/ACK中存在;RFC2543間接指出了在INVITE,200和ACK中的單個事務使用這個方法,但是這個在RFC2543中沒有很好的定義。在SIP擴展中允許使用更復雜的SDP。

o 增加了對URI中和在Via頭域中的IPV6的全面支持。Via頭域允許方括號和冒號的參數,要求對Via頭域的IPV6的支持。這些字符在先前是不允許的。在理論上,這顆一導致和舊規范實現上的相互操作的問題。不過,我們觀察到大部分實現在這個參數上,接受非控制ASCII字符。

o DNS SRV處理步驟現在使用了單獨的規范[4]。這個步驟使用了SRV和NAPTR資源

 

 

小虎 2006-05-25 00:13

記錄,並且不在合並從SRV記錄的數據(RFC2543)。

o 循環檢測變成可選的了,替代為強制使用Max-Forwards參數。這個循環檢測在RFC2543由嚴重bug,可能會在正常情況下把”螺旋”報告成為一個錯誤。在這里,可選的循環檢測步驟是更加全面的和正確的。

o 由於他們現在在對話中是作為識別對話的基本要素存在,所以,對tag的使用是強制的(他們在RFC2543中是可選的)。

o 增加了Supported頭域,允許客戶端向服務器表示它支持什么樣的擴展,服務器可以根據這個字段決定給出包含什么樣擴展的應答,並且把他們需要的擴展支持放在Require字段里邊。

o 幾個頭域的擴展參數的BNF描述在RFC2543中忽略了,這里增加了。

o 處理Route和Record-Route的構在在RFC2543中是很不規范的,並且也不正確。它在本規范中更改成為規范正確的(並且大大簡化了)。這是很大的改變。對於沒有使用”預先加載路由的”部屬情況下(在預先加載路由的時候,初始的請求有一個Route路由集合,這個集合不是由Record-Route構造的),依舊提供了向后兼容性。在預先加載路由的情況下,新舊機制是不能互相操作的。

o 在RFC2543中,消息中的行是可以由CR/LF/或者CRLF終端的。本規范只允許CRLF。

o 在CANCLE和ACK請求中的Route的使用,在RFC2543中沒有精確定義,在本文檔中定義了;如果請求有一個Route頭域,他的為一個給請求的非2xx應答的CANCEL或者ACK應當包含相同的Route頭域。為2xx應答的ACK請求使用2xx應答的Record-Route構造Route頭域值。

o RFC 2543允許一個UDP包中多個請求。這個使用方法在本文檔中刪去了。

o 對於Expires頭域和參數中的絕對時間的使用方式被刪去了。當各個節點之間時間不同步的時候,他會帶來戶操作的問題。本文檔使用相對時間。

o Via頭域中的branch參數現在是強制每一個元素都使用。它現在作為事務的唯一標志。這避免了RFC2543容易出錯的事務鑒別規則的復雜性。我們在這個參數值使用一個亂數cookie來檢查上一個節點產生的這個參數是否全局唯一的,如果不是,那么舊采用舊的比較規則。因此,保證了互操作性。

o 在RFC2543,對TCP連接的關閉和CANCEL是相同的。當TCP連接在兩個proxy之間的時候,這在實現上是幾乎不可能的(也是錯誤的)。這個要求在這里被刪去了,所以,TCP連接狀態和SIP處理之間沒有直接關系。

o RFC2543中,當UA到一個節點可以初始化一個新的事務,當對方正在進行事務處理的時候。在這里被精確定義了。只允許初始化非INVITE請求,不允許INVITE請求。

o PGP被刪去了。它沒有很充分的定義,並且和更復雜的PGP MIME不兼容。替換成為S/MIME。

o 增加了”sips” URI方案用於端到端的TLS通訊。這個方案是和RFC2543不能年個兼容的。現有的節點如果接收到請求的Request-URI中包含SIPS URI方案,將拒絕這個請求。這實際上是一個特性;它保證了對SIPS URI只會在所有節點都是安全的情況下被傳送。

o 在TLS上附加了安全特性,並且這里也更廣泛更完整的描述了安全考慮。

o 在RFC 2543中,並不要求proxy轉發101到199的臨時應答到上行隊列。這里被更改成為必須轉發。這是非常重要的,因為很多后續的特性都依賴於傳送所有的101到199的臨時應答。

o RFC 2543提及了很少的503應答。它用於標志proxy失敗或者過載的情況。這要求某些特別的處理。尤其是,接收到503的接受方應當觸發一個對於下一個節點在DNS SRV的重新查找。同樣的,503應答只由proxy服務器在特定情況下轉發到上行隊列。

o RFC2543定義了,但是沒有充分指出,對於UA認證一個服務器的機制。這個已經刪去了。作為替代,允許使用RFC2617的雙向認證步驟。

o 對於UA來說,除非它收到了初始請求的ACK,不能發送BYE到一個呼叫。這個在RFC2543是允許的,但是可能導致潛在的空轉可能。

o UA或者proxy不能發送CANCEL到一個事務,直到它得到了一個臨時應答。這個在RFC2543中是允許的,但是可能導致空轉的可能。

o 在注冊中的action參數被禁止使用了。它不足以提供任何有用的服務,並且會導致在proxy上的應用程序處理上的沖突。

o RFC2543對於multicast有一堆特別的情況。例如某個應答被禁止了,定時器調整了,等等。multicast現在受到更多限制,並且同unicast相反,協議操作不影響對multicast的使用。

o 基本的認證整個被刪除了,不允許用基本的認證。

o proxy不再立刻收到就轉發6xx應答。他們立刻CANCEL相關等待的分支。這也避免了潛在的空轉,使得UAC再2xx之后收到一個6xx。在除了這個空轉的情況,結果都必須是相同的-6xx轉發到上行隊列。

o RFC2543並不對請求的合並認為是一個錯誤。這就導致在proxy上分支的請求稍后會在一個節點合並。只有在UA上能進行合並操作,並且處理步驟定義的是除了第一個請求都統統拒絕。

 

28.2 小功能性的變更

o 為給用戶展示可選的內容,增加了Alert-Info,Error-Info,Call-Info頭域。

o 增加了Content-Language,Content-Disposition和MIME-Verison頭域。

o 增加了一個”glare hanling”機制來處理雙方同時都向對方提出一個re-INVITE。它使用信的491(Request Pending)錯誤碼。

o 增加了 In-Reply_To和Reply-To頭域來支持稍后對未接呼叫/消息的返回。

o 在SIP通訊協議中增加了TLS和SCTP。

o 描寫了很多機制來處理呼叫中的發生的錯誤;現在做了統一的處理。BYE用來發送會話終結。

o RFC2543要求INVITE應答通過TCP重新傳送,但是注意到實際上只對2xx應答是需要的。這就導致了人為的協議不足。通過定義了一個一致的事務層,這就不在需要了。只有給INVITE的2xx應答需要基於TCP重傳。

o 客戶端和服務端事務機器現在基於超時機制,而不是基於重傳次數。這允許狀態機能夠更好的適應TCP和UDP。

o Date頭域在REGISTER應答中使用,提供一個簡單的自動配置UA的日期的機制。

o 允許注冊服務器拒絕過小的超時時間的注冊。並且為此定義了423應答碼和Min-Expires頭域。

 

29 標准索引

 

[1]    Handley, M. and V. Jacobson, "SDP: Session Description Protocol", RFC 2327, April 1998.

[2]    Bradner, S., "Key words for use in RFCs to Indicate Requirement Levels", BCP 14, RFC 2119, March 1997.

[3]    Resnick, P., "Internet Message Format", RFC 2822, April 2001.

[4]    Rosenberg, J. and H. Schulzrinne, "SIP: Locating SIP Servers",RFC 3263, June 2002.

[5]    Berners-Lee, T., Fielding, R. and L. Masinter, "Uniform Resource Identifiers (URI): Generic Syntax", RFC 2396, August 1998.

[6]    Chown, P., "Advanced Encryption Standard (AES) Ciphersuites for Transport Layer Security (TLS)", RFC 3268, June 2002.

[7]    Yergeau, F., "UTF-8, a transformation format of ISO 10646", RFC 2279, January 1998.

[8]    Fielding, R., Gettys, J., Mogul, J., Frystyk, H., Masinter, L.,Leach, P. and T. Berners-Lee, "Hypertext Transfer Protocol -- HTTP/1.1", RFC 2616, June 1999.

[9]    Vaha-Sipila, A., "URLs for Telephone Calls", RFC 2806, April 2000.

[10]    Crocker, D. and P. Overell, "Augmented BNF for Syntax Specifications: ABNF", RFC 2234, November 1997.

[11]    Freed, F. and N. Borenstein, "Multipurpose Internet Mail Extensions (MIME) Part Two: Media Types", RFC 2046, November 1996.

[12]    Eastlake, D., Crocker, S. and J. Schiller, "Randomness Recommendations for Security", RFC 1750, December 1994.

[13]    Rosenberg, J. and H. Schulzrinne, "An Offer/Answer Model with SDP", RFC 3264, June 2002.

[14]    Postel, J., "User Datagram Protocol", STD 6, RFC 768, August 1980.

[15]    Postel, J., "DoD Standard Transmission Control Protocol", RFC 761, January 1980. 

[16]    Stewart, R., Xie, Q., Morneault, K., Sharp, C., Schwarzbauer,H., Taylor, T., Rytina, I., Kalla, M., Zhang, L. and V. Paxson,"Stream Control Transmission Protocol", RFC 2960, October 2000.

[17]    Franks, J., Hallam-Baker, P., Hostetler, J., Lawrence, S.,Leach, P., Luotonen, A. and L. Stewart, "HTTP authentication:Basic and Digest Access Authentication", RFC 2617, June 1999.

[18]    Troost, R., Dorner, S. and K. Moore, "Communicating Presentation Information in Internet Messages: The Content-Disposition Header Field", RFC 2183, August 1997.

[19]    Zimmerer, E., Peterson, J., Vemuri, A., Ong, L., Audet, F.,Watson, M. and M. Zonoun, "MIME media types for ISUP and QSIG Objects", RFC 3204, December 2001.

[20]    Braden, R., "Requirements for Internet Hosts - Application and Support", STD 3, RFC 1123, October 1989.

[21]    Alvestrand, H., "IETF Policy on Character Sets and Languages",BCP 18, RFC 2277, January 1998.

[22]    Galvin, J., Murphy, S., Crocker, S. and N. Freed, "Security Multiparts for MIME: Multipart/Signed and Multipart/Encrypted", RFC 1847, October 1995.

[23]    Housley, R., "Cryptographic Message Syntax", RFC 2630, June 1999.

[24]    Ramsdell B., "S/MIME Version 3 Message Specification", RFC 2633, June 1999.

[25]    Dierks, T. and C. Allen, "The TLS Protocol Version 1.0", RFC 2246, January 1999.

[26]    Kent, S. and R. Atkinson, "Security Architecture for the Internet Protocol", RFC 2401, November 1998.30 

 

30 信息索引:

[27]    R. Pandya, "Emerging mobile and personal communication systems," IEEE Communications Magazine, Vol. 33, pp. 44--52, June 1995.

[28]    Schulzrinne, H., Casner, S., Frederick, R. and V. Jacobson, "RTP: A Transport Protocol for Real-Time Applications", RFC 1889, January 1996.

[29]    Schulzrinne, H., Rao, R. and R. Lanphier, "Real Time Streaming Protocol (RTSP)", RFC 2326, April 1998.

[30]    Cuervo, F., Greene, N., Rayhan, A., Huitema, C., Rosen, B. and J. Segers, "Megaco Protocol Version 1.0", RFC 3015, November 2000.

[31]    Handley, M., Schulzrinne, H., Schooler, E. and J. Rosenberg, "SIP: Session Initiation Protocol", RFC 2543, March 1999.

[32]    Hoffman, P., Masinter, L. and J. Zawinski, "The mailto URL scheme", RFC 2368, July 1998.

[33]    E. M. Schooler, "A multicast user directory service for synchronous rendezvous," Master’s Thesis CS-TR-96-18, Department of Computer Science, California Institute of Technology, Pasadena, California, Aug. 1996.

[34]    Donovan, S., "The SIP INFO Method", RFC 2976, October 2000.

[35]    Rivest, R., "The MD5 Message-Digest Algorithm", RFC 1321, April 1992.

[36]    Dawson, F. and T. Howes, "vCard MIME Directory Profile", RFC 2426, September 1998.

[37]    Good, G., "The LDAP Data Interchange Format (LDIF) – Technical Specification", RFC 2849, June 2000.

[38]    Palme, J., "Common Internet Message Headers", RFC 2076, February 1997.

[39]    Franks, J., Hallam-Baker, P., Hostetler, J., Leach, P., Luotonen, A., Sink, E. and L. Stewart, "An Extension to HTTP: Digest Access Authentication", RFC 2069, January 1997.

[40]    Johnston, A., Donovan, S., Sparks, R., Cunningham, C., Willis, D., Rosenberg, J., Summers, K. and H. Schulzrinne, "SIP Call Flow Examples", Work in Progress.

[41]    E. M. Schooler, "Case study: multimedia conference control in a packet-switched teleconferencing system," Journal of Internetworking: Research and Experience, Vol. 4, pp. 99--120, June 1993. ISI reprint series ISI/RS-93-359.

[42]    H. Schulzrinne, "Personal mobility for multimedia services inthe Internet," in European Workshop on Interactive Distributed Multimedia Systems and Services (IDMS), (Berlin, Germany), Mar. 1996.

[43]    Floyd, S., "Congestion Control Principles", RFC 2914, September 2000.

 

 

 

定時器值的表格:

表4:本規范使用的定時器意義機器缺省值:

 

定時器    值    章節    意義

T1    500 ms 缺省    17.1.1.1    預估的RTT時間

T2    4s    17.1.2.2    給非INVITE請求和INVITE應答的最大重傳時間間隔

T4    5s    17.1.2.2    最大網絡傳送消息時間

定時器A    初始值T1    17.1.1.2    INVITE請求重傳間隔,UDP only

定時器B    64×T1    17.1.1.2    INVITE請求超時時間

定時器C    >3分鍾    16.6/11    proxyINVITE請求超時時間

定時器D    >32秒 UDP0 TCP/SCTP    17.1.1.2    應答重發的等待時間

定時器E    初始值T1    17.1.2.2    非INVITE請求重傳間隔,UDP only

定時器F    64×T1    17.1.2.2    非INVITE請求事務超時時間

定時器G    初始值T1    17.2.1    INVITE應答重傳間隔

定時器H    64×T1    17.2.1    等待ACK的時間

定時器I    T4 UDP0 TCP/SCTP    17.2.1    ACK重傳的等待時間

定時器J    64×T1 UDP0 TCP/SCTP    17.2.2    非INVITE請求重傳的等待時間

定時器K    T4 UDP0 TCP/SCP    17.1.2.2    應答重傳的等待時間

 

 

 

感謝書

 

我們感謝IETF MMUSIC和SIP WGs的意見和建議。Ofir Arkin,Brian Bidulock,Jim Buller,Neil Deason,Dave Devanathan,Keith Drage,Bill Fenner, Cedric Fluckiger, Yaron Goland, John Hearty, Bernie Hoeneisen, Jo Hornsby, Phil Hoffer, Christian Huitema, Hisham Khartabil, Jean Jervis, Gadi Karmi, Peter Kjellerstedt, Anders Kristensen, Joanthan Lennox, Gethin Liddell, Allison Mankin, William Marshall, Rohan Mahy, Keith Moore, Vern Paxson, Bob Penfield, Moshe J. Sambol, Chip Sharp, igor Slepchin, Eric Tremblay, Rick Workman 提供了詳細的注釋

 

Brian Rosen 提供了完整的BNF

 

Jean Mahoney 提供了技術寫作協助

 

這個工作是基於 inter alia [41,42]

 

 

 

作者地址

 

這里作者的地址是按照字母順序的,首先是作者,接着是RFC2543原著。所有列出的作者都對本文有着巨大貢獻:

 

 

 

Jonathan Rosenberg

dynamicsoft

72 Eagle Rock Ave

East Hanover, NJ 07936

USA

EMail: jdrosen@dynamicsoft.com

 

Henning Schulzrinne

Dept. of Computer Science

Columbia University

1214 Amsterdam Avenue

New York, NY 10027

USA

EMail: schulzrinne@cs.columbia.edu

 

Gonzalo Camarillo

Ericsson

Advanced Signalling Research Lab.

FIN-02420 Jorvas

Finland

EMail: Gonzalo.Camarillo@ericsson.com

 

Alan Johnston

WorldCom

100 South 4th Street

St. Louis, MO 63102

USA

EMail: alan.johnston@wcom.com

 

Jon Peterson

NeuStar, Inc

1800 Sutter Street, Suite 570

Concord, CA 94520

USA

EMail: jon.peterson@neustar.com

 

Robert Sparks

dynamicsoft, Inc.

5100 Tennyson Parkway

Suite 1200

Plano, Texas 75024

USA

EMail: rsparks@dynamicsoft.com

 

Mark Handley

International Computer Science Institute

1947 Center St, Suite 600

Berkeley, CA 94704

USA

EMail: mjh@icir.org

 

Eve Schooler

AT&T Labs-Research

75 Willow Road

Menlo Park, CA 94025

USA

EMail: schooler@research.att.com

 

 

版權聲明

 

Copyright (C) The Internet Society (2002). All Rights Reserved.

This document and translations of it may be copied and furnished to

others, and derivative works that comment on or otherwise explain it

or assist in its implementation may be prepared, copied, published

and distributed, in whole or in part, without restriction of any

kind, provided that the above copyright notice and this paragraph are

included on all such copies and derivative works. However, this

document itself may not be modified in any way, such as by removing

the copyright notice or references to the Internet Society or other

Internet organizations, except as needed for the purpose of

developing Internet standards in which case the procedures for

copyrights defined in the Internet Standards process must be

followed, or as required to translate it into languages other than

English.

The limited permissions granted above are perpetual and will not be

revoked by the Internet Society or its successors or assigns.

This document and the information contained herein is provided on an

"AS IS" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING

TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING

BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION

HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF

MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.

 

 

Acknowledgement

 

Funding for the RFC Editor function is currently provided by the

Internet Society.


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM