add transparent proxy support

add transparent proxy support for dynamic port forward on linux

on linux, can use iptables to redirect all tcp packet to obfssh
 client dynamic listen port(-D), and forward through ssh channel
master
Dingjun 8 years ago
parent 97d3031a2e
commit 2fec17c727

@ -255,10 +255,37 @@ func (cc *Client) handleRemoteForward(conn net.Conn, local string) {
} }
func (cc *Client) handleDynamicForward(conn net.Conn) { func (cc *Client) handleDynamicForward(conn net.Conn) {
addr, err := getOriginDst(conn)
if err == nil {
if addr.String() != conn.LocalAddr().String() {
// transparent proxy
// iptables redirect the packet to this port
cc.handleTransparentProxy(conn, addr)
return
}
} else {
// SO_ORIGNAL_DST failed
// ipv6 not support this syscall
// so ignore it
Log(DEBUG, "get original destination on %s failed: %s, ignore",
conn.LocalAddr(), err)
}
// socks5 to this port
s := socks.Conn{Conn: conn, Dial: cc.client.Dial} s := socks.Conn{Conn: conn, Dial: cc.client.Dial}
s.Serve() s.Serve()
} }
func (cc *Client) handleTransparentProxy(c net.Conn, addr net.Addr) {
c2, err := cc.client.Dial("tcp", addr.String())
if err != nil {
Log(ERROR, "%s", err)
c.Close()
return
}
PipeAndClose(c2, c)
}
func (cc *Client) keepAlive(interval time.Duration, maxCount int) { func (cc *Client) keepAlive(interval time.Duration, maxCount int) {
count := 0 count := 0
c := time.NewTicker(interval) c := time.NewTicker(interval)

@ -0,0 +1,11 @@
// +build !linux
package obfssh
import (
"net"
)
func getOriginDst(c net.Conn) (net.Addr, error) {
return c.LocalAddr(), nil
}

@ -0,0 +1,76 @@
// +build linux
package obfssh
import (
"encoding/binary"
"fmt"
"net"
"syscall"
"unsafe"
)
const (
// SO_ORIGINAL_DST in linux/netfilter_ipv4.h
soOriginalDst = 80
)
func getOriginDst(c net.Conn) (net.Addr, error) {
var sockaddr syscall.RawSockaddrAny
var len = unsafe.Sizeof(sockaddr)
cc, ok := c.(*net.TCPConn)
if !ok {
return nil, fmt.Errorf("only tcp socket supported")
}
f, err := cc.File()
if err != nil {
return nil, err
}
defer f.Close()
// get original ip destination, in C like this
//
// struct sockaddr addr;
// memset(&addr, 0, sizeof(addr);
// int len = sizeof(addr);
// getsocketopt(fd, SOL_IP, SO_ORIGINAL_DST, &addr, &len);
//
_, _, errno := syscall.Syscall6(sysGetSockOpt, f.Fd(),
uintptr(syscall.SOL_IP), uintptr(soOriginalDst),
uintptr(unsafe.Pointer(&sockaddr)),
uintptr(unsafe.Pointer(&len)), 0)
if errno != 0 {
return nil, fmt.Errorf("syscall error %d", errno)
}
var port uint16
var ip net.IP
switch sockaddr.Addr.Family {
case syscall.AF_INET:
a := (*syscall.RawSockaddrInet4)(unsafe.Pointer(&sockaddr))
ip = net.IP(a.Addr[0:])
port = ntohl(a.Port)
case syscall.AF_INET6:
a := (*syscall.RawSockaddrInet6)(unsafe.Pointer(&sockaddr))
ip = net.IP(a.Addr[0:])
port = ntohl(a.Port)
default:
return nil, fmt.Errorf("unknown socket family: %d",
sockaddr.Addr.Family)
}
addr := &net.TCPAddr{IP: ip, Port: int(port)}
return addr, nil
}
func ntohl(a uint16) uint16 {
b := make([]byte, 2)
binary.BigEndian.PutUint16(b, a)
c := binary.LittleEndian.Uint16(b)
return c
}

@ -0,0 +1,5 @@
package obfssh
// getsockopt syscall number
// refer to $GOROOT/src/syscall/syscall_linux_386.go
const sysGetSockOpt = 15

@ -0,0 +1,11 @@
// +build linux, !386
// +build linux
package obfssh
import (
"syscall"
)
// getsockopt syscall number
const sysGetSockOpt = syscall.SYS_GETSOCKOPT
Loading…
Cancel
Save