RFC3261(8 一般用户代理行为)

    一个用户代理代表了一个终端系统。它包含一个用户代理客户端(UAC),用来产生请求;以及一个用户代理服务端(UAS),用来响应请求的。UAC可以由一些外部的触发来发出请求并处理应答(比如用户按了一个按钮,或者按下了电话键产生一个音频信号等等)。UAS能够接收请求,并且产生应答,它可以根据用户输入,外部触发,程序执行结果或者其他什么机制来产生应答。

    UAC发送一个请求,这些请求可能通过一些PROXY(代理服务器)传递到UAS上。当UAS产生一个应答,那么这个应答就会同样的被传送到UAC。UAC和UAS的处理流程强烈的基于两个要素。第一,基于请求或者应答是否在一个对话内,第二,基于请求的方法。对话的详细描述在第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节中j介绍。出于隐私和便利的原因而把这些字段的值设置成为同一个值并不太合适(尤其是如果初始的UA期望Request-URI会在传输中改变的话)。

    在一些特定的情况下,预先设置的路由表会影响消息中的Request-URI。一个预置路由表是由一串服务器的URI组成,这些服务器是UAC往外发送对话外请求所需要经过的。通常,他们是由用户或者服务提供商手工在UA上设置的,或者通过一些非SIP的方法自动设置。当一个提供商希望配置一个出口代理(服务器)给一个UA时,我们强烈建议通过预置一个单个URI的路由表的方式来实现,这个单个路由就是出口proxy。

    当要使用预置路由表时,必须提供Request-URI和Route头域(在12.2.1.1节中有详细描述)(甚至在没有对话存在的时候也必须提供),并且把Request-URI当作远端目标URI。

