First working prototype.
This commit is contained in:
commit
41e7a6df95
|
@ -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())
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
|
@ -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: ""}
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue