SOCKS协议
SOCKS是一种网络传输协议,主要用于客户端与外网服务器之间通讯的中间传递。SOCKS是”SOCKet Secure”的缩写。
当防火墙后的客户端要访问外部的服务器时,就跟SOCKS代理服务器连接。这个代理服务器控制客户端访问外网的资格,允许的话,就将客户端的请求发往外部的服务器。
这个协议最初由David Koblas开发,而后由NEC的Ying-Da Lee将其扩展到SOCKS4。最新协议是SOCKS5,与前一版本相比,增加支持UDP、验证,以及IPv6。
根据OSI模型,SOCKS是会话层的协议,位于表示层与传输层之间。
SOCKS协议不提供加密。
SOCKS5协议
协商
- 客户端连接到 SOCKS 服务端,发送的协议版本与认证方法数据包格式:
1 | +----+----------+----------+ |
- VER 是指协议版本,因为是 socks5,所以值是 0x05,一个字节
- NMETHODS 是指有多少个可以使用的方法,也就是客户端支持的认证方法,一个字节
- METHODS 就是方法值,1-255个字节,有多少个方法就有多少个byte
- 服务端要从给定的方法列表中选择一个方法并返回选择报文:
1 | +----+--------+ |
VER与METHOD的取值同上
目前已定义方法如下:
- 0x00 NO AUTHENTICATION REQUIRED 不需要认证
- 0x01 GSSAPI 参考:GSSAPI
- 0x02 USERNAME/PASSWORD 用户名密码认证
- 0x03 to 0x7F IANA ASSIGNED 一般不用。INNA保留。
- 0x80 to 0xFE RESERVED FOR PRIVATE METHODS 保留作私有用处。
- 0xFF NO ACCEPTABLE METHODS 不接受任何方法/没有合适的方法
请求
一旦认证方法对应的协商完成,客户端就可以发送请求细节了。如果认证方法为了完整性或者可信性的校验,需要对后续请求报文进行封装,则后续请求报文都要按照对应规定进行封装。
SOCKS 请求为如下格式:
1 | +----+-----+-------+------+----------+----------+ |
- VER 还是版本,取值是 0x05
- CMD 是指要做啥,取值如下:
- CONNECT 0x01 连接
- BIND 0x02 端口监听(也就是在Server上监听一个端口)
- UDP ASSOCIATE 0x03 使用UDP
- RSV 是保留位,值是 0x00
- ATYP 是目标地址类型,有如下取值:
- 0x01 IPv4
- 0x03 域名
- 0x04 IPv6
- DST.ADDR 就是目标地址的值了,如果是IPv4,那么就是4 bytes,如果是IPv6那么就是16 bytes,如果是域名,那么第一个字节代表接下来有多少个字节是表示目标地址
- DST.PORT 两个字节代表端口号
SOCKS 服务端会根据请求类型和源、目标地址,执行对应操作,并且返回对应的一个或多个报文信息。
回复
客户端与服务端建立连接并完成认证之后就会发送请求信息,服务端执行对应请求并返回如下格式的报文:
1 | +----+-----+-------+------+----------+----------+ |
- VER 还是版本,值是 0x05
- REP 是状态码,取值如下:
- 0x00 succeeded
- 0x01 general SOCKS server failure
- 0x02 connection not allowed by ruleset
- 0x03 Network unreachable
- 0x04 Host unreachable
- 0x05 Connection refused
- 0x06 TTL expired
- 0x07 Command not supported
- 0x08 Address type not supported
- 0x09 to 0xff unassigned
- RSV 保留位,取值为 0x00
- ATYP 是目标地址类型,有如下取值:
- 0x01 IPv4
- 0x03 域名
- 0x04 IPv6
- DST.ADDR 就是目标地址的值了,如果是IPv4,那么就是4 bytes,如果是IPv6那么就是16 bytes,如果是域名,那么第一个字节代表接下来有多少个字节是表示目标地址
- DST.PORT 两个字节代表端口号
数据转发
- 到了这个阶段基本就是数据转发了,tcp就直接转发,udp还须要做点工作。
- 当一个响应(REP值不为
X00
)指示失败的时候,SOCKS服务器必须在发送这个响应后立刻断开这条TCP连接。这必须发生在检测到引起失败的原因之后的10秒内。 - 如果响应码(REP值为
X00
)指示成功,而且这次请求是BIND或者CONNECT,那么客户端可以立刻开始数据传输。如果选择的认证方法支持针对完整性、认证或私密性目的封装,要传输的数据应该包装在所依赖的认证方法的封装中。同样地,当针对客户端的响应数据到达SOCKS服务器的时候,服务器也必须根据使用的认证方法来封装数据。
- 当一个响应(REP值不为
- 一个基于UDP的客户端必须将它的数据报发送到UDP中继服务器的指定端口——该端口在针对UDP ASSOCIATE的响应中的BND.PORT域指明。如果选择的认证方法提供了针对认证、完整性或私密性目的的封装,数据报必须使用恰当的封装方式进行包装。每一个UDP数据报都随身携带了一个UDP请求头:
1 | +----+------+------+----------+----------+----------+ |
- RSV 保留字段,应当置为 X‘0000’
- FRAG 当前帧序号
- ATYP 地址类型
- IPV4 X‘01’
- 域名 X‘03’
- IPV6 X‘04’
- DST.ADDR 目的地址
- DST.PORT 目的端口
- DATA 用户数据
当一个UDP中继服务器决定中继一个UDP数据报的时候,它会默默的做——不会给客户端返回任何通知。同样的,如果它不能中继数据报那它就会丢弃数据报。当一个UDP中继服务器从远端主机收到一个响应数据报文的时候,它必须根据使用上述的UDP请求头对该响应报文进行封装,然后再进行所依赖的认证方法的封装处理。
SOCKS5 服务器(Python)
不需要认证:
1 | import select |
需要认证:
1 | import logging |
客户端
1 | import socket |
参考:
https://zh.wikipedia.org/wiki/SOCKS
https://www.ietf.org/rfc/rfc1928.txt