Skip to content

Commit

Permalink
tproxy: optimize codes and now works properly
Browse files Browse the repository at this point in the history
  • Loading branch information
nadoo committed Jul 19, 2021
1 parent 650f1ee commit 41861ff
Show file tree
Hide file tree
Showing 7 changed files with 57 additions and 106 deletions.
2 changes: 1 addition & 1 deletion config.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ func parseConfig() *Config {

// setup a log func
if conf.Verbose {
log.SetFlag(conf.LogFlags)
log.SetFlags(conf.LogFlags)
log.F = log.Debugf
}

Expand Down
2 changes: 1 addition & 1 deletion dns/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ func (c *Client) handleAnswer(respBytes []byte, clientAddr, dnsServer, network,
}

c.cache.Set(qKey(resp.Question), valCopy(respBytes), ttl)
log.F("[dns] %s <-> %s(%s) via %s, %s/%d: %s, %ds",
log.F("[dns] %s <-> %s(%s) via %s, %s/%d: %s, ttl: %ds",
clientAddr, dnsServer, network, dialerAddr, resp.Question.QNAME, resp.Question.QTYPE, strings.Join(ips, ","), ttl)

return nil
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ require (
github.com/u-root/uio v0.0.0-20210528151154-e40b768296a7 // indirect
github.com/xtaci/kcp-go/v5 v5.6.1
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97
golang.org/x/net v0.0.0-20210716203947-853a461950ff
golang.org/x/net v0.0.0-20210716203947-853a461950ff // indirect
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c
)

Expand Down
4 changes: 2 additions & 2 deletions log/log.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ import (
// F is the main log function.
var F = func(string, ...interface{}) {}

// SetFlag sets the output flags for the logger.
func SetFlag(flag int) {
// SetFlags sets the output flags for the logger.
func SetFlags(flag int) {
stdlog.SetFlags(flag)
}

Expand Down
20 changes: 10 additions & 10 deletions proxy/tproxy/tproxy_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,9 @@ func NewTProxy(s string, p proxy.Proxy) (*TProxy, error) {
return nil, err
}

addr := u.Host

tp := &TProxy{
proxy: p,
addr: addr,
addr: u.Host,
}

return tp, nil
Expand Down Expand Up @@ -82,29 +80,31 @@ func (s *TProxy) ListenAndServeUDP() {
}

var session *natEntry
v, ok := nm.Load(lraddr.String())
sessionKey := lraddr.String() + dstAddr.String()

v, ok := nm.Load(sessionKey)
if !ok && v == nil {
pc, dialer, writeTo, err := s.proxy.DialUDP("udp", dstAddr.String())
if err != nil {
log.F("[tproxyu] dial to %s error: %v", dstAddr, err)
continue
}

lpc, err := DialUDP("udp", dstAddr, lraddr)
lpc, err := ListenPacket(dstAddr)
if err != nil {
log.F("[tproxyu] dial to %s as %s error: %v", lraddr, dstAddr, err)
log.F("[tproxyu] ListenPacket as %s error: %v", dstAddr, err)
pc.Close()
continue
}

session = newNatEntry(pc, writeTo)
nm.Store(lraddr.String(), session)
nm.Store(sessionKey, session)

go func(lc net.PacketConn, pc net.PacketConn, lraddr *net.UDPAddr) {
go func(lc net.PacketConn, pc net.PacketConn, lraddr *net.UDPAddr, key string) {
proxy.RelayUDP(lc, lraddr, pc, 2*time.Minute)
pc.Close()
nm.Delete(lraddr.String())
}(lpc, pc, lraddr)
nm.Delete(key)
}(lpc, pc, lraddr, sessionKey)

log.F("[tproxyu] %s <-> %s via %s", lraddr, dstAddr, dialer.Addr())

Expand Down
131 changes: 41 additions & 90 deletions proxy/tproxy/udp_linux.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
// MIT License @LiamHaworth
// https://github.com/LiamHaworth/go-tproxy/blob/master/tproxy_udp.go
package tproxy

import (
Expand All @@ -11,27 +9,24 @@ import (
"strconv"
"syscall"
"unsafe"

"golang.org/x/sys/unix"
)

var nativeEndian binary.ByteOrder
var nativeEndian binary.ByteOrder = binary.LittleEndian

func init() {
buf := [2]byte{}
*(*uint16)(unsafe.Pointer(&buf[0])) = uint16(0xABCD)

switch buf {
case [2]byte{0xCD, 0xAB}:
nativeEndian = binary.LittleEndian
case [2]byte{0xAB, 0xCD}:
var x uint16 = 0x0102
if *(*byte)(unsafe.Pointer(&x)) == 0x01 {
nativeEndian = binary.BigEndian
default:
panic("Could not determine native endianness.")
}
}

// ListenUDP will construct a new UDP listener
// socket with the Linux IP_TRANSPARENT option
// set on the underlying socket
// The following code copies from:
// https://github.com/LiamHaworth/go-tproxy/blob/master/tproxy_udp.go
// MIT License by @LiamHaworth

// ListenUDP acts like net.ListenUDP but returns an conn with IP_TRANSPARENT option.
func ListenUDP(network string, laddr *net.UDPAddr) (*net.UDPConn, error) {
listener, err := net.ListenUDP(network, laddr)
if err != nil {
Expand All @@ -45,6 +40,7 @@ func ListenUDP(network string, laddr *net.UDPAddr) (*net.UDPConn, error) {
defer fileDescriptorSource.Close()

fileDescriptor := int(fileDescriptorSource.Fd())

if err = syscall.SetsockoptInt(fileDescriptor, syscall.SOL_IP, syscall.IP_TRANSPARENT, 1); err != nil {
return nil, &net.OpError{Op: "listen", Net: network, Source: nil, Addr: laddr, Err: fmt.Errorf("set socket option: IP_TRANSPARENT: %s", err)}
}
Expand Down Expand Up @@ -113,95 +109,50 @@ func ReadFromUDP(conn *net.UDPConn, b []byte) (int, *net.UDPAddr, *net.UDPAddr,
return n, addr, originalDst, nil
}

// DialUDP connects to the remote address raddr on the network net,
// which must be "udp", "udp4", or "udp6". If laddr is not nil, it is
// used as the local address for the connection.
func DialUDP(network string, laddr *net.UDPAddr, raddr *net.UDPAddr) (*net.UDPConn, error) {
remoteSocketAddress, err := udpAddrToSocketAddr(raddr)
if err != nil {
return nil, &net.OpError{Op: "dial", Err: fmt.Errorf("build destination socket address: %s", err)}
}
// ListenPacket acts like net.ListenPacket but the addr could be non-local.
func ListenPacket(addr *net.UDPAddr) (net.PacketConn, error) {
var af int
var sockaddr syscall.Sockaddr

localSocketAddress, err := udpAddrToSocketAddr(laddr)
if err != nil {
return nil, &net.OpError{Op: "dial", Err: fmt.Errorf("build local socket address: %s", err)}
if len(addr.IP) == 4 {
af = syscall.AF_INET
sockaddr = &syscall.SockaddrInet4{Port: addr.Port}
copy(sockaddr.(*syscall.SockaddrInet4).Addr[:], addr.IP)
} else {
af = syscall.AF_INET6
sockaddr = &syscall.SockaddrInet6{Port: addr.Port}
copy(sockaddr.(*syscall.SockaddrInet6).Addr[:], addr.IP)
}

fileDescriptor, err := syscall.Socket(udpAddrFamily(network, laddr, raddr), syscall.SOCK_DGRAM, 0)
if err != nil {
return nil, &net.OpError{Op: "dial", Err: fmt.Errorf("socket open: %s", err)}
}
var fd int
var err error

if err = syscall.SetsockoptInt(fileDescriptor, syscall.SOL_SOCKET, syscall.SO_REUSEADDR, 1); err != nil {
syscall.Close(fileDescriptor)
return nil, &net.OpError{Op: "dial", Err: fmt.Errorf("set socket option: SO_REUSEADDR: %s", err)}
if fd, err = syscall.Socket(af, syscall.SOCK_DGRAM, 0); err != nil {
return nil, &net.OpError{Op: "fake", Err: fmt.Errorf("socket open: %s", err)}
}

if err = syscall.SetsockoptInt(fileDescriptor, syscall.SOL_IP, syscall.IP_TRANSPARENT, 1); err != nil {
syscall.Close(fileDescriptor)
return nil, &net.OpError{Op: "dial", Err: fmt.Errorf("set socket option: IP_TRANSPARENT: %s", err)}
if err = syscall.SetsockoptInt(fd, syscall.SOL_IP, syscall.IP_TRANSPARENT, 1); err != nil {
syscall.Close(fd)
return nil, &net.OpError{Op: "fake", Err: fmt.Errorf("set socket option: IP_TRANSPARENT: %s", err)}
}

if err = syscall.Bind(fileDescriptor, localSocketAddress); err != nil {
syscall.Close(fileDescriptor)
return nil, &net.OpError{Op: "dial", Err: fmt.Errorf("socket bind: %s", err)}
}
syscall.SetsockoptInt(fd, syscall.SOL_SOCKET, syscall.SO_REUSEADDR, 1)

syscall.SetsockoptInt(fd, syscall.SOL_SOCKET, unix.SO_REUSEPORT, 1)

if err = syscall.Connect(fileDescriptor, remoteSocketAddress); err != nil {
syscall.Close(fileDescriptor)
return nil, &net.OpError{Op: "dial", Err: fmt.Errorf("socket connect: %s", err)}
if err = syscall.Bind(fd, sockaddr); err != nil {
syscall.Close(fd)
return nil, &net.OpError{Op: "fake", Err: fmt.Errorf("socket bind: %s", err)}
}

fdFile := os.NewFile(uintptr(fileDescriptor), fmt.Sprintf("net-udp-dial-%s", raddr.String()))
fdFile := os.NewFile(uintptr(fd), fmt.Sprintf("net-udp-listen-%s", addr.String()))
defer fdFile.Close()

remoteConn, err := net.FileConn(fdFile)
packetConn, err := net.FilePacketConn(fdFile)
if err != nil {
syscall.Close(fileDescriptor)
return nil, &net.OpError{Op: "dial", Err: fmt.Errorf("convert file descriptor to connection: %s", err)}
syscall.Close(fd)
return nil, &net.OpError{Op: "fake", Err: fmt.Errorf("convert file descriptor to connection: %s", err)}
}

return remoteConn.(*net.UDPConn), nil
}

// udpAddToSockerAddr will convert a UDPAddr
// into a Sockaddr that may be used when
// connecting and binding sockets
func udpAddrToSocketAddr(addr *net.UDPAddr) (syscall.Sockaddr, error) {
switch {
case addr.IP.To4() != nil:
ip := [4]byte{}
copy(ip[:], addr.IP.To4())

return &syscall.SockaddrInet4{Addr: ip, Port: addr.Port}, nil

default:
ip := [16]byte{}
copy(ip[:], addr.IP.To16())

zoneID, err := strconv.ParseUint(addr.Zone, 10, 32)
if err != nil {
return nil, err
}

return &syscall.SockaddrInet6{Addr: ip, Port: addr.Port, ZoneId: uint32(zoneID)}, nil
}
}

// udpAddrFamily will attempt to work
// out the address family based on the
// network and UDP addresses
func udpAddrFamily(net string, laddr, raddr *net.UDPAddr) int {
switch net[len(net)-1] {
case '4':
return syscall.AF_INET
case '6':
return syscall.AF_INET6
}

if (laddr == nil || laddr.IP.To4() != nil) &&
(raddr == nil || laddr.IP.To4() != nil) {
return syscall.AF_INET
}
return syscall.AF_INET6
return packetConn, nil
}
2 changes: 1 addition & 1 deletion systemd/[email protected]
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ ExecStart=/usr/bin/glider -config /etc/glider/%i.conf

# NOTE:
# work with systemd v229 or later, so glider can listen on port below 1024 with none-root user
# CAP_NET_ADMIN: ipset
# CAP_NET_ADMIN: ipset, setsockopt: IP_TRANSPARENT
# CAP_NET_BIND_SERVICE: bind ports under 1024
# CAP_NET_RAW: bind raw socket and broadcasting (used by dhcpd)
CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_BIND_SERVICE CAP_NET_RAW
Expand Down

0 comments on commit 41861ff

Please sign in to comment.