SOCKS5协议「RFC1928翻译」

Posted by SingChia Blog on March 21, 2018

序言

  • 网络工作组
  • 编号1928
  • 类别:标准跟踪
  • 相关人员
    • M. Leech@Bell-Northern Research Ltd
    • M. Gains@International Business Machines
    • Y. Lee@NEC Systems Laboratory
    • R. Kuris@Unify Corporation
    • D. Koblas@独立顾问
    • L. Jones@Hewlett-Packard Company
  • 时间:1996.03

文档状态

本文档是一个互联网社区标准跟踪文档,并请求讨论和建议以便更新。请了解当前版本的“互联网官方协议标准”(STD 1)以了解标准化状态和本协议状态。本文档的分发没有限制。

致谢

本协议是Socks 4协议的进化版。本协议发源于激烈讨论和原型实现。主要贡献人有:Marcus Leech@Bell-Northern Research Ltd, David Koblas@独立顾问, Ying-Da Lee@NEC Systems Laboratory,LaMont Jones@Hewlett-Packard Company,Ron Kuris@Unify Corporation,Matt Ganis@International Business Machines。

1. 介绍

网络防火墙和系统有效地将如互联网这样的外部网络跟公司的内部网络隔离开,这种做法正趋于流行。这些防火墙系统典型地表现为网络之间的应用层网关,提供可控的TELNET,FTP以及smtp的访问。随着更多应用层协议的出现,缺少一个通用结构或框架,可以让让这些协议透明并安全穿过防火墙。

此外,协议穿过防火墙时的细粒度的强认证也是需要的。这个需求产自不同组织网络间的客户端-服务端模型,这样的一对关系是需要被控制和强烈认证的。

此处所描述的协议是设计给建立在TCP以及UDP协议上的客户端-服务端应用提供一个通用结构,以更方便安全的使用网络防火墙服务。概念上来说,这个协议是介于应用层和传输层之间的“中间层”,并且并不提供网络层网关服务,比如转发icmp信息。

2. 已有经验

现有的SOCKS 4协议,提供了基于TCP的客户端-服务端的应用不安全地穿过防火墙的方法,包括TELNET,FTP以及其他流行的信息发现协议比如HTTP,WAIS和GOPHER。

本协议扩展了SOCKS 4协议未涉及的UDP功能、通用的强认证功能以及扩展地址形式到域名和IPv6地址。

SOCKS协议的实现一般涉及使用SOCKS提供的封装方法重新封装TCP的客户端应用。

注意: 除了特别说明外,出现在包格式图中的十进制数字代表了相应字段的的字节长度。所有展示的字节都有其特殊的含义,语法上X'hh'是用来表示某个字段里一个字节的值。而Variable是用于表示某字段的长度是可变的,这个长度是由相关的长度字段(一个或两个字节)或者数据类型字段所指定。

3. TCP客户端流程

当一个TCP客户端想跟一个防火墙(其他实现也可以)內的目标建立连接,它必须开启一个TCP连接到SOCKS服务系统上的一个合适的SOCKS端口。传统上SOCKS服务使用TCP端口1080。如果连接成功,客户端紧接着进入认证方法协商阶段,经过协商好的方法认证后,开发发送转发请求。SOCKS服务端评估请求,要么跟应用服务端建立相应的连接,要么拒绝应用客户端连接。

译注:应用客户端 –> SOCKS服务端 –> 应用服务端

除了特别说明外,出现在包格式图中的十进制数字代表了相应字段的的字节长度。所有展示的字节都有其特殊的含义,语法上X'hh'是用来表示某个字段里一个字节的值。而Variable是用于表示某字段的长度是可变的,这个长度是由相关的长度字段(一个或两个字节)或者数据类型字段所指定。

译注:这一段重复在原文档中就有,该协议的编辑审校们十分不上心,甚至还有错单词

客户端连接服务端,然后发送一个版本标识号/方法选择信息:

+-----+----------+---------+
| VER | NMETHODS | METHODS |
+-----+----------+---------+
|  1  |    1     | 1 - 255 |
+-----+----------+---------+

译注:分别为版本号字段1字节,方法数目字段1字节,多方法标识字段1到255字节

本协议要求VER字段设置为X'05'NMETHODS字段包含了出现在METHODS字段里方法标识字节的数目。

SOCKS服务端会在METHODS字段中选择一个方法,然后发送会一个方法选择信息:

+-----+----------+
| VER |  METHOD  |
+-----+----------+
|  1  |    1     |
+-----+----------+

译注:分别为版本号字段1字节,方法标识字段1字节

如果选择的方法标识是X'FF',那么没有任何客户端发送的METHODS字段中的方法会被选择,并且客户端必须关闭连接。

目前定义的方法标识有:

  • X'00' 无需认证
  • X'01' GSSAPI
  • X'02' 用户名/密码
  • X'03' 一直到 X'7F'分配给IANA
  • X'80' 一直到 X'FE'保留用作私有方法
  • X'FF' 没有方法被接受

