SOCKS5 简介
SOCKS5(Socket Secure 5)是一种网络协议,用于在计算机网络上提供代理服务。它允许客户端通过代理服务器与目标服务器进行通信,隐藏客户端的真实IP地址和身份信息。SOCKS5代理是一种传输层代理,工作在网络协议栈中的传输层,可以支持多种网络协议(如TCP和UDP)的代理。
通过 SOCKS5 代理,客户端可以在不直接与目标服务器通信的情况下,通过代理服务器来访问网络资源。这种代理方式对于保护客户端的隐私和实现网络匿名性非常有用,同时还可以绕过防火墙和地理限制。
SOCKS5 原理
-
客户端请求代理连接:客户端通过建立与 SOCKS5 代理服务器的连接来请求代理服务。通常,客户端会在其配置中指定代理服务器的地址和端口。
-
协商认证方式:一旦连接建立,客户端和代理服务器会协商认证方式。SOCKS5 协议支持多种认证方式,包括无需认证、用户名/密码认证等。客户端和代理服务器会选择一种双方都支持的认证方式进行验证。
-
请求目标服务器连接:认证完成后,客户端发送请求给代理服务器,包括目标服务器的地址和端口号。客户端还可以指定是否需要远程解析 DNS,以及是否启用 UDP 代理。
-
代理服务器连接目标服务器:代理服务器使用自己的 IP 地址和端口与目标服务器建立连接。这样,代理服务器充当了客户端和目标服务器之间的中间人。
-
代理数据传输:一旦代理服务器成功建立与目标服务器的连接,它就开始在客户端和目标服务器之间传输数据。客户端发送的数据会经过代理服务器中转,然后代理服务器将响应从目标服务器传回给客户端。
-
连接关闭:当客户端或目标服务器关闭连接时,代理服务器也会关闭与两者的连接。这标志着一次 SOCKS5 代理会话的结束。
v1 建立TCP连接
代码和原理
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})
if err != nil {
break
}
}
}
这段代码是一个简单的TCP服务器。它通过在本地监听 1080 端口,并接受客户端的连接。对于每个连接,它创建一个独立的 goroutine 来处理。
在处理连接的函数 process 中,首先使用 defer 关键字将连接的关闭操作推迟到函数返回之前,确保连接在处理完成后被关闭。然后,创建了一个 bufio.NewReader 用于从连接中读取数据。
随后,使用一个无限循环,每次从连接中读取一个字节,并将其写回连接中。如果读取或写入过程中发生错误,循环会被中断,连接会被关闭。
运行
v2 认证阶段
代码和原理
package main
import (
"bufio"
"fmt"
"io"
"log"
"net"
)
const socks5Ver = 0x05
const cmdBind = 0x01
const atypeIPV4 = 0x01
const atypeHOST = 0x03
const atypeIPV6 = 0x04
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)
err := auth(reader, conn)
if err != nil {
log.Printf("client %v auth failed:%v", conn.RemoteAddr(), err)
return
}
log.Println("auth success")
}
func auth(reader *bufio.Reader, conn net.Conn) (err error) {
// +----+----------+----------+
// |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, err := reader.ReadByte()
if err != nil {
return fmt.Errorf("read ver failed:%w", err)
}
if ver != socks5Ver {
return fmt.Errorf("not supported ver:%v", ver)
}
// 再读支持认证的方法数量
methodSize, err := reader.ReadByte()
if err != nil {
return fmt.Errorf("read methodSize failed:%w", err)
}
// 创建一个方法缓冲区
method := make([]byte, methodSize)
// 读取方法字节
_, err = io.ReadFull(reader, method)
if err != nil {
return fmt.Errorf("read method failed:%w", err)
}
log.Println("ver", ver, "method", method)
// +----+--------+
// |VER | METHOD |
// +----+--------+
// | 1 | 1 |
// +----+--------+
// 构造不需要认证的响应
_, err = conn.Write([]byte{socks5Ver, 0x00})
if err != nil {
return fmt.Errorf("write failed:%w", err)
}
return nil
}
这段代码是一个简单的 SOCKS5 服务器的实现。它通过监听本地的 1080端口,接受客户端的连接请求。当客户端与服务器建立连接后,会调用 process 函数进行处理。
在 process 函数中,首先调用 auth 函数进行认证。auth 函数实现了 SOCKS5 的认证过程。
在认证过程中,首先从客户端读取版本号(ver)和支持的认证方法数量(methodSize)。然后根据方法数量创建一个缓冲区,并读取对应数量的方法字节。接着,服务器向客户端发送一个不需要认证的响应,即版本号为 0x05,认证方法为 0x00。
如果认证过程出现错误,则会记录错误日志,并返回。如果认证成功,则会打印 "auth success"。
整个代码的作用是实现一个简单的 SOCKS5 服务器,用于接收客户端的连接请求,并进行简单的认证处理。目前代码中只实现了认证过程,并返回了不需要认证的响应。
运行
v3 请求阶段
代码和原理
package main
import (
"bufio"
"encoding/binary"
"errors"
"fmt"
"io"
"log"
"net"
)
const socks5Ver = 0x05
const cmdBind = 0x01
const atypeIPV4 = 0x01
const atypeHOST = 0x03
const atypeIPV6 = 0x04
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)
err := auth(reader, conn)
if err != nil {
log.Printf("client %v auth failed:%v", conn.RemoteAddr(), err)
return
}
err = connect(reader, conn)
if err != nil {
log.Printf("client %v auth failed:%v", conn.RemoteAddr(), err)
return
}
}
func auth(reader *bufio.Reader, conn net.Conn) (err error) {
// +----+----------+----------+
// |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, err := reader.ReadByte()
if err != nil {
return fmt.Errorf("read ver failed:%w", err)
}
if ver != socks5Ver {
return fmt.Errorf("not supported ver:%v", ver)
}
methodSize, err := reader.ReadByte()
if err != nil {
return fmt.Errorf("read methodSize failed:%w", err)
}
method := make([]byte, methodSize)
_, err = io.ReadFull(reader, method)
if err != nil {
return fmt.Errorf("read method failed:%w", err)
}
// +----+--------+
// |VER | METHOD |
// +----+--------+
// | 1 | 1 |
// +----+--------+
_, err = conn.Write([]byte{socks5Ver, 0x00})
if err != nil {
return fmt.Errorf("write failed:%w", err)
}
return nil
}
func connect(reader *bufio.Reader, conn net.Conn) (err error) {
// +----+-----+-------+------+----------+----------+
// |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个字节
// 创建缓冲区 读取前四个字段
buf := make([]byte, 4)
_, err = io.ReadFull(reader, buf)
if err != nil {
return fmt.Errorf("read header failed:%w", err)
}
// 验证前两个字段
ver, cmd, atyp := buf[0], buf[1], buf[3]
if ver != socks5Ver {
return fmt.Errorf("not supported ver:%v", ver)
}
if cmd != cmdBind {
return fmt.Errorf("not supported cmd:%v", cmd)
}
addr := ""
// 根据目标地址类型不同
switch atyp {
case atypeIPV4:
// IPV4地址的话 读4个字节
_, err = io.ReadFull(reader, buf)
if err != nil {
return fmt.Errorf("read atyp failed:%w", err)
}
addr = fmt.Sprintf("%d.%d.%d.%d", buf[0], buf[1], buf[2], buf[3])
case atypeHOST:
// 读取域名长度
hostSize, err := reader.ReadByte()
if err != nil {
return fmt.Errorf("read hostSize failed:%w", err)
}
host := make([]byte, hostSize)
_, err = io.ReadFull(reader, host)
if err != nil {
return fmt.Errorf("read host failed:%w", err)
}
addr = string(host)
case atypeIPV6:
return errors.New("IPv6: no supported yet")
default:
return errors.New("invalid atyp")
}
// 读端口号 复用之前定义好的缓冲区
_, err = io.ReadFull(reader, buf[:2])
if err != nil {
return fmt.Errorf("read port failed:%w", err)
}
port := binary.BigEndian.Uint16(buf[:2])
log.Println("dial", addr, port)
// +----+-----+-------+------+----------+----------+
// |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
_, err = conn.Write([]byte{0x05, 0x00, 0x00, 0x01, 0, 0, 0, 0, 0, 0})
if err != nil {
return fmt.Errorf("write failed: %w", err)
}
return nil
}
这段代码是一个简单的 SOCKS5 服务器实现,它可以处理客户端的连接请求并进行简单的认证和连接处理。
首先,在 main 函数中,通过 net.Listen 函数监听 127.0.0.1:1080 的 TCP 连接。然后使用 server.Accept 循环接受客户端的连接请求,并将连接交给 process 函数处理。
在 process 函数中,首先调用 auth 函数进行认证处理。认证过程主要是读取客户端发送的协议版本、支持的认证方法等信息,并返回一个不需要认证的响应。
接下来,调用 connect 函数进行连接处理。连接处理主要是读取客户端发送的目标地址类型、目标地址和目标端口,并返回一个连接成功的响应。
整个代码的作用是实现一个简单的 SOCKS5 服务器,用于接收客户端的连接请求,并进行认证和连接处理。
运行
v4 relay阶段
代码和原理
package main
import (
"bufio"
"context"
"encoding/binary"
"errors"
"fmt"
"io"
"log"
"net"
)
const socks5Ver = 0x05
const cmdBind = 0x01
const atypeIPV4 = 0x01
const atypeHOST = 0x03
const atypeIPV6 = 0x04
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)
err := auth(reader, conn)
if err != nil {
log.Printf("client %v auth failed:%v", conn.RemoteAddr(), err)
return
}
err = connect(reader, conn)
if err != nil {
log.Printf("client %v auth failed:%v", conn.RemoteAddr(), err)
return
}
}
func auth(reader *bufio.Reader, conn net.Conn) (err error) {
// +----+----------+----------+
// |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, err := reader.ReadByte()
if err != nil {
return fmt.Errorf("read ver failed:%w", err)
}
if ver != socks5Ver {
return fmt.Errorf("not supported ver:%v", ver)
}
methodSize, err := reader.ReadByte()
if err != nil {
return fmt.Errorf("read methodSize failed:%w", err)
}
method := make([]byte, methodSize)
_, err = io.ReadFull(reader, method)
if err != nil {
return fmt.Errorf("read method failed:%w", err)
}
// +----+--------+
// |VER | METHOD |
// +----+--------+
// | 1 | 1 |
// +----+--------+
_, err = conn.Write([]byte{socks5Ver, 0x00})
if err != nil {
return fmt.Errorf("write failed:%w", err)
}
return nil
}
func connect(reader *bufio.Reader, conn net.Conn) (err error) {
// +----+-----+-------+------+----------+----------+
// |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个字节
buf := make([]byte, 4)
_, err = io.ReadFull(reader, buf)
if err != nil {
return fmt.Errorf("read header failed:%w", err)
}
ver, cmd, atyp := buf[0], buf[1], buf[3]
if ver != socks5Ver {
return fmt.Errorf("not supported ver:%v", ver)
}
if cmd != cmdBind {
return fmt.Errorf("not supported cmd:%v", cmd)
}
addr := ""
switch atyp {
case atypeIPV4:
_, err = io.ReadFull(reader, buf)
if err != nil {
return fmt.Errorf("read atyp failed:%w", err)
}
addr = fmt.Sprintf("%d.%d.%d.%d", buf[0], buf[1], buf[2], buf[3])
case atypeHOST:
hostSize, err := reader.ReadByte()
if err != nil {
return fmt.Errorf("read hostSize failed:%w", err)
}
host := make([]byte, hostSize)
_, err = io.ReadFull(reader, host)
if err != nil {
return fmt.Errorf("read host failed:%w", err)
}
addr = string(host)
case atypeIPV6:
return errors.New("IPv6: no supported yet")
default:
return errors.New("invalid atyp")
}
_, err = io.ReadFull(reader, buf[:2])
if err != nil {
return fmt.Errorf("read port failed:%w", err)
}
port := binary.BigEndian.Uint16(buf[:2])
// 与ip和对应的端口号建立tcp连接
dest, err := net.Dial("tcp", fmt.Sprintf("%v:%v", addr, port))
if err != nil {
return fmt.Errorf("dial dst failed:%w", err)
}
defer dest.Close()
log.Println("dial", addr, port)
// +----+-----+-------+------+----------+----------+
// |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
_, err = conn.Write([]byte{0x05, 0x00, 0x00, 0x01, 0, 0, 0, 0, 0, 0})
if err != nil {
return fmt.Errorf("write failed: %w", err)
}
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
go func() {
_, _ = io.Copy(dest, reader)
cancel()
}()
go func() {
_, _ = io.Copy(conn, dest)
cancel()
}()
<-ctx.Done()
return nil
}
这段代码是一个实现 SOCKS5 代理服务器的简单示例。以下是代码的主要功能和流程解释:
- 定义了一些常量,包括 SOCKS5 协议版本、命令类型、地址类型等。
- 在
main函数中,创建一个 TCP 服务器监听在127.0.0.1:1080地址上。 - 通过无限循环接受客户端连接,在每个连接上调用
process函数进行处理。 process函数处理每个客户端连接的逻辑。它首先读取客户端发送的认证请求,并根据协议规范进行认证。auth函数处理认证过程。它首先读取客户端发送的协议版本,然后读取支持的认证方法数量和方法列表。根据规范,服务器返回支持的认证方法给客户端。- 客户端在收到认证响应后,可以选择其中一种认证方法进行身份验证。
- 客户端完成认证后,调用
connect函数建立与目标服务器的连接。 connect函数读取客户端发送的连接请求,并解析目标地址和端口。- 根据解析得到的目标地址和端口,建立与目标服务器的连接。
- 客户端和目标服务器之间的数据传输通过两个 goroutine 实现:一个从客户端读取数据并发送到目标服务器,另一个从目标服务器读取数据并发送到客户端。
- 使用上下文
ctx和取消函数cancel来控制数据传输的生命周期。 - 当其中一个 goroutine 完成数据传输或发生错误时,取消函数被调用,通知另一个 goroutine 停止数据传输。
- 最后,函数等待上下文的完成信号,结束处理。
总体而言,这段代码实现了一个简单的 SOCKS5 代理服务器,它可以接受客户端连接并转发流量到目标服务器。