Go 语言实现的 SOCKS5 代理服务器 | 豆包MarsCode AI刷题

114 阅读3分钟

简介

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 代理服务器。这个代理服务器支持无认证模式,可以处理客户端的连接请求,并建立客户端和目标服务器之间的连接。