通过认证方法协商后,客户端和服务端进入特定认证方法的子协商过程。这些特定方法的子协商过程在其他文档中说明。

如果需要开发一个新的认证方法需要联系IANA以获得一个新的认证方法号。 这个分配的认证文档号应该参考已有的方法号和相应的协议。

SOCKS实现上必须支持GSSAPI以及应该支持用户名/密码认证。

4. 请求

一旦协商好的的方法子协商完成后,客户端开始发送请求细节。如果协商好的方法封装了完整性或者机密性检查,那所有的请求必须被封装在该方法的封装协议中。

译注:子协商过程中可以有自己的封装协议,而下面所说的请求必须被封装在这些协议里

SOCKS请求格式如下:

+-----+-----+------+------+----------+---------+
| VER | CMD | RSV  | ATYP | DST.ADDR | DST.PORT|
+-----+-----+------+------+----------+---------+
|  1  |  1  | X'00'|   1  | Variable |    2    |
+-----+-----+------+------+----------+---------+

其中各字段可能的值为:

  • VER 协议版本:X'05'
  • CMD
    • 连接CONNECT): X'01'
    • 绑定BIND): X'02'
    • UDP关联UDP ASSOCIATE): X'03'
  • RSV 保留字段
  • ATYPE 后续地址字段的地址类型
    • IPv4地址:X'01'
    • 域名:X'03'
    • IPv6地址:X'04'
  • DST.ADDR 期望的目的地址
  • DST.PORT 期望的目的端口,按网络字节序

SOCKS服务端一般评估源和目的网络地址,然后根据请求类型返回一条或者多条信息。

5. 地址

地址字段(DST.ADDR, BND.ADDR 译注:BND.ADDR下一节中介绍)中值的类型由ATYP字段中的值决定:

  • X'01'

表示地址字段是一个IPv4地址,占用4字节

  • X'03'

地址字段包含了完整域名。第一个比特包含了后续地址字段的字节数,注意没有NUL字节

  • X'04'

地址是一个IPv6地址,占用16字节

6. 回复

一旦客户端跟SOCKS建立了连接并完成了认证协商,客户端可以立即发送SOCKS请求。SOCKS评估这个请求并按一下格式进行回复:

    +----+-----+-------+------+----------+----------+
    |VER | REP |  RSV  | ATYP | BND.ADDR | BND.PORT |
    +----+-----+-------+------+----------+----------+
    | 1  |  1  | X'00' |  1   | Variable |    2     |
    +----+-----+-------+------+----------+----------+

其中各字段可能的值为:

  • VER 协议版本:X'05'
  • REP 回复字段:
    • X'00' 成功
    • X'01' 通用SOCKS服务端失败
    • X'02' 规则集不允许的连接
    • X'03' 网络不可达
    • X'04' 主机不可达
    • X'05' 连接拒绝
    • X'06' TTL超时
    • X'07' 命令不支持
    • X'08' 地址类型不支持
    • X'09'X'FF' 未分配
  • RSV 保留
  • ATYP 后续的地址类型
    • IPv4地址:X'01'
    • 域名:X'03'
    • IPv6地址:X'04'
  • BND.ADDR SOCKS服务端绑定地址
  • BND.PORT SOCKS服务端绑定端口,按网络字节序

RSV字段必须设置为X'00'

与请求相同,如果在协商好的认证方法中带有了完整性或者机密性检查的封装方法,则回复也必须按照这种方式封装。

连接

给第四节请求中描述的客户端的连接请求的回复里,BND.PORT包括了服务端连接目标主机所分配的端口号,而BND.ADDR包含了相关联的ip地址。该ip地址可能常常和客户端连接SOCKS服务端所用的ip地址不同,因为SOCKS服务是允许多主机的。另外SOCKS服务端应该自行评估客户端发起连接请求的源端口和ip以及请求协议中的DST.ADDRDST.PORT字段。

译注:因为SOCKS协议诞生的最初目标是通过防火墙,因此需要评估源和目的

绑定

协议里使用的绑定请求是用于客户端接收服务端的请求(译注:此处客户端为SOCKS服务端,服务端为应用服务端)。FTP是一个典型的示例,使用客户端到服务端的连接用于传递命令和状态,但是可以使用服务端到客户端的连接用于一些命令的数据传输(如LS、GET、PUT)。

绑定请求用于客户端协议在跟SOCKS服务端使用连接请求建立主连接之后建立第二条连接。SOCKS服务端应该使用DST.ADDRDST.PORT来评估绑定请求。

译注:本段在整个协议介绍中最模糊且最具有歧义,按我理解应该指应用客户端发送绑定请求给SOCKS服务端,但是并没有说明DST.ADDR和DST.PORT应该带上什么。因为应用客户端要求SOCKS服务端绑定一个固定ip和port并返回给应用客户端,应用客户端使用第一条连接发送给应用服务端告诉它需要切换长连接,这样一来开始的DST.ADDR和DST.PORT是没有作用的。此外本段中有It被误写为In的情况,有兴趣的同学可以自行查看原文档第六节绑定主题的第二段。

