migrate to netstack
This commit is contained in:
15 files changed
+80
-319
No files matched your search
+7
-6
@@ -13,8 +13,8 @@ android {
|
||||
applicationId "com.github.kr328.clash"
|
||||
minSdkVersion 24
|
||||
targetSdkVersion 29
|
||||
versionCode 10029
|
||||
versionName "1.0.29-alpha"
|
||||
versionCode 10035
|
||||
versionName "1.0.35-alpha"
|
||||
}
|
||||
buildTypes {
|
||||
release {
|
||||
@@ -42,18 +42,19 @@ dependencies {
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
|
||||
implementation "androidx.lifecycle:lifecycle-livedata:2.1.0"
|
||||
implementation "androidx.lifecycle:lifecycle-common-java8:2.1.0"
|
||||
implementation 'androidx.browser:browser:1.0.0'
|
||||
implementation 'androidx.browser:browser:1.2.0'
|
||||
implementation 'androidx.preference:preference:1.1.0'
|
||||
implementation 'androidx.recyclerview:recyclerview:1.1.0'
|
||||
implementation 'androidx.core:core-ktx:1.2.0-rc01'
|
||||
implementation 'androidx.fragment:fragment-ktx:1.2.0-rc03'
|
||||
implementation 'androidx.fragment:fragment-ktx:1.2.0-rc05'
|
||||
implementation 'androidx.appcompat:appcompat:1.1.0'
|
||||
implementation 'androidx.constraintlayout:constraintlayout:2.0.0-beta3'
|
||||
implementation 'androidx.constraintlayout:constraintlayout:2.0.0-beta4'
|
||||
implementation "androidx.room:room-runtime:$room_version"
|
||||
implementation "androidx.preference:preference-ktx:1.1.0"
|
||||
implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.0.0"
|
||||
implementation "org.jetbrains.kotlinx:kotlinx-serialization-runtime:0.14.0"
|
||||
implementation "com.charleskorn.kaml:kaml:0.14.0"
|
||||
implementation "com.google.android.material:material:1.2.0-alpha02"
|
||||
implementation "com.google.android.material:material:1.2.0-alpha03"
|
||||
implementation 'com.google.firebase:firebase-analytics:17.2.1'
|
||||
implementation 'com.crashlytics.sdk.android:crashlytics:2.10.1'
|
||||
}
|
||||
+1
-1
@@ -3,7 +3,7 @@
|
||||
buildscript {
|
||||
ext {
|
||||
kotlin_version = '1.3.61'
|
||||
room_version = '2.2.2'
|
||||
room_version = '2.2.3'
|
||||
}
|
||||
repositories {
|
||||
google()
|
||||
|
||||
@@ -16,11 +16,9 @@ open class GolangBindTask : DefaultTask() {
|
||||
|
||||
require (
|
||||
github.com/kr328/cfa v0.0.0 // redirect
|
||||
github.com/Dreamacro/clash v0.0.0 // redirect
|
||||
)
|
||||
|
||||
replace github.com/kr328/cfa v0.0.0 => {SOURCE_PATH}
|
||||
replace github.com/Dreamacro/clash v0.0.0 => {SOURCE_PATH}/clash
|
||||
""".trimIndent()
|
||||
private val STUB_GO_FILE_CONTENT = """
|
||||
package main
|
||||
@@ -29,6 +27,8 @@ open class GolangBindTask : DefaultTask() {
|
||||
|
||||
func main() {}
|
||||
""".trimIndent()
|
||||
private val REGEX_REPLACE_TARGET_LOCAL = Regex("=>\\s+\\./")
|
||||
private val REGEX_REPLACE_SOURCE_VERSION = Regex("v.+\\s+=>")
|
||||
}
|
||||
|
||||
private val javaOutput: File
|
||||
@@ -90,7 +90,7 @@ open class GolangBindTask : DefaultTask() {
|
||||
"go get golang.org/x/mobile/cmd/gomobile".exec()
|
||||
|
||||
FileWriter(goBindPath.resolve("go.mod")).use {
|
||||
it.write(STUB_GO_MOD_CONTENT.replace("{SOURCE_PATH}", sourcePath.absolutePath))
|
||||
it.write(appendReplace(sourcePath))
|
||||
}
|
||||
FileWriter(goBindPath.resolve("main.go")).use {
|
||||
it.write(STUB_GO_FILE_CONTENT)
|
||||
@@ -152,6 +152,26 @@ open class GolangBindTask : DefaultTask() {
|
||||
?: throw GradleException("Android SDK not found.")
|
||||
}
|
||||
|
||||
private fun appendReplace(source: File): String {
|
||||
val replaces = source.walk()
|
||||
.filter { it.name == "go.mod" }
|
||||
.flatMap { file ->
|
||||
file.readLines()
|
||||
.asSequence()
|
||||
.filter { line -> line.startsWith("replace") }
|
||||
.map { replace ->
|
||||
replace.replace(REGEX_REPLACE_TARGET_LOCAL, "=> " + file.parentFile.absolutePath + "/")
|
||||
}
|
||||
.map { replace ->
|
||||
replace.replace(REGEX_REPLACE_SOURCE_VERSION, " =>")
|
||||
}
|
||||
}
|
||||
.joinToString("\n")
|
||||
|
||||
return STUB_GO_MOD_CONTENT.replace("{SOURCE_PATH}", source.absolutePath) +
|
||||
"\n\n" + replaces
|
||||
}
|
||||
|
||||
private fun String.exec(pwd: File = File(".")) {
|
||||
val process = with(ProcessBuilder()) {
|
||||
if (Os.isFamily(Os.FAMILY_WINDOWS))
|
||||
|
||||
@@ -8,7 +8,7 @@ import java.net.URL
|
||||
|
||||
open class MMDBDowloadTask : DefaultTask() {
|
||||
companion object {
|
||||
const val URL = "http://geolite.maxmind.com/download/geoip/database/GeoLite2-Country.tar.gz"
|
||||
const val URL = "https://github.com/Dreamacro/maxmind-geoip/releases/latest/download/Country.mmdb"
|
||||
}
|
||||
|
||||
var output: String = ""
|
||||
|
||||
+4
-29
@@ -45,41 +45,16 @@ dependencies {
|
||||
afterEvaluate {
|
||||
def ds = tasks.register("downloadMMDB", MMDBDowloadTask.class) {
|
||||
onlyIf {
|
||||
System.currentTimeMillis() - file("$buildDir/intermediates/cache/Country.tar.gz").lastModified() > 24 * 3600 * 1000L
|
||||
System.currentTimeMillis() - file("$buildDir/intermediates/dynamic_assets/Country.mmdb").lastModified() > 24 * 3600 * 1000L
|
||||
}
|
||||
|
||||
output = "$buildDir/intermediates/cache/Country.tar.gz"
|
||||
}
|
||||
|
||||
def es = tasks.register("extractMMDB", Copy.class) {
|
||||
onlyIf {
|
||||
file("$buildDir/intermediates/cache/Country.tar.gz").lastModified() > file("$buildDir/intermediates/dynamic_assets/Country.mmdb").lastModified()
|
||||
}
|
||||
|
||||
from(tarTree("$buildDir/intermediates/cache/Country.tar.gz")) {
|
||||
include "**/*.mmdb"
|
||||
}
|
||||
into "$buildDir/intermediates/cache/mmdb"
|
||||
|
||||
doLast {
|
||||
file("$buildDir/intermediates/dynamic_assets").mkdirs()
|
||||
fileTree("$buildDir/intermediates/cache/mmdb").visit { FileVisitDetails details ->
|
||||
if ( details.path.endsWith("Country.mmdb") )
|
||||
details.file.renameTo(file("$buildDir/intermediates/dynamic_assets/Country.mmdb"))
|
||||
}
|
||||
}
|
||||
|
||||
dependsOn ds
|
||||
}
|
||||
|
||||
for ( task in tasks ) {
|
||||
if ( task.name.contains("generate") && task.name.contains("Assets") )
|
||||
task.dependsOn(es)
|
||||
output = "$buildDir/intermediates/dynamic_assets/Country.mmdb"
|
||||
}
|
||||
|
||||
def gs = tasks.register("golangBind", GolangBindTask.class)
|
||||
|
||||
preBuild.dependsOn(gs)
|
||||
preBuild.dependsOn(ds, gs)
|
||||
|
||||
}
|
||||
|
||||
repositories {
|
||||
|
||||
@@ -6,11 +6,11 @@ import "github.com/kr328/cfa/profile"
|
||||
|
||||
import "github.com/Dreamacro/clash/tunnel"
|
||||
|
||||
func Init(home string) {
|
||||
func SetBaseDir(home string) {
|
||||
constant.SetHomeDir(home)
|
||||
}
|
||||
|
||||
func Reset() {
|
||||
profile.LoadDefault()
|
||||
tunnel.DefaultManager.Reset()
|
||||
tunnel.DefaultManager.ResetStatistic()
|
||||
}
|
||||
@@ -4,12 +4,8 @@ import (
|
||||
"github.com/kr328/cfa/tun"
|
||||
)
|
||||
|
||||
type TunCallback interface {
|
||||
OnNewSocket(fd int)
|
||||
}
|
||||
|
||||
func StartTunDevice(fd, mtu int, gateway string, dns string, callback TunCallback) error {
|
||||
return tun.StartTunDevice(fd, mtu, gateway, dns, callback)
|
||||
func StartTunDevice(fd, mtu int, dns string) error {
|
||||
return tun.StartTunDevice(fd, mtu, dns)
|
||||
}
|
||||
|
||||
func StopTunDevice() {
|
||||
|
||||
@@ -7,7 +7,9 @@ require (
|
||||
github.com/go-chi/render v1.0.1
|
||||
github.com/google/go-cmp v0.3.1 // indirect
|
||||
golang.org/x/mobile v0.0.0-20191210151939-1a1fef82734d // indirect
|
||||
golang.org/x/sys v0.0.0-20191128015809-6d18c012aee9
|
||||
golang.org/x/sys v0.0.0-20191224085550-c709ea063b76
|
||||
)
|
||||
|
||||
replace github.com/Dreamacro/clash v0.0.0 => ./clash
|
||||
replace github.com/Dreamacro/clash => ./clash
|
||||
|
||||
replace github.com/google/netstack => github.com/comzyh/netstack v0.0.0-20191217044024-67c27819ada4
|
||||
@@ -10,6 +10,8 @@ github.com/Dreamacro/go-shadowsocks2 v0.1.5 h1:BizWSjmwzAyQoslz6YhJYMiAGT99j9cnm
|
||||
github.com/Dreamacro/go-shadowsocks2 v0.1.5/go.mod h1:LSXCjyHesPY3pLjhwff1mQX72ItcBT/N2xNC685cYeU=
|
||||
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/comzyh/netstack v0.0.0-20191217044024-67c27819ada4 h1:30ykXB9NWubvyVWE5pe/YakDgEdu6wJkBZlZYDtV464=
|
||||
github.com/comzyh/netstack v0.0.0-20191217044024-67c27819ada4/go.mod h1:jMMWEkl1smElz5KtnrIWDHxc8gtMIO/Pd8+pLyGRzT8=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
@@ -46,14 +48,20 @@ github.com/miekg/dns v1.1.22 h1:Jm64b3bO9kP43ddLjL2EY3Io6bmy1qGb9Xxz6TqS6rc=
|
||||
github.com/miekg/dns v1.1.22/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso=
|
||||
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/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=
|
||||
github.com/oschwald/geoip2-golang v1.3.0/go.mod h1:0LTTzix/Ao1uMvOhAV4iLU0Lz7eCrP94qZWBTDKf0iE=
|
||||
github.com/oschwald/geoip2-golang v1.4.0 h1:5RlrjCgRyIGDz/mBmPfnAF4h8k0IAcRv9PvrpOfz+Ug=
|
||||
github.com/oschwald/geoip2-golang v1.4.0/go.mod h1:8QwxJvRImBH+Zl6Aa6MaIcs5YdlZSTKtzmPGzQqi9ng=
|
||||
github.com/oschwald/maxminddb-golang v1.3.0 h1:oTh8IBSj10S5JNlUDg5WjJ1QdBMdeaZIkPEVfESSWgE=
|
||||
github.com/oschwald/maxminddb-golang v1.3.0/go.mod h1:3jhIUymTJ5VREKyIhWm66LJiQt04F0UCDdodShpjWsY=
|
||||
github.com/oschwald/maxminddb-golang v1.5.0 h1:rmyoIV6z2/s9TCJedUuDiKht2RN12LWJ1L7iRGtWY64=
|
||||
github.com/oschwald/maxminddb-golang v1.5.0/go.mod h1:3jhIUymTJ5VREKyIhWm66LJiQt04F0UCDdodShpjWsY=
|
||||
github.com/oschwald/maxminddb-golang v1.6.0 h1:KAJSjdHQ8Kv45nFIbtoLGrGWqHFajOIm7skTyz/+Dls=
|
||||
github.com/oschwald/maxminddb-golang v1.6.0/go.mod h1:DUJFucBg2cvqx42YmDa/+xHvb0elJtOm3o4aFQ/nb/w=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/sirupsen/logrus v1.4.1 h1:GL2rEmy6nsikmW0r8opw9JIRScdMF5hA8cOYLH7In1k=
|
||||
@@ -98,6 +106,8 @@ golang.org/x/net v0.0.0-20191126235420-ef20fe5d7933 h1:e6HwijUxhDe+hPNjZQQn9bA5P
|
||||
golang.org/x/net v0.0.0-20191126235420-ef20fe5d7933/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20191207000613-e7e4b65ae663 h1:Dd5RoEW+yQi+9DMybroBctIdyiwuNT7sJFMC27/6KxI=
|
||||
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/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=
|
||||
@@ -111,6 +121,8 @@ golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe h1:6fAMxZRR6sl1Uq8U61gxU+kPT
|
||||
golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191128015809-6d18c012aee9 h1:ZBzSG/7F4eNKz2L3GE9o300RX0Az1Bw5HF7PDraD+qU=
|
||||
golang.org/x/sys v0.0.0-20191128015809-6d18c012aee9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191224085550-c709ea063b76 h1:Dho5nD6R3PcW2SH1or8vS0dszDaXRxIw55lBX7XiE5g=
|
||||
golang.org/x/sys v0.0.0-20191224085550-c709ea063b76/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
|
||||
@@ -68,7 +68,7 @@ func LoadFromFile(path string) error {
|
||||
|
||||
if dns.DefaultResolver == nil {
|
||||
_, ipnet, _ := net.ParseCIDR("198.18.0.1/16")
|
||||
pool, _ := fakeip.New(ipnet, 1000)
|
||||
pool, _ := fakeip.New(ipnet, 1000, nil)
|
||||
|
||||
var defaultDNSResolver = dns.New(dns.Config{
|
||||
Main: []dns.NameServer{
|
||||
|
||||
@@ -1,55 +1,28 @@
|
||||
package tun
|
||||
|
||||
import (
|
||||
"net"
|
||||
"strconv"
|
||||
"syscall"
|
||||
|
||||
"github.com/Dreamacro/clash/global"
|
||||
"github.com/Dreamacro/clash/dns"
|
||||
"github.com/Dreamacro/clash/log"
|
||||
"github.com/Dreamacro/clash/proxy/tun"
|
||||
)
|
||||
|
||||
type Callback interface {
|
||||
OnNewSocket(fd int)
|
||||
}
|
||||
var tunInstance *tun.TunAdapter
|
||||
var dnsAddress string
|
||||
|
||||
var tunInstance *tun.TUN
|
||||
|
||||
func StartTunDevice(fd, mtu int, gateway string, dnsGateway string, callback Callback) error {
|
||||
func StartTunDevice(fd, mtu int, dns string) error {
|
||||
if tunInstance != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
ip, n, err := net.ParseCIDR(gateway)
|
||||
t, err := tun.NewTunProxy("fd://" + strconv.Itoa(fd) + "?mtu=" + strconv.Itoa(mtu))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
n.IP = ip.To4()
|
||||
|
||||
global.DefaultDialer.Control = func(network, address string, c syscall.RawConn) error {
|
||||
return c.Control(func(fd uintptr) {
|
||||
callback.OnNewSocket(int(fd))
|
||||
})
|
||||
}
|
||||
global.DefaultListenConfig.Control = func(network, address string, c syscall.RawConn) error {
|
||||
return c.Control(func(fd uintptr) {
|
||||
callback.OnNewSocket(int(fd))
|
||||
})
|
||||
}
|
||||
|
||||
dnsGatewayIP := net.ParseIP(dnsGateway)
|
||||
|
||||
t, err := tun.NewTunProxy("fd://"+strconv.Itoa(fd)+"?mtu="+strconv.Itoa(mtu), *n, dnsGatewayIP)
|
||||
|
||||
if err != nil {
|
||||
global.DefaultDialer.Control = nil
|
||||
global.DefaultListenConfig.Control = nil
|
||||
|
||||
return err
|
||||
}
|
||||
tunInstance = t
|
||||
tunInstance = &t
|
||||
dnsAddress = dns + ":53"
|
||||
|
||||
ResetDnsRedirect()
|
||||
|
||||
@@ -64,12 +37,7 @@ func StopTunDevice() {
|
||||
return
|
||||
}
|
||||
|
||||
t.Close()
|
||||
|
||||
global.DefaultDialer.Control = nil
|
||||
global.DefaultListenConfig.Control = nil
|
||||
|
||||
tunInstance = nil
|
||||
(*t).Close()
|
||||
}
|
||||
|
||||
func ResetDnsRedirect() {
|
||||
@@ -77,5 +45,5 @@ func ResetDnsRedirect() {
|
||||
return
|
||||
}
|
||||
|
||||
tunInstance.ResetDnsServer()
|
||||
(*tunInstance).ReCreateDNSServer(dns.DefaultResolver, dnsAddress)
|
||||
}
|
||||
@@ -1,72 +0,0 @@
|
||||
package com.github.kr328.clash.core
|
||||
|
||||
import android.net.LocalSocket
|
||||
import android.net.LocalSocketAddress
|
||||
import com.github.kr328.clash.core.utils.Log
|
||||
import java.io.DataInputStream
|
||||
import java.io.DataOutputStream
|
||||
import java.io.File
|
||||
|
||||
@Suppress("SameParameterValue")
|
||||
abstract class BaseClash(private val controllerPath: File) {
|
||||
protected fun <R> runControl(
|
||||
command: Int,
|
||||
block: (LocalSocket, DataInputStream, DataOutputStream) -> R
|
||||
): R {
|
||||
val socket = LocalSocket()
|
||||
|
||||
socket.connect(
|
||||
LocalSocketAddress(
|
||||
controllerPath.absolutePath,
|
||||
LocalSocketAddress.Namespace.FILESYSTEM
|
||||
)
|
||||
)
|
||||
|
||||
val input = DataInputStream(socket.inputStream)
|
||||
val output = DataOutputStream(socket.outputStream)
|
||||
|
||||
output.writeInt(command)
|
||||
|
||||
val result = block(socket, input, output)
|
||||
|
||||
runCatching {
|
||||
socket.close()
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
protected fun runControl(command: Int) {
|
||||
return runControl(command) { _, _, _ -> }
|
||||
}
|
||||
|
||||
protected fun <R> runControlNoException(
|
||||
command: Int,
|
||||
block: (LocalSocket, DataInputStream, DataOutputStream) -> R
|
||||
): R? {
|
||||
return try {
|
||||
runControl(command, block)
|
||||
} catch (ignored: Exception) { null }
|
||||
}
|
||||
|
||||
protected fun runControlNoException(command: Int) {
|
||||
try {
|
||||
runControl(command)
|
||||
} catch (ignored: Exception) {}
|
||||
}
|
||||
|
||||
protected fun DataOutputStream.writeString(string: String) {
|
||||
val buffer = string.toByteArray(Charsets.UTF_8)
|
||||
this.writeInt(buffer.size)
|
||||
this.write(buffer)
|
||||
}
|
||||
|
||||
protected fun DataInputStream.readString(): String {
|
||||
val len = this.readInt()
|
||||
val buffer = ByteArray(len)
|
||||
|
||||
this.readFully(buffer)
|
||||
|
||||
return String(buffer)
|
||||
}
|
||||
}
|
||||
@@ -47,7 +47,7 @@ class Clash(
|
||||
}
|
||||
}
|
||||
|
||||
Bridge.init(home.absolutePath)
|
||||
Bridge.setBaseDir(home.absolutePath)
|
||||
}
|
||||
|
||||
fun getCurrentProcessStatus(): ProcessEvent {
|
||||
@@ -79,15 +79,11 @@ class Clash(
|
||||
fun startTunDevice(
|
||||
fd: Int,
|
||||
mtu: Int,
|
||||
gateway: String,
|
||||
dns: String,
|
||||
onSocket: (Int) -> Unit
|
||||
dns: String
|
||||
) {
|
||||
enforceStarted()
|
||||
|
||||
Bridge.startTunDevice(fd.toLong(), mtu.toLong(), gateway, dns) {
|
||||
onSocket(it.toInt())
|
||||
}
|
||||
Bridge.startTunDevice(fd.toLong(), mtu.toLong(), dns)
|
||||
}
|
||||
|
||||
fun stopTunDevice() {
|
||||
|
||||
@@ -1,133 +0,0 @@
|
||||
package com.github.kr328.clash.core
|
||||
|
||||
import android.content.Context
|
||||
import com.github.kr328.clash.core.event.ProcessEvent
|
||||
import com.github.kr328.clash.core.utils.Log
|
||||
import java.io.File
|
||||
import java.io.FileOutputStream
|
||||
import java.io.IOException
|
||||
import kotlin.concurrent.thread
|
||||
|
||||
class ClashProcess(
|
||||
private val context: Context,
|
||||
private val clashDir: File,
|
||||
private val controllerPath: File,
|
||||
private val listener: (ProcessEvent) -> Unit
|
||||
) {
|
||||
companion object {
|
||||
private const val CONTROLLER_STATUS_PREFIX = "[CONTROLLER]"
|
||||
|
||||
private val PID_PATTERN = Regex("\\[PID]\\s*(\\d+)")
|
||||
private val CONTROLLER_ERROR_PATTERN = Regex("\\[CONTROLLER] ERROR=\\{(.+)\\}")
|
||||
}
|
||||
|
||||
private var pid: Int = 0
|
||||
private var process: Process? = null
|
||||
|
||||
@Synchronized
|
||||
fun start() {
|
||||
if (pid > 0)
|
||||
return
|
||||
|
||||
try {
|
||||
clashDir.mkdirs()
|
||||
controllerPath.parentFile?.mkdirs()
|
||||
controllerPath.delete()
|
||||
|
||||
extractMMDB()
|
||||
|
||||
val clashPath =
|
||||
File(context.applicationInfo.nativeLibraryDir, "libclash.so").absolutePath
|
||||
|
||||
val p = ProcessBuilder().apply {
|
||||
command(clashPath, controllerPath.absolutePath)
|
||||
directory(clashDir)
|
||||
}.start()
|
||||
|
||||
Log.i("Starting clash [$clashPath]")
|
||||
|
||||
val reader = p.inputStream.bufferedReader()
|
||||
var line = ""
|
||||
|
||||
// Parse pid
|
||||
var currentPid = 0
|
||||
while (reader.readLine()?.apply { line = this.trim() } != null) {
|
||||
Log.i(line)
|
||||
|
||||
if (PID_PATTERN.matchEntire(line)?.apply {
|
||||
currentPid = groups[1]!!.value.toInt()
|
||||
} != null)
|
||||
break
|
||||
}
|
||||
|
||||
while (reader.readLine()?.apply { line = this.trim() } != null) {
|
||||
Log.i(line)
|
||||
|
||||
if (line.startsWith(CONTROLLER_STATUS_PREFIX)) {
|
||||
val error = CONTROLLER_ERROR_PATTERN.matchEntire(line)?.groups?.get(1)?.value
|
||||
|
||||
if (error != null)
|
||||
throw IOException("Controller: $error")
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
process = p
|
||||
pid = currentPid
|
||||
|
||||
listener(ProcessEvent.STARTED)
|
||||
|
||||
Log.i("Clash started pid = $pid")
|
||||
|
||||
thread {
|
||||
// Redirect stdout to log
|
||||
while (reader.readLine()?.apply { line = this } != null) {
|
||||
Log.i(line.trim())
|
||||
}
|
||||
|
||||
synchronized(this@ClashProcess) {
|
||||
p.destroy()
|
||||
process = null
|
||||
pid = -1
|
||||
}
|
||||
|
||||
listener(ProcessEvent.STOPPED)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
listener(ProcessEvent.STOPPED)
|
||||
throw e
|
||||
}
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
fun getProcessStatus(): ProcessEvent {
|
||||
return if (pid > 0)
|
||||
ProcessEvent.STARTED
|
||||
else
|
||||
ProcessEvent.STOPPED
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
fun stop() {
|
||||
if ( getProcessStatus() == ProcessEvent.STOPPED )
|
||||
listener(ProcessEvent.STOPPED)
|
||||
|
||||
android.os.Process.killProcess(pid)
|
||||
}
|
||||
|
||||
private fun extractMMDB() {
|
||||
if (context.packageManager.getPackageInfo(context.packageName, 0).lastUpdateTime
|
||||
< clashDir.resolve("Country.mmdb").lastModified()
|
||||
)
|
||||
return
|
||||
|
||||
clashDir.resolve("ui").mkdirs()
|
||||
|
||||
context.resources.assets.open("Country.mmdb").use { input ->
|
||||
FileOutputStream(clashDir.resolve("Country.mmdb")).use { output ->
|
||||
input.copyTo(output)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -10,8 +10,6 @@ import android.os.*
|
||||
import com.github.kr328.clash.core.event.*
|
||||
import com.github.kr328.clash.core.utils.Log
|
||||
import com.github.kr328.clash.service.net.DefaultNetworkObserver
|
||||
import java.net.Inet4Address
|
||||
import java.net.InetAddress
|
||||
|
||||
class TunService : VpnService(), IClashEventObserver {
|
||||
companion object {
|
||||
@@ -118,18 +116,14 @@ class TunService : VpnService(), IClashEventObserver {
|
||||
if ( settings.isDnsHijackingEnabled ) {
|
||||
clash.clash.startTunDevice(
|
||||
fileDescriptor.fd, VPN_MTU,
|
||||
"$PRIVATE_VLAN4_CLIENT/$PRIVATE_VLAN4_SUBNET", VLAN4_ANY
|
||||
) {
|
||||
protect(it.toInt())
|
||||
}
|
||||
VLAN4_ANY
|
||||
)
|
||||
}
|
||||
else {
|
||||
clash.clash.startTunDevice(
|
||||
fileDescriptor.fd, VPN_MTU,
|
||||
"$PRIVATE_VLAN4_CLIENT/$PRIVATE_VLAN4_SUBNET", PRIVATE_VLAN_DNS
|
||||
) {
|
||||
protect(it.toInt())
|
||||
}
|
||||
PRIVATE_VLAN_DNS
|
||||
)
|
||||
}
|
||||
|
||||
fileDescriptor.close()
|
||||
@@ -170,11 +164,13 @@ class TunService : VpnService(), IClashEventObserver {
|
||||
addDisallowedApplication(app)
|
||||
}
|
||||
}
|
||||
addDisallowedApplication(packageName)
|
||||
}
|
||||
ClashSettingService.ACCESS_CONTROL_MODE_ALLOW -> {
|
||||
addAllowedApplication(packageName)
|
||||
for ( app in (settings.accessControlApps.toSet() -
|
||||
resources.getStringArray(R.array.default_disallow_application)) ) {
|
||||
for ( app in settings.accessControlApps.toSet() -
|
||||
resources.getStringArray(R.array.default_disallow_application) -
|
||||
setOf(packageName) ) {
|
||||
runCatching {
|
||||
addAllowedApplication(app)
|
||||
}.onFailure {
|
||||
@@ -183,15 +179,15 @@ class TunService : VpnService(), IClashEventObserver {
|
||||
}
|
||||
}
|
||||
ClashSettingService.ACCESS_CONTROL_MODE_DISALLOW -> {
|
||||
for ( app in (settings.accessControlApps.toSet() +
|
||||
resources.getStringArray(R.array.default_disallow_application) -
|
||||
listOf(packageName)) ) {
|
||||
for ( app in settings.accessControlApps.toSet() +
|
||||
resources.getStringArray(R.array.default_disallow_application) ) {
|
||||
runCatching {
|
||||
addDisallowedApplication(app)
|
||||
}.onFailure {
|
||||
Log.w("Package $app not found")
|
||||
}
|
||||
}
|
||||
addDisallowedApplication(packageName)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user