本文主要介绍 socks5 协议内容以及其中用到的三种代理方法
代理服务器:负责转发客户端请求的服务器
代理服务器在网络中转发流量。 它是一个中间服务器,将客户端与其请求的目的地分开。
socks 是会话层协议,主要用于客户端与外网服务器之间通讯的中间传递。SOCKS是"SOCKet Secure"的缩写。
协议详解
连接
客户端首先和代理服务器进行三次握手建立连接
协商认证
连接成功后进行协商认证,发送一个数据包
±----±---------±---------+
| VER | NMETHODS | METHODS |
±----±---------±---------+
| 1 | 1 | 1 to 255 |
±----±---------±---------+
VER: 协议版本号
NMETHODS:客户端支持的认证方法数量
METHODS:每个byte对应一个认证方法
X’00’ 不需要身份验证(NO AUTHENTICATION REQUIRED)
X’01’ GSSAPI
X’02’ 用户密码认证(USERNAME/PASSWORD)
X’03’ to X’7F’ IANA ASSIGNED
X’80’ to X’FE’ RESERVED FOR PRIVATE METHODS
代理服务器返回一个应答数据包
±----±-------+
| VER | METHOD |
±----±-------+
| 1 | 1 |
±----±-------+
VER: 指定协议版本号,此处为X’05’
METHOD: 指定认证方法。该方法应从客户端提供的认证方法中挑选一个,或者是X’FF’用以拒绝认证
请求代理
客户端在认证成功之后,需要发送一个数据包来请求服务端:
±----±----±------±-----±---------±---------+
| 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 ASSOCIATE X’03’
RSV: 预留位置,标准Socks5应为X’00’
ATYP: 指定DST.ADDR的类型
IPV4地址: X’01’
域名: X’03’
IPV6地址: X’04’
DST.ADDR: 目标的地址,长度由ATYP指定
IPV4: 4个byte
域名: 第一个byte指定长度n,其后跟着n个byte标识域名
IPV6: 16个byte
DST.PORT: 目标的端口
客户端在认证成功之后,需要发送一个数据包来请求服务端:
±----±----±------±-----±---------±---------+
| 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’ 不支持的CMD
X’08’ 不支持的ATYP
X’09’ to X’FF’ Socks5标准中没有分配对应的状态
RSV: 预留位
ATYP: BND.ADDR的类型
IPV4: X’01’
域名: X’03’
IPV6: X’04’
BND.ADDR: 服务端指定的地址,长度由ATYP指定
IPV4: 4个byte
域名: 第一个byte指定长度n,其后跟着n个byte标识域名
IPV6: 16个byte
BND.PORT: 服务端指定的端口
CONNECT 方法:
这个时最常用的代理方法,当服务端接收到的数据包中 CMD 为 0x01 时,服务器使用 Connect 方法进行代理。
此时,服务端尝试使用TCP协议连接对应的 (DST.ADDR, DST.PORT),根据连接的情况,决定 REP 的值。
如果连接成功,回复的数据包中的 BND.ADDR,BND.PORT 没有太大的意义,象征性的填写Socks 服务端在此次连接中使用的 ADDR 和 PORT 即可。
BIND 方法
Bind方法使用于目标主机需要主动连接客户机的情况(如 FTP 协议)
当服务端接收到的数据包中 CMD 为 0x02 时,服务器使用 Bind 方法进行代理。使用 Bind 方法代理时服务端需要回复客户端至多两次数据包。
服务端使用 TCP 协议连接对应的 (DST.ADDR, DST.PORT),如果失败则返回失败状态的数据包并且关闭此次会话。如果成功,则监听 (BND.ADDR, BND.PORT) 来接受请求的主机的请求,然后返回第一次数据包,该数据包用以让客户机发送指定目标主机连接客户机地址和端口的数据包。
在目标主机连接服务端指定的地址和端口成功或失败之后,回复第二次数据包。此时的(BND.ADDR, BND.PORT) 应该为目标主机与服务端建立的连接的地址和端口。
FTP 实例:
FTP Client – SOCKS Client – SOCKS Server – FTP Server
FTP Client 试图建立 FTP 控制流。SOCKS Client 向 SOCKS Server 发送 CONNECT 请求,后者响应请求,最终FTP控制流建立。CONNECT 请求包中指明 FTPServer.ADDR / FTPServer.PORT。
2. FTP Client 试图建立 FTP 数据流。SOCKS Client 建立新的到 SOCKS Server 的 TCP 连接,并在新的 TCP 连接上发送 BIND 请求。BIND 请求包中仍然指明 FTPSERVER.ADDR / FTPSERVER.PORT。
-
SOCKS Server 收到 BIND 请求,创建新套接字,侦听在 AddrA/PortA 上,并向 SOCKSClient 发送第一个 BIND 响应包,包中 BND.ADDR/BND.PORT 即 AddrA/PortA。
-
SOCKS Client 收到第一个 BIND 响应包。FTP Client 通过 FTP 控制流向 FTP Server 发送 PORT 命令,通知 FTP Server 应该主动建立到 AddrA/PortA 的 TCP 连接。
-
FTP Server 收到 PORT 命令,主动建立到 AddrA/PortA 的 TCP 连接,假设 TCP 连接相关四元组是: AddrB,PortB,AddrA,PortA
-
SOCKS Server 收到来自 FTP Server 的 TCP 连接请求,向 SOCKS Client 发送第二个BIND 响应包,包中 BND.ADDR/BND.PORT 即 AddrB/PortB。然后 SOCKS Server 开始转发FTP数据流。
UDP ASSOCIATE 方法:
使用 UDP ASSOCIATE 时,客户端的请求包中 (DST.ADDR, DST.PORT) 不再是目标的地址,而是客户端指定本身用于发送 UDP 数据包的地址和端口。
为了支持 UDP 转发,服务端应该建立一个 UDP 服务器用以接收客户端发送过来的 UDP 包。然后在TCP连接中回复一个数据包,(BND.ADDR, BND.PORT)应该是UDP服务器绑定的地址与端口。
UDP服务器接收到信息之后,对来源进行筛选,如果收到的UDP包来自第一次收到的请求包中的 (DST.ADDR, DST.PORT),那么,就认为是来自客户端,需要对数据包的头部进行解析,获取到该数据包需要到达的地址
±----±-----±-----±---------±---------±---------+
| RSV | FRAG | ATYP | DST.ADDR | DST.PORT | DATA |
±----±-----±-----±---------±---------±---------+
| 2 | 1 | 1 | Variable | 2 | Variable |
±----±-----±-----±---------±---------±---------+
RSV: 保留字段,默认X’0000’。
FRAG: 该数据包的片段序号,如果值为X’00’则说明该数据包为独立数据包,如果为1~127的某个值,则说明为整个数据包的一个片段。
当收到数据包的片段之后,如果构建的Socks5代理服务器支持UDP分包代理,那么需要接收包之后,进行一个缓存拼接,去掉头部信息之后根据序号从小到大拼接。但这种方法较为复杂,服务器可以不接受此种方式。即,丢弃所有FRAG≠X’00’的数据包。
ATYP: 指定DST.ADDR的类型
IPV4: X’01’
域名: X’03’
IPV6: X’04’
DST.ADDR: 该数据包希望到达的目标地址
DST.PORT: 该数据包希望到达的目标端口
DATA: 实际要传输的数据