8.1.1.2 TO

    To头域是第一个并且也是最先指定的请求的”逻辑”接收地,或者是这个请求的用户AOR或者资源。这个域内的地址可以是也可以不是请求的最终接收者。TO头域可以用SIP或者SIPS URI,也可以用其他方式的URI(比如tel(电话)URL (RFC2806[9]))。所有的SIP实现必须支持SIP URI的实现。任何支持TLS的实现必须支持SIPS URI的实现。To头域允许有一个显示用的姓名。

    UAC可以通过无数的方法来在一个给定请求的时候该如何填写TO头域。通常用户会建议采用人机交互界面来输入To头域,可能是通过手工输入这个URI或者从地址簿中选择。但是,用户通常并不输入完整的uri, 而是输入一个简单的字母或者数字组成的串(比如”bob”)。由UA来判断用户输入的这个到底是哪个URI。将用户输入的字串作为用户URI的一分部,意味着用户希望对符号”@”右边的名称进行域名查询(比如sip:bob@example.com)。如果用户输入的字符串作为SIPS URI的一部分,意味着UA希望通讯能够安全,且对符号”@”右边的名称进行域名解析。这个右边的名字通常是请求方的主机所在域名,这个主机允许处理外发请求。这个很像”快速拨号”的机制,这个机制要求请求者自身的主机能够解释这个快速拨号一样。

    如果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头域表明了请求发起者的逻辑标志,可能是用户的AOR。就像To头域一样,From头域也包含一个URI并且可以包含一个显示名。SIP可以用这个头域来实现对请求的检查和选择一个规则进行对请求的处理(比如,自动的呼叫拒绝)。同样的,因为From头域包含的是逻辑名字,所以From URI也可以不包含IP地址或者UA对应的主机名字FQDN。

    From头域可以包含一个显示名。在客户身份隐藏的情况下,一个UAC应该使用显示名字”Anonymous”,连通一个语法正确,但是没有意义的URI(比如:sip:thisis@anonymous.invalid)。

    通常,用户或者用户的本地主机的管理人员会事先配置请求头域中的From头域的值。如果给定的UA是多个用户共同使用的,那么必须有一个匹配当前用户身份的预配置的URI,这样才能够切用户的预配置信息。收到请求的服务方可以根据这个用于分辩身份的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发起一个新的请求,UAC为Call-ID头域产生一个全局(在时间和空间上都是)唯一的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域。协议名字和协议版本必须分别是SIP和2.0。Via头域必须包含一个branch参数。这个参数用于区分请求创建的事务。这个参数客户端和服务器都会使用。

    除了CANCEL和给非2xx应答的ACK以外,branch参数对UA发出的所有的请求来说,在时间和空间上必须唯一。在下边的讲解中,CANCEL请求的branch参数必须和他所取消的请求的branch参数一致。在17.1.1.3节中讲述了给non-2xx应答的ACK必须和其对应的的INVITE请求有相同的branch值。

    利用branch参数的唯一性来作为事务的ID(transaction ID),并非RFC 2543的一部分。根据本规范产生的branch必须用”z9h64bK”开头。这7个字母是一个乱数(定义成为7位的是为了保证旧版本的RFC2543实现不会产生这样的值),这样服务器收到请求之后,可以很方便的知道这个branch是否由本规范所产生的(就是说,全局唯一的)。在这样的要求下,精确的branch的格式必须事先有实现的定义。

    Via头域中,maddr,ttl,和sent-by属性会在传输层处理请求的时候设置(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必须是有效的,甚至后续请求是对话外的。

    如果Request-URI或者Route头域最上层的URI包含了SIPS URI,Contact头域也必须是一个SIPS URI。在20.10节有更进一步的说明。

8.1.1.9 Supported 和 Require

    如果UAC支持能够被服务端响应的SIP扩展请求,UAC应该在请求的时候包含一个Supported头域列出那些SIP扩展的选项标签(option tags)(19.2节)。

    option tags中出现的扩展必须遵循RFCs的标准。这样可以防止客户端实现非标准的、自定义的应用,而这些应用会被服务端拒绝。请求中的Supported头域不能使用试验性质或者说明性质的RFCs文档中定义的扩展,这也是由于这些文档经常使用自定义的扩展。

    如果UAC要求UAS能够支持扩展,以便UAS能够处理UAC的特定请求,那么它必须在请求头中增加一个Require头域来说明处理本特定请求需要什么样的一个扩展option tags。如果UAC需要请求经过的所有proxy都支持它发出的某个请求的扩展部分,它必须增加一个Proxy-Require头域来说明需要Proxy支持何种option tag扩展。

    如同在Supported头域指出的,Require和Proxy-Require头域中的option tag必须限定于RFCs的标准扩展。

8.1.1.10 附加信息部分

    在一个新请求创建以后,以上的头域都被正确初始化了以后,就可以为这个请求增加它所需要的附加头域了。SIP请求允许包含一个MIME-encoded的消息体。无论请求包含哪种消息体,都必须引入头域来指出这个消息体的类型,及该消息体的其他说明。关于这些头域的详细说明,请参见20.11节到20.15节。

8.1.2 发送一个请求

    接下来,开始确定请求发送的目标。除非有其他的特定说明,目标必须是通过DNS来查找的(参见[4]描述的流程)。如果路由表(route set)中的第一个元素表明这是一个严格路由(在12.2.1.1节中讲述),那么请求的Request-URI中必须应用该处理流程。否则,这些过程在请求中被应用于Route头域的第一个值(如果存在),或者请求的Request-URI中(如果Route头域不存在)。这样一些过程产生了一系列的地址,端口,和用于传输的传输器。无论这个[4]中描述的流程输入哪个URI,如果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必须把所有的无法识别的临时的非100应答当作是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增长了,客户端可以对这个URIs,用任意顺序来产生新的请求。常用机制是通过在Contact头域的值中设置”q”参数来指定这个顺序。对这些URIs的请求可以是并行产生的也可以是串行产生的。有一个实现是按照q参数值递减的方法顺序处理URIs,并且对相同q参数值的URIs进行并行处理。还有一种就是直接按照顺序的方法处理URIs,对于q参数值不同的按照递减的顺序处理,对于q参数值相同的按照随机顺序处理。

    如果发送给联系表上的地址失败了,(在下一段我们有定义),那么就选择联系列表中的下一个地址进行发送,直到列表全部遍历一遍。如果列表遍历完了还没有,那么就表示请求失败了。

    通过响应码(大于399)我们可以知道请求的失败;对于网络错误来说,客户端transaction事务层会给TU报告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方法来绑定它的AOR到一个特定的联系地址,将会收到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)的请求。从网络的中央转发到最边缘,转发机制可以适用于跨度很大的网络。

    重定向服务器在逻辑上由transaction层和TU组成,该TU可以访问某些类型的位置服务(参见第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,甚至是不认识的方法名),并且处理上成问题的重定向请求。

posted @ 2012-11-04 18:48  坐看风起云涌  阅读(502)  评论(0编辑  收藏  举报