Grok 生成的简易 socks5 代理服务器

AI 生成的简易 socks5 代理服务器,使用 golang 实现,使用 docker-compose 部署。

Grok 生成的简易 socks5 代理服务器

准备

目录结构

your_project/
├── main.go
├── Dockerfile
└── docker-compose.yml

main.go 代码内容

默认监听 1080 端口,修改端口可以在 docker-compose 映射修改。

package main

import (
    "encoding/binary"
    "fmt"
    "io"
    "log"
    "net"
    "sync"
)

// 全局变量用于会话管理
var (
    // allowedClients 存储允许的客户端 IP 及其活跃会话计数
    allowedClients sync.Map // map[string]interface{}, value is *int
    // destToClient 存储目标地址到客户端 UDP 地址的映射
    destToClient sync.Map // map[string]string
)

func main() {
    // 启动 TCP 监听器
    go func() {
        listener, err := net.Listen("tcp", ":1080")
        if err != nil {
            log.Fatal("TCP 监听失败: ", err)
        }
        defer listener.Close()
        log.Println("TCP 服务器启动在 :1080")

        for {
            conn, err := listener.Accept()
            if err != nil {
                log.Println("接受 TCP 连接失败: ", err)
                continue
            }
            go handleConnection(conn)
        }
    }()

    // 启动 UDP 监听器
    udpAddr, err := net.ResolveUDPAddr("udp", ":1080")
    if err != nil {
        log.Fatal("解析 UDP 地址失败: ", err)
    }
    udpConn, err := net.ListenUDP("udp", udpAddr)
    if err != nil {
        log.Fatal("UDP 监听失败: ", err)
    }
    defer udpConn.Close()
    log.Println("UDP 服务器启动在 :1080")

    buf := make([]byte, 65536)
    for {
        n, clientAddr, err := udpConn.ReadFromUDP(buf)
        if err != nil {
            log.Println("读取 UDP 数据失败: ", err)
            continue
        }
        go handleUDPPacket(udpConn, clientAddr, buf[:n])
    }
}

// handleConnection 处理 TCP 连接
func handleConnection(conn net.Conn) {
    defer conn.Close()

    // 读取客户端问候
    buf := make([]byte, 2)
    _, err := io.ReadFull(conn, buf)
    if err != nil {
        log.Println("读取问候失败: ", err)
        return
    }
    if buf[0] != 0x05 {
        log.Println("不支持的协议版本: ", buf[0])
        return
    }
    nmethods := buf[1]
    methods := make([]byte, nmethods)
    _, err = io.ReadFull(conn, methods)
    if err != nil {
        log.Println("读取认证方法失败: ", err)
        return
    }

    // 回复无认证
    _, err = conn.Write([]byte{0x05, 0x00})
    if err != nil {
        log.Println("发送认证回复失败: ", err)
        return
    }

    // 读取客户端请求
    buf = make([]byte, 4)
    _, err = io.ReadFull(conn, buf)
    if err != nil {
        log.Println("读取请求失败: ", err)
        return
    }
    if buf[0] != 0x05 {
        log.Println("不支持的协议版本: ", buf[0])
        return
    }
    cmd := buf[1]
    if cmd != 0x01 && cmd != 0x03 {
        log.Println("不支持的命令: ", cmd)
        conn.Write([]byte{0x05, 0x07, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})
        return
    }
    atyp := buf[3]
    var addr string
    switch atyp {
    case 0x01: // IPv4
        ip := make([]byte, 4)
        _, err = io.ReadFull(conn, ip)
        if err != nil {
            log.Println("读取 IPv4 地址失败: ", err)
            return
        }
        addr = net.IP(ip).String()
    case 0x03: // 域名
        lenByte := make([]byte, 1)
        _, err = io.ReadFull(conn, lenByte)
        if err != nil {
            log.Println("读取域名长度失败: ", err)
            return
        }
        domainLen := lenByte[0]
        domain := make([]byte, domainLen)
        _, err = io.ReadFull(conn, domain)
        if err != nil {
            log.Println("读取域名失败: ", err)
            return
        }
        addr = string(domain)
    case 0x04: // IPv6
        ip := make([]byte, 16)
        _, err = io.ReadFull(conn, ip)
        if err != nil {
            log.Println("读取 IPv6 地址失败: ", err)
            return
        }
        addr = net.IP(ip).String()
    default:
        log.Println("不支持的地址类型: ", atyp)
        conn.Write([]byte{0x05, 0x08, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})
        return
    }
    portBytes := make([]byte, 2)
    _, err = io.ReadFull(conn, portBytes)
    if err != nil {
        log.Println("读取端口失败: ", err)
        return
    }
    port := binary.BigEndian.Uint16(portBytes)

    if cmd == 0x01 { // CONNECT
        // 连接目标服务器
        destConn, err := net.Dial("tcp", fmt.Sprintf("%s:%d", addr, port))
        if err != nil {
            log.Println("连接目标失败: ", err)
            conn.Write([]byte{0x05, 0x05, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})
            return
        }
        defer destConn.Close()

        // 发送成功回复
        reply := []byte{0x05, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}
        _, err = conn.Write(reply)
        if err != nil {
            log.Println("发送成功回复失败: ", err)
            return
        }

        // 中继数据
        go io.Copy(destConn, conn)
        io.Copy(conn, destConn)
    } else if cmd == 0x03 { // UDP ASSOCIATE
        clientIP := conn.RemoteAddr().(*net.TCPAddr).IP.String()
        val, _ := allowedClients.LoadOrStore(clientIP, new(int))
        count := val.(*int)
        *count++
        defer func() {
            *count--
            if *count == 0 {
                allowedClients.Delete(clientIP)
            }
        }()

        // 发送 UDP 地址回复
        localAddr := conn.LocalAddr().(*net.TCPAddr)
        reply := []byte{0x05, 0x00, 0x00}
        if localAddr.IP.To4() != nil {
            reply = append(reply, 0x01)
            reply = append(reply, localAddr.IP.To4()...)
        } else {
            reply = append(reply, 0x04)
            reply = append(reply, localAddr.IP.To16()...)
        }
        reply = append(reply, byte(localAddr.Port>>8), byte(localAddr.Port&0xff))
        _, err = conn.Write(reply)
        if err != nil {
            log.Println("发送 UDP ASSOCIATE 回复失败: ", err)
            return
        }

        // 保持 TCP 连接打开
        buf := make([]byte, 1)
        _, err = conn.Read(buf)
        if err != nil {
            log.Println("TCP 连接关闭: ", err)
        }
    }
}

