From 3e7d7f5d60fc6b8dd0f4acf01e4986fcd538805c Mon Sep 17 00:00:00 2001 From: Kr328 <39107975+Kr328@users.noreply.github.com> Date: Tue, 18 Feb 2020 15:16:43 +0800 Subject: [PATCH] add dns patch --- .../com/github/kr328/clash/LogcatService.kt | 4 +- app/src/main/res/layout/activity_main.xml | 2 +- app/src/main/res/values/strings.xml | 2 +- core/src/main/golang/bridge/general.go | 10 +-- core/src/main/golang/bridge/profiles.go | 18 ++++ core/src/main/golang/bridge/proxies.go | 6 +- core/src/main/golang/bridge/tun.go | 28 ++++++ core/src/main/golang/clash | 2 +- core/src/main/golang/go.sum | 13 +++ core/src/main/golang/profile/download.go | 2 +- core/src/main/golang/profile/load.go | 87 +++++++++---------- core/src/main/golang/profile/patch.go | 45 ++++++++++ core/src/main/golang/tun/tun.go | 4 +- .../java/com/github/kr328/clash/core/Clash.kt | 21 ++++- .../kr328/clash/service/ClashNotification.kt | 14 ++- .../kr328/clash/service/ClashService.kt | 9 +- .../com/github/kr328/clash/service/Intents.kt | 2 + .../github/kr328/clash/service/Settings.kt | 2 + .../github/kr328/clash/service/TunService.kt | 33 +++++-- .../service/net/DefaultNetworkChannel.kt | 64 +++++++++----- .../clash/service/util/BroadcastUtils.kt | 4 + 21 files changed, 272 insertions(+), 100 deletions(-) create mode 100644 core/src/main/golang/profile/patch.go diff --git a/app/src/main/java/com/github/kr328/clash/LogcatService.kt b/app/src/main/java/com/github/kr328/clash/LogcatService.kt index ed2d1af..5a64cff 100644 --- a/app/src/main/java/com/github/kr328/clash/LogcatService.kt +++ b/app/src/main/java/com/github/kr328/clash/LogcatService.kt @@ -95,6 +95,8 @@ class LogcatService : Service(), CoroutineScope by MainScope(), IInterface { } override fun onDestroy() { + logChannel.close() + cancel() stopForeground(true) @@ -231,7 +233,7 @@ class LogcatService : Service(), CoroutineScope by MainScope(), IInterface { .setSmallIcon(R.drawable.ic_notification) .setColor(getColor(R.color.colorAccentService)) .setContentTitle(getString(R.string.clash_logcat)) - .setContentText(getString(R.string.capturing_clash_log)) + .setContentText(getString(R.string.running)) .setContentIntent( PendingIntent.getActivity( this, diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index cdf67a8..6c428a9 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -16,7 +16,7 @@ Cancel Clash Logcat - Capturing clash core log + Capturing logs History Enabled Log Viewer diff --git a/core/src/main/golang/bridge/general.go b/core/src/main/golang/bridge/general.go index 3127686..07a77b6 100644 --- a/core/src/main/golang/bridge/general.go +++ b/core/src/main/golang/bridge/general.go @@ -16,9 +16,9 @@ func QueryGeneral() *TunnelGeneral { result := &TunnelGeneral{} g := executor.GetGeneral() - t := tunnel.Instance() + m := tunnel.Mode() - result.Mode = t.Mode().String() + result.Mode = m.String() result.HTTPPort = g.Port result.SocksPort = g.SocksPort result.RedirectPort = g.RedirPort @@ -29,10 +29,10 @@ func QueryGeneral() *TunnelGeneral { func SetProxyMode(mode string) { switch mode { case "Direct": - tunnel.Instance().SetMode(tunnel.Direct) + tunnel.SetMode(tunnel.Direct) case "Global": - tunnel.Instance().SetMode(tunnel.Global) + tunnel.SetMode(tunnel.Global) case "Rule": - tunnel.Instance().SetMode(tunnel.Rule) + tunnel.SetMode(tunnel.Rule) } } diff --git a/core/src/main/golang/bridge/profiles.go b/core/src/main/golang/bridge/profiles.go index 2df452a..3b17dc4 100644 --- a/core/src/main/golang/bridge/profiles.go +++ b/core/src/main/golang/bridge/profiles.go @@ -1,9 +1,27 @@ package bridge import ( + "strings" + "github.com/kr328/cfa/profile" ) +func ResetDnsAppend(dns string) { + if len(dns) == 0 { + profile.NameServersAppend = make([]string, 0) + } else { + profile.NameServersAppend = strings.Split(dns, ",") + } +} + +func SetDnsOverrideEnabled(enabled bool) { + if enabled { + profile.DnsPatch = profile.OptionalDnsPatch + } else { + profile.DnsPatch = nil + } +} + func LoadProfileFile(path, baseDir string, callback DoneCallback) { go func() { call(profile.LoadFromFile(path, baseDir), callback) diff --git a/core/src/main/golang/bridge/proxies.go b/core/src/main/golang/bridge/proxies.go index 118c31c..bcb3752 100644 --- a/core/src/main/golang/bridge/proxies.go +++ b/core/src/main/golang/bridge/proxies.go @@ -51,7 +51,7 @@ func StartUrlTest(group string, callback DoneCallback) { go func() { defer callback.Done() - p := tunnel.Instance().Proxies()[group] + p := tunnel.Proxies()[group] pa, ok := p.(*outbound.Proxy) if !ok { @@ -88,7 +88,7 @@ func StartUrlTest(group string, callback DoneCallback) { } func QueryAllProxyGroups(collection ProxyGroupCollection) { - ps := tunnel.Instance().Proxies() + ps := tunnel.Proxies() for _, p := range ps { pa, ok := p.(*outbound.Proxy) @@ -144,7 +144,7 @@ func QueryAllProxyGroups(collection ProxyGroupCollection) { } func SetSelectedProxy(name, proxy string) bool { - p := tunnel.Instance().Proxies()[name] + p := tunnel.Proxies()[name] if p == nil { return false } diff --git a/core/src/main/golang/bridge/tun.go b/core/src/main/golang/bridge/tun.go index 770474e..a081e30 100644 --- a/core/src/main/golang/bridge/tun.go +++ b/core/src/main/golang/bridge/tun.go @@ -1,15 +1,43 @@ package bridge import ( + "net" + "syscall" + + "github.com/Dreamacro/clash/component/dialer" "github.com/kr328/cfa/tun" ) type TunCallback interface { + OnCreateSocket(fd int) OnStop() } var callback TunCallback +func init() { + dialer.DialerHook = onNewDialer + dialer.ListenConfigHook = onNewListenConfig +} + +func onNewDialer(dialer *net.Dialer) { + dialer.Control = onNewSocket +} + +func onNewListenConfig(listen *net.ListenConfig) { + listen.Control = onNewSocket +} + +func onNewSocket(network, address string, c syscall.RawConn) error { + if cb := callback; cb != nil { + c.Control(func(fd uintptr) { + cb.OnCreateSocket(int(fd)) + }) + } + + return nil +} + func StartTunDevice(fd, mtu int, dns string, cb TunCallback) error { callback = cb diff --git a/core/src/main/golang/clash b/core/src/main/golang/clash index 1de7866..e0e7605 160000 --- a/core/src/main/golang/clash +++ b/core/src/main/golang/clash @@ -1 +1 @@ -Subproject commit 1de786635ce775b7053ae68dd73ff916bfb146eb +Subproject commit e0e7605d1a5a93559281e8fea0a51043b71fabbe diff --git a/core/src/main/golang/go.sum b/core/src/main/golang/go.sum index be4383c..daffa04 100644 --- a/core/src/main/golang/go.sum +++ b/core/src/main/golang/go.sum @@ -19,6 +19,8 @@ github.com/eapache/queue v1.1.0 h1:YOEu7KNc61ntiQlcEeUIoDTJ2o8mQznoNvUhiigpIqc= github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= github.com/go-chi/chi v4.0.2+incompatible h1:maB6vn6FqCxrpz4FqWdh4+lwpyZIQS7YEAUcHlgXVRs= github.com/go-chi/chi v4.0.2+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ= +github.com/go-chi/chi v4.0.3+incompatible h1:gakN3pDJnzZN5jqFV2TEdF66rTfKeITyR8qu6ekICEY= +github.com/go-chi/chi v4.0.3+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ= github.com/go-chi/cors v1.0.0 h1:e6x8k7uWbUwYs+aXDoiUzeQFT6l0cygBYyNhD7/1Tg0= github.com/go-chi/cors v1.0.0/go.mod h1:K2Yje0VW/SJzxiyMYu6iPQYa7hMjQX2i/F491VChg1I= github.com/go-chi/render v1.0.1 h1:4/5tis2cKaNdnv9zFLfXzcquC9HbeZgCnxGnKrltBS8= @@ -50,6 +52,8 @@ github.com/miekg/dns v1.1.24 h1:6G8Eop/HM8hpagajbn0rFQvAKZWiiCa8P6N2I07+wwI= github.com/miekg/dns v1.1.24/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso= github.com/miekg/dns v1.1.26 h1:gPxPSwALAeHJSjarOs00QjVdV9QoBvc1D2ujQUr5BzU= github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso= +github.com/miekg/dns v1.1.27 h1:aEH/kqUzUxGJ/UHcEKdJY+ugH6WEzsEBBSPa8zuy1aM= +github.com/miekg/dns v1.1.27/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM= github.com/oschwald/geoip2-golang v1.2.1 h1:3iz+jmeJc6fuCyWeKgtXSXu7+zvkxJbHFXkMT5FVebU= github.com/oschwald/geoip2-golang v1.2.1/go.mod h1:0LTTzix/Ao1uMvOhAV4iLU0Lz7eCrP94qZWBTDKf0iE= github.com/oschwald/geoip2-golang v1.3.0 h1:D+Hsdos1NARPbzZ2aInUHZL+dApIzo8E0ErJVsWcku8= @@ -85,6 +89,8 @@ golang.org/x/crypto v0.0.0-20191128160524-b544559bb6d1 h1:anGSYQpPhQwXlwsu5wmfq0 golang.org/x/crypto v0.0.0-20191128160524-b544559bb6d1/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413 h1:ULYEB3JvPRE/IfO+9uO7vKV/xzVTO7XPAwm8xbf4w2g= golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200214034016-1d94cc7ab1c6 h1:Sy5bstxEqwwbYs6n0/pBuxKENqOeZUgD45Gp3Q3pqLg= +golang.org/x/crypto v0.0.0-20200214034016-1d94cc7ab1c6/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56 h1:estk1glOnSVeJ9tdEZZc5mAMDZk5lNJNyJ6DvrBkTEU= golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56/go.mod h1:JhuoJpWY28nO4Vef9tZUw9qufEGTyX1+7lmHxV5q5G4= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= @@ -95,6 +101,7 @@ golang.org/x/mobile v0.0.0-20191210151939-1a1fef82734d h1:LlA9R5JFi974qK4gm9FRK1 golang.org/x/mobile v0.0.0-20191210151939-1a1fef82734d/go.mod h1:p895TfNkDgPEmEQrNiOtIl3j98d/tGU95djDj7NfyjQ= golang.org/x/mod v0.1.0 h1:sfUMP1Gu8qASkorDVjnMuvgJzwFbTZSeXFiGBYAVdl4= golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= +golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3 h1:0GoQqolDA55aaLxZyTzK/Y2ePZzZTUrRacwib7cNsYQ= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= @@ -108,6 +115,8 @@ golang.org/x/net v0.0.0-20191207000613-e7e4b65ae663 h1:Dd5RoEW+yQi+9DMybroBctIdy golang.org/x/net v0.0.0-20191207000613-e7e4b65ae663/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553 h1:efeOvDhwQ29Dj3SdAV/MJf8oukgn+8D8WgaCaRMchF8= golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200202094626-16171245cfb2 h1:CCH4IOTTfewWjGOlSp+zGcjutRKlBEZQ6wTn8ozI/nI= +golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e h1:vcxGaoTs7kV8m5Np9uUNQin4BrLOthgV7252N8V+FwY= @@ -133,8 +142,10 @@ golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3 golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190909214602-067311248421 h1:NmmWqJbt02YJHmp4A4gBXvsXXIzzixjzE1y6PKUyIjk= golang.org/x/tools v0.0.0-20190909214602-067311248421/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7 h1:9zdDQZ7Thm29KFXgAX/+yaf3eVbP7djjWp/dXAppNCc= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/eapache/channels.v1 v1.1.0 h1:5bGAyKKvyCTWjSj7mhefG6Lc68VyN4MH1v8/7OoeeB4= @@ -143,3 +154,5 @@ gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.7 h1:VUgggvou5XRW9mHwD/yXxIYSMtY0zoKQf/v226p2nyo= gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/core/src/main/golang/profile/download.go b/core/src/main/golang/profile/download.go index 54e5baf..99ec0cf 100644 --- a/core/src/main/golang/profile/download.go +++ b/core/src/main/golang/profile/download.go @@ -26,7 +26,7 @@ var client = &http.Client{ client, server := net.Pipe() - tunnel.Instance().Add(inbound.NewSocket(socks5.ParseAddr(address), server, constant.HTTP, constant.TCP)) + tunnel.Add(inbound.NewSocket(socks5.ParseAddr(address), server, constant.HTTP, constant.TCP)) go func() { if ctx == nil || ctx.Done() == nil { diff --git a/core/src/main/golang/profile/load.go b/core/src/main/golang/profile/load.go index c889fe7..ea38bc7 100644 --- a/core/src/main/golang/profile/load.go +++ b/core/src/main/golang/profile/load.go @@ -3,9 +3,7 @@ package profile import ( "fmt" "io/ioutil" - "net" - "github.com/Dreamacro/clash/component/fakeip" "github.com/Dreamacro/clash/config" "github.com/Dreamacro/clash/constant" "github.com/Dreamacro/clash/dns" @@ -34,6 +32,31 @@ Rule: - 'MATCH,DIRECT' ` +func init() { + defaultNameServers := []string{ + "223.5.5.5", + "119.29.29.29", + "1.1.1.1", + "208.67.222.222", + } + + OptionalDnsPatch = &config.RawDNS{ + Enable: true, + IPv6: true, + NameServer: defaultNameServers, + Fallback: []string{}, + FallbackFilter: config.RawFallbackFilter{ + GeoIP: false, + IPCIDR: []string{}, + }, + Listen: ":0", + EnhancedMode: dns.FAKEIP, + FakeIPRange: "198.18.0.0/16", + FakeIPFilter: []string{}, + DefaultNameserver: defaultNameServers, + } +} + // LoadDefault - load default configure func LoadDefault() { defaultC, err := parseConfig([]byte(defaultConfig), constant.Path.HomeDir()) @@ -42,6 +65,9 @@ func LoadDefault() { return } + DnsPatch = nil + NameServersAppend = make([]string, 0) + executor.ApplyConfig(defaultC, true) tun.ResetDnsRedirect() @@ -59,50 +85,12 @@ func LoadFromFile(path, baseDir string) error { return err } + for _, ns := range cfg.DNS.NameServer { + log.Infoln("DNS: %s", ns.Addr) + } + executor.ApplyConfig(cfg, true) - if dns.DefaultResolver == nil && cfg.DNS.Enable { - c := cfg.DNS - - r := dns.New(dns.Config{ - Main: c.NameServer, - Fallback: c.Fallback, - IPv6: c.IPv6, - EnhancedMode: c.EnhancedMode, - Pool: c.FakeIPRange, - FallbackFilter: dns.FallbackFilter{ - GeoIP: c.FallbackFilter.GeoIP, - IPCIDR: c.FallbackFilter.IPCIDR, - }, - }) - - dns.DefaultResolver = r - } - - if dns.DefaultResolver == nil { - _, ipnet, _ := net.ParseCIDR("198.18.0.1/16") - pool, _ := fakeip.New(ipnet, 1000, nil) - - var defaultDNSResolver = dns.New(dns.Config{ - Main: []dns.NameServer{ - dns.NameServer{Net: "tcp", Addr: "1.1.1.1:53"}, - dns.NameServer{Net: "tcp", Addr: "208.67.222.222:53"}, - dns.NameServer{Net: "", Addr: "119.29.29.29:53"}, - dns.NameServer{Net: "", Addr: "223.5.5.5:53"}, - }, - Fallback: make([]dns.NameServer, 0), - IPv6: false, - EnhancedMode: dns.FAKEIP, - Pool: pool, - FallbackFilter: dns.FallbackFilter{ - GeoIP: false, - IPCIDR: make([]*net.IPNet, 0), - }, - }) - - dns.DefaultResolver = defaultDNSResolver - } - tun.ResetDnsRedirect() log.Infoln("Profile " + path + " loaded") @@ -120,5 +108,14 @@ func parseConfig(data []byte, baseDir string) (*config.Config, error) { raw.ExternalController = "" raw.Rule = append([]string{fmt.Sprintf("IP-CIDR,%s,REJECT", tunAddress)}, raw.Rule...) - return config.ParseRawConfig(raw, baseDir) + patchRawConfig(raw) + + cfg, err := config.ParseRawConfig(raw, baseDir) + if err != nil { + return nil, err + } + + patchConfig(cfg) + + return cfg, nil } diff --git a/core/src/main/golang/profile/patch.go b/core/src/main/golang/profile/patch.go new file mode 100644 index 0000000..f50c46e --- /dev/null +++ b/core/src/main/golang/profile/patch.go @@ -0,0 +1,45 @@ +package profile + +import ( + "github.com/Dreamacro/clash/component/fakeip" + "github.com/Dreamacro/clash/config" +) + +var ( + OptionalDnsPatch *config.RawDNS + DnsPatch *config.RawDNS + NameServersAppend []string + + cachedPool *fakeip.Pool +) + +func patchRawConfig(rawConfig *config.RawConfig) { + if d := DnsPatch; d != nil { + rawConfig.DNS = *d + } else if d := OptionalDnsPatch; d != nil { + if !rawConfig.DNS.Enable { + rawConfig.DNS = *d + } + } + + if append := NameServersAppend; len(append) > 0 { + d := &rawConfig.DNS + nameservers := make([]string, len(append)+len(d.NameServer)) + copy(nameservers, append) + copy(nameservers[len(append):], d.NameServer) + + d.NameServer = nameservers + } +} + +func patchConfig(config *config.Config) { + if config.DNS.FakeIPRange != nil { + if c := cachedPool; c != nil { + if config.DNS.FakeIPRange.Gateway().String() == c.Gateway().String() { + config.DNS.FakeIPRange = c + } + } else { + cachedPool = config.DNS.FakeIPRange + } + } +} diff --git a/core/src/main/golang/tun/tun.go b/core/src/main/golang/tun/tun.go index 9f85a21..5b0e3c6 100644 --- a/core/src/main/golang/tun/tun.go +++ b/core/src/main/golang/tun/tun.go @@ -4,7 +4,7 @@ import ( "strconv" "sync" - "github.com/Dreamacro/clash/dns" + "github.com/Dreamacro/clash/component/resolver" "github.com/Dreamacro/clash/log" "github.com/Dreamacro/clash/proxy/tun" ) @@ -56,5 +56,5 @@ func ResetDnsRedirect() { return } - (*tunInstance).ReCreateDNSServer(dns.DefaultResolver, dnsAddress) + (*tunInstance).ReCreateDNSServer(resolver.DefaultResolver, dnsAddress) } diff --git a/core/src/main/java/com/github/kr328/clash/core/Clash.kt b/core/src/main/java/com/github/kr328/clash/core/Clash.kt index 1e570cf..4a14c64 100644 --- a/core/src/main/java/com/github/kr328/clash/core/Clash.kt +++ b/core/src/main/java/com/github/kr328/clash/core/Clash.kt @@ -2,6 +2,7 @@ package com.github.kr328.clash.core import android.content.Context import bridge.Bridge +import bridge.TunCallback import com.github.kr328.clash.core.event.EventStream import com.github.kr328.clash.core.event.LogEvent import com.github.kr328.clash.core.model.General @@ -47,15 +48,31 @@ object Clash { fd: Int, mtu: Int, dns: String, - onStop: () -> Unit + onNewSocket: (Int) -> Boolean, + onTunStop: () -> Unit ) { - Bridge.startTunDevice(fd.toLong(), mtu.toLong(), dns, onStop) + Bridge.startTunDevice(fd.toLong(), mtu.toLong(), dns, object: TunCallback { + override fun onCreateSocket(fd: Long) { + onNewSocket(fd.toInt()) + } + override fun onStop() { + onTunStop() + } + }) } fun stopTunDevice() { Bridge.stopTunDevice() } + fun appendDns(dns: List) { + Bridge.resetDnsAppend(dns.joinToString(",")) + } + + fun setDnsOverrideEnabled(enabled: Boolean) { + Bridge.setDnsOverrideEnabled(enabled) + } + fun loadProfile(path: File, baseDir: File): CompletableDeferred { return DoneCallbackImpl().apply { Bridge.loadProfileFile(path.absolutePath, baseDir.absolutePath, this) diff --git a/service/src/main/java/com/github/kr328/clash/service/ClashNotification.kt b/service/src/main/java/com/github/kr328/clash/service/ClashNotification.kt index 0de773a..52d6b4c 100644 --- a/service/src/main/java/com/github/kr328/clash/service/ClashNotification.kt +++ b/service/src/main/java/com/github/kr328/clash/service/ClashNotification.kt @@ -13,6 +13,7 @@ import android.os.PowerManager import androidx.core.app.NotificationCompat import androidx.core.app.NotificationManagerCompat import com.github.kr328.clash.core.Clash +import com.github.kr328.clash.core.utils.Log import com.github.kr328.clash.core.utils.asBytesString import com.github.kr328.clash.core.utils.asSpeedString import kotlinx.coroutines.* @@ -105,10 +106,17 @@ class ClashNotification(private val context: ClashService, enableRefresh: Boolea private fun startTicker(): Job { return launch { - while (isActive) { - tickerChannel.send(Unit) + Log.d("Clash Notification Started") - delay(1000) + try { + while (isActive) { + tickerChannel.send(Unit) + + delay(1000) + } + } + finally { + Log.d("Clash Notification Stopped") } } } diff --git a/service/src/main/java/com/github/kr328/clash/service/ClashService.kt b/service/src/main/java/com/github/kr328/clash/service/ClashService.kt index eca7856..03d44f8 100644 --- a/service/src/main/java/com/github/kr328/clash/service/ClashService.kt +++ b/service/src/main/java/com/github/kr328/clash/service/ClashService.kt @@ -25,7 +25,7 @@ class ClashService : BaseService() { private var stopReason: String? = null private val reloadChannel = Channel(Channel.CONFLATED) private val settings: Settings by lazy { Settings(ClashManager(this, this)) } - private val profileObserver = object : BroadcastReceiver() { + private val reloadReceiver = object : BroadcastReceiver() { override fun onReceive(context: Context?, intent: Intent?) { if (intent?.`package` != packageName) return @@ -64,7 +64,10 @@ class ClashService : BaseService() { if (startVpn) startService(TunService::class.intent) - registerReceiver(profileObserver, IntentFilter(Intents.INTENT_ACTION_PROFILE_CHANGED)) + registerReceiver(reloadReceiver, IntentFilter().apply { + addAction(Intents.INTENT_ACTION_PROFILE_CHANGED) + addAction(Intents.INTENT_ACTION_NETWORK_CHANGED) + }) reloadChannel.offer(Unit) @@ -85,7 +88,7 @@ class ClashService : BaseService() { broadcastClashStopped(this, stopReason) - unregisterReceiver(profileObserver) + unregisterReceiver(reloadReceiver) isServiceRunning = false diff --git a/service/src/main/java/com/github/kr328/clash/service/Intents.kt b/service/src/main/java/com/github/kr328/clash/service/Intents.kt index 626e815..4dedd7d 100644 --- a/service/src/main/java/com/github/kr328/clash/service/Intents.kt +++ b/service/src/main/java/com/github/kr328/clash/service/Intents.kt @@ -13,6 +13,8 @@ object Intents { "${BuildConfig.LIBRARY_PACKAGE_NAME}.intent.action.profile.setup" const val INTENT_ACTION_PROFILE_LOADED = "${BuildConfig.LIBRARY_PACKAGE_NAME}.intent.action.profile.loaded" + const val INTENT_ACTION_NETWORK_CHANGED = + "${BuildConfig.LIBRARY_PACKAGE_NAME}.intent.action.network.changed" const val INTENT_EXTRA_CLASH_STOP_REASON = "${BuildConfig.LIBRARY_PACKAGE_NAME}.intent.extra.clash.stop.reason" diff --git a/service/src/main/java/com/github/kr328/clash/service/Settings.kt b/service/src/main/java/com/github/kr328/clash/service/Settings.kt index d634116..b07e289 100644 --- a/service/src/main/java/com/github/kr328/clash/service/Settings.kt +++ b/service/src/main/java/com/github/kr328/clash/service/Settings.kt @@ -11,6 +11,8 @@ class Settings(private val clashManager: IClashManager) { val ACCESS_CONTROL_PACKAGES = PackageListSetting("access_control_packages", emptyList()) val DNS_HIJACKING = BooleanSetting("dns_hijacking", true) val NOTIFICATION_REFRESH = BooleanSetting("notification_refresh", true) + val AUTO_ADD_SYSTEM_DNS = BooleanSetting("auto_add_system_dns", true) + val OVERRIDE_DNS = BooleanSetting("override_dns", true) } fun put(key: String, value: String) { diff --git a/service/src/main/java/com/github/kr328/clash/service/TunService.kt b/service/src/main/java/com/github/kr328/clash/service/TunService.kt index 36d966d..28f2393 100644 --- a/service/src/main/java/com/github/kr328/clash/service/TunService.kt +++ b/service/src/main/java/com/github/kr328/clash/service/TunService.kt @@ -5,7 +5,10 @@ import android.os.Build import com.github.kr328.clash.core.Clash import com.github.kr328.clash.core.utils.Log import com.github.kr328.clash.service.net.DefaultNetworkChannel +import com.github.kr328.clash.service.util.broadcastNetworkChanged import kotlinx.coroutines.* +import kotlinx.coroutines.channels.broadcast +import java.net.InetAddress class TunService : VpnService(), CoroutineScope by MainScope() { companion object { @@ -40,9 +43,9 @@ class TunService : VpnService(), CoroutineScope by MainScope() { Log.i("TunService.startTun ${fd.fd}") - Clash.startTunDevice(fd.fd, VPN_MTU, dnsAddress) { - stopSelf() - } + Clash.setDnsOverrideEnabled(settings.get(Settings.OVERRIDE_DNS)) + + Clash.startTunDevice(fd.fd, VPN_MTU, dnsAddress, this::protect, this::stopSelf) fd.close() } @@ -64,7 +67,24 @@ class TunService : VpnService(), CoroutineScope by MainScope() { } while (isActive) { - setUnderlyingNetworks(defaultNetworkChannel.receive()?.let { arrayOf(it) }) + val d = defaultNetworkChannel.receive() + + if ( d == null ) { + setUnderlyingNetworks(null) + continue + } + + setUnderlyingNetworks(arrayOf(d.first)) + + if ( settings.get(Settings.AUTO_ADD_SYSTEM_DNS) ) { + withContext(Dispatchers.Default) { + Clash.appendDns(d.second.dnsServers + .map(InetAddress::getHostName) + .filter(String::isNotBlank)) + } + } + + broadcastNetworkChanged(this@TunService) } } } @@ -107,12 +127,10 @@ class TunService : VpnService(), CoroutineScope by MainScope() { addDisallowedApplication(app) } } - addDisallowedApplication(packageName) } Settings.ACCESS_CONTROL_MODE_WHITELIST -> { for (app in settings.get(Settings.ACCESS_CONTROL_PACKAGES).toSet() - - resources.getStringArray(R.array.default_disallow_application) - - setOf(packageName)) { + resources.getStringArray(R.array.default_disallow_application)) { runCatching { addAllowedApplication(app) }.onFailure { @@ -129,7 +147,6 @@ class TunService : VpnService(), CoroutineScope by MainScope() { Log.w("Package $app not found") } } - addDisallowedApplication(packageName) } else -> throw IllegalArgumentException("Invalid mode") } diff --git a/service/src/main/java/com/github/kr328/clash/service/net/DefaultNetworkChannel.kt b/service/src/main/java/com/github/kr328/clash/service/net/DefaultNetworkChannel.kt index a2eb707..9145530 100644 --- a/service/src/main/java/com/github/kr328/clash/service/net/DefaultNetworkChannel.kt +++ b/service/src/main/java/com/github/kr328/clash/service/net/DefaultNetworkChannel.kt @@ -2,40 +2,37 @@ package com.github.kr328.clash.service.net import android.content.Context -import android.net.ConnectivityManager -import android.net.Network -import android.net.NetworkCapabilities -import android.net.NetworkRequest -import com.github.kr328.clash.core.utils.Log -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers +import android.net.* +import kotlinx.coroutines.* import kotlinx.coroutines.channels.Channel -import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext +import kotlinx.coroutines.sync.Mutex class DefaultNetworkChannel(val context: Context, scope: CoroutineScope) : - CoroutineScope by scope, Channel by Channel(Channel.CONFLATED) { + CoroutineScope by scope, Channel?> by Channel(Channel.CONFLATED) { + private var currentNetwork: Network? = null + private val detectDelayLock = Mutex() private val connectivity = context.getSystemService(ConnectivityManager::class.java)!! private val callback = object : ConnectivityManager.NetworkCallback() { override fun onAvailable(network: Network) { - launch { - send(rebuildNetworkList()) - } + sendDefaultNetwork(true) } override fun onLost(network: Network) { - launch { - send(rebuildNetworkList()) - } + sendDefaultNetwork(true) } override fun onCapabilitiesChanged( network: Network, networkCapabilities: NetworkCapabilities ) { - launch { - send(rebuildNetworkList()) - } + if ( network == currentNetwork && + !networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) ) + sendDefaultNetwork(true) + } + + override fun onLinkPropertiesChanged(network: Network, linkProperties: LinkProperties) { + if ( network == currentNetwork ) + sendDefaultNetwork(false) } } @@ -47,7 +44,30 @@ class DefaultNetworkChannel(val context: Context, scope: CoroutineScope) : connectivity.unregisterNetworkCallback(callback) } - private suspend fun rebuildNetworkList(): Network? = withContext(Dispatchers.Default) { + private fun sendDefaultNetwork(ignoreSameNetwork: Boolean) { + if (detectDelayLock.tryLock()) { + launch { + delay(1000) + + val network = detectDefaultNetwork() + if ( ignoreSameNetwork && network == currentNetwork ) + return@launch + + currentNetwork = network + + val linkProperties = network?.let { connectivity.getLinkProperties(it) } + + if ( network != null && linkProperties != null ) + send(network to linkProperties) + else + send(null) + + detectDelayLock.unlock() + } + } + } + + private suspend fun detectDefaultNetwork(): Network? = withContext(Dispatchers.Default) { return@withContext try { connectivity.allNetworks .asSequence() @@ -71,10 +91,6 @@ class DefaultNetworkChannel(val context: Context, scope: CoroutineScope) : else 0 } - .map { - Log.i("Network ${it.first}") - it - } .map { it.second } diff --git a/service/src/main/java/com/github/kr328/clash/service/util/BroadcastUtils.kt b/service/src/main/java/com/github/kr328/clash/service/util/BroadcastUtils.kt index 37e6899..b438263 100644 --- a/service/src/main/java/com/github/kr328/clash/service/util/BroadcastUtils.kt +++ b/service/src/main/java/com/github/kr328/clash/service/util/BroadcastUtils.kt @@ -24,6 +24,10 @@ fun broadcastProfileLoaded(context: Context, profileEntity: ClashProfileEntity) context.sendBroadcastSelf(intent) } +fun broadcastNetworkChanged(context: Context) { + context.sendBroadcastSelf(Intent(Intents.INTENT_ACTION_NETWORK_CHANGED)) +} + fun broadcastClashStarted(context: Context) { context.sendBroadcastSelf(Intent(Intents.INTENT_ACTION_CLASH_STARTED)) }