package main
import (
"encoding/binary"
"fmt"
"io"
"log"
"net"
"sync/atomic"
"time"
)
const (
socksVersion5 = 0x05
socksCmdConnect = 0x01
socksAddrTypeIPv4 = 0x01
socksAddrTypeDomain = 0x03
socksAddrTypeIPv6 = 0x04
)
// 带计数的 Reader
type countingReader struct {
r io.Reader
n *int64
}
func (cr *countingReader) Read(p []byte) (int, error) {
n, err := cr.r.Read(p)
if n > 0 {
atomic.AddInt64(cr.n, int64(n))
}
return n, err
}
// 带计数的 Writer
type countingWriter struct {
w io.Writer
n *int64
}
func (cw *countingWriter) Write(p []byte) (int, error) {
n, err := cw.w.Write(p)
if n > 0 {
atomic.AddInt64(cw.n, int64(n))
}
return n, err
}
func main() {
addr := ":1080" // 监听端口
l, err := net.Listen("tcp", addr)
if err != nil {
log.Fatalf("listen %s failed: %v", addr, err)
}
log.Printf("SOCKS5 proxy listening on %s", addr)
for {
conn, err := l.Accept()
if err != nil {
log.Printf("accept error: %v", err)
continue
}
go handleConn(conn)
}
}
func handleConn(c net.Conn) {
defer c.Close()
start := time.Now()
clientAddr := c.RemoteAddr().String()
var upBytes, downBytes int64 // 上行/下行字节数
// SOCKS5 握手
if err := socks5Handshake(c); err != nil {
log.Printf("[%s] handshake error: %v", clientAddr, err)
return
}
targetConn, targetAddr, err := socks5HandleRequest(c)
if err != nil {
log.Printf("[%s] request error: %v", clientAddr, err)
return
}
defer targetConn.Close()
log.Printf("[%s] connected to %s", clientAddr, targetAddr)
// 使用计数器包装
// 上行:client -> target
clientReader := &countingReader{r: c, n: &upBytes}
targetWriter := &countingWriter{w: targetConn, n: &upBytes}
// 下行:target -> client
targetReader := &countingReader{r: targetConn, n: &downBytes}
clientWriter := &countingWriter{w: c, n: &downBytes}
// 双向拷贝
errCh := make(chan error, 2)
go func() {
_, err := io.Copy(targetWriter, clientReader)
errCh <- err
}()
go func() {
_, err := io.Copy(clientWriter, targetReader)
errCh <- err
}()
// 等待任意一边结束
<-errCh
duration := time.Since(start)
log.Printf("[%s] %s closed, up=%.2f KB, down=%.2f KB, duration=%s",
clientAddr,
targetAddr,
float64(upBytes)/1024.0,
float64(downBytes)/1024.0,
duration,
)
}
// 简单 SOCKS5 握手:只支持 NO AUTH (0x00)
func socks5Handshake(c net.Conn) error {
buf := make([]byte, 258) // VER + NMETHODS + 255 METHODS
// 读取 VER、NMETHODS
if _, err := io.ReadFull(c, buf[:2]); err != nil {
return fmt.Errorf("read head: %w", err)
}
if buf[0] != socksVersion5 {
return fmt.Errorf("unsupported socks version: %d", buf[0])
}
nMethods := int(buf[1])
if nMethods == 0 {
return fmt.Errorf("no methods")
}
if _, err := io.ReadFull(c, buf[:nMethods]); err != nil {
return fmt.Errorf("read methods: %w", err)
}
// 我们只接受 NO AUTH (0x00)
// 直接回复服务端选择 0x00
resp := []byte{socksVersion5, 0x00}
if _, err := c.Write(resp); err != nil {
return fmt.Errorf("write method selection: %w", err)
}
return nil
}
// 处理 SOCKS5 请求,只实现 CONNECT
func socks5HandleRequest(c net.Conn) (net.Conn, string, error) {
header := make([]byte, 4)
if _, err := io.ReadFull(c, header); err != nil {
return nil, "", fmt.Errorf("read request header: %w", err)
}
ver, cmd, _, atyp := header[0], header[1], header[2], header[3]
if ver != socksVersion5 {
return nil, "", fmt.Errorf("invalid version: %d", ver)
}
if cmd != socksCmdConnect {
replySocks5(c, 0x07, nil) // Command not supported
return nil, "", fmt.Errorf("unsupported cmd: %d", cmd)
}
// 解析地址
var host string
switch atyp {
case socksAddrTypeIPv4:
addr := make([]byte, 4)
if _, err := io.ReadFull(c, addr); err != nil {
return nil, "", fmt.Errorf("read ipv4: %w", err)
}
host = net.IP(addr).String()
case socksAddrTypeDomain:
var l [1]byte
if _, err := io.ReadFull(c, l[:]); err != nil {
return nil, "", fmt.Errorf("read domain len: %w", err)
}
domain := make([]byte, l[0])
if _, err := io.ReadFull(c, domain); err != nil {
return nil, "", fmt.Errorf("read domain: %w", err)
}
host = string(domain)
case socksAddrTypeIPv6:
addr := make([]byte, 16)
if _, err := io.ReadFull(c, addr); err != nil {
return nil, "", fmt.Errorf("read ipv6: %w", err)
}
host = net.IP(addr).String()
default:
replySocks5(c, 0x08, nil) // Address type not supported
return nil, "", fmt.Errorf("unsupported atyp: %d", atyp)
}
// 读取端口
var portBuf [2]byte
if _, err := io.ReadFull(c, portBuf[:]); err != nil {
return nil, "", fmt.Errorf("read port: %w", err)
}
port := binary.BigEndian.Uint16(portBuf[:])
target := fmt.Sprintf("%s:%d", host, port)
// 连接目标
targetConn, err := net.Dial("tcp", target)
if err != nil {
replySocks5(c, 0x05, nil) // Connection refused
return nil, "", fmt.Errorf("connect target %s failed: %w", target, err)
}
// 成功:回复客户端
// 这里 bind addr 使用 0.0.0.0:0
bindAddr := &net.TCPAddr{IP: net.IPv4zero, Port: 0}
if err := replySocks5(c, 0x00, bindAddr); err != nil {
targetConn.Close()
return nil, "", fmt.Errorf("reply failed: %w", err)
}
return targetConn, target, nil
}
// 回复 SOCKS5 请求
func replySocks5(c net.Conn, rep byte, bindAddr *net.TCPAddr) error {
if bindAddr == nil {
// 默认 0.0.0.0:0
bindAddr = &net.TCPAddr{IP: net.IPv4zero, Port: 0}
}
buf := make([]byte, 10)
buf[0] = socksVersion5
buf[1] = rep
buf[2] = 0x00 // RSV
buf[3] = socksAddrTypeIPv4
copy(buf[4:8], bindAddr.IP.To4())
binary.BigEndian.PutUint16(buf[8:10], uint16(bindAddr.Port))
_, err := c.Write(buf)
return err
}
版权归属:
BvBeJ
许可协议:
本文使用《署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0)》协议授权
评论区