SOCKS Protocol Version 5是对Version 4的扩展。主要增加了安全认证,UDP和IPv6的支持。
SOCKS5 协议在概念上属于应用层(application layer)和传输层(transport layer)之间的垫片层(shim-layer),或者说它属于会话层。所以它不能代理网络层的协议,比如ICMP等;能代理应用层基于TCP,UDP的协议,比如 HTTP、SSH、FTP等。
应用程序使用SOCKS5代理,需要重新编写程序,支持将直接和目标服务器建立的连接文件描述符,替换成通过SOCKS5建立的连接文件描述符。
下文先描述下SOCKS5协议内容,然后举几个例子,期望能对读者有帮助。
SOCKS5协议内容
代理服务器侦听TCP 1080端口(SOCKS默认端口), 客户端向其建立连接。之后的通信要符合协议规定。
首先,客户端发送版本/模式选择消息,告诉代理服务器自己支持的认证模式:
VER:版本号,SOCKS5固定为0x05; METHOD就一个,服务器选择的之后要用的模式: 0x00 不需要验证 0x01 通用安全服务应用程序接口(GSSAPI) 0x02 用户名密码验证 0x03 至 0x7f 互联网数字分配机构(IANA)定义的方式 0x80 至 0xfe 保留给私有方式使用的 0xff 没有可用的模式 之后,客户端使用刚才协商的模式进行验证身份等,并且有些模式可能 需要对数据进行特定的封装,之后的交互可能是经过特定封装的。 具体验证封装细节此处省略。 然后,客户端向服务器发代理请求: (之后表格中的数字默认是字节数,或者固定值,或者长度可变)
VER:版本号,SOCKS5固定0x05 CMD:后文会详细介绍三者的示例 连接(CONNECT) 0x01 主动连接目标服务器 绑定(BIND) 0x02 绑定一个地址,目标服务器反向连接过来(FTP的主动模式) UDP关联(UDP ASSOCIATE) 0x03 RSV:保留字段(RESERVED) ATYP:地址类型选择 IPv4: 0x01,长度4字节 域名: 0x03,长度由ADDR的第一个字节指定 IPv6: 0x04,长度16字节 DST.ADDR: 期望的地址,长度由ATYPE类型决定,详见下文 DST.PORT:期望的端口,详见下文
DST.ADDR和DST.PORT根据CMD的不同有不同的意义:
对于CONNECT命令,它们代表要连接的目标服务器的地址和端口;
对于BIND命令,代理服务器侦听一个地址,等待目标服务器连接过来(FTP的主动模式),ADDR和PORT是指定目标服务器连接过来时使用的地址和端口,代理服务器校验使用。
对于UDP关联命令,这两个值是指客户端即将使用的和代理服务器通信的地址和端口。也是代理服务器校验用,不需要校验可以填0。
有点绕,下文的具体示例会有所体现。
再之后,代理服务器返回对应的连接建立情况:
VER:版本号,SOCKS5固定为 0x05
REP: 响应内容:
o 0x00 成功
o 0x01 普通的SOCKS服务失败
o 0x02 连接不被规则允许
o 0x03 网络不可达
o 0x04 主机不可达
o 0x05 连接被拒绝
o 0x06 连接超时
o 0x07 命令不支持
o 0x08 地址类型不支持
o 0x09 至 0xff 未定义
RSV:保留字段 固定 0x00ATYP:地址类型选择
IPv4: 0x01,长度4字节
域名: 0x03,长度由ADDR的第一个字节指定
IPv6: 0x04,长度16字节
BND.ADDR:代理服务器绑定的地址
BND.PORT:代理服务器绑定的端口
同样,BND.ADDR和BND.PORT的意义也依赖请求的CMD命令:
对于CONNECT命令,这俩代表了代理服务器连接目标服务器使用的本地地址;
对于BIND命令,代理服务器会返回两条消息,第一条消息,告诉客户端我侦听好地址了,侦听的地址是什么。第二条消息,告诉客户端目标服务器连上来了,它的地址是什么。
对于UDP关联命令,告诉客户端代理服务器侦听的UDP端口是什么,之后客户端向此地址发送代理请求。
对于TCP代理,之后只需要在对应连接上传数据就行了。
但如果是UDP代理,那么客户端还需要封装特定的UDP格式,主要就是UDP没有连接概念,每个数据包的代理都要带上一个UDP代理头,表明目标服务器的地址。
RSV 保留位 固定0x0000
FRAG 分段编号
ATYP 地址类型,和上文一样:
IPv4: 0x01,长度4字节
域名: 0x03,长度由ADDR的第一个字节指定
IPv6: 0x04,长度16字节
DST.ADDR 目标服务器地址
DST.PORT 目标服务器端口
DATA 用户数据
当目标服务器返回数据时,代理服务器是否会加上UDP头我不太清楚,有文章说也会带,客户端还要剥离这个头才能取得数据,我觉得不带更好,后边示例先按返回数据不封装代理头描述。
TCP代理示例:
FTP代理示例:
被动模式下,两个连接都是客户端连接服务器,所以直接使用两次TCP代理模式即可。
下边是主动模式下,使用BIND命令反向连接:
首先建立发送命令的连接:
建立反向连接,发送数据:
UDP代理示例:
UDP代理,首先也是和代理服务器建立TCP连接,沟通建立UDP通道。之后在UDP通道代理UDP数据,这个TCP连接如果被关闭,UDP通道也会被关闭。如果代理服务器支持,可以用一个TCP连接,创建多个UDP通道。
下边就是UDP数据包的代理过程