XEP-0065

来自Jabber/XMPP中文翻译计划
跳转到: 导航, 搜索


本文的英文原文来自XEP-0065

XEP-0065: SOCKS5字节流

本文定一个XMPP协议扩展,用于在任意两个XMPP用户之间建立一个带外字节流,主要目的是文件传输. 这个字节流可以是直连(点对点)或间接连接(通过一个专用的代理服务器). 典型的传输协议使用TCP, 尽管也可选的支持UDP.

注意: 这里定义的协议是XMPP标准化基金会的一个草案标准.对本协议的执行是被鼓励的,也适于部署到生产系统,但是在它成为最终标准之前可能还会有一些变动.

文档信息

系列: [XEP] 译文

编号: 0065

发行者: XMPP标准化基金会 译文

状态: 草案

类型: 标准跟踪

版本: 1.7

最后更新日期: 2007-05-21

批准机构: XMPP理事会 译文

依赖于: XMPP Core, RFC 1928, RFC 3174, XEP-0030

上文: 无

下文: 无

简称: bytestreams

名字空间的XML方案: <http://www.xmpp.org/schemas/bytestreams.xsd>

Wiki页: <http://wiki.jabber.org/index.php/SOCKS5 Bytestreams (XEP-0065)>

作者信息

Dave Smith

Email: dizzyd@jabber.org
JabberID: [xmpp:dizzyd@jabber.org dizzyd@jabber.org]

Matthew Miller

Email: linuxwolf@outer-planes.net
JabberID: [xmpp:linuxwolf@outer-planes.net linuxwolf@outer-planes.net]

Peter Saint-Andre

