RFC1928 Sock5

RFC1928-Sock5

SOCKS是一种网络传输协议,主要用于客户端与外网服务器之间通讯的中间传递。当防火墙后的客户端要访问外部的服务器时,就跟SOCKS代理服务器连接。这个代理服务器控制客户端访问外网的资格,允许的话,就将客户端的请求发往外部的服务器。根据OSI模型,SOCKS是会话层的协议,位于表示层与传输层之间。

基于TCP的实现

当一个基于TCP协议的客户端希望与一个只能通过防火墙可以到达的目标(这个由实现决定的)建立连接,它必须先建立一个与socks服务器上socks端口的TCP连接。通常这个TCP端口是1080。当连接建立后,客户端进入协议的“握手”过程:

  • 认证方式的选择;
  • 根据选中的方式进行认证;
  • 然后发送转发的请求。

socks服务器检查这个请求,根据结果,或建立合适的连接,或拒绝。

除非特别声明,所有出现的数据格式图中的十进制均表示相应域的字节长度,如果某域需要给定一个字节的值,用0x’hh’来表示这个字节中的值。如果某域中用到单词’Variable’,这表示该域的长度是可变的,且该长度定义在一个和这个域相关联(1-2个字节)的域中,或一个数据类型中。

第一步

创建与SOCKS5服务器的TCP连接后客户端需要先发送请求来协议版本及认证方式,格式为(以字节为单位):

VERNMETHODSMETHODS
111 - 255
  • VER是SOCKS版本,这里应该是0x05;
  • NMETHODS是METHODS部分的长度;
  • METHODS是客户端支持的认证方式列表,每个方法占1字节。当前的定义是:
    • 0x00 不需要认证
    • 0x01 GSSAPI
    • 0x02 用户名、密码认证
    • 0x03 - 0x7F由IANA分配(保留)
    • 0x80 - 0xFE为私人方法保留
    • 0xFF 无可接受的方法

服务器从客户端提供的方法中选择一个并通过以下消息通知客户端(以字节为单位):

VERMETHOD
11
  • VER是SOCKS版本,这里应该是0x05;
  • METHOD是服务端选中的方法。如果返回0xFF表示没有一个认证方法被选中,客户端需要关闭连接。

第二步

一旦方法选择商议结束,客户机就发送请求细节,如果商议方法包括了完整性检查的目的或机密性封装,则请求必然被封在方法选择的封装中。

SOCKS5请求格式(以字节为单位):

VERCMDRSVATYPDST.ADDRDST.PORT
110x001动态2
  • VER是SOCKS版本,这里应该是0x05;
  • CMD是SOCK的命令码
    • 0x01 表示CONNECT请求
    • 0x02 表示BIND请求
    • 0x03 表示UDP转发
  • RSV 0x00,保留字段
  • ATYP DST.ADDR类型
    • 0x01 IPv4地址,DST.ADDR部分4字节长度
    • 0x03 域名,DST.ADDR部分第一个字节为域名长度,DST.ADDR剩余的内容为域名,没有\0结尾。
    • 0x04 IPv6地址,16个字节长度。
  • DST.ADDR
    • 如果是IPv4地址,这里是big-endian序的4字节数据
    • 如果是FQDN,比如 www.nsfocus.net,这里将是: 0F 77 77 77 2E 6E 73 66 6F 63 75 73 2E 6E 65 74,注意,没有结尾的NUL字符,非ASCIZ串,第一字节是长度域
    • 如果是IPv6地址,这里是16字节数据。
  • DST.PORT 网络字节序表示的目的端口

服务器按以下格式回应客户端的请求(以字节为单位):

VERREPRSVATYPBND.ADDRBND.PORT
110x001Variable2
  • VER是SOCKS版本,这里应该是0x05;
  • REP应答字段
    • 0x00 表示成功
    • 0x01 普通SOCKS服务器连接失败
    • 0x02 现有规则不允许连接
    • 0x03 网络不可达
    • 0x04 主机不可达
    • 0x05 连接被拒
    • 0x06 TTL超时
    • 0x07 不支持的命令
    • 0x08 不支持的地址类型
    • 0x09 0xFF未定义
  • RSV 0x00,保留
  • ATYP BND.ADDR类型
    • 0x01 IPv4地址,DST.ADDR部分4字节长度
    • 0x03 域名,DST.ADDR部分第一个字节为域名长度,DST.ADDR剩余的内容为域名,没有\0结尾。
    • 0x04 IPv6地址,16个字节长度。
  • BND.ADDR 服务器绑定的地址
  • BND.PORT 网络字节序表示的服务器绑定的端口

验证

用户名密码验证

在客户端、服务端协商使用用户名密码认证后,客户端发出用户名密码,格式为(以字节为单位):

VERULENUNAMEPLENPASSWD
111 to 25511 to 255
  • VER:鉴定协议版本
  • ULEN:用户名长度
  • UNAME:用户名
  • PLEN:密码长度
  • PASSWD:密码

服务器鉴定后发出如下回应:

VERSTATUS
11
  • VER:鉴定协议版本
  • STATUS:鉴定状态

其中鉴定状态 0x00 表示成功,0x01 表示失败。鉴定失败后,server需关闭连接。

CONNECT

