diff --git a/README.md b/README.md index 45d0239..e9369ec 100644 --- a/README.md +++ b/README.md @@ -42,7 +42,7 @@ General: TODO: - [x] UDP over TCP Tunnel (client <-udp-> uottun <-tcp-> ss <-udp-> target) -- [ ] Linux tproxy support +- [ ] Transparent UDP proxy (linux tproxy) - [ ] TUN/TAP device support - [ ] Code refactoring: support proxy registering so it can be pluggable - [ ] Conditional compilation so we can abandon needless proxy type and get a smaller binary size diff --git a/config/README.md b/config/README.md index 05e8137..cc110f1 100644 --- a/config/README.md +++ b/config/README.md @@ -43,7 +43,7 @@ dns://53 # global remote dns server (you can specify different dns server in rule file) dnsserver=8.8.8.8:53 -# Create and mange ipset on linux based on destinations in rule files +# Create and manage ipset on linux based on destinations in rule files # - add ip/cidrs in rule files on startup # - add resolved ips for domains in rule files by dns forwarder server # Usually used in transparent proxy mode on linux diff --git a/redir_linux.go b/redir_linux.go index 167e2ca..a2879bf 100644 --- a/redir_linux.go +++ b/redir_linux.go @@ -8,8 +8,6 @@ import ( "net" "syscall" "unsafe" - - "github.com/shadowsocks/go-shadowsocks2/socks" ) const ( @@ -32,7 +30,7 @@ func NewRedirProxy(addr string, sDialer Dialer) (*RedirProxy, error) { return s, nil } -// ListenAndServe redirected requests as a server. +// ListenAndServe . func (s *RedirProxy) ListenAndServe() { l, err := net.Listen("tcp", s.addr) if err != nil { @@ -84,7 +82,7 @@ func (s *RedirProxy) ListenAndServe() { } // Get the original destination of a TCP connection. -func getOrigDst(conn net.Conn, ipv6 bool) (socks.Addr, error) { +func getOrigDst(conn net.Conn, ipv6 bool) (Addr, error) { c, ok := conn.(*net.TCPConn) if !ok { return nil, errors.New("only work with TCP connection") @@ -112,7 +110,7 @@ func getOrigDst(conn net.Conn, ipv6 bool) (socks.Addr, error) { } // Call getorigdst() from linux/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c -func getorigdst(fd uintptr) (socks.Addr, error) { +func getorigdst(fd uintptr) (Addr, error) { raw := syscall.RawSockaddrInet4{} siz := unsafe.Sizeof(raw) if err := socketcall(GETSOCKOPT, fd, syscall.IPPROTO_IP, SO_ORIGINAL_DST, uintptr(unsafe.Pointer(&raw)), uintptr(unsafe.Pointer(&siz)), 0); err != nil { @@ -120,7 +118,7 @@ func getorigdst(fd uintptr) (socks.Addr, error) { } addr := make([]byte, 1+net.IPv4len+2) - addr[0] = socks.AtypIPv4 + addr[0] = socks5IP4 copy(addr[1:1+net.IPv4len], raw.Addr[:]) port := (*[2]byte)(unsafe.Pointer(&raw.Port)) // big-endian addr[1+net.IPv4len], addr[1+net.IPv4len+1] = port[0], port[1] @@ -129,7 +127,7 @@ func getorigdst(fd uintptr) (socks.Addr, error) { // Call ipv6_getorigdst() from linux/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c // NOTE: I haven't tried yet but it should work since Linux 3.8. -func ipv6_getorigdst(fd uintptr) (socks.Addr, error) { +func ipv6_getorigdst(fd uintptr) (Addr, error) { raw := syscall.RawSockaddrInet6{} siz := unsafe.Sizeof(raw) if err := socketcall(GETSOCKOPT, fd, syscall.IPPROTO_IPV6, IP6T_SO_ORIGINAL_DST, uintptr(unsafe.Pointer(&raw)), uintptr(unsafe.Pointer(&siz)), 0); err != nil { @@ -137,7 +135,7 @@ func ipv6_getorigdst(fd uintptr) (socks.Addr, error) { } addr := make([]byte, 1+net.IPv6len+2) - addr[0] = socks.AtypIPv6 + addr[0] = socks5IP6 copy(addr[1:1+net.IPv6len], raw.Addr[:]) port := (*[2]byte)(unsafe.Pointer(&raw.Port)) // big-endian addr[1+net.IPv6len], addr[1+net.IPv6len+1] = port[0], port[1] diff --git a/redir_other.go b/redir_other.go index 9487e7a..41cf4df 100644 --- a/redir_other.go +++ b/redir_other.go @@ -15,7 +15,7 @@ func NewRedirProxy(addr string, sDialer Dialer) (*RedirProxy, error) { return nil, errors.New("redir not supported on this os") } -// ListenAndServe redirected requests as a server. +// ListenAndServe . func (s *RedirProxy) ListenAndServe() { log.Fatal("redir not supported on this os") } diff --git a/socks5.go b/socks5.go index 7cb772f..300b890 100644 --- a/socks5.go +++ b/socks5.go @@ -77,7 +77,7 @@ func NewSOCKS5(addr, user, pass string, cDialer Dialer, sDialer Dialer) (*SOCKS5 return s, nil } -// ListenAndServe connects to the address addr on the network net via the SOCKS5 proxy. +// ListenAndServe . func (s *SOCKS5) ListenAndServe() { l, err := net.Listen("tcp", s.addr) if err != nil { diff --git a/tcptun.go b/tcptun.go index 9a89196..51626e1 100644 --- a/tcptun.go +++ b/tcptun.go @@ -21,7 +21,7 @@ func NewTCPTun(addr, raddr string, sDialer Dialer) (*TCPTun, error) { return s, nil } -// ListenAndServe redirected requests as a server. +// ListenAndServe . func (s *TCPTun) ListenAndServe() { l, err := net.Listen("tcp", s.addr) if err != nil { diff --git a/tproxy.go b/tproxy.go new file mode 100644 index 0000000..5db9e67 --- /dev/null +++ b/tproxy.go @@ -0,0 +1,68 @@ +// +build linux + +package main + +import ( + "net" + "syscall" +) + +type TProxy struct { + *Forwarder // as client + sDialer Dialer // dialer for server +} + +// NewTProxy returns a tproxy. +func NewTProxy(addr string, sDialer Dialer) (*TProxy, error) { + s := &TProxy{ + Forwarder: NewForwarder(addr, nil), + sDialer: sDialer, + } + + return s, nil +} + +// ListenAndServe . +func (s *TProxy) ListenAndServe() { + // go s.ListenAndServeTCP() + s.ListenAndServeUDP() +} + +func (s *TProxy) ListenAndServeTCP() { + +} + +func (s *TProxy) ListenAndServeUDP() { + laddr, err := net.ResolveUDPAddr("udp", s.addr) + if err != nil { + logf("proxy-tproxy failed to resolve addr %s: %v", s.addr, err) + return + } + + listener, err := net.ListenUDP("udp", laddr) + if err != nil { + logf("proxy-tproxy failed to listen on %s: %v", s.addr, err) + return + } + + fd, err := listener.File() + if err != nil { + logf("proxy-tproxy failed to get file descriptor: %v", err) + return + } + defer fd.Close() + + fileDescriptor := int(fd.Fd()) + if err = syscall.SetsockoptInt(fileDescriptor, syscall.SOL_IP, syscall.IP_TRANSPARENT, 1); err != nil { + syscall.Close(fileDescriptor) + logf("proxy-tproxy failed to set socket option IP_TRANSPARENT: %v", err) + return + } + + if err = syscall.SetsockoptInt(fileDescriptor, syscall.SOL_IP, syscall.IP_RECVORIGDSTADDR, 1); err != nil { + syscall.Close(fileDescriptor) + logf("proxy-tproxy failed to set socket option IP_RECVORIGDSTADDR: %v", err) + return + } + +} diff --git a/tproxy_other.go b/tproxy_other.go new file mode 100644 index 0000000..ff8b3c1 --- /dev/null +++ b/tproxy_other.go @@ -0,0 +1,20 @@ +// +build !linux + +package main + +import ( + "errors" + "log" +) + +type TProxy struct{} + +// NewTProxy returns a tproxy. +func NewTProxy(addr string, sDialer Dialer) (*TProxy, error) { + return nil, errors.New("tproxy not supported on this os") +} + +// ListenAndServe . +func (s *TProxy) ListenAndServe() { + log.Fatal("tproxy not supported on this os") +} diff --git a/uottun.go b/uottun.go index 0cc77f7..9803832 100644 --- a/uottun.go +++ b/uottun.go @@ -24,7 +24,7 @@ func NewUoTTun(addr, raddr string, sDialer Dialer) (*UoTTun, error) { return s, nil } -// ListenAndServe redirected requests as a server. +// ListenAndServe . func (s *UoTTun) ListenAndServe() { c, err := net.ListenPacket("udp", s.addr)