JID: [xmpp:stpeter@jabber.org stpeter@jabber.org
URI: [1]

法律通告

版权

XMPP扩展协议的版权(1999-2008)归XMPP标准化基金会(XSF)所有.

权限

特此授权,费用全免,对任何获得本协议副本的人,对使用本协议没有限制,包括不限制在软件程序中实现本协议,不限制在网络服务中布署本协议,不限制拷贝,修改,合并,发行,翻译,分发,转授,或销售本协议的副本,被允许使用本协议做了以上工作的人士,应接受前述的版权声明和本许可通知并且必须包含在所有的副本或实质性部分的规格中.除非单独的许可,被重新分发的修改工作,不得含有关于作者,标题,编号,或出版者的规格的误导性资料,并不得宣称修改工作是由本文的作者,作者所属的任何组织或项目,或XMPP标准基金会签注。

免责声明

注意:本协议是提供的“原样”的基础,没有担保或任何形式的条件,明示或暗示,包括,但不限于任何担保或关于名称,非侵权性,适销性或适合作某一特定目的的条件.在任何情况XMPP标准基金会或作者不对此协议承担任何责任索赔,损害赔偿,或其他责任,无论是在一项行动的合同,侵权,或否则,所产生的,运出,或在他涉嫌与规格或执行,部署或以其它方式使用本协议. ##

责任限制

在任何情况下以及没有任何法律规定时,不论是侵权行为(包括疏忽),合同或其它方面,除非根据适用法律的要求(如蓄意和有严重疏忽行为)或同意以书面形式,XMPP标准基金会或任何作者不对本协议承担所造成的损失,包括任何直接,间接,特殊,偶发,或相应的损害赔偿的任何字符利用所产生的或不能使用的规格(包括但不限于善意的损失,停止作业,电脑失灵或故障,或任何和所有其他商业损害或损失) ,即使XMPP标准基金会或作者已被告知此类损害的可能性。

知识产权的一致性

XMPP扩展协议完全遵守XSF的知识产权策略(可在<http://www.xmpp.org/extensions/ipr-policy.shtml>找到副本或写信给XSF, P.O. Box 1641, Denver, CO 80201 USA).

讨论地点

首选的讨论的地方是标准讨论邮件列表: <http://mail.jabber.org/mailman/listinfo/standards>.

勘误表发送到editor@xmpp.org

XMPP 相关信息

XMPP 是由XSF(XMPP标准化基金会)按互联网标准程序贡献的,和 IETF的RFC 2026兼容的规范,包括 XMPP核心(RFC 3920)和 XMPP IM(RFC 3921).在本文中定义的任何协议,都是在互联网标准程序之外开发的,是扩展XMPP,而不是改变、发展和修改 XMPP本身。

一致性术语

本文中以下关键词的含义如 RFC 2119 所述: "MUST", "SHALL", "REQUIRED"; "MUST NOT", "SHALL NOT"; "SHOULD", "RECOMMENDED"; "SHOULD NOT", "NOT RECOMMENDED"; "MAY", "OPTIONAL".

目录

绪论

XMPP是为在2个网络实体间发送相对较小的XML片段而设计的(参考XMPP Core)而不是为发送二进制字节设计的。然而,有时也需要发送给在XMPP网络上的已知另一个实体(比如,发送文件)。因此在Jabber社区里,如果有一个通用的有关在网络上任意2个实体间发送二进制流的协议的话,就会被广泛认可。字节流技术的主要应用就是文件传输,因为现在有一些不相容的协议(导致缺乏兼容性)。然而,其他应用程序却是有可能的,这就是为什么开发一个通用的协议而不是专用的协议比如文件传输,是如此重要。本文档定义的协议满足以下条件:

  • 字节流是建立标准TCP连接上的(RFC 793)或者UDP连接(RFC 768),TCP的支持是被要求的(REQUIRED),UDP的支持是可选的。
  • 套接字可能是直连的(点对点)或者是间接的(通过字节流服务)。
  • 在有可能的地方,使用标准的有线协议

特别的,本文档建议Jabber社区使用SOCKS5协议,该协议是IETF批准,也将支持IPv6的字节流技术。(注意:此处的建议是使用SOCKS5协议的子集,是为了特别适应Jabber字节流,现存的SOCKC5代理如果不修改的话,不能用于实现本协议)。

术语

以下术语在本文档中使用。

表格 1: 实体的术语

术语 描述
初始方 想要和另一个实体建立字节流的Jabber实体。
目标方 初始方想要建立字节流连接的实体
代理 不在NAT或者防火墙之后的,并且在初始方和目标方充当中间人的Jabber实体
流主机 目标方连接的系统,并且控制着字节流,可能是初始方或者是代理
流ID 该连接相关的唯一流ID;是由发送者为了跟踪目的而生成的并且,长度必须(MUST)小于128个字符。

叙述

有2个满足本协议的方案:

  1. 直接连接(换言之,流主机是初始方)
  2. 间接连接(换言之,流主机是代理)

为了更容易理解,较好的方法就是分开描述。这些方案的完整描述在正式用例中。该叙述只描述了TCP连接;UDP连接将会在本文档的“可选的UDP连接”部分描述。

直接连接

直接连接是比较简单的方式。在此情况下,流主机好似初始方(流主机/发送方),这意味着初始方知道流主机的网络地址并且知道何时激活流自己。建立流字节的过程如下:

  1. 初始方发送IQ-set节点给指定了全JID的目标方以及流主机/初始方的网络和字节流的流ID(SID)。
  2. 目标方打开一个TCP套接字连接到指定的网络地址。
  3. 目标方通过SOCKS5请求连接,请求参数设置为下面定义的DST.ADDR 和 DST.PORT
  4. 流主机/初始方发送成功通过SOCKS5连接的应答。
  5. 目标方发送IQ-result节点给初始方,该'id'与初始方发送的IQ-set相同。
  6. 流主机/初始方激活字节流。
  7. 初始方和目标方就可以开始字节流。

间接连接

间接连接稍微复杂点。在此情景下,流主机不是初始方而是代理,这意味着初始方必须在发送IQ-set之前先查询到流主机的网络地址,并且必须和流主机协商,目标方也是如此,然后,在它可用之前先请求激活字节流。建立字节流的过程如下:

  1. 可选的,初始方可以在带内查询的流主机网络地址。
  2. 初始方发送IQ-set给指定了全JID的目标方以及流主机的网络地址和流ID(SID)。
  3. 目标方打开选择的流主机的TCP套接字。
  4. 目标方通过SOCKS5建立连接,连接参数是在下面定义的DST.ADDR和DST.PORT
  5. 流主机发送一个通过SOCKS5连接目标方成功的应答。
  6. 目标方发送IQ-result给初始方,该'id'与初始方发送的IQ-set相同。
  7. 初始方打开连接到流主机的TCP套接字。
  8. 初始方通过SOCKS5建立连接,参数为以下定义的DST.ADDR和DST.PORT
  9. 流主机发送一个通过SOCKS5连接初始方成功的应答
  10. 初始方发送IQ-set到流主机请求流主机激活和流ID相关的字节流。
  11. 流主机激活字节流。(数据可以通过该代理在这2个SOCKS5连接之间传递。)
  12. 流主机发送IQ-result给初始方应答字节流已激活(或者是错误)。
  13. 初始方和目标方开始使用字节流。

协议

初始方查询目标方是否支持字节流

在初始化字节流之前,初始方可能想要知道目标方是否支持字节流协议。这可以按照如下方式,通过使用服务发现来确定:

例子1. 初始方发送服务发现请求给目标方

<iq type='get' 
    from='initiator@example.com/foo' 
    to='target@example.org/bar' 
    id='hello'>
  <query xmlns='http://jabber.org/protocol/disco#info'/>
</iq>

如果目标方支持字节流,它必须(MUST)在服务发现结果中答复。

例子2. 目标方应答服务发现请求

<iq type='result' 
    from='target@example.org/bar' 
    to='initiator@example.com/foo' 
    id='hello'>
  <query xmlns='http://jabber.org/protocol/disco#info'>
    <identity 
        category='proxy'
        type='bytestreams'
        name='SOCKS5 Bytestreams Service'/>
    ...
    <feature var='http://jabber.org/protocol/bytestreams'/>
    ...
  </query>
</iq>

初始方查找代理服务

在初始化字节流之前,初始方需要找到代理。可以使用如下的服务发现:

例子3. 初始方发送服务发现请求给服务器

<iq type='get' 
    from='initiator@example.com/foo'
    to='example.com' 
    id='server_items'>
  <query xmlns='http://jabber.org/protocol/disco#items'/>
</iq>

服务器返回在disco列表中已知的所有JID

例子4. 服务器应答服务发现请求

<iq type='result' 
    from='example.com' 
    to='initiator@example.com/foo' 
    id='server_items'>
  <query xmlns='http://jabber.org/protocol/disco#items'>
    ...
    <item jid='streamhostproxy.example.net' name='Bytestreams Proxy'/>
    ...
  </query>
</iq>

在这个例子中,字节流代理由第三方服务器"example.net"管理,该代理有自己的地址"streamhostproxy.example.net"

初始方查询确定是否代理

对于disco#items结果的每一项,初始方必须查询并确定其是否是字节流代理。它可以采取服务发现,见如下方式:

例子5. 初始方发送服务发现请求给代理

<iq type='get' 
    from='initiator@example.com/foo'
    to='streamhostproxy.example.net' 
    id='proxy_info'>
  <query xmlns='http://jabber.org/protocol/disco#info'/>
</iq>

代理会返回信息。初始方应该(SHOULD)检查每个identity看其是否包含目录为"proxy"和类型为"bytestreams"的identity。

例子6. 服务器响应服务发现请求

<iq type='result' 
    from='streamhostproxy.example.net' 
    to='initiator@example.com/foo' 
    id='proxy_info'>
  <query xmlns='http://jabber.org/protocol/disco#info'>
    ...
    <identity category='proxy'
              type='bytestreams'
              name='SOCKS5 Bytestreams Service'/>
    ...
    <feature var='http://jabber.org/protocol/bytestreams'/>
    ...
  </query>
</iq>

===初始方查询流主机的网络地址

如果流主机是代理,那么初始方必须先使用字节流请求所有网络地址(显而易见的,如果流主机是初始方就不用请求)。可以通过在字节流命名空间内发送IQ-set给代理方,如下:

例子7. 初始方从代理方那里请求网络地址

<iq type='get' 
    from='initiator@example.com/foo' 
    to='streamhostproxy.example.net' 
    id='discover'>
  <query xmlns='http://jabber.org/protocol/bytestreams'/>
</iq>

指定了网络地址的<streamhost/>元素必须(MUST)有如下属性:

  • jid = 用来在Jabber中交互的流主机的JID

另外,<streamhost/>必须(MUST)包含:

要么是

  • host = 基于TCP,为了SOCKS5交互的主机名或者IP地址
  • port = 基于TCP为了SOCKS5交互和主机或者IP地址相关的端口

或者是

  • zeroconf = 一个可连接的实体的zeroconf标识符,其服务标识和协议名应该(SHOULD)是"_jabber.bytestreams"。

例子8. 代理通知初始方网络地址

<iq type='result' 
    from='streamhostproxy.example.net' 
    to='initiator@example.com/foo' 
    id='discover'>
  <query xmlns='http://jabber.org/protocol/bytestreams'>
    <streamhost 
        jid='streamhostproxy.example.net' 
        host='24.24.24.1' 
        zeroconf='_jabber.bytestreams'/>
  </query>
</iq>

如果初始方无论因为什么原因而不能初始流字节(比如,代理可能允许管理员禁止某些JID或者域名使用代理),代理必须(MUST)返回<forbidden/>错误给初始方(关于错误信息的语法,参考“错误条件映射”):

例子9. 代理返回错误给初始方

<iq type='error' 
    from='initiator@example.com/foo' 
    to='streamhostproxy.example.net' 
    id='discover'>
  <query xmlns='http://jabber.org/protocol/bytestreams'/>
  <error code='403' type='auth'>
    <forbidden xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>
  </error>
</iq>

如果代理不能作为流主机,那么代理应该(SHOULD)返回一个<not-allowed/>错误给初始方:

例子10. 代理返回错误给初始方

<iq type='error' 
    from='initiator@example.com/foo' 
    to='streamhostproxy.example.net' 
    id='discover'>
  <query xmlns='http://jabber.org/protocol/bytestreams'/>
  <error code='405' type='cancel'>
    <not-allowed xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>
  </error>
</iq>

初始方通知目标方流主机

为了初始方和目标方之间建立字节流,初始方必须提供关于流主机的网络地址给目标方。这可以在带发送一个IQ-set,其中必须包含如下信息:

  • 至少一个流主机网络地址,目标方很有可能连接此地址
  • 该连接的流ID
  • 要使用的"mode", 一般是"tcp",但是可选的,可以是"udp"(参考本文档的“可选的UDP支持”)

协议格式显示如下。

例子11. 交互的初始化(译者注:其中的流ID属性sid要与Stream Initation中初始化的时候用的sid相同)

<iq type='set' 
    from='initiator@example.com/foo' 
    to='target@example.org/bar' 
    id='initiate'>
  <query xmlns='http://jabber.org/protocol/bytestreams' 
         sid='mySID' 
	 mode='tcp'>
    <streamhost 
        jid='initiator@example.com/foo' 
        host='192.168.4.1' 
        port='5086'/>
    <streamhost 
        jid='streamhostproxy.example.net' 
        host='24.24.24.1' 
        zeroconf='_jabber.bytestreams'/>
  </query>
</iq>

如果目标方不想接收字节流,它必须(MUST)返回<not-acceptable/>错误给初始方。

例子12. 目标方拒绝字节流

<iq type='error' 
    from='target@example.org/bar' 
    to='initiator@example.com/foo' 
    id='initiate'>
  <error code='406' type='auth'>
    <not-acceptable xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>
  </error>
</iq>

目标方使用流主机建立SOCKS5连接

如果目标方接受字节流,它必须(MUST)打开标准的TCP套接字连接到和初始方交互的流主机。如果初始方提供了多个流主机,那么目标方应该(SHOULD)试着按他们出现的顺序连接他们。

如果目标方不能连接到任何流主机或者不想从这边连接,它必须(MUST)返回一个<item-not-found/>错误给初始者。

例子13. 目标方不能连接任何流主机并且终止该事物

<iq type='error' 
    from='target@example.org/bar' 
    to='initiator@example.com/foo' 
    id='initiate'>
  <error code='404' type='cancel'>
    <item-not-found xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>
  </error>
</iq>

如果目标方能够打开TCP套接字连接到一个流主机,那么它必须(MUST)使用RFC 1928指定的SOCKS5协议建立与流主机的连接。根据RFC的SOCKS5,目标方为了使用代理可能(MAY)需要验证。然而,任何验证要求已经超出本文档的范畴。

一旦目标方成功通过代理服务的验证(即使是匿名的),它为了继续协商应该(SHOULD)发送一个连接请求给适当的主机。使用如下规则:

  1. 主机名必须(MUST)SHA1编码的(SID + Initiator JID + Target JID),就如在RFC 3174中定义的SHA1算法并且输出是十六进制编码的(不是二进制)。
  2. 端口必须(MUST)是0。
  3. 提供的JID必须(MUST)是用于IQ交换的JID,可以(MAY)是全JID(<localpart@domain.tld/resource>或者是<domain.tld/resource>)或者是春JID(<localpart@domain.tld>或者是<domain.tld>)。
  4. 在应用SHA1算法之前,JID必须(MUST)要做适当的字符准备(XMPP Core指定的)

例子14. 目标连接流主机

CMD = X'01'

ATYP = X'03'

DST.ADDR = SHA1 Hash of: (SID + Initiator JID + Target JID)

DST.PORT = 0

例子15. 流主机应答连接

STATUS = X'00'

根据RFC 1928第六部分应答客户端时,流主机应该(SHOULD)设置BND.ADDR和BND.PORT为客户端连接时请求中相应的值。

目标方确认SOCKS5连接

在目标方通过流主机的验证后,它必须(MUST)发送一个IQ-result给初始方指出使用了哪个流主机。

例子16. 目标方通知初始方关于连接的信息

<iq type='result' 
    from='target@example.org/bar' 
    to='initiator@example.com/foo' 
    id='initiate'>
  <query xmlns='http://jabber.org/protocol/bytestreams'>
    <streamhost-used jid='streamhostproxy.example.net'/>
  </query>
</iq>

此时,初始方知道了目标方使用了哪个流主机。

初始方与流主机建立SOCKS5连接

如果被采用的流主机是代理,那么初始方必须(MUST)在请求激活字节流之前通过验证和建立与流主机的连接。初始方将会使用和目标方一样方式建立SOCKS5连接(使用相同的请求参数),见如下示例。

例子17. 初始方连接流主机

CMD = X'01'

ATYP = X'03'

DST.ADDR = SHA1 Hash of: (SID + Initiator JID + Target JID)

DST.PORT = 0

例子18. 流主机应答初始方的连接

STATUS = X'00'

激活字节流

为了使用字节流,它必须(MUST)先激活流主机。如果流主机是初始方,那么很简单不需要任何带内协议。然而,如果流主机是代理,那么初始方必须(MUST)发送一个带内的请求给流主机。可以通过发送一个IQ-set节点给代理,该节点包含<activate/>元素,元素的内容是目标方的全JID。

例子19. 初始方请求激活流

<iq type='set' 
    from='initiator@example.com/foo' 
    to='streamhostproxy.example.net' 
    id='activate'>
  <query xmlns='http://jabber.org/protocol/bytestreams' sid='mySID'>
    <activate>target@example.org/bar</activate>
  </query>
</iq>

使用了数据包中的SID以及from地址这些信息后,代理才能根据SID + Initiator JID + Target JID的哈希值激活流。并提供一个合理的信任等级激活来自初始方的请求。

如果代理能完成请求,它必须(MUST)使用IQ-result响应初始方

例子20. 代理通知初始方有关激活的信息

<iq type='result' 
    from='streamhostproxy.example.net' 
    to='initiator@example.com/foo' 
    id='activate'/>

如果代理不能完成请求,它必须(MUST)返回一个IQ-error错误给初始方;错误条件定义如下:

  • <item-not-found/>错误,如果from地址不匹配初始方的全JID。
  • <not-allowed/>错误,如果只有一方(初始方或者接收方,但是不是同时连接)连接到代理
  • <internal-server-error/>错误,如果代理因为一些内部故障不能激活。

规范用例

这是以上提供的信息的规范表述。基础角色是初始方,目标是在初始方和目标方之间建立字节流。(注意:"UCE"代表"Use Case Ends"(我们假定是成功的除非有其他指定的问题),"P"代表"Primary Flow","A"代表"Alternate Flow")。

基本流程

  1. 初始方想要与目标方建立一个字节流。
  2. 初始方发送一个IQ-set给目标方以指定一个流ID和一个或多个流主机的网络地址. [A1|XMPP文档列表/XMPP扩展/XEP-0065#A1]
  3. 目标方希望建立与初始方的字节流[A2|XMPP文档列表/XMPP扩展/XEP-0065#A2]
  4. 目标方请求与流主机的TCP连接[A3|XMPP文档列表/XMPP扩展/XEP-0065#A3]
  5. 目标方从流主机接收TCP应答[A4|XMPP文档列表/XMPP扩展/XEP-0065#A4]
  6. 目标方通过SOCKS5提交验证证书。
  7. 目标方通过SOCKS5接收流主机的验证应答。[A5|XMPP文档列表/XMPP扩展/XEP-0065#A5]
  8. 目标方通过SOCKS5请求与流主机连接
  9. 目标方通过SOCKS5从流主机处接收连接成功的应答.[A7|XMPP文档列表/XMPP扩展/XEP-0065#A7]
  10. 目标方发送IQ-result给初始方通知其连接流主机成功.[A6|XMPP文档列表/XMPP扩展/XEP-0065#A6]
  11. 用例结束(字节流已建立并且已准备好)

替代流程

A1. 初始方不知道流主机的完全网络地址(换言之,就是代理的地址)

  1. 初始方发送IQ-get到代理
  2. 初始方从代理接收包含网络地址的IQ-result. [A9|XMPP文档列表/XMPP扩展/XEP-0065#A9][A10|XMPP文档列表/XMPP扩展/XEP-0065#A10]
  3. 返回P2

A2. 目标方不想与初始方建立字节流

  1. 初始方从目标方接收<not-acceptable/>错误
  2. 因没有成功,终止用例

A3. 在列表中没有流主机(目标方不能连接任何流主机)

  1. 目标方返回<remote-server-not-found/>错误给初始方
  2. 因没有成功,终止用例

A4. 目标方不能连接流主机

  1. 返回P4

A5. 目标方在流主机上验证失败

  1. 返回P4

A6. 代理不愿作为流主机

  1. 初始方从代理接收到<forbidden/>错误
  2. 返回P2

A7. 代理不能作为流主机

  1. 初始方从代理处接收到<not-allowed/>错误
  2. 返回P2

A8. 目标方连接代理

  1. 初始方连接代理
  2. 目标方从流主机接收TCP应答。
  3. 初始方通过SOCKS5在代理服务上进行验证
  4. 初始方通过SOCKS5收到代理服务器的验证应答. [A10|XMPP文档列表/XMPP扩展/XEP-0065#A10]
  5. 初始方通过SOCKS5向代理请求连接
  6. 初始方通过SOCKS5接收连接成功的应答. [A11|XMPP文档列表/XMPP扩展/XEP-0065#A11]
  7. 初始方向代理发送IQ-set请求激活字节流
  8. 初始方从代理接收到IQ-result字节流激活应答. [A12|XMPP文档列表/XMPP扩展/XEP-0065#A12]
  9. 返回P9

A9. 初始方不能连接代理

  1. 因没有成功,终止用例

A10. 初始方没有通过代理的验证

  1. 因没有成功,终止用例

A11.初始方不能连接到代理

  1. 因没有成功,终止用例

A12. 代理不能激活字节流

  1. 初始方从代理服务器接收到<internal-server-error/>错误
  2. 因没有成功,终止用例

正式描述

<query/>元素

<query/>元素是所有带内交互的容器。这个元素必须(MUST)是在"http://jabber.org/protocol/bytestreams"命名空间内。此元素有个表示流会话标识的属性,并包含了多个<streamhost/>元素,一个<streamhost-used/>元素或者一个<activate/>元素。

"sid"指明了字节流会话的标识。该属性必须(MUST)出现。属性值可以是任何字符。

"mode"属性指明了使用的模式,可以是'tcp'或者'udp'。如果没有属性,那么必须(MUST)假设默认值是"tcp"。

<streamhost/>元素是负责传送网络连接的信息。在初始方发送给目标方的初始IQ-set中,至少有一个实例必须(MUST)出现。如果有多个实例出现,每个必须(MUST)是独立的主机/端口组成。

<streamhost-used/>元素指明正在使用的带外传输的流主机。它必须(MUST)在从目标方发送给初始方的IQ-set中出现,并且必须(MUST)只有一个实例。

<activate/>元素是用来请求激活双向的或者单向的字节流。它必须(MUST)在从初始方发送给流主机的的IQ-set中出现,而且是在从目标方接收到IQ-result之后,并且必须(MUST)只有一个实例。

<streamhost/>元素

<streamhost/>元素包含了字节流连接的信息。该元素有表示流主机的JID属性,网络主机/地址,网路端口。该元素不能(MUST NOT)包含任何文本节点。

"jid"属性指明了流主机的JID。该属性必须(MUST)出现,并且必须(MUST)在<iq/>中是合法的JID。

"host"属性指明了要连接的主机。该属性必须(MUST)出现。该属性的值必须(MUST)要么是可分解的域名或者是“带点的十进制”IP地址(比如1.2.3.4)。

"port"属性指定了要连接的端口。该属性可以(MAY)出现。该属性的值必须(MUST)是十进制的合法的数字。

"zeroconf"属性指定了对于字节流可用的zero-configuratio服务。属性应该(SHOULD)出现。该属性值应该是(SHOULD) '_jabber.bytestreams'。

当正在连接可用的主机时,初始方必须(MUST)要么包含主机和端口要么包含zeroconf信息。

<streamhost-used/>元素

<streamhost-used/>元素指定了连接的流主机。该元素有个目的方连接的流主机的JID。该元素不能(MUST NOT)包含任何文本节点。

"jid"属性指定了流主机的全JID。该属性必须(MUST)出现,并且必须(MUST)在<iq/>中是合法的JID。

<activate/>元素

<activate/>元素是一个用来触发代理完成连接的标志。

可选的UDP支持

对UDP连接的支持完全是可选的(OPTIONAL)。然而,支持UDP连接的具体实现必须(MUST)遵循本文档描述部分。

查询UDP的支持

如果一个协议的实现支持UDP连接,它必须(MUST)在服务发现请求的响应中,发布'http://jabber.org/protocol/bytestreams#udp'特性。

例子21. 初始方发送服务发现请求给目标方

<iq type='get' 
    from='initiator@example.com/foo' 
    to='target@example.org/bar' 
    id='hello2'>
  <query xmlns='http://jabber.org/protocol/disco#info'/>
</iq>

如果目标方支持UDP连接,它必须(MUST)在服务发现结果中响应这个结果。

例子22. 目标方响应服务发现请求

<iq type='result' 
    from='target@example.org/bar' 
    to='initiator@example.com/foo' 
    id='hello2'>
  <query xmlns='http://jabber.org/protocol/disco#info'>
    <identity 
        category='proxy'
        type='bytestreams'
        name='SOCKS5 Bytestreams Service'/>
    ...
    <feature var='http://jabber.org/protocol/bytestreams'/>
    <feature var='http://jabber.org/protocol/bytestreams#udp'/>
    ...
  </query>
</iq>

请求UDP模式

UDP连接要求将'mode'属性设置为"udp"而不是"tcp"。

例子23. 初始化交互(UDP)

<iq type='set' 
    from='initiator@example.com/foo' 
    to='target@example.org/bar' 
    id='initiate'>
  <query xmlns='http://jabber.org/protocol/bytestreams' 
         sid='mySID' 
	 mode='udp'>
    <streamhost 
        jid='initiator@example.com/foo' 
        host='192.168.4.1' 
        port='5086'/>
  </query>
</iq>

UDP处理

UDP模式和TCP模式有一个主要的不同:不是简单的建立TCP连接,目标方或者初始方必须(MUST)(1)建立UDP连接(2)初始化UDP频道。特别地:

  • 如果接下来是直接连接,目标方必须(MUST)在通过<streamhost-used/>元素通知初始方建立连接成功之前,完成UDP连接和初始化UDP频道。
  • 如果接下来是间接连接,(1)目标方必须(MUST)在通知初始方建立连接成功之前,完成UDP连接并且初始化UDP连接,并且(2)初始方必须(MUST)在请求激活流主机之前完成UDP连接和初始化UDP频道。

建立UDP连接和初始化UDP频道的过程如下。

建立UDP连接

一旦目标方成功通过代理的验证(在以下的“目标方与流主机建立SOCKS5连接”),它必须(MUST)发送UDP连接(ASSOCIATE)请求(而不是CONNECT)请求给以上算法指定的主机。

例子24. 目标方向流主机请求UDP连接

CMD = X'03'

ATYP = X'03'

DST.ADDR = SHA1 Hash of: (SID + Initiator JID + Target JID)

DST.PORT = 0

流主机应答请求

例子25.流主机应答请求

STATUS = X'00'

初始化UDP频道

在连接了流主机之后,目标方(直接连接)或者目标方和初始方(间接连接)都必须(MUST)初始化UDP频道。为了初始话频道,每个发送实体必须(MUST)发送SOCKS5的UDP包给与初始化TCP连接相同的流主机的端口(根据在之前的例子中,主机是192.168.4.1,端口是5086),DST.PORT设置为'1',DATA包含发送实体的JID(也就是,要么是目标方的JID要么是初始方的JID)。

例子26. 目标方或者初始方发送UDP初始包到流主机

ATYP = X'03'

DST.ADDR = SHA1 Hash of: (SID + Initiator JID + Target JID)

DST.PORT = 1

DATA = Target or Initiator JID

在流主机成功接收到了之后,流主机必须(MUST)响应一个消息通知来指明以及成功:

例子27. 流主机通知目标方或者初始方,初始化UDP成功

<message 
    from='streamhostproxy.example.net' 
    to='target@example.org/bar' 
    id='initiate'>
  <udpsuccess xmlns='http://jabber.org/protocol/bytestreams' dstaddr='Value of Hash'/>
</message>

<udpsuccess/>元素表明流主机已经接收到UDP初始包。该元素有一个属性,此属性包含了在先前UDP包中使用的DST.ADDR。

如果目标方不能初始化UDP频道,那么它必须(MUST)返回一个<remote-server-not-found/>错误给初始方

注意:因为UDP是不可靠的,如果在短时间内(推荐(RECOMMENDED)5秒的重试时间)没有收到响应通知,目标方应该(SHOULD)重新发送UDP包。一旦响应了一个通知,流主机应该(SHOULD)忽略因接收到之后的UDP初始包而发生的冲突。

交换UDP包

一旦UDP连接建立,就能和流主机交换UDP包。当任何一方发送了UDP包,它必须(MUST)包含4个字节的头信息(除了其他可能的头信息以外,比如SOCKS5),头信息由发送源的实际端口以及目标源的实际端口组成,这2者都是16位的网络字节序列。这可以允许网络节点在一个会话中可以多路传输许多包到不同的目标。实际的应用程序的数据应该跟在这些头信息之后,那么发送的大小是“应用程序数据大小 + 4”。

对于发送到流主机的所有包,DST.PORT设置位0,DATA包含了承载发送的数据

例子28. 发送UDP到流主机

ATYP = X'03'

DST.ADDR = SHA1 Hash of: (SID + Initiator JID + Target JID)

DST.PORT = 0

DATA = (payload)

从流主机发送的UDP包没有任何SOCKS5头,所以承载发送的数据应该就按照本来的数据发送。

应用程序界面对于SOCKS5的UDP字节流的处理必须(MUST)报告一个可用的UDP数据包的缓存空间,如果可以的话,该空间小于操作系统和SOCKS5层提供的实际空间,换言之,小于4个八位字节或更多。

实现注意

流主机要求

一个流主机必须(MUST)支持TCP连接。

一个流主机应该(SHOULD):

  1. 允许在初始方和接收方之间的双向的字节流
  2. 只允许一个目标方连接到字节流(换言之,不允许多播)。
  3. 跟踪基于流ID和初始方全JID的标识的会话,允许初始方建立多个同步会话。
  4. 忽略但是不要丢失任何在流激活之前发送的字节
  5. 如果支持zero-configuration的话,优先使用。

一个流主机可以(MAY)

  1. 除了支持TCP连接以外来还可以支持UDP连接
  2. 如果需要的话,忽略DST.ADDR和DST.PORT参数。

SOCKS5参数映射

为了便于使用SOCKS5,命令参数必须(MUST)映射到合适的值. 没有在下表中指定的参数的使用应该参考RFC 1928的定义

表格 2: 请求的连接参数映射

参数
CMD 1 (CONNECT)
ATYP Hardcoded to 3 (DOMAINNAME) in this usage
DST.ADDR (SID + Initiator JID + Target JID)的SHA1哈希值
DST.PORT 0

表格 3: 请求中的UDP连接的参数映射

参数
CMD 3 (UDP ASSOCIATE)
ATYP Hardcoded to 3 (DOMAINNAME) in this usage
DST.ADDR (SID + Initiator JID + Target JID)的SHA1哈希值
DST.PORT 0

表格 4: 请求中的UDP包的参数映射

参数
ATYP Hardcoded to 3 (DOMAINNAME) in this usage
DST.ADDR SHA1哈希值: (SID + Initiator JID + Target JID)
DST.PORT 0 or 1, 分别代表负载或和初始化包

安全事项

本协议没有包含安全加密SOCKS5的方法,如果需要安全连接,它必须(MUST)使用标准的协议通过字节流进行协商(一旦建立之后),比如SSL或者TLS。安全协商的方式已经超出了本文档的范畴。

IANA事项

本文档不需要与Internet Assigned Numbers Authority (IANA)交互。

XMPP注册事项

协议命名空间

在协议命名空间的注册项中包含了'http://jabber.org/protocol/bytestreams'

服务发现特性

在服务发现特征中的注册项中包含了'http://jabber.org/protocol/bytestreams#udp'

服务发现目录/类型

在服务发现注册项中包含了"proxy"目录和"bytestreams"类型。注册项提交如下:

  <category>
    <name>proxy</name>
    <desc>Proxy servers or services</desc>
    <type>
      <name>bytestreams</name>
      <desc>A proxy for SOCKS5 bytestreams</desc>
      <doc>XEP-0065</doc>
    </type>
  </category>

Schema

<?xml version='1.0' encoding='UTF-8'?>
 
<xs:schema
    xmlns:xs='http://www.w3.org/2001/XMLSchema'
    targetNamespace='http://jabber.org/protocol/bytestreams'
    xmlns='http://jabber.org/protocol/bytestreams'
    elementFormDefault='qualified'>
 
  <xs:annotation>
    <xs:documentation>
      The protocol documented by this schema is defined in
      XEP-0065: http://www.xmpp.org/extensions/xep-0065.html
    </xs:documentation>
  </xs:annotation>
 
  <xs:element name='query'>
    <xs:complexType>
      <xs:choice>
        <xs:element ref='streamhost' minOccurs='0' maxOccurs='unbounded'/>
        <xs:element ref='streamhost-used' minOccurs='0'/>
        <xs:element name='activate' type='empty' minOccurs='0'/>
      </xs:choice>
      <xs:attribute name='sid' type='xs:string' use='optional'/>
      <xs:attribute name='mode' use='optional' default='tcp'>
        <xs:simpleType>
          <xs:restriction base='xs:NCName'>
            <xs:enumeration value='tcp'/>
            <xs:enumeration value='udp'/>
          </xs:restriction>
        </xs:simpleType>
      </xs:attribute>
    </xs:complexType>
  </xs:element>
 
  <xs:element name='streamhost'>
    <xs:complexType>
      <xs:simpleContent>
        <xs:extension base='empty'>
          <xs:attribute name='jid' type='xs:string' use='required'/>
          <xs:attribute name='host' type='xs:string' use='required'/>
          <xs:attribute name='zeroconf' type='xs:string' use='optional'/>
          <xs:attribute name='port' type='xs:string' use='optional'/>
        </xs:extension>
      </xs:simpleContent>
    </xs:complexType>
  </xs:element>
 
  <xs:element name='udpsuccess'>
    <xs:complexType>
      <xs:simpleContent>
        <xs:extension base='empty'>
          <xs:attribute name='dstaddr' type='xs:string' use='required'/>
        </xs:extension>
      </xs:simpleContent>
    </xs:complexType>
  </xs:element>
 
  <xs:element name='streamhost-used'>
    <xs:complexType>
      <xs:simpleContent>
        <xs:extension base='empty'>
          <xs:attribute name='jid' type='xs:string' use='required'/>
        </xs:extension>
      </xs:simpleContent>
    </xs:complexType>
  </xs:element>
 
  <xs:simpleType name='empty'>
    <xs:restriction base='xs:string'>
      <xs:enumeration value=''/>
    </xs:restriction>
  </xs:simpleType>
 
</xs:schema>

备注

  1. RFC 3920: Extensible Messaging and Presence Protocol (XMPP): Core <http://tools.ietf.org/html/rfc3920>.
  2. RFC 793: Transmission Control Protocol <http://tools.ietf.org/html/rfc0793>.
  3. RFC 768: User Datagram Protocol <http://tools.ietf.org/html/rfc0768>.
  4. XEP-0030: Service Discovery <http://www.xmpp.org/extensions/xep-0030.html>.
  5. Zeroconf is a set of protocols that enable IP networking without the need for configuration. For further information, refer to <http://www.zeroconf.org/>.
  6. XEP-0086: Error Condition Mappings <http://www.xmpp.org/extensions/xep-0086.html>.
  7. RFC 1928: SOCKS Protocol Version 5 <http://tools.ietf.org/html/rfc1928>.
  8. RFC 3174: US Secure Hash Algorithm 1 (SHA1) <http://tools.ietf.org/html/rfc3174>.
  9. RFC 3920: Extensible Messaging and Presence Protocol (XMPP): Core <http://tools.ietf.org/html/rfc3920>.
  10. The Internet Assigned Numbers Authority (IANA) is the central coordinator for the assignment of unique parameter values for Internet protocols, such as port numbers and URI schemes. For further information, see <http://www.iana.org/>.
  11. The XMPP Registrar maintains a list of reserved protocol namespaces as well as registries of parameters used in the context of XMPP extension protocols approved by the XMPP Standards Foundation. For further information, see <http://www.xmpp.org/registrar/>.

修订历史

Version 1.7 (2007-05-21)

Incorporated errata: specified format for SHA1 output; specified BND.ADDR and BND.PORT for SOCKS5 reply; removed extraneous SOCKS5 acknowledgement example from Section 4.9; clarified rules for creation of SOCKS5 connection request in Section 4.6; added examples to Section 4.8; specified that ATYP value is hardcoded to 3 in this usage.

(psa)

Version 1.6 (2004-11-12)

Added UDP support (OPTIONAL).

(ds/psa)

Version 1.5 (2004-06-29)

Added requirement to apply stringprep profiles before SHA1 hashing; added reference to RFC 3174.

(psa)

Version 1.4 (2004-06-28)

Cleaned up narratives to reflect current practices and removed unnecessary authentication references; fixed mismatch SOCKS5 parameter table values.

(ds)

Version 1.3 (2003-09-24)

Added disco#info <identity/> and corresponding XMPP Registrar submission; added XMPP error handling.

(psa)

Version 1.2 (2003-07-15)

Removed SIDs from the result queries, you should key off the IQ 'id' attribute instead. Added the disco exchange for finding available proxies.

(rwe)

Version 1.1 (2003-07-09)

Changed srvid to zeroconf; cleaned up use cases; updated the schema.

(ds)

Version 1.0 (2003-04-21)

Per a vote of the Jabber Council, advanced status to Draft.

(psa)

Version 0.7 (2003-03-04)

Clarified that this proposal uses an adaptation of the SOCKS5 protocol, not the full protocol; replaced DTD with schema; added security considerations.

(psa)

Version 0.6 (2003-01-27)

Added service discovery example; added 'srvid' attribute to streamhost element and required inclusion of either 'srvid' or 'port' attribute; improved the algorithms for generating SOCKS5 UNAME and PASSWD parameters; specified that the DST.ADDR and DST.PORT parameters may be ignored; removed references to connected/disconnected notification, bidirectional bytestreams, and multiple targets; updated implementation notes.

(psa/ds)

Version 0.5 (2002-12-20)

Specified option of "reversing the connection" (Target becomes Initiator); added more error cases; resurrected and cleaned up formal use case.


(psa)

Version 0.4 (2002-12-19)

Added section on connected/disconnected notifications sent from Proxy to Initiator; cleaned up several examples; specified more error conditions; clarified the formal descriptions; added implementation notes and future considerations.

(psa, mm)

Version 0.3 (2002-12-17)

Added lots of detail to the narrative and protocol.

(psa)

Version 0.2 (2002-12-16)

Added SOCKS info.

(ds)

Version 0.1 (2002-12-13)

Initial version.

(ds)

结束

个人工具