blog-site

git clone git://git.lin.moe/blog-site.git

 1---
 2title: "简单的 TProxy 代理 (Golang)"
 3date: 2023-04-16T10:04:19+08:00
 4---
 5
 6> 代码参考了 https://github.com/KatelynHaworth/go-tproxy
 7
 8实际很简单,只是在普通的 TCPListener 上,使用 `setsockopt` 系统调用,在 IP 层设置 `IP_TRANSPARENT` 属性。  
 9
10示例代码如下:
11```
12import (
13	"fmt"
14	"io"
15	"net"
16	"syscall"
17)
18
19func main() {
20	listener, err := net.ListenTCP("tcp", &net.TCPAddr{IP: net.ParseIP("0.0.0.0"), Port: 8080})
21	if err != nil {
22		panic(err)
23	}
24	defer listener.Close()
25
26	if err := initTCPTProxy(listener); err != nil {
27		panic(err)
28	}
29
30	for {
31		conn, err := listener.Accept()
32		if err != nil {
33			panic(err)
34		}
35		defer conn.Close()
36
37		localAddr := conn.LocalAddr()
38		remoteAddr := conn.RemoteAddr()
39
40		fmt.Printf("Local address: %v\n", localAddr)
41		fmt.Printf("Remote address: %v\n", remoteAddr)
42		r_conn, err := net.Dial("tcp", localAddr.String())
43		if err != nil {
44			panic(err)
45		}
46
47		go func() {
48			defer r_conn.Close()
49			defer conn.Close()
50			io.Copy(r_conn, conn)
51		}()
52		go func() {
53			defer r_conn.Close()
54			defer conn.Close()
55			io.Copy(conn, r_conn)
56		}()
57
58	}
59}
60
61func initTCPTProxy(l *net.TCPListener) error {
62	fileDescriptorSource, err := l.File()
63	if err != nil {
64		return &net.OpError{Op: "listen", Net: l.Addr().Network(), Source: nil, Addr: l.Addr(), Err: fmt.Errorf("get file descriptor: %s", err)}
65	}
66	defer fileDescriptorSource.Close()
67
68	if err = syscall.SetsockoptInt(int(fileDescriptorSource.Fd()), syscall.SOL_IP, syscall.IP_TRANSPARENT, 1); err != nil {
69		return &net.OpError{Op: "listen", Net: l.Addr().Network(), Source: nil, Addr: l.Addr(), Err: fmt.Errorf("set socket option: IP_TRANSPARENT: %s", err)}
70	}
71	return nil
72}
73```
74
75UDP 的非常类似,只是从 net.UDPConn 对象里获取 socket fd , 而不是 listener 。
76