// handleUDPPacket 处理 UDP 数据包
func handleUDPPacket(udpConn *net.UDPConn, srcAddr *net.UDPAddr, data []byte) {
    clientIP := srcAddr.IP.String()
    if _, ok := allowedClients.Load(clientIP); !ok {
        log.Println("来自未授权客户端的 UDP 数据包: ", clientIP)
        return
    }

    // 判断是否为客户端发送的 SOCKS5 UDP 数据包
    if len(data) < 3 || binary.BigEndian.Uint16(data[0:2]) != 0 || data[2] != 0 {
        // 来自目标服务器的响应
        destKey := srcAddr.String()
        if val, ok := destToClient.Load(destKey); ok {
            clientUDPAddrStr := val.(string)
            clientUDPAddr, err := net.ResolveUDPAddr("udp", clientUDPAddrStr)
            if err != nil {
                log.Println("解析客户端 UDP 地址失败: ", err)
                return
            }
            // 封装 SOCKS5 头部
            response := []byte{0x00, 0x00, 0x00} // RSV 和 FRAG
            if ip := srcAddr.IP.To4(); ip != nil {
                response = append(response, 0x01)
                response = append(response, ip...)
            } else if ip := srcAddr.IP.To16(); ip != nil {
                response = append(response, 0x04)
                response = append(response, ip...)
            } else {
                log.Println("不支持的 IP 类型")
                return
            }
            response = append(response, byte(srcAddr.Port>>8), byte(srcAddr.Port&0xff))
            response = append(response, data...)
            _, err = udpConn.WriteToUDP(response, clientUDPAddr)
            if err != nil {
                log.Println("发送响应到客户端失败: ", err)
            }
        }
        return
    }

    // 来自客户端的请求
    atyp := data[3]
    var destAddr string
    var destPort uint16
    var offset int
    switch atyp {
    case 0x01: // IPv4
        if len(data) < 10 {
            log.Println("无效的 UDP 数据包")
            return
        }
        destAddr = net.IP(data[4:8]).String()
        destPort = binary.BigEndian.Uint16(data[8:10])
        offset = 10
    case 0x03: // 域名
        if len(data) < 5 {
            log.Println("无效的 UDP 数据包")
            return
        }
        domainLen := data[4]
        if len(data) < int(5+domainLen+2) {
            log.Println("无效的 UDP 数据包")
            return
        }
        domain := string(data[5 : 5+domainLen])
        destAddr = domain
        destPort = binary.BigEndian.Uint16(data[5+domainLen : 7+domainLen])
        offset = int(7 + domainLen)
    case 0x04: // IPv6
        if len(data) < 22 {
            log.Println("无效的 UDP 数据包")
            return
        }
        destAddr = net.IP(data[4:20]).String()
        destPort = binary.BigEndian.Uint16(data[20:22])
        offset = 22
    default:
        log.Println("不支持的地址类型: ", atyp)
        return
    }
    payload := data[offset:]

    // 发送数据到目标
    destUDPAddr, err := net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%d", destAddr, destPort))
    if err != nil {
        log.Println("解析目标 UDP 地址失败: ", err)
        return
    }
    _, err = udpConn.WriteToUDP(payload, destUDPAddr)
    if err != nil {
        log.Println("发送数据到目标失败: ", err)
        return
    }

    // 记录目标到客户端的映射
    destKey := destUDPAddr.String()
    clientKey := srcAddr.String()
    destToClient.Store(destKey, clientKey)
}

初始化 mod 并且下载依赖

执行以下命令:

go mod init socks5-server
go mod tidy

Dockerfile 内容

# 使用官方 Golang 镜像作为基础镜像
FROM golang:1.20-alpine

# 设置工作目录
WORKDIR /app

# 将当前目录下的所有文件复制到容器的 /app 目录中
COPY . .

# 编译 Golang 应用程序
RUN go build -o socks5-server

# 运行编译好的二进制文件
CMD ["./socks5-server"]

docker-compose.yml 内容

services:
  socks5-server:  # 服务名称
    build:
      context: .  # 构建上下文为当前目录
      dockerfile: Dockerfile  # 指定使用的 Dockerfile
    ports:
      - "1080:1080"  # 将主机的 1080 端口映射到容器的 1080 端口
      - "1080:1080/udp"

启动

docker compose up -d