本文档为Internet社区指定了一个网络标准追踪协议,并且仍需要更多讨论和建议以改进。请参阅当前版本的“互联网官方协议标准”(STD 1)查看本协议的标准声明和当前状态。本文档可以无限制自由转载。
本中文文档由@doem97翻译完成,可无限制自由转载。
这份文档描述了一个协议,它是SOCKS第4版本的改进版。这个新协议源于积极讨论和原型实现。 主要的贡献者是:
- Marcus Leech:北方贝尔研究室
- David Koblas:独立顾问
- Ying-Da Lee:NEC系统实验室
- LaMont Jones:惠普公司
- Ron Kuris:Unify公司
- Matt Ganis:国际商业机械公司
网络防火墙(即有效地将组织内部网络结构与外部网络隔离的系统,如内网机制)正逐渐流行起来。这些防火墙通常充当应用层网关,并提供可控的TELNET、FTP和SMTP访问。相应地,人们也设计了越来越多更复杂的应用层协议(用以穿透防火墙),才能保证信息交流。因此有必要为这些协议提供一个通用的,能够透明和安全地穿越防火墙的框架。此外,穿越防火墙还必须保障存在一定加密安全机制,本文在可实现的前提下尽可能详细地描述了这种安全机制。
这篇文档的产生源于我们意识到:客户机-服务器的连接需要跨越许多不同的网络,因此必须要保证连接安全,并且给出强力的认证才能确保信息不被泄露。
本文描述的协议旨在为工作于TCP和UDP域的客户机-服务器上的应用程序提供一个框架,以方便且安全地使用穿透网络防火墙服务。本协议从概念上讲,应属于应用层和传输层之间的一个中介层,因此不提供如转发ICMP之类只能由网络层网关提供的服务。
目前通用的协议为SOCKS版本4协议,它能使基于tcp的客户机-服务器间应用协议(包括TELNET、FTP和HTTP、WAIS和GOPHER等)穿透防火墙,但没有加入安全加密机制。本协议扩展了SOCKS版本4协议,包含了UDP,并扩展了框架,使协议能够支持通用的几种强认证方案,并扩展了寻址方案,以包含域名寻址和IPv6寻址方式。 为了实现SOCKS协议,通常需要重新编译或重新连接使用tcp连接的客户端应用,以使用SOCKS库中相应的预封装函数。
注意:除非另有说明,在包格式示意图中出现的十进制数均用八位字节表示的相应字段的长度。当某个字节必须取特定值时,用语法X'hh'表示该字节的值。当某个域使用单词'Variable'时,这表示该域的长度是可变的,且该长度定义在一个和这个域相关联(1-2个字节)的域中,或某个数据类型域中。
当一个基于TCP的客户端希望与一个只能通过防火墙才能到达的对象建立连接时(由具体实现所决定),它必须首先在SOCKS服务器上与SOCKS端口建立一个TCP连接。SOCKS服务通常位于TCP端口1080上。如果连接建立成功,客户端将进行所选加密方案的握手,首先进行认证,然后发送转发的请求,之后由SOCKS服务器对请求进行评估,并建立适当的连接或拒绝它。
注意: 除非另有说明,在包格式示意图中出现的十进制数均为八位字节表示的相应字段的长度。当某个字节必须取特定值时,用语法X'hh'表示该字节的值。当某个域使用单词'Variable'时,这表示该域的长度是可变的,且该长度定义在一个和这个域相关联(1-2个字节)的域中,或某个数据类型域中。
客户端连接到服务器后,向服务器发送一个请求,用于协商版本(VER)及给出客户端支持的认证方案列表(NMETHODS & METHODS):
VER | NMETHODS | METHODS |
---|---|---|
1 | 1 | 1 到 255 |
本协议中,VER字段被设置为X'05';NMETHODS字段以字节为单位,标识了在METHODS字段中列出的认证方案的数目。服务器从这些认证方案中选择一个方案,并返回一个消息给客户端,告知客户端哪个方案被选中。
VER | NMETHOD |
---|---|
1 | 1 |
如果服务器返回的方案是X'FF',意味着客户端列出的认证方案都不被接收,并且客户端必须关闭连接。
当前已定义的认证方案及标识有:
- X'00':无需认证
- X'01':GSSAPI
- X'02':用户名/密码
- X'03'~X'7F':由IANA分配
- X'80'~X'FE':为私人方案保留
- X'FF':没有可接受方案
如果返回了双方接受的方案,客户端和服务器就进入由选定的认证方案所决定的握手子进程(sub-negotiation)。各方案所需的握手子进程的描述可以从它们的说明文档中查阅到。
如果开发者想要为自己的认证方案获取一个公开的方案号,可以联系IANA来分配。想要查看当前所有被分配的认证方案标识列表和它们所实现的具体协议,可以查阅ASSIGNED NUMBERS文档。
想要符合本SOCKS5协议的所有认证方案都必须支持GSSAPI,并且能够支持用户名/密码认证方式。
一旦认证方案所需的握手子进程完成,客户端就会发送请求的详细信息。 如果选择的认证方案要求对数据进行用于检查完整性或确保安全性的的封装,则请求信息必须以规定的格式封装。
SOCKS请求的格式如下:
VER | CMD | RSV | ATYP | DST.ADDR | DST.PORT |
---|---|---|---|---|---|
1 | 1 | X'00' | 1 | Variable | 2 |
其中各字段值为:
- VER:协议版本号(本协议为X'05')
- CMD:发出的请求命令(具体含义见本文第6节)
- CONNECT请求:X'01/
- BIND请求:X'02'
- UDP ASSOCIATE请求:X'03'
- RSV:保留值
- ATYP:DST.ADDR中所写的地址类型
- IPv4地址:X'01'
- 域名地址:X'03'
- IPv6地址:X'04'
- DST.ADDR:目的地址
- DST.PORT:目的端口(用2个字节表示)
SOCKS服务器会根据源地址和目标地址分析请求,并根据请求信息返回一个或多个应答消息。
在地址栏中,ATYP字段描述地址字段(DST.ADDR/BND.ADDR)包含的地址类型:
- X'01':4字节(32位)长的IPv4地址
- X'03':基于域名的地址,并且第一字节为用8进制表示的地址长度,其后跟随用8进制表示的域名地址,不需要写终止符NUL
- X'04':16字节(128位)长的IPv6地址
一旦客户端与SOCKS5服务端建立起连接并完成握手认证过程,客户端就会发出一个SOCKS5请求信息。服务端根据请求信息返回以下格式的应答信息:
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:服务器绑定地址
- BND.PORT:服务器绑定端口
注意: RSV字段必须置为X'00';如果选择的认证方案要求对数据进行用于检查完整性或确保安全性的的封装,则应答信息必须以规定的格式封装。
对类型为CONNECT的请求,BND.PORT字段需要写入SOCKS服务器访问最终目的地址的端口,BND.ADDR写入SOCKS服务器访问最终目的地址所用的IP。这里BND.ADDR的IP地址通常与客户端访问SOCKS服务器时所用的IP不同,因为SOCKS服务器通常会绑定很多IP。
我们建议SOCKS服务器通过分析DST.ADDR和DST.PORT字段以及客户端源地址/端口来分析CONNECT请求。
BIND请求通常用在一些让客户端接收来自服务器的连接的协议上,例如FTP协议:它建立了一个从客户端到服务器的连接,仅用以执行命令和接收状态报告;同时使用另一个从服务器到客户端的连接,接收传输数据的命令(如ls,get,put等)。
建议客户端应用协议首先使用CONNECT建立主连接后,再使用BIND协议建立子连接。建议SOCKS服务器使用DST.ADDR和DST.PORT分析BIND请求。
在BIND操作器件,SOCKS服务器会向客户端发送两个应答:
第一种应答位于服务器创建和绑定了一个新的socket隧道之后,BND.PORT字段为被服务器监听的入向端口,BND.ADDR字段为相关联的IP地址。客户端通常会使用这两条信息(通过主链接或控制子链接)将交汇所需路由告知目的应用的服务器。
第二种应答仅发生在预期的入连接成功或失败时。在第二种情况下,BND.PORT和BND.ADDR字段需包含发生此入连接的主机IP和端口号。
UDP ASSOCIATE请求通常是为转发UDP数据报建立一个UDP转发进程。DST.ADDR和DST.PORT字段需写入客户端请求的用于发送UDP数据报的IP地址和端口号,服务器可以利用这条信息限制进入的连接。如果客户端在发送UDP ASSOCIATE请求时不指定地址和端口信息,必须用全0来填充DST.ADDR和DST.PORT字段。 当用于转发UDP的TCP连接中断时,该UDP连接也必须中断。 应答UDP ASSOCIATE请求时,BND.PORT和BND.ADDR字段指明了客户端应该向哪个IP/端口发送中继数据包。
当应答信息为失败时(REP字段不为X'00'),SOCKS服务必须在发送应答后很短的时间内终止与客户端的连接。从监测到失败到切断链接,不能超过10秒。
如果应答为成功(REP字段为X'00'),并且请求类型为BIND或CONNECT时,客户端就可以传输被中继的数据包了。
注意: 如果选择的认证方案要求对数据进行用于检查完整性或确保安全性的的封装,则被中继数据包信息必须以规定的格式封装。与此相似,数据包到达SOCKS服务器后,服务器按规定的格式封装数据包。
基于UDP转发时,客户端必须将它的数据报发送到由BND.ADDR和BND.PORT指定的UDP中继服务器地址及对应端口。如果选择的认证方案要求对数据进行用于检查完整性或确保安全性的的封装,则被中继数据包信息必须以规定的格式封装。与此相似,数据包到达SOCKS服务器后,服务器按规定的格式封装数据包。
每个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'
- DST.ADDR:目的地址
- DST.PORT:目的端口
- DATA:用户数据报文
当UDP中继服务器决定中继一个UDP数据报时,它会自己默默执行,而不会向客户端发出任何通知。类似地,它会删除它不能或不会传递的数据报。当UDP中继服务器接收来自远程主机的应答数据报时,它必须使用上面的UDP请求头部封装数据报,并且需要进行基于身份验证方法(如果有)的封装。
UDP中继服务器必须从SOCKS服务器上获得客户端的IP地址,并将数据报发送到UDP ASSOCIATE应答中给定的端口号。如果数据报来自一个与指定的IP地址不同的IP,那么该数据报会被丢弃。它必须从任何源IP地址中删除任何来自于特定关联的IP地址的数据。
FRAG字段表明这个数据报是否是多分片UDP数据报中的一片。实现此功能时,FRAG字段为X'00'意味着这个数据包为独立数据包;为其他值时,按数字大小排序数据包序列。此字段用1~127之间的值代表数据包在分片序列中的位置。所有接收端都需要为分片序列提供一个重组队列和一个重组计时器,并且重组队列必须在重组计时器超时后重新初始化,并丢弃超时的数据报。如果一个新到达的数据包比当前在处理的数据包序列中的最大FRAG值小时,也需要重新初始化重组队列。重组计时器阈值必须小于5秒。除此外,我们建议应用程序尽可能不要使用分片。
可以选择是否使用分片:如果某SOCKS连接不支持分片,所有FRAG字段不为X'00'的数据包都必须被丢弃。
由于SOCKS实现在支持UDP转发时会在原始UDP数据区前增加一个SOCKS协议相关的头,因此为UDP数据区分配空间时要为这个头留足空间:
ATYP | 头占用字节 |
---|---|
X'01' | 10 |
X'03' | 262 |
X'04' | 20 |
本文档描述了一种应用层的,可以穿透IP网络防火墙的协议,基于本协议的穿透高度依赖于选择哪种认证方法和加密方案,因此网络管理员应当谨慎选择加密方案。