这是我参与「第五届青训营」笔记创作活动的第5天!
案例三 SOCKS5代理
案例题目
Socks5概述
Socks5协议是一款广泛使用的代理协议,它在使用TCP/IP协议通讯的客户端和服务器之间扮演一个中介角色,使得内部网中的客户端变得能够访问Internet网中的服务器,或者使C/S(Client和Server)之间的通讯更加安全。SOCKS5 代理服务器通过将客户端发来的请求转发给真正的目标服务器, 模拟了一个客户端请求操作。在这里,客户端和SOCKS5代理服务器之间也是通过TCP/IP协议进行通讯,客户端将原本要发送给真正服务器的请求先发送给SOCKS5服务器,然后SOCKS5服务器再将请求转发给真正的服务器。
Socks5代理优点
-
绕过互联网块
-
没有程序,协议或流量限制
-
更快,更可靠的连接
-
错误减少,整体性能提升
-
在P2P平台上表现更好
Socks5原理流程图(可分为4个阶段)
- 协商阶段
- 认证阶段
- 请求阶段
- relay阶段
思路分析
1. TCP-echo sever搭建
具体代码实现 主要是process函数的实现
package main
import (
"bufio"
"log"
"net"
)
func main() {
server, err := net.Listen("tcp", "127.0.0.1:1080")
if err != nil {
panic(err)
}
for {
client, err := server.Accept()
if err != nil {
log.Printf("Accept failed %v", err)
continue
}
go process(client)
}
}
func process(conn net.Conn) {
defer conn.Close() //退出时关闭连接
reader := bufio.NewReader(conn) //创建只读待缓冲流
for {
b, err := reader.ReadByte() //每次读一个字节
if err != nil {
break
}
_, err = conn.Write([]byte{b}) //写入(这里[]byte做强制类型转换)
if err != nil {
break
}
}
}
运行后用nc命令进行测试(我的电脑好像nc命令没法识别。。希望懂的友友给点建议)
2. 认证阶段auth函数
修改process函数删掉死循环
func process(conn net.Conn) {
defer conn.Close()
reader := bufio.NewReader(conn)
err := auth(reader, conn)
if err != nil {
log.Printf("client %v auth failed:%v", conn.RemoteAddr(), err)
return
}
log.Println("auth success")
}
Socks5认证格式
// +----+----------+----------+
// |VER | NMETHODS | METHODS |
// +----+----------+----------+
// | 1 | 1 | 1 to 255 |
// +----+----------+----------+
// VER: 协议版本,socks5为0x05
// NMETHODS: 支持认证的方法数量
// METHODS: 对应NMETHODS,NMETHODS的值为多少,METHODS就有多少个字节。RFC预定义了一些值的含义,内容如下:
// X’00’ NO AUTHENTICATION REQUIRED
// X’02’ USERNAME/PASSWORD
// +----+--------+
// |VER | METHOD |
// +----+--------+
// | 1 | 1 |
// +----+--------+
// VER: 协议版本,socks5为0x05
// NMETHOD: 服务器端匹配结果
认证方式:
- 0x00: 无验证需求
- 0x01: GSSAPI,通用安全服务应用程序接口
- 0x02: USERNAME&PASSWORD,用户名密码验证
- 0x03 - 0x07: IANA ASSIGNED,IANA分配
- 0x80 - 0xfe: RESERVED FOR PRIVATE METHODS,私人方法保留
- 0xff: NO ACCEPTABLE METHODS,无可接受方法
如果服务器返回是0xff,则表明服务端不支持客户端所有的认证方式,也就是认证失败。 而如果你不需要认证,则直接发送0x00即可
3. 请求阶段格式
// +----+-----+-------+------+----------+----------+
// |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 一个可变长度的值
// DST.PORT 目标端口,固定2个字节
对于CMD字段:
- 0x01: CONNECT请求
- 0x02: BIND请求
- 0x03: UDP转发
对于ATYP字段
- 0x01: IPv4地址,DST.ADDR部分4字节长度
- 0x03: 域名,DST.ADDR部分第一个字节为域名长度,DST.ADDR剩余的内容为域名,没有\0结尾
- 0x04: IPv6地址,16个字节长度
4. 返回阶段格式
// +----+-----+-------+------+----------+----------+
// |VER | REP | RSV | ATYP | BND.ADDR | BND.PORT |
// +----+-----+-------+------+----------+----------+
// | 1 | 1 | X'00' | 1 | Variable | 2 |
// +----+-----+-------+------+----------+----------+
// VER socks版本,这里为0x05
// REP Relay field,内容取值如下 X’00’ succeeded
// RSV 保留字段
// ATYPE 地址类型
// BND.ADDR 服务绑定的地址
// BND.PORT 服务绑定的端口DST.PORT
对于REP应答字段: 除了0x00其他都是出现错误
- 0x00: 成功
- 0x01: 普通SOCKS服务器连接失败
- 0x02: 现有规则不允许连接
- 0x03: 网络不可达
- 0x04: 主机不可达
- 0x05: 连接被拒
- 0x06: TTL超时
- 0x07: 不支持的命令
- 0x08: 不支持的地址类型
- 0x09 - 0xff: 未定义
对于ATYP字段
- 0x01: IPv4地址,DST.ADDR部分4字节长度
- 0x03: 域名,DST.ADDR部分第一个字节为域名长度,DST.ADDR剩余的内容为域名,没有\0结尾
- 0x04: IPv6地址,16个字节长度
总结
SOCKS5这部分运用了许多计算机网络中的内容,包含了TCP三握手和建立连接等知识。与SOCKS5协议不同,HTTP代理是通过HTTP协议进行的,HTTP代理服务器软件了解通讯包的内部结构,在转发过程中还要对通讯进行某种程度的修改和转换。和HTTP代理协议不同,SOCKS5实际上是一个传输层的代理协议OCKS工作在比HTTP代理更低的层次,SOCKS使用握手协议来通知代理软件其客户端试图进行SOCKS连接,然后尽可能透明的进行操作。也就是说,SOCKS希望的是充当一个透明的代理中介,尽可能的不对请求数据进行过多的更改。而常规的代理(如HTTP)可能会解释和重写报头。
GO语言学习路线
完整的Go语言学习流程图如下,保持谦虚,保持努力!