mirror of
https://github.com/nadoo/glider.git
synced 2025-02-23 17:35:40 +08:00
tproxy: optimize codes and now works properly
This commit is contained in:
parent
650f1eed4e
commit
41861ff48e
@ -83,7 +83,7 @@ func parseConfig() *Config {
|
|||||||
|
|
||||||
// setup a log func
|
// setup a log func
|
||||||
if conf.Verbose {
|
if conf.Verbose {
|
||||||
log.SetFlag(conf.LogFlags)
|
log.SetFlags(conf.LogFlags)
|
||||||
log.F = log.Debugf
|
log.F = log.Debugf
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -120,7 +120,7 @@ func (c *Client) handleAnswer(respBytes []byte, clientAddr, dnsServer, network,
|
|||||||
}
|
}
|
||||||
|
|
||||||
c.cache.Set(qKey(resp.Question), valCopy(respBytes), ttl)
|
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)
|
clientAddr, dnsServer, network, dialerAddr, resp.Question.QNAME, resp.Question.QTYPE, strings.Join(ips, ","), ttl)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
2
go.mod
2
go.mod
@ -18,7 +18,7 @@ require (
|
|||||||
github.com/u-root/uio v0.0.0-20210528151154-e40b768296a7 // indirect
|
github.com/u-root/uio v0.0.0-20210528151154-e40b768296a7 // indirect
|
||||||
github.com/xtaci/kcp-go/v5 v5.6.1
|
github.com/xtaci/kcp-go/v5 v5.6.1
|
||||||
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97
|
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
|
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -8,8 +8,8 @@ import (
|
|||||||
// F is the main log function.
|
// F is the main log function.
|
||||||
var F = func(string, ...interface{}) {}
|
var F = func(string, ...interface{}) {}
|
||||||
|
|
||||||
// SetFlag sets the output flags for the logger.
|
// SetFlags sets the output flags for the logger.
|
||||||
func SetFlag(flag int) {
|
func SetFlags(flag int) {
|
||||||
stdlog.SetFlags(flag)
|
stdlog.SetFlags(flag)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -28,11 +28,9 @@ func NewTProxy(s string, p proxy.Proxy) (*TProxy, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
addr := u.Host
|
|
||||||
|
|
||||||
tp := &TProxy{
|
tp := &TProxy{
|
||||||
proxy: p,
|
proxy: p,
|
||||||
addr: addr,
|
addr: u.Host,
|
||||||
}
|
}
|
||||||
|
|
||||||
return tp, nil
|
return tp, nil
|
||||||
@ -82,8 +80,9 @@ func (s *TProxy) ListenAndServeUDP() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var session *natEntry
|
var session *natEntry
|
||||||
v, ok := nm.Load(lraddr.String())
|
sessionKey := lraddr.String() + dstAddr.String()
|
||||||
|
|
||||||
|
v, ok := nm.Load(sessionKey)
|
||||||
if !ok && v == nil {
|
if !ok && v == nil {
|
||||||
pc, dialer, writeTo, err := s.proxy.DialUDP("udp", dstAddr.String())
|
pc, dialer, writeTo, err := s.proxy.DialUDP("udp", dstAddr.String())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -91,20 +90,21 @@ func (s *TProxy) ListenAndServeUDP() {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
lpc, err := DialUDP("udp", dstAddr, lraddr)
|
lpc, err := ListenPacket(dstAddr)
|
||||||
if err != nil {
|
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
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
session = newNatEntry(pc, writeTo)
|
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)
|
proxy.RelayUDP(lc, lraddr, pc, 2*time.Minute)
|
||||||
pc.Close()
|
pc.Close()
|
||||||
nm.Delete(lraddr.String())
|
nm.Delete(key)
|
||||||
}(lpc, pc, lraddr)
|
}(lpc, pc, lraddr, sessionKey)
|
||||||
|
|
||||||
log.F("[tproxyu] %s <-> %s via %s", lraddr, dstAddr, dialer.Addr())
|
log.F("[tproxyu] %s <-> %s via %s", lraddr, dstAddr, dialer.Addr())
|
||||||
|
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
// MIT License @LiamHaworth
|
|
||||||
// https://github.com/LiamHaworth/go-tproxy/blob/master/tproxy_udp.go
|
|
||||||
package tproxy
|
package tproxy
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@ -11,27 +9,24 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
"syscall"
|
"syscall"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
|
|
||||||
|
"golang.org/x/sys/unix"
|
||||||
)
|
)
|
||||||
|
|
||||||
var nativeEndian binary.ByteOrder
|
var nativeEndian binary.ByteOrder = binary.LittleEndian
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
buf := [2]byte{}
|
var x uint16 = 0x0102
|
||||||
*(*uint16)(unsafe.Pointer(&buf[0])) = uint16(0xABCD)
|
if *(*byte)(unsafe.Pointer(&x)) == 0x01 {
|
||||||
|
|
||||||
switch buf {
|
|
||||||
case [2]byte{0xCD, 0xAB}:
|
|
||||||
nativeEndian = binary.LittleEndian
|
|
||||||
case [2]byte{0xAB, 0xCD}:
|
|
||||||
nativeEndian = binary.BigEndian
|
nativeEndian = binary.BigEndian
|
||||||
default:
|
|
||||||
panic("Could not determine native endianness.")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ListenUDP will construct a new UDP listener
|
// The following code copies from:
|
||||||
// socket with the Linux IP_TRANSPARENT option
|
// https://github.com/LiamHaworth/go-tproxy/blob/master/tproxy_udp.go
|
||||||
// set on the underlying socket
|
// 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) {
|
func ListenUDP(network string, laddr *net.UDPAddr) (*net.UDPConn, error) {
|
||||||
listener, err := net.ListenUDP(network, laddr)
|
listener, err := net.ListenUDP(network, laddr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -45,6 +40,7 @@ func ListenUDP(network string, laddr *net.UDPAddr) (*net.UDPConn, error) {
|
|||||||
defer fileDescriptorSource.Close()
|
defer fileDescriptorSource.Close()
|
||||||
|
|
||||||
fileDescriptor := int(fileDescriptorSource.Fd())
|
fileDescriptor := int(fileDescriptorSource.Fd())
|
||||||
|
|
||||||
if err = syscall.SetsockoptInt(fileDescriptor, syscall.SOL_IP, syscall.IP_TRANSPARENT, 1); err != nil {
|
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)}
|
return nil, &net.OpError{Op: "listen", Net: network, Source: nil, Addr: laddr, Err: fmt.Errorf("set socket option: IP_TRANSPARENT: %s", err)}
|
||||||
}
|
}
|
||||||
@ -113,95 +109,50 @@ func ReadFromUDP(conn *net.UDPConn, b []byte) (int, *net.UDPAddr, *net.UDPAddr,
|
|||||||
return n, addr, originalDst, nil
|
return n, addr, originalDst, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// DialUDP connects to the remote address raddr on the network net,
|
// ListenPacket acts like net.ListenPacket but the addr could be non-local.
|
||||||
// which must be "udp", "udp4", or "udp6". If laddr is not nil, it is
|
func ListenPacket(addr *net.UDPAddr) (net.PacketConn, error) {
|
||||||
// used as the local address for the connection.
|
var af int
|
||||||
func DialUDP(network string, laddr *net.UDPAddr, raddr *net.UDPAddr) (*net.UDPConn, error) {
|
var sockaddr syscall.Sockaddr
|
||||||
remoteSocketAddress, err := udpAddrToSocketAddr(raddr)
|
|
||||||
if err != nil {
|
if len(addr.IP) == 4 {
|
||||||
return nil, &net.OpError{Op: "dial", Err: fmt.Errorf("build destination socket address: %s", err)}
|
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)
|
||||||
}
|
}
|
||||||
|
|
||||||
localSocketAddress, err := udpAddrToSocketAddr(laddr)
|
var fd int
|
||||||
if err != nil {
|
var err error
|
||||||
return nil, &net.OpError{Op: "dial", Err: fmt.Errorf("build local socket address: %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)}
|
||||||
}
|
}
|
||||||
|
|
||||||
fileDescriptor, err := syscall.Socket(udpAddrFamily(network, laddr, raddr), syscall.SOCK_DGRAM, 0)
|
if err = syscall.SetsockoptInt(fd, syscall.SOL_IP, syscall.IP_TRANSPARENT, 1); err != nil {
|
||||||
if err != nil {
|
syscall.Close(fd)
|
||||||
return nil, &net.OpError{Op: "dial", Err: fmt.Errorf("socket open: %s", err)}
|
return nil, &net.OpError{Op: "fake", Err: fmt.Errorf("set socket option: IP_TRANSPARENT: %s", err)}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = syscall.SetsockoptInt(fileDescriptor, syscall.SOL_SOCKET, syscall.SO_REUSEADDR, 1); err != nil {
|
syscall.SetsockoptInt(fd, syscall.SOL_SOCKET, syscall.SO_REUSEADDR, 1)
|
||||||
syscall.Close(fileDescriptor)
|
|
||||||
return nil, &net.OpError{Op: "dial", Err: fmt.Errorf("set socket option: SO_REUSEADDR: %s", err)}
|
syscall.SetsockoptInt(fd, syscall.SOL_SOCKET, unix.SO_REUSEPORT, 1)
|
||||||
|
|
||||||
|
if err = syscall.Bind(fd, sockaddr); err != nil {
|
||||||
|
syscall.Close(fd)
|
||||||
|
return nil, &net.OpError{Op: "fake", Err: fmt.Errorf("socket bind: %s", err)}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = syscall.SetsockoptInt(fileDescriptor, syscall.SOL_IP, syscall.IP_TRANSPARENT, 1); err != nil {
|
fdFile := os.NewFile(uintptr(fd), fmt.Sprintf("net-udp-listen-%s", addr.String()))
|
||||||
syscall.Close(fileDescriptor)
|
|
||||||
return nil, &net.OpError{Op: "dial", 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)}
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = syscall.Connect(fileDescriptor, remoteSocketAddress); err != nil {
|
|
||||||
syscall.Close(fileDescriptor)
|
|
||||||
return nil, &net.OpError{Op: "dial", Err: fmt.Errorf("socket connect: %s", err)}
|
|
||||||
}
|
|
||||||
|
|
||||||
fdFile := os.NewFile(uintptr(fileDescriptor), fmt.Sprintf("net-udp-dial-%s", raddr.String()))
|
|
||||||
defer fdFile.Close()
|
defer fdFile.Close()
|
||||||
|
|
||||||
remoteConn, err := net.FileConn(fdFile)
|
packetConn, err := net.FilePacketConn(fdFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
syscall.Close(fileDescriptor)
|
syscall.Close(fd)
|
||||||
return nil, &net.OpError{Op: "dial", Err: fmt.Errorf("convert file descriptor to connection: %s", err)}
|
return nil, &net.OpError{Op: "fake", Err: fmt.Errorf("convert file descriptor to connection: %s", err)}
|
||||||
}
|
}
|
||||||
|
|
||||||
return remoteConn.(*net.UDPConn), nil
|
return packetConn, 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
|
|
||||||
}
|
}
|
||||||
|
@ -13,7 +13,7 @@ ExecStart=/usr/bin/glider -config /etc/glider/%i.conf
|
|||||||
|
|
||||||
# NOTE:
|
# NOTE:
|
||||||
# work with systemd v229 or later, so glider can listen on port below 1024 with none-root user
|
# 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_BIND_SERVICE: bind ports under 1024
|
||||||
# CAP_NET_RAW: bind raw socket and broadcasting (used by dhcpd)
|
# CAP_NET_RAW: bind raw socket and broadcasting (used by dhcpd)
|
||||||
CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_BIND_SERVICE CAP_NET_RAW
|
CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_BIND_SERVICE CAP_NET_RAW
|
||||||
|
Loading…
Reference in New Issue
Block a user