RFC6120
"本文的英文原文来自RFC 6120
互联网工程任务组(IETF) | P. Saint-Andre |
申请讨论: 6120 | Cisco |
取代: 3920 | 2011年3月 |
类别: 标准跟踪 | |
ISSN: 2070-1721 |
- 可扩展的消息和出席信息协议 (XMPP): 核心协议
摘要
- 可扩展的消息和出席信息协议(XMPP)是一个XML应用,让任何两个或多个网络实体之间进行结构化和可扩展的准实时信息交流. 本文定义了XMPP的核心协议方法: XML流的配置和解除, 通道加密, 验证, 错误处理, 以及消息通讯基础, 网络可用性 ("presence"), 和 请求-应答 交互. 本文取代了 RFC 3920.
本文的状态
- 这是一个互联网标准跟踪文档.
- 本文是互联网工程工作组(IETF)的一个成果. 它代表了IETF社区的一致意见. 它已经公开审核并由互联网工程控制组(IESG)批准发布了. 更多关于互联网标准的信息请参见RFC 5741第2章.
- 关于本文当前状态的信息, 任何错误, 以及如何对它提出反馈,请到 http://www.rfc-editor.org/info/rfc6120 .
版权通知
- Copyright (c) 2011 IETF Trust and the persons identified as the document authors. All rights reserved.
- This document is subject to BCP 78 and the IETF Trust's Legal Provisions Relating to IETF Documents (http://trustee.ietf.org/license-info) in effect on the date of publication of this document. Please review these documents carefully, as they describe your rights and restrictions with respect to this document. Code Components extracted from this document must include Simplified BSD License text as described in Section 4.e of the Trust Legal Provisions and are provided without warranty as described in the Simplified BSD License.
序论
概述
可扩展的消息和出席信息协议(XMPP)是一个可扩展标记语言XML应用,让任何两个或多个网络实体之间进行结构化和可扩展的准实时信息交流. 本文定义了XMPP的核心协议方法: XML流的配置和解除, 通道加密, 验证, 错误处理, 以及消息通讯基础, 网络可用性 ("presence"), 和 请求-应答 交互.
历史
XMPP的基本语法和语义最开始是由Jabber开源社区开发的, 主要是在1999年. 2002年, 根据 IMP‑REQS ,XMPP工作组被允许基于Jabber协议开发一个适合IETF的即时消息和出席信息技术. 到了2004年10月, 发布了 RFC3920 和 RFC3921 , 意味着那时候XMPP的主要定义完成了.
从2004年开始,互联网社区已经获得了广泛的XMPP实现和布署经验, 包括XMPP标准基金会(XSF)主持下开展的正式的互操作性测试. 本文全面整合了从软件开发者和XMPP服务提供者得到的反馈, 包含了一系列向后兼容的修改,见 附录D . 结果是, 本文反映了互联网社区对于XMPP1.0核心功能的初步共识, 因此废止了RFC 3920.
功能汇总
这个不规范的章节提供了一个方便开发者的XMPP功能汇总; 接下来的其他章节则是XMPP的规范定义.
XMPP的目标是允许两个(或多个)实体通过网络来交换相关的小件结构化数据(所谓"XML节"). XMPP典型地使用分布式的 客户端-服务器 体系结构来实现, 这里客户端需要连接到一个服务器以获得对网络的访问,从而被允许和其他实体(可能在其他服务器上)交换XML节. 一个客户端连接到一个服务器,交换XML节,以及结束连接,这样的流程如下:
- 确定要连接的IP地址和端口号, 典型的做法是对一个合格的域名做出解析( 3.2 )
- 打开一个传输控制协议 TCP 连接
- 通过TCP打开一个XML流 4.2
- 握手最好使用传输层安全性 TLS 来进行通道加密( 5 )
- 使用简单验证和安全层 SASL 机制来验证 ( 6 )
- 绑定一个资源到这个留上 ( 7 )
- 和其他网络上的实体交换不限数量的XML节( 8 )
- 关闭XML流 ( 4.4 )
- 关闭TCP连接
在XMPP中, 一个服务器可以选择性地连接到另一个服务器以激活域间或服务器间的通讯. 这种情形下, 两个服务器需要在他们自身之间建立一个连接然后交换XML节; 这个过程所做的事情如下:
- 确定要连接的IP地址和端口号, 典型的做法是对一个合格的域名做出解析( 3.2 )
- 打开一个TCP连接
- 打开一个XML流 4.2
- 握手最好使用TLS来进行通道加密( 5 )
- 使用简单验证和安全层 SASL 机制来验证 ( 6 ) *
- 交换不限数量的XML节,可以服务器之间直接交换,也可以代表每台服务器上的相关实体来交换,例如那些连到服务器上的客户端 ( 8 )
- 关闭XML流 ( 4.4 )
- 关闭TCP连接
- 互操作性提示: 在本文写就的时候, 大多数已布署的服务器仍使用服务器回拨协议 XEP‑0220 来提供弱身份验证,而不是使用SASL的 PKIX证书来提供强验证, 特别在这些情况下,SASL握手无论如何将不会得到强验证 (例如, 因为TLS握手没有被对方服务器强制要求, 或因为当TLS握手时对方服务器提供的PKIX证书是自签名的并且之前没有被接受过); 细节请见 XEP‑0220 . 本文的解决方案显然提供了一个更高级别的安全性 (参见 13.6 ).
本文指定了客户端如何连接到服务器以及基本的XML节语义. 然而, 本文不定义一个连接成功建立之后可能用来交换的XML节的"载荷"; 反之, 那些载荷被定义在各种XMPP扩展之中. 例如, XMPP‑IM 定义了基本的即时消息和出席信息功能的扩展. 另外, XSF创造了各种扩展协议,即XEP系列 XEP‑0001 ,也为广泛的应用程序定义了扩展.
术语
本文中的关键字 "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", 和 "OPTIONAL" 的解释参见RFC 2119 关键字 .
特定的安全相关的术语的含义参见 安全术语 ; 这些术语包括但不限于, "assurance", "attack", "authentication", "authorization", "certificate", "certification authority", "certification path", "confidentiality", "credential", "downgrade", "encryption", "hash value", "identity", "integrity", "signature", "self-signed certificate", "sign", "spoof", "tamper", "trust", "trust anchor", "validate", and "verify".
特定的和证书,域名,应用服务身份相关的术语参见 TLS‑证书 ; 这包括但不限于, "PKIX certificate", "source domain", "derived domain", 以及身份类型 "CN-ID", "DNS-ID", 和 "SRV-ID".
其他安全相关的术语定义于参考协议中 (例如, "denial of service" (拒绝服务)定义于 DOS 或 "end entity certificate" (终端实体证书)定义于 PKIX ).
术语 "whitespace" (空格) 用于指代 XML 中任何匹配"S"的字符或字符串, 也就是说, 一个或多个满足 ABNF 定义的SP, HTAB, CR, 或 LF 规则的实例.
术语 "localpart" (本地部分), "domainpart" (域部分), 以及 "resourcepart" (资源部分)定义于 XMPP地址 .
术语 "bare JID" (纯JID) 指代一个格式为 <localpart@domainpart> (对于一个位于某个服务器上的帐户而言) 或 <domainpart> (对于一个服务器而言) 的XMPP地址.
术语 "full JID" (全JID) 指代一个格式为 <localpart@domainpart/resourcepart> (对一个典型的已授权客户端或和某个帐号相关的设备而言) 或 <domainpart/resourcepart> (对于一个典型的资源或和某个服务器相关的文字)的XMPP地址.
术语 "XML stream" (也称为 "stream" (流)) 定义于 4.1 .
术语 "XML stanza" (也称为 "stanza" (节)) 定义于 4.1 . 有三种 stanzas(节): message, presence, 和 IQ ("Info/Query"的简称). 这些通讯原语分别定义于 8.2.1 , 8.2.2 , 和 8.2.3 .
术语 "originating entity" (原实体)指的是第一次生成一个发送到XMPP网络的stanza(节)的实体(例如, 一个已连接的客户端, 一个附加的服务, 或一个服务器). 术语 "generated stanza" (生成的节)值的是生成的节那个节.
术语 "input stream" (输入流)指定这样一个XML流,服务器通过这个流从一个已连接的客户端或远端服务器接收数据, 而术语 "output stream" (输出流)指定这样一个流,服务器通过这个流发送数据到一个已连接的客户端或远程服务器. 以下术语指定一些动作,处理从输入流收到的数据时服务器可以执行这些动作:
route(路由): 传递数据到一个远端服务器让它自行处理或最终递送到一个和远端服务器关联的客户端 deliver(递送): 传递数据到一个已连接的客户端 ignore(忽略): 丢弃数据不做任何处理或返回一个错误给发送者sender
当术语 "ignore" (忽略)用于客户端处理收到的数据时, 短语 "without acting upon it" (不做任何处理)明确的包括不展示任何数据给使用者(人).
接下来的 "XML符号" 被 IRI 用于展示无法用仅用ASCII码呈现的字符, 本文的一些例子使用了类似 "&#x...." 的格式来表现 UNICODE 字符串 (例如, 字符串 "ř" 表示Unicode字符 LATIN SMALL LETTER R WITH CARON); 这种形式是绝对不会在XMPP系统将通过网络发送的.
和 URI 展现统一资源定位符的规则一样, XMPP地址文本也是用 '<' 和 '>' 括起来的(尽管基本上它们不属于 URIs).
例如, 被括起来的行是用来提高可读性的, "[...]" 表示省略, 并且还是用了以下预定义字符串 (这些预定义的字符串不会通过网络发送出去):
- C: = 客户端
- E: = 任何XMPP实体
- I: = 发起实体
- P: = 对端服务器
- R: = 接收实体
- S: = 服务器
- S1: = 服务器1
- S2: = 服务器2
读者需要注意这些例子不包括细节, 并且例子里的一些协议流程中, 展示的备用步骤不一定是由前一个步骤发送的确切的数据触发的; 本文或常用参考文档中的协议规范所用到的所有用例里面提供的例子都遵从上述规则. 所有例子都是虚构的并且交换的信息 (例如, 用户名和密码) 不代表任何现存的用户和服务器.
体系结构
XMPP提供一种异步的端到端的结构化数据交换技术,在一个分布式的可全球寻址和出席信息感知的客户端和服务器的网络中使用直接的持久XML流。这种体系结构形式包含了普遍的网络可用性的知识,以及在给定的客户端-服务器和服务器-服务器会话的时候,不限数量的并发信息交易的概念,所以我们把它称为 "并发交易可用性" ("Availability for Concurrent Transactions") (简称ACT) 来把它和来自WWW的 "Representational State Transfer" REST 体系结构形式区别开. 尽管XMPP的体系结构很大程度上类似于 email (参见 EMAIL‑ARCH, 它引入了一些变化以便于准实时通讯. ACT体系结构形式的独特特性如下.
全局地址
和email一样, 为了通过网络路由和递送消息,XMPP使用全球唯一地址(基于DNS). 所有XMPP实体可以在网络上被寻址, 大部分客户端和服务器以及很多外部服务可以被客户端和服务器访问. 通常, 服务器地址的格式为 <域部分> (例如, <im.example.com>), 属于某台服务器的帐号的格式为 <本地部分@域部分> (例如, <juliet@im.example.com>, 称为 "纯JID"), 而连接到一个特定的设备或资源并且已经被(服务器)授权可以和外部交互的客户端的格式为 <本地部分@域部分/资源部分> (例如, <juliet@im.example.com/balcony>, 称为 "全JID"). 因为历史原因, XMPP地址常被称为Jabber IDs 或 JIDs. 因为XMPP地址格式的正式规范依赖于国际化技术(本文撰写时正在制定中),这个格式定义于 XMPP‑ADDR 而非本文之中. 术语 "localpart"(本地部分), "domainpart"(域部分), 和 "resourcepart"(资源部分) 正式定义于 XMPP‑ADDR .
出席信息
XMPP让一个实体能够向其他实体声明它的网络可用性或者 "presence"(出席信息) . 在XMPP中, 这种可通讯状态是用端到端的专用通讯元素来标识的: 即 <presence/> 节. 尽管网络可用性对于XMPP消息交换并不是必需的, 它还是可以促进实时交互,因为消息发起者可以在发消息之前知道接收者在线并处于可通讯状态. 端到端的出席信息定义于 XMPP‑IM .
持久流
每个点对点的一"跳"都建立了基于TCP长连接的持久XML流来保持可通讯状态. 这些 "always-on" 客户端-服务器 和 服务器-服务器 流使得任何时间每方都能够推送数据到另一方并且立即路由和递送. XML流定义于 4 .
结构化数据
XMPP中基本的协议数据单元不是一个XML流 (它只是为点对点通讯提供传输层) 而是一个 XML 节("stanza"), 它是一个通过流发送的XML片段. 一个节的根元素包括路由属性 (类似 "from" 和 "to" 地址), 而节的子元素包含了递送给目标接收者的载荷. XML节定义于 8 .
客户端和服务器的分布式网络
在实践之中, XMPP是一个包含了很多互相通讯的客户端和服务器的网络(当然, 任何两个给定的布署服务器之间的通讯都是严格谨慎的并且也和本地服务策略有关). 因此, 例如, 与服务器 <im.example.com> 关联的用户 <juliet@im.example.com> 能够和服务器 <example.net> 关联的用户 <romeo@example.net> 交换消息,出席信息和其他结构化数据. 这个模式对使用全局地址的消息协议是很常见的, 例如email网络 (见 SMTP 和 EMAIL‑ARCH . 结果, 在XMPP中端到端的通讯是逻辑上的点对点,而物理结构则是 客户端-服务器-服务器-客户端, 如下图所示.
图1: 分布式客户端-服务器 体系结构
example.net <--------------> im.example.com ^ ^ | | v v romeo@example.net juliet@im.example.com
- 提示性备注: 体系结构使用 XML流 和 XML节 ,但是两个客户端之间直接建立端到端的连接则使用基于 LINKLOCAL 的技术, 不过那个体系结构没有定义在本协议之中,它只能说是 "类XMPP"; 详见 XEP‑0174 . 另外, XML流可以在任何可靠的传输层上简历端到端的连接, 包括XMPP本真的扩展; 无论如何, 这些方法没有包含在本文之中.
以下段落描述客户端和服务器们各自在网络中负责什么.
一个客户端就是一个实体,它先和它的注册帐号所在服务器建立XML流 (通过 SASL握手 ) , 然后完成 资源绑定 , 这样就能通过建好的流在客户端和服务器之间递送XML节. 客户端使用 XMPP 来和它的服务器, 其他客户端以及任何其他网络上的实体通讯, 这里服务器负责递送节到同一台服务器上其他已连接的客户端,或把它们路由到远程服务器上. 一个服务器上的注册帐号可以同时使用多个客户端连接到一台服务器上, 这里每个客户端的XMPP地址的 资源部分 是不同的 (例如, <juliet@im.example.com/balcony> 和 <juliet@im.example.com/chamber>), 定义于 XMPP‑ADDR 和 7
一个服务器是一个实体,主要负责以下事项:
取决于服务器的不同, 一个XMPP服务器的次要责任可能包括:
TCP绑定
范围
如本文定义的XMPP所述, 一个发起方实体在 (客户端或服务器) 和接收方实体协商XML流之前必须(MUST) 打开一个到接收方实体 (服务器) 的 TCP 连接. 然后在使用XML流期间双方一直保持那个TCP连接. 这个规则在下面章节中的TCP绑定要用到.
- 提示性备注: 不一定要把XML流建立在TCP上, 其他传输协议也是可以的. 例如, 两个实体可以通过 HTTP 互相连接(定义于 XEP‑0124 和 XEP‑0206 . 无论如何, 本协议只定义把XMPP绑定到TCP.
合格的全域名解析
因为XML流是是通过TCP发送的, 发起方实体在尝试打开一个XML流之前需要确定接收方实体的IPv4或IPv6地址(以及端口). 一般来说这是通过解析接收方实体的合格的全域名(简称FQDN,参见 DNS概念 )来实现的.
首选流程:SRV查询
FQDN解析的首选流程是如下使用 DNS‑SRV 记录:
- 发起方实体构造一个 DNS SRV 查询,参数如下:
- 一个 "xmpp-client" (用于 客户端-服务器 连接) 或 "xmpp-server" (用于 服务器-服务器 连接)服务
- 一个 "tcp" 协议
- 一个对应发起方实体希望连接的XMPP服务的 "原有域"( TLS‑CERTS )的名字 (例如, "example.net" 或 "im.example.com")
- 得到一个类似 "_xmpp-client._tcp.example.net." 或 "_xmpp-server._tcp.im.example.com." 的查询.
- 如果收到应答, 它将包含一个或多个FDQN和端口的组合, 每个都拥有权重和优先级 (如 DNS‑SRV 所述). (无论如何, 如果SRV查询的结果是一个单独的资源记录 ".", 即根域名, 那么发起方实体必须(MUST)在这时终止SRV处理,因为根据 DNS‑SRV ,这样一个结果意味着那个服务在本域中是不可用的)
- 发起方实体至少选择返回的FQDNs记录中的一个来解决 (根据 DNS‑SRV 规则,对FDQN执行 DNS "A" 或 "AAAA" 查询; 这将返回一个 IPv4 或 IPv6 的地址.
- 成功解析FDQN(包括SRV查询返回的相应的端口号)之后,发起方实体就使用IP地址来连接接收方实体.
- 如果发起方实体使用那个IP地址连接失败,而的 "A" 或 "AAAA" 记录查询返回了不止一个IP地址, 那么发起方实体使用那个FDQN的下一个解析好的IP地址作为连接地址.
- 如果发起方实体用给定的FDQN的所有解析出来的IP地址都无法连接, 那么它重复解析过程并使用基于优先级和权重的SRV查询(定义于 DNS SRV )返回的下一个FQDN来连接.
- 如果发起方实体接收到它的SRV应答但是无法使用接收到的应答数据来建立一个XMPP连接, 它不应该(SHOULD NOT)尝试下面描述的备用流程(这有助于防止入站和出站连接状态不匹配).
- 如果发起方实体不能从它的SRV查询接收到应答, 它应该(SHOULD)尝试下一节描述的备用流程.
后备流程
后备流程应该(SHOULD)是一个常规的 "A" 或 "AAAA" 地址记录解析以决定原始域的IPv4或IPv6地址, 而端口则为 "xmpp-client" 端口(5222)用于客户端-服务器连接或 "xmpp-server" 端口(5269)用于服务器-服务器连接 (这些是在IANA注册的缺省端口,14.7 .
如果通过TCP连接不成功, 发起方实体可能尝试找到并使用替代连接方法例如HTTP绑定 (见 XEP‑0124 和 XEP‑0206 , 它可能使用 DNS‑TXT 记录(参见 XEP‑0156) 来搜索.
什么时候不用SRV
如果发起方实体已经被显式地配置为一个关联到接收实体的原始域的一个特定的FQDN (以及潜在的端口) (比如,一个特定的原始域 example.net "写死" 到一个配置好的 apps.example.com 的 FQDN), 鼓励初始方实体使用配置好的名字而不是建议的对原始域的SRV解析流程.
附加服务使用SRV记录
很多XMPP服务器以可托管附加服务 (超出本文和 XMPP-IM 定义范围) 这种方式来实现,附加服务的DNS域名典型的形式是主XMPP服务的 "子域名" (例如, conference.example.net 用于 XEP‑0045 服务,相关的XMPP主服务为 example.net ) 或 底层服务的一级域名的 "子域名" (例如, muc.example.com 用于 XEP‑0045 服务,相关的XMPP主服务为 im.example.com ). 如果一个和远程XMPP服务关联的实体希望连接到这样一个附加服务上, 它将生成一个适当的XML节,而远程服务器将尝试通过一个SRV查询资源记录类似 "_xmpp-server._tcp.conference.example.net." 或 "_xmpp-server._tcp.muc.example.com." 来解析该附加服务的DNS域名. 所以, 如果一个XMPP服务的管理员希望让远程服务器相关的实体能访问这样的附加服务, 除了用于他们的主XMPP服务的 "_xmpp-server" 记录之外, 他们还需要声明适当的 "_xmpp-server" SRV 记录. 当 SRV 记录不可用的时候, 可使用后备方法 3.2.2 来为附加服务解析域名.
重连
XMPP服务器可能在会向连接的客户端和远端服务器提供TCP连接服务的时候意外掉线. 因为这些连接的数量可能非常大, 实体的重连机制寻求解决重连可能导致的对软件性能的冲剂和网络堵塞. 如果实体选择重连, 它:
- 应该把重连之前等待的秒数设置为0到60之间 (这有助于确保不会所有实体在掉线的同一个时间间隔之后同时尝试重连).
- 如果第一次尝试重连没有成功则随后的尝试重连的时间间隔应该越来越长 (例如, 按照 ETHERNET 描述的 "动态二进制指数后退算法" ).
建议重连的时候使用TLS会话恢复 TLS‑RESUME . 本文未来的某个版本, 或某个独立的协议, 可能会提供更多详细的关于加速重连过程的方法的指南.
可靠性
在XMPP使用常连的TCP连接意味着通过XML流发送的XML节可能不可靠, 因为长连的TCP的各方可能无法及时地了解掉线情况. 在XMPP应用层, 长连接掉线可能导致无法发送节. 尽管本文定义的核心XMPP技术未包括克服这一可靠性缺陷的特性, 有一个XMPP扩展在做这件事 (例如, XEP‑0198 ).
XML流
流基础
两个基本概念,使得XMPP实体之间的小的结构化信息有效载荷能快速地进行异步交换:XML流和XML节。这些术语的定义如下。
- XML流的定义:
- XML流是一个容器,用于任何两个实体通过网络进行XML元素的交换. XML流的开始明确表达为一个打开的 "流头" (即, 一个包含了适当树形和命名空间声明的 XML <stream> 标签), 而这个XML流的结尾明确表达为一个关闭的XML </stream> 标签. 在流的生存期间, 发起方实体可以通过这个流发送不限数量的XML元素, 这些元素或用来协商这个流 (例如, 完成 TLS协商 或 SASL协商 ) 或用于 XML节. "发起流" 是从发起方实体 (通常是一个客户端或服务器) 到接收方实体 (通常是一个服务器), 也可视为对应发起方 "连接到" 或 "和......开启会话" 接收方实体. 发起流允许从发起方实体到接收方实体的单向通讯; 为了让接收方实体能够向发起方实体发送节, 接收方实体必须(MUST) 协商一个相反的流 ("应答流").
- XML节的定义:
- XML节是一个XMPP中的基本语义单位. 一个节就是一个第一层元素 (在流的深度=1),它的元素名是 "message", "presence", 或 "iq" ,而它的合格命名空间是 'jabber:client' 或 'jabber:server'. 相比之下, 任何其他命名空间限定的第一层元素都不是一个XML节 (stream errors, stream features, TLS相关的元素, SASL相关的元素, 等等.), 由'jabber:client' 或 'jabber:server' 命名空间限定的 <message/>, <presence/>, 或 <iq/> 元素但不在第一层 (例如, 包含在一个扩展元素中的 <message/> 元素 ( 做报告用的 8.4 )也不是一个XML节, 不是命名空间 'jabber:client' 或 'jabber:server'限定的 <message/>, <presence/>, 或 <iq/> 元素也不是一个XML节. 一个XML节典型的包含一个或多个必要的子元素 (以及相关的属性, 元素, 和 XML 字符串数据) 来传达所需的信息, 子元素可以(MAY)使用任何XML命名空间 (见 XML‑NAMES 和本协议的 8.4).
有三种节: message, presence, 和 IQ ("Info/Query"的缩写). 这些节类型提供三种不同的通讯原语: 一个 "推送" 机制用于已生成的消息, 一个特定的 "发行-订阅" 机制用于广播网络可用性信息, 和一个 "请求-应答" 机制用于更结构化的数据交换 (类似 HTTP . 更多解释分别位于 8.2.1 , 8.2.2 , 和 8.2.3 .
考虑一个客户端连接到一个服务器的例子. 客户端通过发送一个流头来发起一个XML流到服务器, 最好在前面加上一个XML声明来指定XML版本和支持的字符串编码 (见 11.5 和 11.6 ). 遵循本地策略和服务设置, 该服务器接着以第二个XML流应答回客户端, 最好再次在前面加上一个XML声明. 一旦客户端完成 SASL协商 和 资源绑定 , 该客户端就能通过这个流来发送不限数量的XML节. 当客户端想要关闭这个流的时候, 它只要简单的发送一个关闭 </stream> 标签给服务器,如 4.4 .
基本上, 接着, 一个XML流功能作为会话期间发送的XML节的信封而另一个XML流功能作为会话期间接收的XML节的信封. 我们可以如下做一个简单的展示.
图2: 两个流的简单视图
+--------------------+--------------------+ | INITIAL STREAM | RESPONSE STREAM | +--------------------+--------------------+ | <stream> | | |--------------------|--------------------| | | <stream> | |--------------------|--------------------| | <presence> | | | <show/> | | | </presence> | | |--------------------|--------------------| | <message to='foo'> | | | <body/> | | | </message> | | |--------------------|--------------------| | <iq to='bar' | | | type='get'> | | | <query/> | | | </iq> | | |--------------------|--------------------| | | <iq from='bar' | | | type='result'> | | | <query/> | | | </iq> | |--------------------|--------------------| | [ ... ] | | |--------------------|--------------------| | | [ ... ] | |--------------------|--------------------| | </stream> | | |--------------------|--------------------| | | </stream> | +--------------------+--------------------+
那些习惯于把XML当成一个文档中心的方式的人可能会发现下面的类比是有益的:
无论如何, 这些描述只是类比, 因为XMPP不处理文档和片段而是处理流和节.
本节的其余部分定义XML流(连同相关主题)的以下几个方面:
打开流
连接到接收方实体的适当的IP地址和端口之后, 发起方实体通过发送一个流头 ("发起流头") 来打开到接收方实体的流.
I: <?xml version='1.0'?> <stream:stream from='juliet@im.example.com' to='im.example.com' version='1.0' xml:lang='en' xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams'>
然后接收方实体通过发送一个它自己的流头 ("应答流头") 来回复发起方实体.
R: <?xml version='1.0'?> <stream:stream from='im.example.com' id='++TR84Sm6A3hnt3Q065SnAbbk3Y=' to='juliet@im.example.com' version='1.0' xml:lang='en' xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams'>
接着实体们就可以在进行流协商过程的其他步骤了.
流协商
基本概念
因为流的接收方实体可以说是它所服务的域的看门人, 它对客户端或对端服务器的发起的连接有一定的条件要求. 最低程度, 发起方实体在接收方实体被允许发送节之前需要验证接收方实体, (对客户-服务器流来说这意味着使用 6 中描述的SASL). 无论如何, 接收方实体可以考虑其他验证条件来强制协商, 例如使用 5 描述的TLS加密. 接收方实体通知发起方实体这些条件通过 "stream features": 发起方需要在接收方接受它发送的XML节之前完成的一系列特别的协议交互, 以及任何自愿协商但是可以提高XML流处理的协议交互 (例如, XEP‑0138 描述的建立应用层压缩).
连接条件的存在意味着流需要协商. 层的顺序 ( 13.3 描述的的TCP, 然后是TLS, 然后是SASL, 然后是XMPP) 意味着流协商是一个多级的过程. 进一步的结构由两个因素来施加: (1) 一个给定的流特性可以仅对特定的实体提供,或只在特定的其他特性已经被协商之后提供 (例如, 资源绑定仅在SASL验证之后提供), 和 (2) 流特性可能是强制协商也可能是自愿协商. 最后, 基于安全的原因一个流的参与者们在成功地完成用于特定特性的协议交互之后需要忘掉它们在协商过程中获得的知识 (例如, 在所有情况下的TLS和当可能建立一个安全层的情况下的SASL, 如有关SASL机制的规范所述). 使用刷新旧的流上下文和通过现有的TCP连接交换新的流头就可以做到这一点.
流特性格式
如果发起方实体包含在发起流头里的 'version' 属性值设为不低于 "1.0" (见 4.7.5 ), 接收方实体在发送应答流头之后必须发送一个<features/> 子元素 (通常使用 4.8.5 描述的流命名空间前缀作为前缀) 给发起方实体以声明使流协商过程继续下去的任何条件. 每个条件用 <features/> 元素的子元素的格式, 由一个不同于流命名空间和内容命名空间的命名空间来限定. <features/> 元素可以包含一个字元素,多个子元素,或者为空.
- 实现备注: 包含在任何给定的<features/>元素的子元素的顺序不重要.
如果一个特殊的流特性是或者可以是强制协商的, 那个特性的定义需要做以下几件事之一:
- 声明这个特性总是强制协商的 (例如, XMPP客户端的资源绑定就是这样的); 或
- 为接收方实体指定一个方法来标记这个特性在这次交互中为强制协商 (例如, 对于 STARTTLS, 这是通过包含一个空的 <required/> 元素到流特性广告中来实现的, 但是对所有流特性的通用格式); 建议用于新的强制协商特性的流特性定义如 STARTTLS 所做的那样通过包含一个空的 <required/> 元素来实现.
- 提示性备注: 因为没有通用格式来说明一个特性是强制协商的, 有可能会出现一个发起方实体不能理解的特性而被接收方认为是强制协商的, 而导致流协商过程失败. 尽管这样一个结果是不可取的, 本工作组认为不需要通用格式的情况是很罕见的.
基于安全性的原因, 对于必要的特定流特性,在该特性协商成功之后,发起方将发送一个新的发起流头 (例如, 任何情况下的TLS和当建立了安全层的情况下的SASL). 如果一个给定的流特性出现在这种情况下, 那个特性的定义需要指定该特性协商之后的流重启.
一个包含至少一个强制协商特性的<features/>元素标识流协商没有完成并且发起方实体必须协商更多的特性.
R: <stream:features> <starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'> <required/> </starttls> </stream:features>
一个<features/>元素可以包含不止一个强制协商特性. 这意味着发起方实体能在流协商过程的这个阶段中对强制协商特性进行选择. 举例来说, 可能一个将来的技术将执行和TLS一样的功能, 所以接收方实体可能在这次流协商过程中的同一个阶段声明同时支持TLS和这个将来的技术. 无论如何, 这只适用于流协商过程中的给定阶段而不适用于不同阶段的强制协商特性 (例如, 接收方实体不会声明同时支持STARTTLS和SASL作为强制性协商, 或同时声明SASL和资源绑定为强制协商, 因为TLS需要在SASL之前协商并且SASL需要在资源绑定之前协商).
一个同时包含强制协商和自愿协商特性的<features/>元素指示协商未完成并且发起方实体可以在它尝试协商强制协商特性之前完成自愿协商特性.
R: <stream:features> <bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'/> <compression xmlns='http://jabber.org/features/compress'> <method>zlib</method> <method>lzw</method> </compression> </stream:features>
一个只包含自愿协商特性的的<features/>元素指示流协商未完成并且发起方实体可以开始发送XML节了, 但是如果发起方实体愿意可以协商更多特性.
R: <stream:features> <compression xmlns='http://jabber.org/features/compress'> <method>zlib</method> <method>lzw</method> </compression> </stream:features>
一个空的<features/>元素指示流协商已经完成并且发起方实体可以开始发送XML节.
R: <stream:features/>
重启
一个需要流重启的特性要成功地进行协商, 双方都必须确认前一个流被取代了但是必须不发送一个关闭的</stream>标签并且必须不能终止底层的TCP连接; 反之, 双方必须重用现有的连接, 它可以是一个新的状态(例如, 作为一个TLS协商的的结果被加密). 然后发起方实体必须发送一个新的发起流头, 它应该被一个XML声明放在前面,如 11.5 所述. 当接收方实体接收到新的发起流头, 它必须在发送一个新的应答流头(它应该被一个XML声明放在前面,如 11.5 所述)之前生成一个新的流ID(而不是重用旧的流ID).
重发特性
接收方实体必须在一个流重启之后发送一个流特性的更新列表给发起方实体. 如果没有更多的特性要被声明,这个更新的流特性列表可以是空的,这个列表也也可以包含任何特性的组合.
完成流协商
接收方实体通过发送一个空的<features/>元素或只包含自愿协商特性的<features/>元素来表明流协商过程的完成. 这样做之后, 接收方实体可以发送一个空的<features/> 元素(例如, 在这些自愿协商特性协商完成之后) 但是必须不能发送额外的流特性给发起方实体(如果接收方实体有新的特性提供, 最好仅限于强制协商或安全关键的特性, 它可以简单地以一个<reset/>流错误( 4.9.3.16 )来关闭流并且等发起方重新连接的时候声明新的特性, 最好以一个交错的方法来关闭现有的流,这样不会让所有的发起方同时进行重连). 一旦流协商完成, 发起方就可以一直通过这个流来发送XML节,只要双方都维持着这个流.
- 提示性备注: 在下面 第7章 定义的资源绑定是一个前述规则的一个历史性的例外, 因为对于客户端来说它是强制协商的但使用XML节来达成协商.
在流协商完成之前,发起方实体不能(MUST NOT)尝试发送 XML节 给非自身的实体(也就是是说, 客户端的已连接资源或客户端帐号的任何其他已验证的资源) 或给它连接的服务器. 即使发起方尝试这么做, 接收方实体也不能(MUST NOT)接受这些节并且必须以一个<not-authorized/>流错误( 4.9.3.12 )来关闭流. 这个规则只适用于XML节(也就是说, 由内容命名空间限定的 <message/>, <presence/>, 和 <iq/> 元素) 而不是用于流协商的XML元素(例如, 完成 TLS协商 或 SASL协商 的元素).
确定地址
在一个XML流的双方已经完成了流协商的适当步骤之后, 流的接收方实体必须确定发起方实体的JID.
对于客户端-服务器的通讯, 在服务器能确定客户端的地址之前,SASL协商 和 资源绑定 必须完成. 客户端的纯JID (<localpart@domainpart>) 必须是授权身份 (如 SASL 所定义的, 要么 (1) 是 SASL协商 期间客户端直接与之通讯的身份,要么 (2) 如果SASL协商期间没有指定授权身份,则是从服务器的验证身份指定的. 全JID(<localpart@domainpart/resourcepart>)的资源部分(resourcepart)必须是客户端和服务器在 资源绑定 期间协商得来的那个资源. 客户端必须不去尝试猜测它的JID而是必须相信在资源绑定期间服务器返回给它的JID. 服务器必须确保返回的这个JID (包含 本地部分(localpart), 域部分(domainpart), 资源部分(resourcepart), 以及分隔符) 遵循定义于 XMPP‑ADDR 的XMPP地址规范格式; 为了满足这个限定, 服务器可以把客户端的发来的JID替换成服务器确定的规范JID并在资源绑定期间使用那个JID来和客户端通讯.
对于服务器-服务器通讯, 发起方服务器的纯JID (<domainpart>) 必须是授权的身份 (定义于 SASL ), 要么 (1) 是SASL协商 期间发起方服务器直接与之通讯的身份,要么 (2) 如果SASL协商期间没有指定授权身份,则是从接收方服务器的验证身份指定的. 在缺少SASL协商的情况下, 接收方服务器可以认为授权身份是一个和相关确认协议协商的身份(例如, 在 服务器回拨XEP‑0220 中 <result/> 元素的'from'属性.
- 安全警告: 因为可能会有第三方在一个安全层例如TLS成功协商之前篡改流上发送的数据, 建议接收服务器谨慎对待这些未得到保护的信息; 这特别适用于由发起方实体发送的第一个发起流头中的'from'和'to'地址.
流程图
我们在接下来的非规范性的流程图里为流协商过程总结前述的规则, 以发起方实体的视角来展示.
+---------------------+ | 打开的TCP连接 | +---------------------+ | v +---------------+ | 发送发起流头 |<-------------------------+ | | ^ +---------------+ | | | v | +------------------+ | | 接收应答流头 | | | | | +------------------+ | | | v | +----------------+ | | 接收流特性 | | +------------------>| | | ^ {可选的} +----------------+ | | | | | v | | +<-----------------+ | | | | | {空? ----> {全部自愿? } ----> {一些强制性? } | | | 否 | 否 | | | | 是 | 是 | 是 | | | v v | | | +---------------+ +----------------+ | | | | 可以协商或不协商 | | 必须协商一个特性 | | | | | 任何一个 | | | | | | +---------------+ +----------------+ | | v | | | | +---------+ v | | | | 完成 |<----- {协商? } | | | +---------+ 否 | | | | 是 | | | | v v | | +--------->+<---------+ | | | | | v | +<-------------------------- {强制性重新开始? } ------------>+ 否 是
- 图表3: 流协商流程图