简介
SOCKS5 是一种网络协议,它通过代理服务器在客户端和目标服务器之间建立一条安全的通道。在这篇博客中,我们将介绍如何使用 Go 语言实现一个简单的 SOCKS5 代理服务器。
代码实现
包引入
首先,我们需要引入一些必要的包:
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()
处理客户端连接
当有客户端连接到代理服务器时,我们需要对其进行处理。这个过程包括身份验证和连接建立:
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的数据对应这个字段的类型。
// 读取客户端请求
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)
}
// 解析目标地址
var addr []byte
switch atyp {
case atypeIPV4:
addr = make([]byte, 4)
_, err = io.ReadFull(reader, addr)
case atypeHOST:
hostSize, err := reader.ReadByte()
if err!= nil {
return fmt.Errorf("read host size failed:%w", err)
}
addr = make([]byte, hostSize+2)
_, err = io.ReadFull(reader, addr)
case atypeIPV6:
addr = make([]byte, 16)
_, err = io.ReadFull(reader, addr)
default:
return fmt.Errorf("not supported atyp:%v", atyp)
}
if err!= nil {
return fmt.Errorf("read addr failed:%w", err)
}
// 解析目标端口
port := make([]byte, 2)
_, err = io.ReadFull(reader, port)
if err!= nil {
return fmt.Errorf("read port failed:%w", err)
}
// 连接目标服务器
destAddr := fmt.Sprintf("%s:%d", addr, binary.BigEndian.Uint16(port))
destConn, err := net.Dial("tcp", destAddr)
if err!= nil {
return fmt.Errorf("dial to %s failed:%w", destAddr, err)
}
defer destConn.Close()
// 响应客户端
_, err = conn.Write([]byte{socks5Ver, 0x00, 0x00, atypeIPV4, 0, 0, 0, 0, 0, 0})
if err!= nil {
return fmt.Errorf("write response failed:%w", err)
}
// 数据转发
go func() {
_, err = io.Copy(destConn, conn)
if err!= nil {
log.Printf("copy from client to dest failed: %v", err)
}
}()
go func() {
_, err = io.Copy(conn, destConn)
if err!= nil {
log.Printf("copy from dest to client failed: %v", err)
}
}()
return nil
}
总结
通过以上代码,我们实现了一个简单的 SOCKS5 代理服务器。这个代理服务器支持无认证模式,可以处理客户端的连接请求,并建立客户端和目标服务器之间的连接。