在对一个CONNECT命令的应答中,BND.PORT包含了服务器分配的用来连到目标机的端口号,BND.ADDR则是相应的IP地址。由于SOCKS服务器通常有多个IP,应答中的BND.ADDR常和客户端连到SOCKS服务器的那个IP不同。

SOCKS服务器可以利用DST.ADDR和DST.PORT,以及客户端源地址和端口来对一个CONNECT请求进行分析。

BIND

BIND请求通常被用在那些要求客户端接受来自服务器的连接的协议上。FTP是一个典型的例子。它建立一个从客户端到服务器端的连接来执行命令以及接收状态的报告,而使用另一个从服务器到客户端的连接来接收传输数据的要求(如LS,GET,PUT)。

建议只有在一个应用协议的客户端在使用CONNECT命令建立主连接后才可以使用BIND命令建立第二个连接。建议SOCKS服务器使用DST.ADDR和DST.PORT来评价BIND请求。

在一个BIND请求的操作过程中,SOCKS服务器要发送两个应答给客户端。当服务器建立并绑定一个新的套接口时发送第一个应答。BND.PORT字段包含SOCKS服务器用来监听进入的连接的端口号,BAND.ADDR字段包含了对应的IP地址。客户端通常使用这些信息来告诉(通过主连接或控制连接)应用服务器连接的汇接点。第二个应答仅发生在所期望到来的连接成功或失败之后。在第二个应答中,BND.PORT和BND.ADDR字段包含了连上来的主机的IP地址和端口号。

UDP ASSOCIATE

UDP ASSOCIATE请求通常是要求建立一个UDP转发进程来控制到来的UDP数据报。DST.ADDR和DST.PORT 字段包含客户端所希望的用来发送UDP数据报的IP地址和端口号。服务器可以使用这个信息来限制进入的连接。如果客户端在发送这个请求时没有地址和端口信息,客户端必须用全0来填充。当与UDP相应的TCP连接中断时,该UDP连接也必须中断。

应答UDP ASSOCIATE请求时,BND.PORT 和BND.ADDR字段指明了客户发送UDP消息至服务器的端口和地址。

应答处理

  • 当一个应答(REP值不等于00)指明出错时,SOCKS服务器必须在发送完应答消息后一小段时间内终止TCP连接。这段时间应该在发现错误后少于10秒。
  • 如果一个应答(REP值等于00)指明成功,并且请求是一个BIND或CONNECT时,客户端就可以开始发送数据了。如果协商的认证方法中有以完整性、认证和/或安全性为目的的封装,这些请求必须按照该方法所定义的方式进行封装。类似的,当以客户机为目的地的数据到达SOCKS服务器时,SOCKS服务器必须用正在使用的方法对这些数据进行封装。

基于UDP的实现

在UDP ASSOCIATE应答中由BND.PORT指明了服务器所使用的UDP端口,一个基于UDP协议的客户必须发送数据报至UDP转发服务器的该端口上。如果协商的认证方法中有以完整性、认证和/或安全性为目的的封装,这些数据报必须按照该方法所定义的方式进行封装。每个UDP数据报都有一个UDP请求头在其首部:

RSVFRAGATYPDST.ADDRDST.PORTDATA
211Variable2Variable

在UDP请求头中的字段是:

  • RSV 保留字段 0x0000
  • FRAG 当前的分段号
  • ATYP 后面的地址类型
    • IPV4:0x01
    • 域名:0x03
    • IPV6:0x04
  • DST.ADDR 目的地址
  • DST.PORT 以网络字节顺序出现的端口号
  • DATA 用户数据

当一个UDP转发服务器转发一个UDP数据报时,不会发送任何通知给客户端;同样,它也将丢弃任何它不能发至远端主机的数据报。当UDP转发服务器从远端服务器收到一个应答的数据报时,必须加上上述UDP请求头,并对数据报进行封装。

UDP转发服务器必须从SOCKS服务器得到期望的客户端IP地址,并将数据报发送到UDP ASSOCIATE应答中给定的端口号。如果数据报从任何IP地址到来,而该IP地址与该特定连接中指定的IP地址不同,那么该数据报会被丢弃。

FRAG字段指明数据报是否是一些分片中的一片。如果SOCKS服务器要实现这个功能,X’00’指明数据报是独立的;其他则越大越是数据报的尾端。介于1到127之间的值说明了该分片在分片序列里的位置。每个接收者都为这些分片提供一个重组队列和一个重组的计时器。这个重组队列必须在重组计时器超时后重新初始化,并丢弃相应的数据报。或者当一个新到达的数据报有一个比当前在处理的数据报序列中最大的FRAG值要小时,也必须重新初始化从组队列。重组计时器必须小于5秒。只要有可能,应用程序最好不要使用分片。

分片的实现是可选的;如果某实现不支持分片,所有FRAG字段不为0的数据报都必须被丢弃。
一个SOCKS的UDP编程界面(The programming interface for a SOCKS-aware UDP)必须报告当前可用UDP数据报缓存空间小于操作系统提供的实际空间。

  • 如果 ATYP是 0x01 - 10+method_dependent octets smaller
  • 如果 ATYP是 0x03 - 262+method_dependent octets smaller
  • 如果 ATYP是 0x04 - 20+method_dependent octets smaller