实际很简单,只是在普通的 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 。