Socks5协议

最后更新时间: 2018-09-27 | 作者: AberSheeran | 捐助

SOCKS是一种网络传输协议,主要用于客户端与外网服务器之间通讯的中间传递。SOCKS是"SOCKetS"的缩写。

当防火墙后的客户端要访问外部的服务器时,就跟SOCKS代理服务器连接。这个代理服务器控制客户端访问外网的资格,允许的话,就将客户端的请求发往外部的服务器。

这个协议最初由David Koblas开发,而后由NEC的Ying-Da Lee将其扩展到版本4。最新协议是版本5,与前一版本相比,增加支持UDP、验证,以及IPv6。

根据OSI模型,SOCKS是会话层的协议,位于表示层与传输层之间。

协商认证

客户端使用TCP协议连接上Socks5服务器之后,发送一个数据包:

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

服务器需要回应一个数据包

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

密码认证

若服务器选择密码认证,则客户端接下来需要发送如下数据包

+-----+------+----------+------+----------+
| VER | ULEN |  UNAME   | PLEN |  PASSWD  |
+-----+------+----------+------+----------+
|  1  |  1   | 1 to 255 |  1   | 1 to 255 |
+-----+------+----------+------+----------+

服务器将解析该数据包,并使用其用户名与密码进行验证,然后回复一个数据包

+-----+--------+
| VER | STATUS |
+-----+--------+
|  1  |   1    |
+-----+--------+

请求代理

客户端在认证成功之后,需要发送一个数据包来请求服务端:

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

服务端在对请求进行评估判断之后,回应数据包:

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

Connect方法

当服务端接收到的数据包中CMD为X'01'时,服务器使用Connect方法进行代理。

此时,服务端尝试使用TCP协议连接对应的(DST.ADDR, DST.PORT),根据连接的情况,决定REP的值。

如果连接成功,BND.ADDR,BND.PORT没有太大的意义,象征性的填写Socks服务端在此次连接中使用的ADDR和PORT即可。

Bind方法

Bind方法使用于目标主机需要主动连接客户机的情况

当服务端接收到的数据包中CMD为X'02'时,服务器使用Bind方法进行代理。使用Bind方法代理时服务端需要回复客户端至多两次数据包。

服务端使用TCP协议连接对应的(DST.ADDR, DST.PORT),如果失败则返回失败状态的数据包并且关闭此次会话。如果成功,则监听(BND.ADDR, BND.PORT)来接受请求的主机的请求,然后返回第一次数据包,该数据包用以让客户机发送指定目标主机连接客户机地址和端口的数据包。

在目标主机连接服务端指定的地址和端口成功或失败之后,回复第二次数据包。此时的(DST.ADDR, DST.PORT)应该为目标主机与服务端建立的连接的地址和端口。

UDP转发

使用UDP ASSOCIATE时,客户端的请求包中(DST.ADDR, DST.PORT)不再是目标的地址,而是客户端指定本身用于发送UDP数据包的地址和端口。

为了支持UDP转发,服务端应该建立一个UDP服务器用以接收客户端发送过来的UDP包。然后在TCP连接中回复一个数据包,(BND.ADDR, BND.PORT)应该是UDP服务器绑定的地址与端口。需要注意,在这个会话没有结束之前,TCP连接不能中断,否则客户端会认为服务端挂了。

UDP的数据处理

相比于TCP单纯的数据转发,UDP的数据处理稍显复杂。

UDP服务器接收到信息之后,对来源进行筛选,如果收到的UDP包来自第一次收到的请求包中的(DST.ADDR, DST.PORT),那么,就认为是来自客户端,需要对数据包的头部进行解析

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

当接收到数据包后,将DATA转发至此次数据包头部指定的(DST.ADDR, DST.PORT)。如果UDP服务器接收到来自对应的(DST.ADDR, DST.PORT)的UDP包,则需要将UDP包加上同样的头部转发至客户端。(这意味着你需要在UDP服务器中建立一张Client与远程主机的IP:PORT的映射表.)


RFC1928与RFC1929,原文档言简意赅,令人难懂。本文是我结合实际使用与RFC文档所写

一个Python3.6+的Socks5 aio-server

标签: Socks5
收录于#RFC