简单的 TProxy 代理 (Golang)

2023-04-16 10:04 +0800

代码参考了 https://github.com/KatelynHaworth/go-tproxy

实际很简单,只是在普通的 TCPListener 上,使用 setsockopt 系统调用,在 IP 层设置 IP_TRANSPARENT 属性。

示例代码如下:

import (
	"fmt"
	"io"
	"net"
	"syscall"
)

func main() {
	listener, err := net.ListenTCP("tcp", &net.TCPAddr{IP: net.ParseIP("0.0.0.0"), Port: 8080})
	if err != nil {
		panic(err)
	}
	defer listener.Close()

	if err := initTCPTProxy(listener); err != nil {
		panic(err)
	}

	for {
		conn, err := listener.Accept()
		if err != nil {
			panic(err)
		}
		defer conn.Close()

		localAddr := conn.LocalAddr()
		remoteAddr := conn.RemoteAddr()

		fmt.Printf("Local address: %v\n", localAddr)
		fmt.Printf("Remote address: %v\n", remoteAddr)
		r_conn, err := net.Dial("tcp", localAddr.String())
		if err != nil {
			panic(err)
		}

		go func() {
			defer r_conn.Close()
			defer conn.Close()
			io.Copy(r_conn, conn)
		}()
		go func() {
			defer r_conn.Close()
			defer conn.Close()
			io.Copy(conn, r_conn)
		}()

	}
}

func initTCPTProxy(l *net.TCPListener) error {
	fileDescriptorSource, err := l.File()
	if err != nil {
		return &net.OpError{Op: "listen", Net: l.Addr().Network(), Source: nil, Addr: l.Addr(), Err: fmt.Errorf("get file descriptor: %s", err)}
	}
	defer fileDescriptorSource.Close()

	if err = syscall.SetsockoptInt(int(fileDescriptorSource.Fd()), syscall.SOL_IP, syscall.IP_TRANSPARENT, 1); err != nil {
		return &net.OpError{Op: "listen", Net: l.Addr().Network(), Source: nil, Addr: l.Addr(), Err: fmt.Errorf("set socket option: IP_TRANSPARENT: %s", err)}
	}
	return nil
}

UDP 的非常类似,只是从 net.UDPConn 对象里获取 socket fd , 而不是 listener 。