add dns patch

This commit is contained in:
Kr328 committed 2020-02-18 15:16:43 +08:00
1 parent 8a4149c800
commit 3e7d7f5d60
21 files changed
+272 -100

No files matched your search

@@ -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,
+1 -1
View File
@@ -16,7 +16,7 @@
<LinearLayout
android:layout_width="match_parent"
android:layout_height="70dp"
android:layout_height="90dp"
android:gravity="center_vertical">
<View
+1 -1
View File
@@ -91,7 +91,7 @@
<string name="cancel">Cancel</string>
<string name="clash_logcat">Clash Logcat</string>
<string name="capturing_clash_log">Capturing clash core log</string>
<string name="capturing_clash_log">Capturing logs</string>
<string name="history">History</string>
<string name="enabled">Enabled</string>
<string name="log_viewer">Log Viewer</string>
+5 -5
View File
@@ -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)
}
}
+18
View File
@@ -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)
+3 -3
View File
@@ -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
}
+28
View File
@@ -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
+13
View File
@@ -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=
+1 -1
View File
@@ -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 {
+42 -45
View File
@@ -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
}
+45
View File
@@ -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
}
}
}
+2 -2
View File
@@ -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)
}
@@ -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<String>) {
Bridge.resetDnsAppend(dns.joinToString(","))
}
fun setDnsOverrideEnabled(enabled: Boolean) {
Bridge.setDnsOverrideEnabled(enabled)
}
fun loadProfile(path: File, baseDir: File): CompletableDeferred<Unit> {
return DoneCallbackImpl().apply {
Bridge.loadProfileFile(path.absolutePath, baseDir.absolutePath, this)
@@ -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")
}
}
}
@@ -25,7 +25,7 @@ class ClashService : BaseService() {
private var stopReason: String? = null
private val reloadChannel = Channel<Unit>(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
@@ -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"
@@ -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) {
@@ -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")
}
@@ -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<Network?> by Channel(Channel.CONFLATED) {
CoroutineScope by scope, Channel<Pair<Network, LinkProperties>?> 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
}
@@ -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))
}