From 56277acb7d3a700e67f7bb7f741fe7ebe830d98f Mon Sep 17 00:00:00 2001 From: nadoo <287492+nadoo@users.noreply.github.com> Date: Sat, 17 Jul 2021 22:36:42 +0800 Subject: [PATCH] proxy: added tproxy module (only udp now) --- feature_linux.go | 1 + go.mod | 7 +- go.sum | 10 ++- proxy/tproxy/tproxy_linux.go | 137 +++++++++++++++++++++++++++++++++++ proxy/vless/vless.go | 15 ++-- service/dhcpd/dhcpd.go | 14 ++-- 6 files changed, 161 insertions(+), 23 deletions(-) create mode 100644 proxy/tproxy/tproxy_linux.go diff --git a/feature_linux.go b/feature_linux.go index 1bfb905..cc254ae 100644 --- a/feature_linux.go +++ b/feature_linux.go @@ -6,5 +6,6 @@ import ( // comment out the protocols you don't need to make the compiled binary smaller. _ "github.com/nadoo/glider/proxy/redir" + _ "github.com/nadoo/glider/proxy/tproxy" _ "github.com/nadoo/glider/proxy/unix" ) diff --git a/go.mod b/go.mod index 5818895..89266d1 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module github.com/nadoo/glider go 1.16 require ( + github.com/LiamHaworth/go-tproxy v0.0.0-20190726054950-ef7efd7f24ed github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da github.com/dgryski/go-camellia v0.0.0-20191119043421-69a8a13fb23d github.com/dgryski/go-idea v0.0.0-20170306091226-d2fb45a411fb @@ -17,9 +18,9 @@ require ( github.com/tjfoc/gmsm v1.4.1 // indirect 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-20210616213533-5ff15b29337e - golang.org/x/net v0.0.0-20210614182718-04defd469f4e // indirect - golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c // indirect + golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 + golang.org/x/net v0.0.0-20210716203947-853a461950ff + golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c ) // Replace dependency modules with local developing copy diff --git a/go.sum b/go.sum index c00f2a2..e8b23a8 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,7 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/LiamHaworth/go-tproxy v0.0.0-20190726054950-ef7efd7f24ed h1:eqa6queieK8SvoszxCu0WwH7lSVeL4/N/f1JwOMw1G4= +github.com/LiamHaworth/go-tproxy v0.0.0-20190726054950-ef7efd7f24ed/go.mod h1:rA52xkgZwql9LRZXWb2arHEFP6qSR48KY2xOfWzEciQ= github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da h1:KjTM2ks9d14ZYCvmHS9iAKVt9AyzRSqNU1qabPih5BY= github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da/go.mod h1:eHEWzANqSiWQsof+nXEI9bUVUyV6F53Fp89EuCh2EAA= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= @@ -104,8 +106,8 @@ golang.org/x/crypto v0.0.0-20191219195013-becbf705a915/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201012173705-84dcc777aaee/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e h1:gsTQYXdTw2Gq7RBsWvlQ91b+aEQ6bXFUngBGuR8sPpI= -golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 h1:/UOmuWzQfxxo9UtlXMwuQU8CMgg1eZXqTRwkSQJWKOI= +golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= @@ -129,8 +131,8 @@ golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81R golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210614182718-04defd469f4e h1:XpT3nA5TvE525Ne3hInMh6+GETgn27Zfm9dxsThnX2Q= -golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20210716203947-853a461950ff h1:j2EK/QoxYNBsXI4R7fQkkRUk8y6wnOBI+6hgPdP/6Ds= +golang.org/x/net v0.0.0-20210716203947-853a461950ff/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= diff --git a/proxy/tproxy/tproxy_linux.go b/proxy/tproxy/tproxy_linux.go new file mode 100644 index 0000000..3af95d7 --- /dev/null +++ b/proxy/tproxy/tproxy_linux.go @@ -0,0 +1,137 @@ +// ref: https://www.kernel.org/doc/Documentation/networking/tproxy.txt +// @LiamHaworth: https://github.com/LiamHaworth/go-tproxy/blob/master/tproxy_udp.go + +package tproxy + +import ( + "net" + "net/url" + "sync" + "time" + + tp "github.com/LiamHaworth/go-tproxy" + + "github.com/nadoo/glider/log" + "github.com/nadoo/glider/proxy" +) + +// TProxy struct. +type TProxy struct { + proxy proxy.Proxy + addr string +} + +func init() { + proxy.RegisterServer("tproxy", NewTProxyServer) +} + +// NewTProxy returns a tproxy. +func NewTProxy(s string, p proxy.Proxy) (*TProxy, error) { + u, err := url.Parse(s) + if err != nil { + log.F("[tproxy] parse err: %s", err) + return nil, err + } + + addr := u.Host + + tp := &TProxy{ + proxy: p, + addr: addr, + } + + return tp, nil +} + +// NewTProxyServer returns a udp tunnel server. +func NewTProxyServer(s string, p proxy.Proxy) (proxy.Server, error) { + return NewTProxy(s, p) +} + +// ListenAndServe listens on server's addr and serves connections. +func (s *TProxy) ListenAndServe() { + // go s.ListenAndServeTCP() + s.ListenAndServeUDP() +} + +// ListenAndServeTCP . +func (s *TProxy) ListenAndServeTCP() { + log.F("[tproxy] tcp mode not supported now, please use 'redir' instead") +} + +// ListenAndServeUDP . +func (s *TProxy) ListenAndServeUDP() { + laddr, err := net.ResolveUDPAddr("udp", s.addr) + if err != nil { + log.F("[tproxyu] failed to resolve addr %s: %v", s.addr, err) + return + } + + lc, err := tp.ListenUDP("udp", laddr) + if err != nil { + log.F("[tproxyu] failed to listen on %s: %v", s.addr, err) + return + } + + var nm sync.Map + buf := make([]byte, proxy.UDPBufSize) + + for { + n, lraddr, dstAddr, err := tp.ReadFromUDP(lc, buf) + if err != nil { + log.F("[tproxyu] read error: %v", err) + continue + } + + var session *natEntry + v, ok := nm.Load(lraddr.String()) + + 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 := tp.DialUDP("udp", dstAddr, lraddr) + if err != nil { + log.F("[tproxyu] dial to %s as %s error: %v", lraddr, dstAddr, err) + continue + } + + session = newNatEntry(pc, writeTo) + nm.Store(lraddr.String(), session) + + go func(lc net.PacketConn, pc net.PacketConn, lraddr *net.UDPAddr) { + proxy.RelayUDP(lc, lraddr, pc, 2*time.Minute) + pc.Close() + nm.Delete(lraddr.String()) + }(lpc, pc, lraddr) + + log.F("[tproxyu] %s <-> %s via %s", lraddr, dstAddr, dialer.Addr()) + + } else { + session = v.(*natEntry) + } + + _, err = session.WriteTo(buf[:n], session.writeTo) + if err != nil { + log.F("[tproxyu] writeTo %s error: %v", session.writeTo, err) + continue + } + } +} + +// Serve . +func (s *TProxy) Serve(c net.Conn) { + log.F("[tproxy] func Serve: can not be called directly") +} + +type natEntry struct { + net.PacketConn + writeTo net.Addr +} + +func newNatEntry(pc net.PacketConn, writeTo net.Addr) *natEntry { + return &natEntry{PacketConn: pc, writeTo: writeTo} +} diff --git a/proxy/vless/vless.go b/proxy/vless/vless.go index fe1c408..503b47c 100644 --- a/proxy/vless/vless.go +++ b/proxy/vless/vless.go @@ -49,16 +49,13 @@ func NewVLess(s string, d proxy.Dialer, p proxy.Proxy) (*VLess, error) { return nil, err } + query := u.Query() v := &VLess{ - dialer: d, - proxy: p, - addr: addr, - uuid: uuid, - // fallback: "127.0.0.1:80", - } - - if custom := u.Query().Get("fallback"); custom != "" { - v.fallback = custom + dialer: d, + proxy: p, + addr: addr, + uuid: uuid, + fallback: query.Get("fallback"), } return v, nil diff --git a/service/dhcpd/dhcpd.go b/service/dhcpd/dhcpd.go index 11b1ded..1ab8b2d 100644 --- a/service/dhcpd/dhcpd.go +++ b/service/dhcpd/dhcpd.go @@ -36,7 +36,7 @@ func (*dpcpd) Run(args ...string) { leaseTime = time.Duration(i) * time.Minute } - ip, mask, err := intfaceIP4(iface) + ip, mask, _, err := ifaceAddr(iface) if err != nil { log.F("[dhcpd] get ip of interface '%s' error: %s", iface, err) return @@ -154,27 +154,27 @@ func existsServer(iface string) (exists bool) { return true } -func intfaceIP4(iface string) (net.IP, net.IPMask, error) { +func ifaceAddr(iface string) (net.IP, net.IPMask, net.HardwareAddr, error) { intf, err := net.InterfaceByName(iface) if err != nil { - return nil, nil, err + return nil, nil, nil, err } addrs, err := intf.Addrs() if err != nil { - return nil, nil, err + return nil, nil, intf.HardwareAddr, err } for _, addr := range addrs { if ipnet, ok := addr.(*net.IPNet); ok { if ipnet.IP.IsLoopback() { - return nil, nil, errors.New("can't use loopback interface") + return nil, nil, intf.HardwareAddr, errors.New("can't use loopback interface") } if ip4 := ipnet.IP.To4(); ip4 != nil { - return ip4, ipnet.Mask, nil + return ip4, ipnet.Mask, intf.HardwareAddr, nil } } } - return nil, nil, errors.New("no ip/mask defined on this interface") + return nil, nil, intf.HardwareAddr, errors.New("no ip/mask defined on this interface") }