绑定期间SOCKS服务端发送两条回复给客户端。第一条是SOCKS服务器创建并绑定一个新socket之后。返回的BND.PORT为SOCKS服务端用来接收新连接的端口。BND.ADDR包括了相应的ip地址。客户端拿到这些信息后可以使用第一条连接来通知应用服务端来切换新连接地址。第二条回复发生在应用服务端连接SOCKS端口成功或者失败之后。

第二条回复中,BND.ADDRBND.PORT字段包含了连接过来主机的ip地址和端口。

译注:之所以但是绑定请求还是因为当时的应用协议很少,连HTTP协议还是1999年出的标准,用来简陋的防止中间人攻击。但在今天已经很少有实现带上绑定协议,包括ShadowSocks

UDP关联

UDP关联请求用于建立关联,使用一个UDP转发进程(译注:即为UDP转发服务器)DST.ADDRDST.PORT字段包含了应用客户端发送的应用服务端的ip和端口。SOCKS服务端可以用这些信息来限制该该关联的访问。如果客户端在UDP关联请求中没有携带这些信息,则应该置0。

UDP关联请求使用的TCP连接终止的时候该UDP关联也应该终止。

UDP关联的回复中,BND.PORTBND.ADDR字段表示客户端必须要发送的UDP请求的目的地址端口,以便于转发。

回复处理

当一个回复表示失败的时候(REP的值是非X'00'),SOCKS服务端必须在发送该回复后终结TCP连接。检测到失败后终结连接操作要在10秒内完成。

如果返回成功(REP值为X'00'),且请求是绑定或者连接,那客户端则可以开始传递数据。如果选定的认证方法支持完整性、机密性以及认证封装,则数据需要使用这些方式封装。类似的,应用服务端的数据达到SOCKS服务端也同样必须来封装。

7. UDP客户端流程

UDP客户端的请求必须要使用SOCKS服务端回复UDP关联请求的BND.PORT携带的端口。 如果认证方法中带有完整性等封装,UDP数据流同样需要按该方法加密。每个UDP流携带的UDP请求头如下:

  +----+------+------+----------+----------+----------+
  |RSV | FRAG | ATYP | DST.ADDR | DST.PORT |   DATA   |
  +----+------+------+----------+----------+----------+
  | 2  |  1   |  1   | Variable |    2     | Variable |
  +----+------+------+----------+----------+----------+

其中UDP请求头中的字段有:

  • RSV 保留字段X'0000'
  • FRAG 当前的段号
  • ATYP 后续的地址类型
    • IPv4地址:X'01'
    • 域名:X'03'
    • IPv6地址:X'04'
  • BND.ADDR SOCKS服务端绑定地址
  • BND.PORT SOCKS服务端绑定端口,按网络字节序
  • DATA 用户数据

当一个UDP转发服务端决定要转发一个UDP流时,这个操作是隐式的,不会通知请求的应用客户端。类似的,也会隐式的丢掉无法转发的包。当UDP转发包接收到来自远程主机的包,也必须使用上述的UDP头部并加上适当的封装。

UDP关联请求中客户端的源ip地址被记录下来,UDP转发服务端可以从SOCKS中拿到这个ip地址。如果后续发送请求客户端的源ip地址与已经记录的源ip地址不同,则必须被丢弃。

FRAG字段表示这个包是否属于一个分段的一部分。如果要实现的话,最高位表示段终结序列,即值X'00'表示这个包是独立的。而1127则表示分段序列中的位置。每一个接收端对这些分段应该有一个重装队列以及重装定时器。重装队列应该重整相关联的分段,并丢弃重装定时器超时的,一个新包到来时携带的FRAG字段小于最高已处理的FRAG序列也应该被丢弃。重整定时器必须不小于5秒。应用应该尽可能的不要分段。

分段的实现是可选的;如果一个实现不支持分段则必须丢弃FRAG字段不等于X'00'的。

SOCKS感知的UDP的编程接口必须报告一个UDP包的可用缓存空间,这个空间肯定比操作系统实际提供的肯定要小:

  • 如果ATYPEX'01' 10+小于特定方法字节
  • 如果ATYPEX'03' 262+小于特定方法字节
  • 如果ATYPEX'04' 20+小于特定方法字节

译注:完全不知道协议编辑人员在想什么

8. 安全考虑

本文档描述的是通过网络防火墙的应用层协议。这种穿过防火墙的安全性取决于特定实现的特定认证和封装,并在SOCKS客户端和SOCKS服务端的协商中选定。

管理员应该对认证方法的选择仔细考虑。

9. 引用

[1] Koblas, D., "SOCKS", Proceedings: 1992 Usenix Security Symposium.

作者地址

   Marcus Leech
   Bell-Northern Research Ltd
   P.O. Box 3511, Stn. C,
   Ottawa, ON
   CANADA K1Y 4H7

   Phone: (613) 763-9145
   EMail: mleech@bnr.ca