First working prototype.
This commit is contained in:
		
							
								
								
									
										59
									
								
								main.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								main.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,59 @@
 | 
				
			|||||||
 | 
					package main
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"git-core.megvii-inc.com/yuyifu/transparent-proxy/server"
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"net"
 | 
				
			||||||
 | 
						"io"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func main()  {
 | 
				
			||||||
 | 
						proxy_listener, err := server.NewIPv4TransparentListener(":9091")
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							panic(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						for {
 | 
				
			||||||
 | 
							conn, err := proxy_listener.Accept()
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								fmt.Println("Accept Error: ", err.Error())
 | 
				
			||||||
 | 
								continue
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							fmt.Printf("Accepted connection: %s => %s\n", conn.TCPConn().RemoteAddr().String(), conn.RealAddr().String())
 | 
				
			||||||
 | 
							go handle(conn)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func pipeThenClose(reader io.Reader, writer io.WriteCloser) {
 | 
				
			||||||
 | 
						defer writer.Close()
 | 
				
			||||||
 | 
						buff := make([]byte, 4096)
 | 
				
			||||||
 | 
						for {
 | 
				
			||||||
 | 
							n, err := reader.Read(buff)
 | 
				
			||||||
 | 
							if n > 0 {
 | 
				
			||||||
 | 
								_, err := writer.Write(buff[:n])
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									fmt.Printf("Write Error: %#v\n", err.Error())
 | 
				
			||||||
 | 
									return
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if err == io.EOF {
 | 
				
			||||||
 | 
								fmt.Println("Reader reached EOF, closing.")
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								// Enable this line ONLY FOR DEBUG PURPOSE.
 | 
				
			||||||
 | 
								// fmt.Printf("Read Error: %#v\n", err.Error())
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func handle(conn server.TransparentConnection) {
 | 
				
			||||||
 | 
						rconn, err := net.DialTCP("tcp4", nil, conn.RealAddr())
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							fmt.Println("Connect to ", conn.RealAddr().String(), " error: ", err.Error())
 | 
				
			||||||
 | 
							conn.TCPConn().Close()
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						go pipeThenClose(conn.TCPConn(), rconn)
 | 
				
			||||||
 | 
						go pipeThenClose(rconn, conn.TCPConn())
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										26
									
								
								server/hack.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								server/hack.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,26 @@
 | 
				
			|||||||
 | 
					package server
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"unsafe"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func isLittleEndian() bool {
 | 
				
			||||||
 | 
						var placeHolder uint32 = 0x0000FFFF
 | 
				
			||||||
 | 
						return (*[4]byte)(unsafe.Pointer(&placeHolder))[0] == 0xFF
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func be32toh(host uint32) uint32 {
 | 
				
			||||||
 | 
						if isLittleEndian() {
 | 
				
			||||||
 | 
							return ((host & 255) << 24) | (((host >> 8) & 255) << 16) | (((host >> 16) & 255) << 8) | (host >> 24)
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							return host
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func be16toh(host uint16) uint16 {
 | 
				
			||||||
 | 
						if isLittleEndian() {
 | 
				
			||||||
 | 
							return ((host & 255) << 8) | (host >> 8)
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							return host
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										69
									
								
								server/hack_linux.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										69
									
								
								server/hack_linux.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,69 @@
 | 
				
			|||||||
 | 
					package server
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * WARNING WARNING WARNING
 | 
				
			||||||
 | 
					 * This file contains MANY DIRTY HACKS, and most of them made much assume to go internal code layout.
 | 
				
			||||||
 | 
					 * For every new go version, you MUST try to use every function, make sure they didn't panic for you.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// WARZONE BEGINS HERE! MIND YOUR HEAD!
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"reflect"
 | 
				
			||||||
 | 
						"net"
 | 
				
			||||||
 | 
						"golang.org/x/sys/unix"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const (
 | 
				
			||||||
 | 
						SO_ORIGINAL_DST = 80
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func GetFDFromTCPConn(conn *net.TCPConn) int {
 | 
				
			||||||
 | 
						// Actual fd is stored at: (*(*TCPConn).conn.fd).sysfd
 | 
				
			||||||
 | 
						v := reflect.ValueOf(*conn)
 | 
				
			||||||
 | 
						c := v.FieldByName("conn")
 | 
				
			||||||
 | 
						fdp := c.FieldByName("fd")
 | 
				
			||||||
 | 
						fd := reflect.Indirect(fdp)
 | 
				
			||||||
 | 
						sysfd := fd.FieldByName("sysfd")
 | 
				
			||||||
 | 
						return int(sysfd.Int())
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func GetAddr4FromFD(fd int) *net.TCPAddr {
 | 
				
			||||||
 | 
						mtuinfo, err := unix.GetsockoptIPv6MTUInfo(fd, unix.IPPROTO_IP, SO_ORIGINAL_DST)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							panic(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						addr := mtuinfo.Addr
 | 
				
			||||||
 | 
						/*
 | 
				
			||||||
 | 
							RawSockaddrInet6 layout:
 | 
				
			||||||
 | 
								Family 2byte ignore or assert == AF_INET
 | 
				
			||||||
 | 
								Port 2byte ipv4 port
 | 
				
			||||||
 | 
								Flowinfo 4byte ipv4 address
 | 
				
			||||||
 | 
								Addr 16byte ignore for ipv4
 | 
				
			||||||
 | 
								Scope_id 4byte ignore for ipv4
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						addr.Flowinfo = be32toh(addr.Flowinfo)
 | 
				
			||||||
 | 
						a, b, c, d := byte(addr.Flowinfo >> 24), byte((addr.Flowinfo >> 16) & 255), byte((addr.Flowinfo >> 8) & 255), byte(addr.Flowinfo & 255)
 | 
				
			||||||
 | 
						ip := net.IPv4(a, b, c, d)
 | 
				
			||||||
 | 
						return &net.TCPAddr{IP: ip, Port: int(be16toh(addr.Port)), Zone: ""}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func GetAddr6FromFD(fd int) *net.TCPAddr {
 | 
				
			||||||
 | 
						mtuinfo, err := unix.GetsockoptIPv6MTUInfo(fd, unix.IPPROTO_IPV6, SO_ORIGINAL_DST)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							panic(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						addr := mtuinfo.Addr
 | 
				
			||||||
 | 
						/*
 | 
				
			||||||
 | 
							RawSockaddrInet6 layout:
 | 
				
			||||||
 | 
								Family 2byte ignore or assert == AF_INET6
 | 
				
			||||||
 | 
								Port 2byte ipv6 port
 | 
				
			||||||
 | 
								Flowinfo 4byte ipv6 flowinfo ignore
 | 
				
			||||||
 | 
								Addr 16byte ipv6 addr
 | 
				
			||||||
 | 
								Scope_id 4byte ipv6 scope id ignore
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						v6addr := make(net.IP, 16)
 | 
				
			||||||
 | 
						// Make GC happy?
 | 
				
			||||||
 | 
						copy(v6addr, addr.Addr[:])
 | 
				
			||||||
 | 
						return &net.TCPAddr{IP: v6addr, Port: int(be16toh(addr.Port)), Zone: ""}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										48
									
								
								server/ipv4.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								server/ipv4.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,48 @@
 | 
				
			|||||||
 | 
					package server
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"net"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type IPv4TransparentListener struct {
 | 
				
			||||||
 | 
						listener *net.TCPListener
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type IPv4TransparentConnection struct {
 | 
				
			||||||
 | 
						conn *net.TCPConn
 | 
				
			||||||
 | 
						remoteAddr *net.TCPAddr
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func NewIPv4TransparentListener(addr string) (*IPv4TransparentListener, error) {
 | 
				
			||||||
 | 
						tcpaddr, err := net.ResolveTCPAddr("tcp4", addr)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						listener, err := net.ListenTCP("tcp4", tcpaddr)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return &IPv4TransparentListener{listener: listener}, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (l *IPv4TransparentListener) Close() error {
 | 
				
			||||||
 | 
						return l.listener.Close()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (l *IPv4TransparentListener) Accept() (*IPv4TransparentConnection, error) {
 | 
				
			||||||
 | 
						conn, err := l.listener.AcceptTCP()
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						fd := GetFDFromTCPConn(conn)
 | 
				
			||||||
 | 
						remoteAddr := GetAddr4FromFD(fd)
 | 
				
			||||||
 | 
						return &IPv4TransparentConnection{conn: conn, remoteAddr: remoteAddr}, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (c *IPv4TransparentConnection) TCPConn() *net.TCPConn {
 | 
				
			||||||
 | 
						return c.conn
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (c *IPv4TransparentConnection) RealAddr() *net.TCPAddr {
 | 
				
			||||||
 | 
						return c.remoteAddr
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										48
									
								
								server/ipv6.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								server/ipv6.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,48 @@
 | 
				
			|||||||
 | 
					package server
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"net"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type IPv6TransparentListener struct {
 | 
				
			||||||
 | 
						listener *net.TCPListener
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type IPv6TransparentConnection struct {
 | 
				
			||||||
 | 
						conn *net.TCPConn
 | 
				
			||||||
 | 
						remoteAddr *net.TCPAddr
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func NewIPv6TransparentListener(addr string) (*IPv6TransparentListener, error) {
 | 
				
			||||||
 | 
						tcpaddr, err := net.ResolveTCPAddr("tcp6", addr)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						listener, err := net.ListenTCP("tcp6", tcpaddr)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return &IPv6TransparentListener{listener: listener}, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (l *IPv6TransparentListener) Close() error {
 | 
				
			||||||
 | 
						return l.listener.Close()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (l *IPv6TransparentListener) Accept() (*IPv6TransparentConnection, error) {
 | 
				
			||||||
 | 
						conn, err := l.listener.AcceptTCP()
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						fd := GetFDFromTCPConn(conn)
 | 
				
			||||||
 | 
						remoteAddr := GetAddr6FromFD(fd)
 | 
				
			||||||
 | 
						return &IPv6TransparentConnection{conn: conn, remoteAddr: remoteAddr}, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (c *IPv6TransparentConnection) TCPConn() *net.TCPConn {
 | 
				
			||||||
 | 
						return c.conn
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (c *IPv6TransparentConnection) RealAddr() *net.TCPAddr {
 | 
				
			||||||
 | 
						return c.remoteAddr
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										17
									
								
								server/listener.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								server/listener.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,17 @@
 | 
				
			|||||||
 | 
					package server
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"net"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type TransparentListener interface {
 | 
				
			||||||
 | 
						Accept() (TransparentConnection, error)
 | 
				
			||||||
 | 
						Close() error
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type TransparentConnection interface {
 | 
				
			||||||
 | 
						TCPConn() *net.TCPConn
 | 
				
			||||||
 | 
						RealAddr() *net.TCPAddr
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		Reference in New Issue
	
	Block a user