这是我参与「第五届青训营 」伴学笔记创作活动的第 2 天
Socks5简介
SOCKS是一种网络传输协议,为Socket Secure的缩写,主要用于客户端与外网服务器之间通讯的中间传递。它在使用TCP/IP协议通讯的前端机器和服务器机器之间扮演一个中介角色,使得内部网中的前端机器变得能够访问Internet网中的服务器。
这篇文章主要来介绍如何通过GO语言来完成一个简单的Socks5代理服务器。
Socks5代理
用GO语言来完成Socks5主要用到net
模块的内容,同时要求你了解在socks5协议的一些消息格式。
根据以下流程图,在代理通信过程中,主要包含三个步骤:
- 代理服务器和客户端Client进行认证
- 代理服务器获取目标Server的信息,建立TCP连接
- 代理服务器作为中间角色,互相传送消息
认证阶段
客户端向代理服务器发送认证消息,格式为:
VER | NMMETHODS | METHODS |
---|---|---|
1 | 1 | 1 to 255 |
- VER: 协议版本,socks5为
0x05
- NMETHODS:METHODS部分的长度
- METHODS: METHODS是客户端支持的认证方式列表,每个方法占1字节。当前的定义是
- 0x01 GSSAPI
- 0x02 用户名、密码认证
- 0x03 - 0x7F由IANA分配(保留)
- 0x80 - 0xFE为私人方法保留
- 0xFF 无可接受的方法
于是就需要这样读取以下信息:
ver, err := reader.ReadByte()
methodSize, err := reader.ReadByte()
method := make([]byte, methodSize)
_, err = io.ReadFull(reader, method)
接下来代理服务器向客户端返回认证成功消息,格式为
VER | METHOD |
---|---|
1 | 1 |
我们这里VER返回socks的协议版本,METHOD返回0,见上面的说明。
_, err = conn.Write([]byte{socks5Ver, 0x00})
连接阶段
连接阶段需要获取客户端发过来的关于目标Server的信息,消息格式为:
VER | CMD | RSV | ATYP | DST.ADDR | DST.PORT |
---|---|---|---|---|---|
1 | 1 | X'00' | 1 | Variable | 2 |
- VER 版本号,socks5的值为0x05
- CMD 0x01表示CONNECT请求
- RSV 保留字段,值为0x00
- ATYP 目标地址类型,DST.ADDR的数据对应这个字段的类型。
- 0x01 IPv4地址,DST.ADDR部分4字节长度
- 0x03 域名,DST.ADDR部分第一个字节为域名长度,DST.ADDR剩余的内容为域名,没有\0结尾。
- 0x04 IPv6地址,16个字节长度。
- DST.ADDR 一个可变长度的值
- DST.PORT 目标端口,固定2个字节
代理服务器解析消息内容,并根据atyp获取目标服务的地址和端口:
buf := make([]byte, 4)
_, err := io.ReadFull(reader, buf)
ver, cmd, atyp := buf[0], buf[1], buf[3]
// ...
解析后,代理服务器就可以向DST发起TCP连接,这里借助net
模块即可完成
dest, err := net.Dial("tcp", fmt.Sprintf("%v:%v", addr, port))
发起TCP连接成功后,代理服务器需要返回给客户端一个同样的如上的消息格式,这里可以正常根据上面的IP返回,也可以简单处理返回一下:
_, err = conn.Write([]byte{0x05, 0x00, 0x00, 0x01, 0, 0, 0, 0, 0, 0})
通信阶段
互发消息就可以了,不需要再解析消息格式了。
go func() {
_, _ = io.Copy(dest, conn)
cancel()
}()
go func() {
_, _ = io.Copy(conn, dest)
cancel()
}()
<-ctx.Done()
代理检验
你可以借助curl
命令或SwitchyOmega
这一个浏览器插件来启动一个socks5客户端进行检测,可模仿如下配置: