diff --git a/README.md b/README.md index eb4968c..e45df51 100644 --- a/README.md +++ b/README.md @@ -56,13 +56,13 @@ we can set up local listeners as proxy servers, and forward requests to internet |SSR | | |√| |client only |SSH | | |√| |client only |SOCKS4 | | |√| |client only +|TCP |√| |√| |tcp tunnel client & server +|UDP | |√| |√|udp tunnel client & server |TLS |√| |√| |transport client & server |KCP | |√|√| |transport client & server |Unix |√| |√| |transport client & server |Websocket |√| |√| |transport client & server |Simple-Obfs | | |√| |transport client only -|TCPTun |√| | | |transport server only -|UDPTun | |√| | |transport server only |Redir |√| | | |linux only |Redir6 |√| | | |linux only(ipv6) |Reject | | |√|√|reject all requests @@ -94,7 +94,12 @@ glider -h click to see details ```bash -glider 0.12.3 usage: +glider 0.13.0 usage: + -check string + check=tcp[://HOST:PORT]: tcp port connect check + check=http://HOST[:PORT][/URI][#expect=STRING_IN_RESP_LINE] + check=file://SCRIPT_PATH: run a check script, healthy when exitcode=0, environment variables: FORWARDER_ADDR + check=disable: disable health check (default "http://www.msftconnecttest.com/connecttest.txt#expect=200") -checkdisabledonly check disabled fowarders only -checkinterval int @@ -103,8 +108,6 @@ glider 0.12.3 usage: fowarder check timeout(seconds) (default 10) -checktolerance int fowarder check tolerance(ms), switch only when new_latency < old_latency - tolerance, only used in lha mode - -checkwebsite string - fowarder check HTTP(NOT HTTPS) website address, format: HOST[:PORT], default port: 80 (default "www.apple.com") -config string config file path -dialtimeout int @@ -149,8 +152,8 @@ glider 0.12.3 usage: verbose mode Available schemes: - listen: mixed ss socks5 http vless trojan trojanc redir redir6 tcptun udptun tls ws unix kcp - forward: reject ss socks4 socks5 http ssr ssh vless vmess trojan trojanc tls ws unix kcp simple-obfs + listen: mixed ss socks5 http vless trojan trojanc redir redir6 tcp udp tls ws unix kcp + forward: reject ss socks4 socks5 http ssr ssh vless vmess trojan trojanc tcp udp tls ws unix kcp simple-obfs Socks5 scheme: socks://[user:pass@]host:port @@ -292,26 +295,20 @@ Examples: ./glider -listen redir://:1081 -forward ss://method:pass@1.1.1.1:8443 -listen on :1081 as a transparent redirect server, forward all requests via remote ss server. - ./glider -listen redir://:1081 -forward "ssr://method:pass@1.1.1.1:8444?protocol=a&protocol_param=b&obfs=c&obfs_param=d" - -listen on :1081 as a transparent redirect server, forward all requests via remote ssr server. - ./glider -listen redir://:1081 -forward "tls://abc.com:443,vmess://security:uuid@?alterID=10" -listen on :1081 as a transparent redirect server, forward all requests via remote tls+vmess server. - ./glider -listen redir://:1081 -forward "ws://1.1.1.1:80,vmess://security:uuid@?alterID=10" - -listen on :1081 as a transparent redirect server, forward all requests via remote ws+vmess server. + ./glider -listen tcp://:80 -forward tcp://2.2.2.2:80 + -tcp tunnel: listen on :80 and forward all requests to 2.2.2.2:80. - ./glider -listen tcptun://:80=2.2.2.2:80 -forward ss://method:pass@1.1.1.1:8443 - -listen on :80 and forward all requests to 2.2.2.2:80 via remote ss server. - - ./glider -listen udptun://:53=8.8.8.8:53 -forward ss://method:pass@1.1.1.1:8443 + ./glider -listen udp://:53 -forward ss://method:pass@1.1.1.1:8443,udp://8.8.8.8:53 -listen on :53 and forward all udp requests to 8.8.8.8:53 via remote ss server. ./glider -listen socks5://:1080 -listen http://:8080 -forward ss://method:pass@1.1.1.1:8443 -listen on :1080 as socks5 server, :8080 as http proxy server, forward all requests via remote ss server. - ./glider -listen redir://:1081 -dns=:53 -dnsserver=8.8.8.8:53 -forward ss://method:pass@server1:port1,ss://method:pass@server2:port2 - -listen on :1081 as transparent redirect server, :53 as dns server, use forward chain: server1 -> server2. + ./glider -listen redir://:1081 -dns=:53 -dnsserver=8.8.8.8:53 -forward ss://method:pass@server:port + -listen on :1081 as transparent redirect server, :53 as dns server, forward via ss server. ./glider -listen socks5://:1080 -forward ss://method:pass@server1:port1 -forward ss://method:pass@server2:port2 -strategy rr -listen on :1080 as socks5 server, forward requests via server1 and server2 in round robin mode. diff --git a/config.go b/config.go index 22b3932..54275ce 100644 --- a/config.go +++ b/config.go @@ -131,8 +131,8 @@ func usage() { fmt.Fprintf(w, "\n") fmt.Fprintf(w, "Available schemes:\n") - fmt.Fprintf(w, " listen: mixed ss socks5 http vless trojan trojanc redir redir6 tcptun udptun tls ws unix kcp\n") - fmt.Fprintf(w, " forward: reject ss socks4 socks5 http ssr ssh vless vmess trojan trojanc tls ws unix kcp simple-obfs\n") + fmt.Fprintf(w, " listen: mixed ss socks5 http vless trojan trojanc redir redir6 tcp udp tls ws unix kcp\n") + fmt.Fprintf(w, " forward: reject ss socks4 socks5 http ssr ssh vless vmess trojan trojanc tcp udp tls ws unix kcp simple-obfs\n") fmt.Fprintf(w, "\n") fmt.Fprintf(w, "Socks5 scheme:\n") @@ -302,26 +302,20 @@ func usage() { fmt.Fprintf(w, " "+app+" -listen redir://:1081 -forward ss://method:pass@1.1.1.1:8443\n") fmt.Fprintf(w, " -listen on :1081 as a transparent redirect server, forward all requests via remote ss server.\n") fmt.Fprintf(w, "\n") - fmt.Fprintf(w, " "+app+" -listen redir://:1081 -forward \"ssr://method:pass@1.1.1.1:8444?protocol=a&protocol_param=b&obfs=c&obfs_param=d\"\n") - fmt.Fprintf(w, " -listen on :1081 as a transparent redirect server, forward all requests via remote ssr server.\n") - fmt.Fprintf(w, "\n") fmt.Fprintf(w, " "+app+" -listen redir://:1081 -forward \"tls://abc.com:443,vmess://security:uuid@?alterID=10\"\n") fmt.Fprintf(w, " -listen on :1081 as a transparent redirect server, forward all requests via remote tls+vmess server.\n") fmt.Fprintf(w, "\n") - fmt.Fprintf(w, " "+app+" -listen redir://:1081 -forward \"ws://1.1.1.1:80,vmess://security:uuid@?alterID=10\"\n") - fmt.Fprintf(w, " -listen on :1081 as a transparent redirect server, forward all requests via remote ws+vmess server.\n") + fmt.Fprintf(w, " "+app+" -listen tcp://:80 -forward tcp://2.2.2.2:80\n") + fmt.Fprintf(w, " -tcp tunnel: listen on :80 and forward all requests to 2.2.2.2:80.\n") fmt.Fprintf(w, "\n") - fmt.Fprintf(w, " "+app+" -listen tcptun://:80=2.2.2.2:80 -forward ss://method:pass@1.1.1.1:8443\n") - fmt.Fprintf(w, " -listen on :80 and forward all requests to 2.2.2.2:80 via remote ss server.\n") - fmt.Fprintf(w, "\n") - fmt.Fprintf(w, " "+app+" -listen udptun://:53=8.8.8.8:53 -forward ss://method:pass@1.1.1.1:8443\n") + fmt.Fprintf(w, " "+app+" -listen udp://:53 -forward ss://method:pass@1.1.1.1:8443,udp://8.8.8.8:53\n") fmt.Fprintf(w, " -listen on :53 and forward all udp requests to 8.8.8.8:53 via remote ss server.\n") fmt.Fprintf(w, "\n") fmt.Fprintf(w, " "+app+" -listen socks5://:1080 -listen http://:8080 -forward ss://method:pass@1.1.1.1:8443\n") fmt.Fprintf(w, " -listen on :1080 as socks5 server, :8080 as http proxy server, forward all requests via remote ss server.\n") fmt.Fprintf(w, "\n") - fmt.Fprintf(w, " "+app+" -listen redir://:1081 -dns=:53 -dnsserver=8.8.8.8:53 -forward ss://method:pass@server1:port1,ss://method:pass@server2:port2\n") - fmt.Fprintf(w, " -listen on :1081 as transparent redirect server, :53 as dns server, use forward chain: server1 -> server2.\n") + fmt.Fprintf(w, " "+app+" -listen redir://:1081 -dns=:53 -dnsserver=8.8.8.8:53 -forward ss://method:pass@server:port\n") + fmt.Fprintf(w, " -listen on :1081 as transparent redirect server, :53 as dns server, forward via ss server.\n") fmt.Fprintf(w, "\n") fmt.Fprintf(w, " "+app+" -listen socks5://:1080 -forward ss://method:pass@server1:port1 -forward ss://method:pass@server2:port2 -strategy rr\n") fmt.Fprintf(w, " -listen on :1080 as socks5 server, forward requests via server1 and server2 in round robin mode.\n") diff --git a/config/glider.conf.example b/config/glider.conf.example index 8c0b574..da31ce1 100644 --- a/config/glider.conf.example +++ b/config/glider.conf.example @@ -51,12 +51,6 @@ listen=socks5://:1080 # listen on 1081 as a linux transparent proxy server. # listen=redir://:1081 -# listen on 1082 as a tcp tunnel, all requests to :1082 will be forward to 1.1.1.1:80 -# listen=tcptun://:1082=1.1.1.1:80 - -# listen on 1083 as a udp tunnel, all requests to :1083 will be forward to 1.1.1.1:53 -# listen=udptun://:1083=1.1.1.1:53 - # http over tls (HTTPS proxy) # listen=tls://:443?cert=crtFilePath&key=keyFilePath,http:// diff --git a/feature.go b/feature.go index a9c8cc2..930a157 100644 --- a/feature.go +++ b/feature.go @@ -22,8 +22,4 @@ import ( _ "github.com/nadoo/glider/proxy/vless" _ "github.com/nadoo/glider/proxy/vmess" _ "github.com/nadoo/glider/proxy/ws" - - // deprecated, to be removed. - _ "github.com/nadoo/glider/proxy/tcptun" - _ "github.com/nadoo/glider/proxy/udptun" ) diff --git a/go.mod b/go.mod index 6f358a0..84aff94 100644 --- a/go.mod +++ b/go.mod @@ -15,7 +15,7 @@ require ( github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect github.com/xtaci/kcp-go/v5 v5.6.1 golang.org/x/crypto v0.0.0-20201124201722-c8d3bf9c5392 - golang.org/x/sys v0.0.0-20201119102817-f84b799fce68 // indirect + golang.org/x/sys v0.0.0-20201126233918-771906719818 // indirect golang.org/x/tools v0.0.0-20201125231158-b5590deeca9b // indirect gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b // indirect ) diff --git a/go.sum b/go.sum index 5a72ba5..3e5f153 100644 --- a/go.sum +++ b/go.sum @@ -128,8 +128,8 @@ golang.org/x/sys v0.0.0-20201009025420-dfb3f7c4e634/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20201101102859-da207088b7d1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201112073958-5cba982894dd h1:5CtCZbICpIOFdgO940moixOPjc0178IU44m4EjOO5IY= golang.org/x/sys v0.0.0-20201112073958-5cba982894dd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68 h1:nxC68pudNYkKU6jWhgrqdreuFiOQWj1Fs7T3VrH4Pjw= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201126233918-771906719818 h1:f1CIuDlJhwANEC2MM87MBEVMr3jl5bifgsfj90XAF9c= +golang.org/x/sys v0.0.0-20201126233918-771906719818/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221 h1:/ZHdbVpdR/jk3g30/d4yUL0JU9kksj8+F/bnQUVLGDM= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= diff --git a/proxy/proxy.go b/proxy/proxy.go index 7b72f2f..03eda74 100644 --- a/proxy/proxy.go +++ b/proxy/proxy.go @@ -8,7 +8,7 @@ type Proxy interface { Dial(network, addr string) (c net.Conn, dialer Dialer, err error) // DialUDP connects to the given address via the proxy. - DialUDP(network, addr string) (pc net.PacketConn, writeTo net.Addr, err error) + DialUDP(network, addr string) (pc net.PacketConn, dialer UDPDialer, writeTo net.Addr, err error) // Get the dialer by dstAddr. NextDialer(dstAddr string) Dialer diff --git a/proxy/socks5/server.go b/proxy/socks5/server.go index 7bfdb67..0b33428 100644 --- a/proxy/socks5/server.go +++ b/proxy/socks5/server.go @@ -124,7 +124,7 @@ func (s *Socks5) ListenAndServeUDP() { continue } - lpc, nextHop, err := s.proxy.DialUDP("udp", c.tgtAddr.String()) + lpc, _, nextHop, err := s.proxy.DialUDP("udp", c.tgtAddr.String()) if err != nil { log.F("[socks5u] remote dial error: %v", err) continue diff --git a/proxy/ss/server.go b/proxy/ss/server.go index af01ce9..4c348fe 100644 --- a/proxy/ss/server.go +++ b/proxy/ss/server.go @@ -108,7 +108,7 @@ func (s *SS) ListenAndServeUDP() { var pc *PktConn v, ok := nm.Load(raddr.String()) if !ok && v == nil { - lpc, nextHop, err := s.proxy.DialUDP("udp", c.tgtAddr.String()) + lpc, _, nextHop, err := s.proxy.DialUDP("udp", c.tgtAddr.String()) if err != nil { log.F("[ssu] remote dial error: %v", err) continue diff --git a/proxy/tcptun/tcptun.go b/proxy/tcptun/tcptun.go deleted file mode 100644 index 013a78d..0000000 --- a/proxy/tcptun/tcptun.go +++ /dev/null @@ -1,99 +0,0 @@ -package tcptun - -import ( - "errors" - "net" - "net/url" - "strings" - - "github.com/nadoo/glider/log" - "github.com/nadoo/glider/proxy" -) - -// TCPTun struct. -type TCPTun struct { - proxy proxy.Proxy - addr string - - raddr string -} - -func init() { - proxy.RegisterServer("tcptun", NewTCPTunServer) -} - -// NewTCPTun returns a tcptun proxy. -func NewTCPTun(s string, p proxy.Proxy) (*TCPTun, error) { - u, err := url.Parse(s) - if err != nil { - log.F("[tcptun] parse err: %s", err) - return nil, err - } - - addr := u.Host - d := strings.Split(addr, "=") - if len(d) < 2 { - return nil, errors.New("error in strings.Split") - } - - t := &TCPTun{ - proxy: p, - addr: d[0], - raddr: d[1], - } - - return t, nil -} - -// NewTCPTunServer returns a udp tunnel server. -func NewTCPTunServer(s string, p proxy.Proxy) (proxy.Server, error) { - return NewTCPTun(s, p) -} - -// ListenAndServe listens on server's addr and serves connections. -func (s *TCPTun) ListenAndServe() { - l, err := net.Listen("tcp", s.addr) - if err != nil { - log.F("[tcptun] failed to listen on %s: %v", s.addr, err) - return - } - - log.F("[tcptun] listening TCP on %s", s.addr) - - for { - c, err := l.Accept() - if err != nil { - log.F("[tcptun] failed to accept: %v", err) - continue - } - - go s.Serve(c) - } -} - -// Serve serves a connection. -func (s *TCPTun) Serve(c net.Conn) { - defer c.Close() - - if c, ok := c.(*net.TCPConn); ok { - c.SetKeepAlive(true) - } - - rc, dialer, err := s.proxy.Dial("tcp", s.raddr) - if err != nil { - log.F("[tcptun] %s <-> %s via %s, error in dial: %v", c.RemoteAddr(), s.addr, dialer.Addr(), err) - s.proxy.Record(dialer, false) - return - } - defer rc.Close() - - log.F("[tcptun] %s <-> %s via %s", c.RemoteAddr(), s.raddr, dialer.Addr()) - - if err = proxy.Relay(c, rc); err != nil { - log.F("[tcptun] %s <-> %s via %s, relay error: %v", c.RemoteAddr(), s.raddr, dialer.Addr(), err) - // record remote conn failure only - if !strings.Contains(err.Error(), s.addr) { - s.proxy.Record(dialer, false) - } - } -} diff --git a/proxy/udp/udp.go b/proxy/udp/udp.go index c6216d0..90c577f 100644 --- a/proxy/udp/udp.go +++ b/proxy/udp/udp.go @@ -73,10 +73,11 @@ func (s *UDP) ListenAndServe() { var raddr net.Addr var pc net.PacketConn + var dialer proxy.UDPDialer v, ok := nm.Load(lraddr.String()) if !ok && v == nil { - pc, raddr, err = s.proxy.DialUDP("udp", "") + pc, dialer, raddr, err = s.proxy.DialUDP("udp", "") if err != nil { log.F("[udp] remote dial error: %v", err) continue @@ -100,7 +101,7 @@ func (s *UDP) ListenAndServe() { continue } - log.F("[udp] %s <-> %s", lraddr, raddr) + log.F("[udp] %s <-> %s", lraddr, dialer.Addr()) } } diff --git a/proxy/udptun/udptun.go b/proxy/udptun/udptun.go deleted file mode 100644 index e974829..0000000 --- a/proxy/udptun/udptun.go +++ /dev/null @@ -1,113 +0,0 @@ -package udptun - -import ( - "errors" - "net" - "net/url" - "strings" - "sync" - "time" - - "github.com/nadoo/glider/log" - "github.com/nadoo/glider/proxy" -) - -// UDPTun is a base udptun struct. -type UDPTun struct { - proxy proxy.Proxy - addr string - taddr string // tunnel addr string - tuaddr *net.UDPAddr // tunnel addr -} - -func init() { - proxy.RegisterServer("udptun", NewUDPTunServer) -} - -// NewUDPTun returns a UDPTun proxy. -func NewUDPTun(s string, p proxy.Proxy) (*UDPTun, error) { - u, err := url.Parse(s) - if err != nil { - log.F("[udptun] parse err: %s", err) - return nil, err - } - - addr := u.Host - d := strings.Split(addr, "=") - if len(d) < 2 { - return nil, errors.New("error in strings.Split") - } - - ut := &UDPTun{ - proxy: p, - addr: d[0], - taddr: d[1], - } - - ut.tuaddr, err = net.ResolveUDPAddr("udp", ut.taddr) - return ut, err -} - -// NewUDPTunServer returns a udp tunnel server. -func NewUDPTunServer(s string, p proxy.Proxy) (proxy.Server, error) { - return NewUDPTun(s, p) -} - -// ListenAndServe listen and serves on the given address. -func (s *UDPTun) ListenAndServe() { - c, err := net.ListenPacket("udp", s.addr) - if err != nil { - log.F("[udptun] failed to listen on %s: %v", s.addr, err) - return - } - defer c.Close() - - log.F("[udptun] listening UDP on %s", s.addr) - - var nm sync.Map - buf := make([]byte, proxy.UDPBufSize) - - for { - n, raddr, err := c.ReadFrom(buf) - if err != nil { - log.F("[udptun] read error: %v", err) - continue - } - - var pc net.PacketConn - - v, ok := nm.Load(raddr.String()) - if !ok && v == nil { - pc, _, err = s.proxy.DialUDP("udp", s.taddr) - if err != nil { - log.F("[udptun] remote dial error: %v", err) - continue - } - - nm.Store(raddr.String(), pc) - - go func(c, pc net.PacketConn, raddr net.Addr) { - proxy.RelayUDP(c, raddr, pc, 2*time.Minute) - pc.Close() - nm.Delete(raddr.String()) - }(c, pc, raddr) - - } else { - pc = v.(net.PacketConn) - } - - _, err = pc.WriteTo(buf[:n], s.tuaddr) - if err != nil { - log.F("[udptun] remote write error: %v", err) - continue - } - - log.F("[udptun] %s <-> %s", raddr, s.taddr) - - } -} - -// Serve serves a net.Conn, can not be called directly. -func (s *UDPTun) Serve(c net.Conn) { - log.F("[udptun] func Serve: can not be called directly") -} diff --git a/rule/forward.go b/rule/forward.go index 73d7b4a..b76d1f4 100644 --- a/rule/forward.go +++ b/rule/forward.go @@ -48,16 +48,26 @@ func ForwarderFromURL(s, intface string, dialTimeout, relayTimeout time.Duration return nil, err } + var addrs []string for _, url := range strings.Split(ss[0], ",") { d, err = proxy.DialerFromURL(url, d) if err != nil { return nil, err } + cnt := len(addrs) + if cnt == 0 || + (cnt > 0 && d.Addr() != addrs[cnt-1]) { + addrs = append(addrs, d.Addr()) + } } f.Dialer = d f.addr = d.Addr() + if len(addrs) > 0 { + f.addr = strings.Join(addrs, ",") + } + // set forwarder to disabled by default f.Disable() @@ -70,7 +80,6 @@ func DirectForwarder(intface string, dialTimeout, relayTimeout time.Duration) *F if err != nil { return nil } - return &Forwarder{Dialer: d, addr: d.Addr()} } @@ -93,6 +102,7 @@ func (f *Forwarder) parseOption(option string) error { } // Addr returns the forwarder's addr. +// NOTE: addr returns for chained dialers: dialer1Addr,dialer2Addr,... func (f *Forwarder) Addr() string { return f.addr } @@ -103,7 +113,6 @@ func (f *Forwarder) Dial(network, addr string) (c net.Conn, err error) { if err != nil { f.IncFailures() } - return c, err } diff --git a/rule/group.go b/rule/group.go index c10eab2..bb2d858 100644 --- a/rule/group.go +++ b/rule/group.go @@ -102,8 +102,10 @@ func (p *FwdrGroup) Dial(network, addr string) (net.Conn, proxy.Dialer, error) { } // DialUDP connects to the given address. -func (p *FwdrGroup) DialUDP(network, addr string) (pc net.PacketConn, writeTo net.Addr, err error) { - return p.NextDialer(addr).DialUDP(network, addr) +func (p *FwdrGroup) DialUDP(network, addr string) (pc net.PacketConn, dialer proxy.UDPDialer, writeTo net.Addr, err error) { + nd := p.NextDialer(addr) + pc, wt, err := nd.DialUDP(network, addr) + return pc, nd, wt, err } // NextDialer returns the next dialer. @@ -244,6 +246,7 @@ func (p *FwdrGroup) check(fwdr *Forwarder, checker Checker) { if errors.Is(err, proxy.ErrNotSupported) { fwdr.SetMaxFailures(0) log.F("[check] %s(%d), %s, stop checking", fwdr.Addr(), fwdr.Priority(), err) + fwdr.Enable() break } @@ -258,9 +261,9 @@ func (p *FwdrGroup) check(fwdr *Forwarder, checker Checker) { } wait = 1 - fwdr.Enable() fwdr.SetLatency(int64(elapsed)) log.F("[check] %s(%d), SUCCESS. elapsed: %s", fwdr.Addr(), fwdr.Priority(), elapsed) + fwdr.Enable() } } diff --git a/rule/proxy.go b/rule/proxy.go index 9bb1d55..b131f02 100644 --- a/rule/proxy.go +++ b/rule/proxy.go @@ -61,7 +61,7 @@ func (p *Proxy) Dial(network, addr string) (net.Conn, proxy.Dialer, error) { } // DialUDP connects to the given address via the proxy. -func (p *Proxy) DialUDP(network, addr string) (pc net.PacketConn, writeTo net.Addr, err error) { +func (p *Proxy) DialUDP(network, addr string) (pc net.PacketConn, dialer proxy.UDPDialer, writeTo net.Addr, err error) { return p.findDialer(addr).DialUDP(network, addr) }