Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
981c27c9f1 | ||
|
|
11c4e19e0c | ||
|
|
fa23be76ed | ||
|
|
8e50d35eba | ||
|
|
1112534be6 | ||
|
|
784f249d54 | ||
|
|
d6ad21b402 | ||
|
|
bd03cac80d | ||
|
|
da3ee71dfd | ||
|
|
62fb089e27 | ||
|
|
51dab21f6f | ||
|
|
9e89b3a201 | ||
|
|
1c1e2b9f3d | ||
|
|
e2e0238dcd | ||
|
|
9745e11da4 | ||
|
|
8ae7ccbfc9 | ||
|
|
5a2229596a | ||
|
|
a463d94480 | ||
|
|
750abc8c71 | ||
|
|
8375fbd8b3 | ||
|
|
394e406a36 | ||
|
|
2645af0d4c | ||
|
|
48222c22c8 | ||
|
|
d6a71267c6 | ||
|
|
0f4a46188c | ||
|
|
5917b90837 | ||
|
|
a222e90d1f | ||
|
|
3f60d713f8 | ||
|
|
9cb8433f3b | ||
|
|
428ca53532 | ||
|
|
6c4d7e537b | ||
|
|
58ab89736a | ||
|
|
5e34221a09 | ||
|
|
ac35f2a5f4 | ||
|
|
73992dca54 | ||
|
|
53dc20109d | ||
|
|
e7fef0a767 | ||
|
|
c73beabf7e | ||
|
|
c7409d7ac6 | ||
|
|
5e238ab5d3 | ||
|
|
a8f502ef4f | ||
|
|
c48ce82640 | ||
|
|
5594485bec | ||
|
|
f967bd299a | ||
|
|
a75c8f5dfc | ||
|
|
271d56c01c | ||
|
|
12220789a3 | ||
|
|
ca85a688cc | ||
|
|
0e3ed548c9 | ||
|
|
27dc5395e0 | ||
|
|
4f05ba1ac6 | ||
|
|
1a36218c80 | ||
|
|
3125b90efe | ||
|
|
2a7425700a |
No files matched your search
@@ -0,0 +1,42 @@
|
||||
name: Build Unsigned
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
paths-ignore:
|
||||
- '.github/**'
|
||||
- '.idea/**'
|
||||
- '.gitignore'
|
||||
- '.gitmodules'
|
||||
- '**.md'
|
||||
- 'LICENSE'
|
||||
- 'NOTICE'
|
||||
pull_request:
|
||||
paths-ignore:
|
||||
- '.github/**'
|
||||
- '.idea/**'
|
||||
- '.gitignore'
|
||||
- '.gitmodules'
|
||||
- '**.md'
|
||||
- 'LICENSE'
|
||||
- 'NOTICE'
|
||||
jobs:
|
||||
BuildUnsigned:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout Repository
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: Setup Java
|
||||
uses: actions/setup-java@v1
|
||||
with:
|
||||
java-version: 11
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: 1.17
|
||||
- name: Setup Android SDK
|
||||
uses: android-actions/setup-android@v2
|
||||
- name: Build
|
||||
run: ./gradlew --no-daemon app:assembleFossRelease
|
||||
+4
-2
@@ -19,9 +19,11 @@ gradle-app.setting
|
||||
# Ignore IDEA config
|
||||
*.iml
|
||||
/.idea/*
|
||||
/core/src/main/golang/.idea/*
|
||||
!/.idea/codeStyles
|
||||
!/core/src/main/golang/.idea/codeStyles
|
||||
/core/src/foss/go/.idea/*
|
||||
!/core/src/foss/go/.idea/codeStyles
|
||||
/core/src/premium/go/.idea/*
|
||||
!/core/src/premium/go/.idea/codeStyles
|
||||
|
||||
# KeyStore
|
||||
*.keystore
|
||||
|
||||
+2
-8
@@ -1,9 +1,3 @@
|
||||
[submodule "core/src/main/golang/clash"]
|
||||
path = core/src/main/golang/clash
|
||||
[submodule "clash-foss"]
|
||||
path = core/src/foss/go/clash
|
||||
url = https://github.com/Kr328/clash.git
|
||||
[submodule "kaidl"]
|
||||
path = kaidl
|
||||
url = https://github.com/Kr328/kaidl.git
|
||||
[submodule "core/src/main/golang/tun2socket"]
|
||||
path = core/src/main/golang/tun2socket
|
||||
url = https://github.com/Kr328/tun2socket-lwip.git
|
||||
@@ -8,26 +8,20 @@ A Graphical user interface of [clash](https://github.com/Dreamacro/clash) for An
|
||||
|
||||
Fully feature of [clash](https://github.com/Dreamacro/clash) ~~(Exclude `external-controller`~~
|
||||
|
||||
|
||||
|
||||
### Requirement
|
||||
|
||||
* Android 5.0+ (minimum)
|
||||
* Android 7.0+ (recommend)
|
||||
* `armeabi-v7a` , `arm64-v8a`, `x86` or `x86_64` Architecture
|
||||
- Android 5.0+ (minimum)
|
||||
- Android 7.0+ (recommend)
|
||||
- `armeabi-v7a` , `arm64-v8a`, `x86` or `x86_64` Architecture
|
||||
|
||||
### License
|
||||
|
||||
See also [LICENSE](./LICENSE) and [NOTICE](./NOTICE)
|
||||
|
||||
|
||||
|
||||
### Privacy Policy
|
||||
### Privacy Policy
|
||||
|
||||
See also [PRIVACY_POLICY.md](./PRIVACY_POLICY.md)
|
||||
|
||||
|
||||
|
||||
### Build
|
||||
|
||||
1. Update submodules
|
||||
@@ -38,7 +32,7 @@ See also [PRIVACY_POLICY.md](./PRIVACY_POLICY.md)
|
||||
|
||||
2. Install **OpenJDK 11**, **Android SDK**, **CMake** and **Golang**
|
||||
|
||||
3. Create `local.properties` in project root with
|
||||
3. Create `local.properties` in project root with
|
||||
|
||||
```properties
|
||||
sdk.dir=/path/to/android-sdk
|
||||
@@ -51,7 +45,7 @@ See also [PRIVACY_POLICY.md](./PRIVACY_POLICY.md)
|
||||
storePassword=<key store password>
|
||||
keyAlias=<key alias>
|
||||
keyPassword=<key password>
|
||||
```
|
||||
```
|
||||
|
||||
5. Build
|
||||
|
||||
@@ -59,4 +53,4 @@ See also [PRIVACY_POLICY.md](./PRIVACY_POLICY.md)
|
||||
./gradlew app:assembleFossRelease
|
||||
```
|
||||
|
||||
6. Pick `app-release-<arch>.apk` in `app/build/outputs/apks`
|
||||
6. Pick `app-foss-<arch>-release-signed.apk` in `app/build/outputs/apk/foss/release/`
|
||||
+19
-121
@@ -1,133 +1,31 @@
|
||||
import java.util.*
|
||||
|
||||
plugins {
|
||||
id("com.android.application")
|
||||
kotlin("android")
|
||||
kotlin("kapt")
|
||||
}
|
||||
|
||||
android {
|
||||
compileSdk = buildTargetSdkVersion
|
||||
|
||||
flavorDimensions(buildFlavor)
|
||||
|
||||
defaultConfig {
|
||||
applicationId = "com.github.kr328.clash"
|
||||
|
||||
minSdk = buildMinSdkVersion
|
||||
targetSdk = buildTargetSdkVersion
|
||||
|
||||
versionCode = buildVersionCode
|
||||
versionName = buildVersionName
|
||||
|
||||
resConfigs("zh-rCN", "zh-rHK", "zh-rTW")
|
||||
|
||||
resValue("string", "release_name", "v$buildVersionName")
|
||||
resValue("integer", "release_code", "$buildVersionCode")
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
named("release") {
|
||||
isMinifyEnabled = true
|
||||
isShrinkResources = true
|
||||
proguardFiles(
|
||||
getDefaultProguardFile("proguard-android-optimize.txt"),
|
||||
"proguard-rules.pro"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
productFlavors {
|
||||
create("foss") {
|
||||
dimension = "foss"
|
||||
versionNameSuffix = ".foss"
|
||||
applicationIdSuffix = ".foss"
|
||||
}
|
||||
create("premium") {
|
||||
dimension = "premium"
|
||||
versionNameSuffix = ".premium"
|
||||
|
||||
if (buildFlavor == "premium") {
|
||||
val appCenterKey = rootProject.file("local.properties").inputStream()
|
||||
.use { Properties().apply { load(it) } }
|
||||
.getProperty("appcenter.key", null)
|
||||
|
||||
Objects.requireNonNull(appCenterKey)
|
||||
|
||||
buildConfigField("String", "APP_CENTER_KEY", "\"$appCenterKey\"")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val signingFile = rootProject.file("keystore.properties")
|
||||
if (signingFile.exists()) {
|
||||
val properties = Properties().apply {
|
||||
signingFile.inputStream().use {
|
||||
load(it)
|
||||
}
|
||||
}
|
||||
signingConfigs {
|
||||
create("release") {
|
||||
storeFile = rootProject.file(properties.getProperty("storeFile")!!)
|
||||
storePassword = properties.getProperty("storePassword")!!
|
||||
keyAlias = properties.getProperty("keyAlias")!!
|
||||
keyPassword = properties.getProperty("keyPassword")!!
|
||||
}
|
||||
}
|
||||
buildTypes {
|
||||
named("release") {
|
||||
signingConfig = signingConfigs["release"]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
buildFeatures {
|
||||
dataBinding = true
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility = JavaVersion.VERSION_1_8
|
||||
targetCompatibility = JavaVersion.VERSION_1_8
|
||||
}
|
||||
|
||||
kotlinOptions {
|
||||
jvmTarget = "1.8"
|
||||
}
|
||||
|
||||
splits {
|
||||
abi {
|
||||
isEnable = true
|
||||
isUniversalApk = true
|
||||
}
|
||||
}
|
||||
id("com.android.application")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
val premiumImplementation by configurations
|
||||
|
||||
api(project(":core"))
|
||||
api(project(":service"))
|
||||
api(project(":design"))
|
||||
api(project(":common"))
|
||||
|
||||
premiumImplementation("com.microsoft.appcenter:appcenter-analytics:$appcenterVersion")
|
||||
premiumImplementation("com.microsoft.appcenter:appcenter-crashes:$appcenterVersion")
|
||||
implementation(project(":core"))
|
||||
implementation(project(":service"))
|
||||
implementation(project(":design"))
|
||||
implementation(project(":common"))
|
||||
|
||||
implementation(kotlin("stdlib-jdk7"))
|
||||
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutineVersion")
|
||||
implementation("androidx.core:core-ktx:$ktxVersion")
|
||||
implementation("androidx.activity:activity:$activityVersion")
|
||||
implementation("androidx.appcompat:appcompat:$appcompatVersion")
|
||||
implementation("androidx.coordinatorlayout:coordinatorlayout:$coordinatorlayoutVersion")
|
||||
implementation("androidx.recyclerview:recyclerview:$recyclerviewVersion")
|
||||
implementation("androidx.fragment:fragment:$fragmentVersion")
|
||||
implementation("com.google.android.material:material:$materialVersion")
|
||||
implementation(deps.kotlin.coroutine)
|
||||
implementation(deps.androidx.core)
|
||||
implementation(deps.androidx.activity)
|
||||
implementation(deps.androidx.fragment)
|
||||
implementation(deps.androidx.appcompat)
|
||||
implementation(deps.androidx.coordinator)
|
||||
implementation(deps.androidx.recyclerview)
|
||||
implementation(deps.google.material)
|
||||
|
||||
val premiumImplementation by configurations
|
||||
|
||||
premiumImplementation(deps.appcenter.analytics)
|
||||
premiumImplementation(deps.appcenter.crashes)
|
||||
}
|
||||
|
||||
task("cleanRelease", type = Delete::class) {
|
||||
tasks.getByName("clean", type = Delete::class) {
|
||||
delete(file("release"))
|
||||
}
|
||||
|
||||
afterEvaluate {
|
||||
tasks["clean"].dependsOn(tasks["cleanRelease"])
|
||||
}
|
||||
Vendored
+26
@@ -31,3 +31,29 @@
|
||||
public static void checkParameterIsNotNull(...);
|
||||
public static void checkNotNullParameter(...);
|
||||
}
|
||||
|
||||
# Kotlin Coroutine
|
||||
# Allow R8 to optimize away the FastServiceLoader.
|
||||
# Together with ServiceLoader optimization in R8
|
||||
# this results in direct instantiation when loading Dispatchers.Main
|
||||
-assumenosideeffects class kotlinx.coroutines.internal.MainDispatcherLoader {
|
||||
boolean FAST_SERVICE_LOADER_ENABLED return false;
|
||||
}
|
||||
|
||||
-assumenosideeffects class kotlinx.coroutines.internal.FastServiceLoaderKt {
|
||||
boolean ANDROID_DETECTED return true;
|
||||
}
|
||||
|
||||
-keep class kotlinx.coroutines.android.AndroidDispatcherFactory {*;}
|
||||
|
||||
# Disable support for "Missing Main Dispatcher", since we always have Android main dispatcher
|
||||
-assumenosideeffects class kotlinx.coroutines.internal.MainDispatchersKt {
|
||||
boolean SUPPORT_MISSING return false;
|
||||
}
|
||||
|
||||
# Statically turn off all debugging facilities and assertions
|
||||
-assumenosideeffects class kotlinx.coroutines.DebugKt {
|
||||
boolean getASSERTIONS_ENABLED() return false;
|
||||
boolean getDEBUG() return false;
|
||||
boolean getRECOVER_STACK_TRACES() return false;
|
||||
}
|
||||
@@ -41,7 +41,8 @@
|
||||
android:name=".MainActivity"
|
||||
android:configChanges="uiMode"
|
||||
android:exported="true"
|
||||
android:label="@string/launch_name">
|
||||
android:label="@string/launch_name"
|
||||
android:launchMode="singleTop">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
|
||||
|
||||
@@ -24,8 +24,6 @@ class AppCrashedActivity : BaseActivity<AppCrashedDesign>() {
|
||||
SystemLogcat.dumpCrash()
|
||||
}
|
||||
|
||||
Tracker.uploadLogcat(logs)
|
||||
|
||||
design.setAppLogs(logs)
|
||||
|
||||
while (isActive) {
|
||||
|
||||
@@ -114,7 +114,7 @@ abstract class BaseActivity<D : Design<*>> :
|
||||
|
||||
Remote.broadcasts.addObserver(this)
|
||||
|
||||
events.offer(Event.ActivityStart)
|
||||
events.trySend(Event.ActivityStart)
|
||||
}
|
||||
|
||||
override fun onStop() {
|
||||
@@ -124,7 +124,7 @@ abstract class BaseActivity<D : Design<*>> :
|
||||
|
||||
Remote.broadcasts.removeObserver(this)
|
||||
|
||||
events.offer(Event.ActivityStop)
|
||||
events.trySend(Event.ActivityStop)
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
@@ -174,23 +174,23 @@ abstract class BaseActivity<D : Design<*>> :
|
||||
}
|
||||
|
||||
override fun onProfileChanged() {
|
||||
events.offer(Event.ProfileChanged)
|
||||
events.trySend(Event.ProfileChanged)
|
||||
}
|
||||
|
||||
override fun onProfileLoaded() {
|
||||
events.offer(Event.ProfileLoaded)
|
||||
events.trySend(Event.ProfileLoaded)
|
||||
}
|
||||
|
||||
override fun onServiceRecreated() {
|
||||
events.offer(Event.ServiceRecreated)
|
||||
events.trySend(Event.ServiceRecreated)
|
||||
}
|
||||
|
||||
override fun onStarted() {
|
||||
events.offer(Event.ClashStart)
|
||||
events.trySend(Event.ClashStart)
|
||||
}
|
||||
|
||||
override fun onStopped(cause: String?) {
|
||||
events.offer(Event.ClashStop)
|
||||
events.trySend(Event.ClashStop)
|
||||
|
||||
if (cause != null && activityStarted) {
|
||||
launch {
|
||||
|
||||
@@ -141,7 +141,7 @@ class FilesActivity : BaseActivity<FilesDesign>() {
|
||||
}
|
||||
|
||||
override fun onBackPressed() {
|
||||
design?.requests?.offer(FilesDesign.Request.PopStack)
|
||||
design?.requests?.trySend(FilesDesign.Request.PopStack)
|
||||
}
|
||||
|
||||
private suspend fun FilesDesign.fetch(client: FilesClient, stack: Stack<String>, root: String) {
|
||||
|
||||
@@ -20,9 +20,9 @@ import com.github.kr328.clash.common.util.intent
|
||||
import com.github.kr328.clash.core.model.LogMessage
|
||||
import com.github.kr328.clash.log.LogcatCache
|
||||
import com.github.kr328.clash.log.LogcatWriter
|
||||
import com.github.kr328.clash.service.ClashManager
|
||||
import com.github.kr328.clash.service.remote.IClashManager
|
||||
import com.github.kr328.clash.service.RemoteService
|
||||
import com.github.kr328.clash.service.remote.ILogObserver
|
||||
import com.github.kr328.clash.service.remote.IRemoteService
|
||||
import com.github.kr328.clash.service.remote.unwrap
|
||||
import com.github.kr328.clash.util.logsDir
|
||||
import kotlinx.coroutines.*
|
||||
@@ -52,7 +52,7 @@ class LogcatService : Service(), CoroutineScope by CoroutineScope(Dispatchers.De
|
||||
|
||||
showNotification()
|
||||
|
||||
bindService(ClashManager::class.intent, connection, Context.BIND_AUTO_CREATE)
|
||||
bindService(RemoteService::class.intent, connection, Context.BIND_AUTO_CREATE)
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
@@ -88,7 +88,7 @@ class LogcatService : Service(), CoroutineScope by CoroutineScope(Dispatchers.De
|
||||
return stopSelf()
|
||||
|
||||
launch(Dispatchers.IO) {
|
||||
val service = binder.unwrap(IClashManager::class)
|
||||
val service = binder.unwrap(IRemoteService::class).clash()
|
||||
val channel = Channel<LogMessage>(CACHE_CAPACITY)
|
||||
|
||||
try {
|
||||
@@ -97,7 +97,7 @@ class LogcatService : Service(), CoroutineScope by CoroutineScope(Dispatchers.De
|
||||
LogcatWriter(this@LogcatService).use {
|
||||
val observer = object : ILogObserver {
|
||||
override fun newItem(log: LogMessage) {
|
||||
channel.offer(log)
|
||||
channel.trySend(log)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -47,7 +47,7 @@ class LogsActivity : BaseActivity<LogsDesign>() {
|
||||
deleteAllLogs()
|
||||
}
|
||||
|
||||
events.offer(Event.ActivityStart)
|
||||
events.trySend(Event.ActivityStart)
|
||||
}
|
||||
}
|
||||
is LogsDesign.Request.OpenFile -> {
|
||||
|
||||
@@ -32,4 +32,8 @@ class MainApplication : Application() {
|
||||
sendServiceRecreated()
|
||||
}
|
||||
}
|
||||
|
||||
fun finalize() {
|
||||
Global.destroy()
|
||||
}
|
||||
}
|
||||
@@ -73,7 +73,7 @@ class ProxyActivity : BaseActivity<ProxyDesign>() {
|
||||
}
|
||||
ProxyDesign.Request.ReloadAll -> {
|
||||
names.indices.forEach { idx ->
|
||||
design.requests.offer(ProxyDesign.Request.Reload(idx))
|
||||
design.requests.trySend(ProxyDesign.Request.Reload(idx))
|
||||
}
|
||||
}
|
||||
is ProxyDesign.Request.Reload -> {
|
||||
|
||||
@@ -8,7 +8,8 @@ object SystemLogcat {
|
||||
"Go",
|
||||
"DEBUG",
|
||||
"AndroidRuntime",
|
||||
"ClashForAndroid"
|
||||
"ClashForAndroid",
|
||||
"LwIP",
|
||||
)
|
||||
|
||||
fun dumpCrash(): String {
|
||||
|
||||
@@ -10,13 +10,12 @@ import com.github.kr328.clash.store.AppStore
|
||||
import com.github.kr328.clash.util.ApplicationObserver
|
||||
import com.github.kr328.clash.util.verifyApk
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.channels.Channel
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
object Remote {
|
||||
val broadcasts: Broadcasts = Broadcasts(Global.application)
|
||||
val services: Services = Services(Global.application) {
|
||||
val service: Service = Service(Global.application) {
|
||||
ApplicationObserver.createdActivities.forEach { it.finish() }
|
||||
|
||||
val intent = AppCrashedActivity::class.intent
|
||||
@@ -30,9 +29,9 @@ object Remote {
|
||||
fun launch() {
|
||||
ApplicationObserver.attach(Global.application)
|
||||
|
||||
ApplicationObserver.onVisibleChanged(visible::offer)
|
||||
ApplicationObserver.onVisibleChanged { visible.trySend(it) }
|
||||
|
||||
GlobalScope.launch(Dispatchers.IO) {
|
||||
Global.launch(Dispatchers.IO) {
|
||||
run()
|
||||
}
|
||||
}
|
||||
@@ -57,10 +56,10 @@ object Remote {
|
||||
|
||||
while (true) {
|
||||
if (visible.receive()) {
|
||||
services.bind()
|
||||
service.bind()
|
||||
broadcasts.register()
|
||||
} else {
|
||||
services.unbind()
|
||||
service.unbind()
|
||||
broadcasts.unregister()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
package com.github.kr328.clash.remote
|
||||
|
||||
import android.app.Application
|
||||
import android.content.ComponentName
|
||||
import android.content.Context
|
||||
import android.content.ServiceConnection
|
||||
import android.os.IBinder
|
||||
import com.github.kr328.clash.Tracker
|
||||
import com.github.kr328.clash.common.log.Log
|
||||
import com.github.kr328.clash.common.util.intent
|
||||
import com.github.kr328.clash.log.SystemLogcat
|
||||
import com.github.kr328.clash.service.RemoteService
|
||||
import com.github.kr328.clash.service.remote.IRemoteService
|
||||
import com.github.kr328.clash.service.remote.unwrap
|
||||
import com.github.kr328.clash.util.unbindServiceSilent
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
class Service(private val context: Application, val crashed: () -> Unit) {
|
||||
val remote = Resource<IRemoteService>()
|
||||
|
||||
private val connection = object : ServiceConnection {
|
||||
private var lastCrashed: Long = -1
|
||||
|
||||
override fun onServiceConnected(name: ComponentName?, service: IBinder) {
|
||||
remote.set(service.unwrap(IRemoteService::class))
|
||||
}
|
||||
|
||||
override fun onServiceDisconnected(name: ComponentName?) {
|
||||
remote.set(null)
|
||||
|
||||
Tracker.uploadLogcat(SystemLogcat.dumpCrash())
|
||||
|
||||
if (System.currentTimeMillis() - lastCrashed < TOGGLE_CRASHED_INTERVAL) {
|
||||
unbind()
|
||||
|
||||
crashed()
|
||||
}
|
||||
|
||||
lastCrashed = System.currentTimeMillis()
|
||||
|
||||
Log.w("RemoteManager crashed")
|
||||
}
|
||||
}
|
||||
|
||||
fun bind() {
|
||||
try {
|
||||
context.bindService(RemoteService::class.intent, connection, Context.BIND_AUTO_CREATE)
|
||||
} catch (e: Exception) {
|
||||
unbind()
|
||||
|
||||
crashed()
|
||||
}
|
||||
}
|
||||
|
||||
fun unbind() {
|
||||
context.unbindServiceSilent(connection)
|
||||
|
||||
remote.set(null)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val TOGGLE_CRASHED_INTERVAL = TimeUnit.SECONDS.toMillis(10)
|
||||
}
|
||||
}
|
||||
@@ -1,88 +0,0 @@
|
||||
package com.github.kr328.clash.remote
|
||||
|
||||
import android.app.Application
|
||||
import android.content.ComponentName
|
||||
import android.content.Context
|
||||
import android.content.ServiceConnection
|
||||
import android.os.IBinder
|
||||
import com.github.kr328.clash.common.log.Log
|
||||
import com.github.kr328.clash.common.util.intent
|
||||
import com.github.kr328.clash.service.ClashManager
|
||||
import com.github.kr328.clash.service.ProfileService
|
||||
import com.github.kr328.clash.service.remote.IClashManager
|
||||
import com.github.kr328.clash.service.remote.IProfileManager
|
||||
import com.github.kr328.clash.service.remote.unwrap
|
||||
import com.github.kr328.clash.util.unbindServiceSilent
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
class Services(private val context: Application, val crashed: () -> Unit) {
|
||||
val clash = Resource<IClashManager>()
|
||||
val profile = Resource<IProfileManager>()
|
||||
|
||||
private val clashConnection = object : ServiceConnection {
|
||||
private var lastCrashed: Long = -1
|
||||
|
||||
override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
|
||||
clash.set(service?.unwrap(IClashManager::class))
|
||||
}
|
||||
|
||||
override fun onServiceDisconnected(name: ComponentName?) {
|
||||
clash.set(null)
|
||||
|
||||
if (System.currentTimeMillis() - lastCrashed < TOGGLE_CRASHED_INTERVAL) {
|
||||
unbind()
|
||||
|
||||
crashed()
|
||||
}
|
||||
|
||||
lastCrashed = System.currentTimeMillis()
|
||||
|
||||
Log.w("ClashManager crashed")
|
||||
}
|
||||
}
|
||||
|
||||
private val profileConnection = object : ServiceConnection {
|
||||
private var lastCrashed: Long = -1
|
||||
|
||||
override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
|
||||
profile.set(service?.unwrap(IProfileManager::class))
|
||||
}
|
||||
|
||||
override fun onServiceDisconnected(name: ComponentName?) {
|
||||
profile.set(null)
|
||||
|
||||
if (System.currentTimeMillis() - lastCrashed < TOGGLE_CRASHED_INTERVAL) {
|
||||
unbind()
|
||||
|
||||
crashed()
|
||||
}
|
||||
|
||||
lastCrashed = System.currentTimeMillis()
|
||||
|
||||
Log.w("ProfileService crashed")
|
||||
}
|
||||
}
|
||||
|
||||
fun bind() {
|
||||
try {
|
||||
context.bindService(ClashManager::class.intent, clashConnection, Context.BIND_AUTO_CREATE)
|
||||
context.bindService(ProfileService::class.intent, profileConnection, Context.BIND_AUTO_CREATE)
|
||||
} catch (e: Exception) {
|
||||
unbind()
|
||||
|
||||
crashed()
|
||||
}
|
||||
}
|
||||
|
||||
fun unbind() {
|
||||
context.unbindServiceSilent(clashConnection)
|
||||
context.unbindServiceSilent(profileConnection)
|
||||
|
||||
clash.set(null)
|
||||
profile.set(null)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val TOGGLE_CRASHED_INTERVAL = TimeUnit.SECONDS.toMillis(10)
|
||||
}
|
||||
}
|
||||
@@ -14,14 +14,15 @@ suspend fun <T> withClash(
|
||||
block: suspend IClashManager.() -> T
|
||||
): T {
|
||||
while (true) {
|
||||
val client = Remote.services.clash.get()
|
||||
val remote = Remote.service.remote.get()
|
||||
val client = remote.clash()
|
||||
|
||||
try {
|
||||
return withContext(context) { client.block() }
|
||||
} catch (e: DeadObjectException) {
|
||||
Log.w("Remote services panic")
|
||||
|
||||
Remote.services.clash.reset(client)
|
||||
Remote.service.remote.reset(remote)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -31,14 +32,15 @@ suspend fun <T> withProfile(
|
||||
block: suspend IProfileManager.() -> T
|
||||
): T {
|
||||
while (true) {
|
||||
val client = Remote.services.profile.get()
|
||||
val remote = Remote.service.remote.get()
|
||||
val client = remote.profile()
|
||||
|
||||
try {
|
||||
return withContext(context) { client.block() }
|
||||
} catch (e: DeadObjectException) {
|
||||
Log.w("Remote services panic")
|
||||
|
||||
Remote.services.profile.reset(client)
|
||||
Remote.service.remote.reset(remote)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,127 @@
|
||||
@file:Suppress("UNUSED_VARIABLE")
|
||||
|
||||
import com.android.build.gradle.BaseExtension
|
||||
import java.net.URL
|
||||
|
||||
buildscript {
|
||||
repositories {
|
||||
mavenCentral()
|
||||
mavenLocal()
|
||||
google()
|
||||
maven("https://maven.kr328.app/releases")
|
||||
}
|
||||
dependencies {
|
||||
classpath(deps.build.android)
|
||||
classpath(deps.build.kotlin.common)
|
||||
classpath(deps.build.kotlin.serialization)
|
||||
classpath(deps.build.ksp)
|
||||
classpath(deps.build.golang)
|
||||
}
|
||||
}
|
||||
|
||||
allprojects {
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
maven("https://maven.kr328.app/releases")
|
||||
}
|
||||
}
|
||||
|
||||
subprojects {
|
||||
val isApp = name == "app"
|
||||
|
||||
apply(plugin = if (isApp) "com.android.application" else "com.android.library")
|
||||
|
||||
extensions.configure(BaseExtension::class) {
|
||||
val minSdkVersion = 21
|
||||
val targetSdkVersion = 30
|
||||
val buildVersionCode = 204009
|
||||
val buildVersionName = "2.4.9"
|
||||
val defaultDimension = "feature"
|
||||
|
||||
ndkVersion = "23.0.7599858"
|
||||
|
||||
compileSdkVersion(targetSdkVersion)
|
||||
|
||||
defaultConfig {
|
||||
if (isApp) {
|
||||
applicationId = "com.github.kr328.clash"
|
||||
}
|
||||
|
||||
minSdk = minSdkVersion
|
||||
targetSdk = targetSdkVersion
|
||||
|
||||
versionName = buildVersionName
|
||||
versionCode = buildVersionCode
|
||||
|
||||
if (!isApp) {
|
||||
consumerProguardFiles("consumer-rules.pro")
|
||||
}
|
||||
|
||||
resValue("string", "release_name", "v$buildVersionName")
|
||||
resValue("integer", "release_code", "$buildVersionCode")
|
||||
|
||||
externalNativeBuild {
|
||||
cmake {
|
||||
abiFilters("arm64-v8a", "armeabi-v7a", "x86", "x86_64")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
externalNativeBuild {
|
||||
cmake {
|
||||
version = "3.18.1"
|
||||
}
|
||||
}
|
||||
|
||||
if (isApp) {
|
||||
packagingOptions {
|
||||
excludes.add("DebugProbesKt.bin")
|
||||
}
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
named("release") {
|
||||
isMinifyEnabled = isApp
|
||||
isShrinkResources = isApp
|
||||
proguardFiles(
|
||||
getDefaultProguardFile("proguard-android-optimize.txt"),
|
||||
"proguard-rules.pro"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
productFlavors {
|
||||
flavorDimensions(defaultDimension)
|
||||
|
||||
create("foss") {
|
||||
dimension = defaultDimension
|
||||
versionNameSuffix = ".foss"
|
||||
|
||||
if (isApp) {
|
||||
applicationIdSuffix = ".foss"
|
||||
}
|
||||
}
|
||||
create("premium") {
|
||||
dimension = defaultDimension
|
||||
versionNameSuffix = ".premium"
|
||||
}
|
||||
}
|
||||
|
||||
buildFeatures.apply {
|
||||
dataBinding {
|
||||
isEnabled = name != "hideapi"
|
||||
}
|
||||
}
|
||||
|
||||
if (isApp) {
|
||||
splits {
|
||||
abi {
|
||||
isEnable = true
|
||||
isUniversalApk = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,35 +0,0 @@
|
||||
plugins {
|
||||
kotlin("jvm") version "1.5.0"
|
||||
`java-gradle-plugin`
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
google()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(kotlin("stdlib"))
|
||||
|
||||
compileOnly(gradleApi())
|
||||
|
||||
api(kotlin("gradle-plugin"))
|
||||
api(kotlin("serialization"))
|
||||
api("com.android.tools.build:gradle:4.2.1") {
|
||||
exclude("org.jetbrains.kotlin", "kotlin-stdlib-jdk8")
|
||||
exclude("org.jetbrains.kotlin", "kotlin-stdlib-jdk7")
|
||||
exclude("org.jetbrains.kotlin", "kotlin-reflect")
|
||||
}
|
||||
api("com.google.devtools.ksp:symbol-processing-gradle-plugin:1.5.0-1.0.0-alpha10") {
|
||||
exclude("com.android.tools.build", "gradle")
|
||||
}
|
||||
}
|
||||
|
||||
gradlePlugin {
|
||||
plugins {
|
||||
create("golang") {
|
||||
id = "clash-build"
|
||||
implementationClass = "com.github.kr328.clash.tools.ClashBuildPlugin"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
import org.gradle.api.Project
|
||||
|
||||
const val buildVersionCode = 204002
|
||||
const val buildVersionName = "2.4.2"
|
||||
|
||||
const val buildMinSdkVersion = 21
|
||||
const val buildTargetSdkVersion = 30
|
||||
|
||||
const val buildNdkVersion = "23.0.7123448"
|
||||
|
||||
val Project.buildFlavor: String
|
||||
get() {
|
||||
return if (project(":core").file("src/main/golang/clash/script/script.go").exists())
|
||||
"premium"
|
||||
else
|
||||
"foss"
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
const val activityVersion = "1.2.3"
|
||||
const val coroutineVersion = "1.4.3"
|
||||
const val roomVersion = "2.3.0"
|
||||
const val ktxVersion = "1.3.2"
|
||||
const val appcompatVersion = "1.2.0"
|
||||
const val muiltprocessVersion = "1.0.0"
|
||||
const val appcenterVersion = "4.1.1"
|
||||
const val serializationVersion = "1.2.1"
|
||||
const val materialVersion = "1.3.0"
|
||||
const val coordinatorlayoutVersion = "1.1.0"
|
||||
const val recyclerviewVersion = "1.2.0"
|
||||
const val fragmentVersion = "1.3.3"
|
||||
const val viewpagerVersion = "1.0.0"
|
||||
@@ -1,8 +0,0 @@
|
||||
import org.gradle.api.Project
|
||||
import java.io.File
|
||||
|
||||
val Project.golangSource: File
|
||||
get() = file("src/main/golang")
|
||||
|
||||
val Project.golangBuild: File
|
||||
get() = buildDir.resolve("intermediates/golang")
|
||||
@@ -1,25 +0,0 @@
|
||||
package com.github.kr328.clash.tools
|
||||
|
||||
import com.android.build.gradle.BaseExtension
|
||||
import com.android.build.gradle.api.BaseVariant
|
||||
import java.io.Serializable
|
||||
|
||||
data class BuildConfig(
|
||||
val debug: Boolean,
|
||||
val premium: Boolean,
|
||||
val abis: List<NativeAbi>,
|
||||
val minSdkVersion: Int,
|
||||
) : Serializable {
|
||||
companion object {
|
||||
fun of(extension: BaseExtension, variant: BaseVariant): BuildConfig {
|
||||
return BuildConfig(
|
||||
debug = variant.buildType.isDebuggable,
|
||||
premium = variant.flavorName == "premium",
|
||||
abis = extension.defaultConfig.externalNativeBuild.cmake.abiFilters
|
||||
.map { NativeAbi.parse(it) }
|
||||
.distinct(),
|
||||
minSdkVersion = extension.defaultConfig.minSdkVersion!!.apiLevel
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
package com.github.kr328.clash.tools
|
||||
|
||||
import com.android.build.gradle.LibraryExtension
|
||||
import golangBuild
|
||||
import golangSource
|
||||
import org.gradle.api.Plugin
|
||||
import org.gradle.api.Project
|
||||
import java.util.*
|
||||
|
||||
class ClashBuildPlugin : Plugin<Project> {
|
||||
override fun apply(target: Project) {
|
||||
target.afterEvaluate {
|
||||
target.extensions.getByType(LibraryExtension::class.java).apply {
|
||||
libraryVariants.forEach { variant ->
|
||||
val config = BuildConfig.of(this, variant)
|
||||
val buildDir = target.golangBuild.resolve(variant.name)
|
||||
val capitalize = variant.name.capitalize(Locale.getDefault())
|
||||
|
||||
val task = target.tasks.register(
|
||||
"externalGolangBuild$capitalize",
|
||||
ClashBuildTask::class.java
|
||||
) {
|
||||
it.config.set(config)
|
||||
it.ndkDirectory.set(ndkDirectory)
|
||||
it.inputDirectory.set(target.golangSource)
|
||||
it.outputDirectory.set(buildDir)
|
||||
}
|
||||
|
||||
sourceSets.named(variant.name) {
|
||||
it.jniLibs {
|
||||
srcDir(buildDir)
|
||||
}
|
||||
}
|
||||
|
||||
variant.externalNativeBuildProviders.forEach {
|
||||
it.get().dependsOn(task)
|
||||
}
|
||||
target.tasks.filter { it.name.startsWith("buildCMake") }.forEach {
|
||||
it.mustRunAfter(task)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
package com.github.kr328.clash.tools
|
||||
|
||||
import org.gradle.api.DefaultTask
|
||||
import org.gradle.api.file.DirectoryProperty
|
||||
import org.gradle.api.provider.Property
|
||||
import org.gradle.api.tasks.Input
|
||||
import org.gradle.api.tasks.InputDirectory
|
||||
import org.gradle.api.tasks.OutputDirectory
|
||||
import org.gradle.api.tasks.TaskAction
|
||||
import java.io.File
|
||||
|
||||
abstract class ClashBuildTask : DefaultTask() {
|
||||
abstract val config: Property<BuildConfig>
|
||||
@Input get
|
||||
|
||||
abstract val ndkDirectory: DirectoryProperty
|
||||
@InputDirectory get
|
||||
|
||||
abstract val inputDirectory: DirectoryProperty
|
||||
@InputDirectory get
|
||||
|
||||
abstract val outputDirectory: DirectoryProperty
|
||||
@OutputDirectory get
|
||||
|
||||
@TaskAction
|
||||
fun build() {
|
||||
val input = inputDirectory.file
|
||||
val output = outputDirectory.file
|
||||
|
||||
val config = config.get()
|
||||
val environment = Environment(ndkDirectory.file, config.minSdkVersion)
|
||||
|
||||
val tags = listOf("without_gvisor", "without_system") +
|
||||
(if (config.debug) listOf("debug") else emptyList()) +
|
||||
(if (config.premium) listOf("premium") else emptyList())
|
||||
|
||||
Command.ofGoModuleTidy(input).exec()
|
||||
|
||||
config.abis.forEach {
|
||||
Command.ofGoRun(
|
||||
"make/make.go",
|
||||
listOf("bridge", "native", "build", "android", it.goArch),
|
||||
input.resolve("tun2socket/bridge"),
|
||||
environment.ofLwipBuild(it)
|
||||
).exec()
|
||||
|
||||
Command.ofGoBuild(
|
||||
"c-shared",
|
||||
output.resolve("${it.value}/libclash.so"),
|
||||
tags,
|
||||
!config.debug,
|
||||
input,
|
||||
environment.ofCoreBuild(it)
|
||||
).exec()
|
||||
}
|
||||
}
|
||||
|
||||
private val DirectoryProperty.file: File
|
||||
get() = get().asFile
|
||||
}
|
||||
@@ -1,84 +0,0 @@
|
||||
package com.github.kr328.clash.tools
|
||||
|
||||
import org.gradle.api.GradleException
|
||||
import java.io.File
|
||||
import kotlin.concurrent.thread
|
||||
|
||||
class Command(
|
||||
private val command: Array<String>,
|
||||
workingDir: File,
|
||||
environments: Map<String, String>
|
||||
) {
|
||||
private val processBuilder: ProcessBuilder = ProcessBuilder(*command)
|
||||
.redirectErrorStream(true)
|
||||
.directory(workingDir)
|
||||
.apply { environment().putAll(environments) }
|
||||
|
||||
fun exec() {
|
||||
val process = processBuilder.start()
|
||||
|
||||
thread {
|
||||
process.inputStream.copyTo(System.out)
|
||||
}
|
||||
|
||||
val result = process.waitFor()
|
||||
|
||||
if (result != 0) {
|
||||
throw GradleException("exec ${command.joinToString(" ")}: exit with $result")
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun ofGoModuleTidy(workingDir: File): Command {
|
||||
return Command(arrayOf("go", "mod", "tidy"), workingDir, System.getenv())
|
||||
}
|
||||
|
||||
fun ofGoBuild(
|
||||
mode: String,
|
||||
output: File,
|
||||
tags: List<String>,
|
||||
strip: Boolean,
|
||||
workingDir: File,
|
||||
environments: Map<String, String>
|
||||
): Command {
|
||||
val command = mutableListOf("go", "build")
|
||||
|
||||
// go build mode
|
||||
command += "-buildmode"
|
||||
command += mode
|
||||
|
||||
// output file
|
||||
command += "-o"
|
||||
command += output.absolutePath
|
||||
|
||||
// trim path prefix
|
||||
command += "-trimpath"
|
||||
|
||||
if (tags.isNotEmpty()) {
|
||||
command += "-tags"
|
||||
command += tags.joinToString(",")
|
||||
}
|
||||
|
||||
if (strip) {
|
||||
command += "-ldflags"
|
||||
command += "-s -w"
|
||||
}
|
||||
|
||||
return Command(command.toTypedArray(), workingDir, environments)
|
||||
}
|
||||
|
||||
fun ofGoRun(
|
||||
file: String,
|
||||
args: List<String>,
|
||||
workingDir: File,
|
||||
environments: Map<String, String>
|
||||
): Command {
|
||||
val command = mutableListOf("go", "run")
|
||||
|
||||
command += file
|
||||
command += args
|
||||
|
||||
return Command(command.toTypedArray(), workingDir, environments)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
package com.github.kr328.clash.tools
|
||||
|
||||
import org.apache.tools.ant.taskdefs.condition.Os
|
||||
import org.gradle.api.GradleException
|
||||
import java.io.File
|
||||
|
||||
class Environment(
|
||||
private val ndkDirectory: File,
|
||||
private val minSdkVersion: Int,
|
||||
) {
|
||||
fun ofCoreBuild(abi: NativeAbi): Map<String, String> {
|
||||
val host = when {
|
||||
Os.isFamily(Os.FAMILY_WINDOWS) ->
|
||||
"windows"
|
||||
Os.isFamily(Os.FAMILY_MAC) ->
|
||||
"darwin"
|
||||
Os.isFamily(Os.FAMILY_UNIX) ->
|
||||
"linux"
|
||||
else ->
|
||||
throw GradleException("Unsupported host: ${System.getProperty("os.name")}")
|
||||
}
|
||||
|
||||
val compiler = ndkDirectory.resolve("toolchains/llvm/prebuilt/$host-x86_64/bin")
|
||||
.resolve("${abi.compiler}${minSdkVersion}-clang")
|
||||
|
||||
return mapOf(
|
||||
"CC" to compiler.absolutePath,
|
||||
"GOOS" to "android",
|
||||
"GOARCH" to abi.goArch,
|
||||
"GOARM" to abi.goArm,
|
||||
"CGO_ENABLED" to "1",
|
||||
"CFLAGS" to "-O3 -Werror",
|
||||
)
|
||||
}
|
||||
|
||||
fun ofLwipBuild(abi: NativeAbi): Map<String, String> {
|
||||
return mapOf(
|
||||
"CMAKE_SYSTEM_NAME" to "Android",
|
||||
"CMAKE_ANDROID_NDK" to ndkDirectory.absolutePath,
|
||||
"CMAKE_ANDROID_ARCH_ABI" to abi.value,
|
||||
"CMAKE_SYSTEM_VERSION" to minSdkVersion.toString()
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
package com.github.kr328.clash.tools
|
||||
|
||||
enum class NativeAbi(
|
||||
val value: String,
|
||||
val compiler: String,
|
||||
val goArch: String,
|
||||
val goArm: String
|
||||
) {
|
||||
ArmeabiV7a("armeabi-v7a", "armv7a-linux-androideabi", "arm", "7"),
|
||||
Arm64V8a("arm64-v8a", "aarch64-linux-android", "arm64", ""),
|
||||
X86("x86", "i686-linux-android", "386", ""),
|
||||
X64("x86_64", "x86_64-linux-android", "amd64", "");
|
||||
|
||||
companion object {
|
||||
fun parse(value: String): NativeAbi {
|
||||
return when (value) {
|
||||
ArmeabiV7a.value -> ArmeabiV7a
|
||||
Arm64V8a.value -> Arm64V8a
|
||||
X86.value -> X86
|
||||
X64.value -> X64
|
||||
else -> throw IllegalArgumentException("unsupported abi $value")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+3
-41
@@ -1,50 +1,12 @@
|
||||
plugins {
|
||||
id("com.android.library")
|
||||
kotlin("android")
|
||||
}
|
||||
|
||||
android {
|
||||
compileSdk = buildTargetSdkVersion
|
||||
|
||||
defaultConfig {
|
||||
minSdk = buildMinSdkVersion
|
||||
targetSdk = buildTargetSdkVersion
|
||||
|
||||
versionCode = buildVersionCode
|
||||
versionName = buildVersionName
|
||||
|
||||
consumerProguardFiles("consumer-rules.pro")
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
named("release") {
|
||||
isMinifyEnabled = false
|
||||
proguardFiles(
|
||||
getDefaultProguardFile("proguard-android-optimize.txt"),
|
||||
"proguard-rules.pro"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility = JavaVersion.VERSION_1_8
|
||||
targetCompatibility = JavaVersion.VERSION_1_8
|
||||
}
|
||||
|
||||
kotlinOptions {
|
||||
jvmTarget = "1.8"
|
||||
}
|
||||
id("com.android.library")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compileOnly(project(":hideapi"))
|
||||
|
||||
implementation(kotlin("stdlib-jdk7"))
|
||||
implementation("androidx.core:core-ktx:$ktxVersion")
|
||||
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutineVersion")
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
google()
|
||||
implementation(deps.kotlin.coroutine)
|
||||
implementation(deps.androidx.core)
|
||||
}
|
||||
@@ -1,8 +1,11 @@
|
||||
package com.github.kr328.clash.common
|
||||
|
||||
import android.app.Application
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.cancel
|
||||
|
||||
object Global {
|
||||
object Global : CoroutineScope by CoroutineScope(Dispatchers.IO) {
|
||||
val application: Application
|
||||
get() = application_
|
||||
|
||||
@@ -11,4 +14,8 @@ object Global {
|
||||
fun init(application: Application) {
|
||||
this.application_ = application
|
||||
}
|
||||
|
||||
fun destroy() {
|
||||
cancel()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="receive_clash_broadcasts">接收 Clash 廣播</string>
|
||||
<string name="receive_broadcasts_of_clash">接收來自 Clash 內部的廣播</string>
|
||||
</resources>
|
||||
+28
-54
@@ -1,12 +1,13 @@
|
||||
import com.github.kr328.golang.GolangPlugin
|
||||
import java.io.FileOutputStream
|
||||
import java.net.URL
|
||||
import java.time.Duration
|
||||
|
||||
plugins {
|
||||
id("com.android.library")
|
||||
kotlin("android")
|
||||
id("com.android.library")
|
||||
id("kotlinx-serialization")
|
||||
id("clash-build")
|
||||
id("golang-android")
|
||||
}
|
||||
|
||||
val geoipDatabaseUrl =
|
||||
@@ -14,60 +15,31 @@ val geoipDatabaseUrl =
|
||||
val geoipInvalidate = Duration.ofDays(7)!!
|
||||
val geoipOutput = buildDir.resolve("intermediates/golang_blob")
|
||||
|
||||
android {
|
||||
compileSdk = buildTargetSdkVersion
|
||||
|
||||
ndkVersion = buildNdkVersion
|
||||
|
||||
flavorDimensions(buildFlavor)
|
||||
|
||||
defaultConfig {
|
||||
minSdk = buildMinSdkVersion
|
||||
targetSdk = buildTargetSdkVersion
|
||||
|
||||
versionCode = buildVersionCode
|
||||
versionName = buildVersionName
|
||||
|
||||
consumerProguardFiles("consumer-rules.pro")
|
||||
|
||||
externalNativeBuild {
|
||||
cmake {
|
||||
abiFilters("arm64-v8a", "armeabi-v7a", "x86", "x86_64")
|
||||
arguments(
|
||||
"-DGO_SOURCE:STRING=$golangSource",
|
||||
"-DGO_OUTPUT:STRING=$golangBuild",
|
||||
"-DFLAVOR_NAME=$buildFlavor",
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
named("release") {
|
||||
isMinifyEnabled = false
|
||||
proguardFiles(
|
||||
getDefaultProguardFile("proguard-android-optimize.txt"),
|
||||
"proguard-rules.pro"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
productFlavors {
|
||||
golang {
|
||||
sourceSets {
|
||||
create("foss") {
|
||||
dimension = "foss"
|
||||
srcDir.set(file("src/foss/go"))
|
||||
}
|
||||
create("premium") {
|
||||
dimension = "premium"
|
||||
srcDir.set(file("src/premium/go"))
|
||||
}
|
||||
all {
|
||||
fileName.set("libclash.so")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility = JavaVersion.VERSION_1_8
|
||||
targetCompatibility = JavaVersion.VERSION_1_8
|
||||
}
|
||||
|
||||
kotlinOptions {
|
||||
jvmTarget = "1.8"
|
||||
android {
|
||||
productFlavors {
|
||||
all {
|
||||
externalNativeBuild {
|
||||
cmake {
|
||||
arguments("-DGO_SOURCE:STRING=${golang.sourceSets.getByName(name).srcDir.asFile.get()}")
|
||||
arguments("-DGO_OUTPUT:STRING=${GolangPlugin.outputDirOf(project, null, null)}")
|
||||
arguments("-DFLAVOR_NAME:STRING=$name")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
externalNativeBuild {
|
||||
@@ -78,12 +50,12 @@ android {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
api(project(":common"))
|
||||
implementation(project(":common"))
|
||||
|
||||
implementation(kotlin("stdlib-jdk7"))
|
||||
implementation("androidx.core:core-ktx:$ktxVersion")
|
||||
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutineVersion")
|
||||
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:$serializationVersion")
|
||||
implementation(deps.androidx.core)
|
||||
implementation(deps.kotlin.coroutine)
|
||||
implementation(deps.kotlin.serialization.json)
|
||||
}
|
||||
|
||||
repositories {
|
||||
@@ -108,6 +80,8 @@ task("downloadGeoipDatabase") {
|
||||
var GeoipDatabase []byte
|
||||
""".trimIndent()
|
||||
|
||||
outputs.dir(geoipOutput)
|
||||
|
||||
onlyIf {
|
||||
System.currentTimeMillis() - databaseFile.lastModified() > geoipInvalidate.toMillis()
|
||||
}
|
||||
|
||||
Generated
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Submodule
+1
Submodule core/src/foss/go/clash added at a918699140.
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -25,13 +25,12 @@ type Status struct {
|
||||
|
||||
var client = &http.Client{
|
||||
Transport: &http.Transport{
|
||||
// from http.DefaultTransport
|
||||
MaxIdleConns: 100,
|
||||
IdleConnTimeout: 90 * time.Second,
|
||||
DisableKeepAlives: true,
|
||||
TLSHandshakeTimeout: 10 * time.Second,
|
||||
ExpectContinueTimeout: 1 * time.Second,
|
||||
DialContext: dialer.DefaultTunnelDialer,
|
||||
},
|
||||
Timeout: 60 * time.Second,
|
||||
}
|
||||
|
||||
func openUrl(url string) (io.ReadCloser, error) {
|
||||
File renamed without changes.
File renamed without changes.
@@ -5,6 +5,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/Dreamacro/clash/log"
|
||||
"github.com/dlclark/regexp2"
|
||||
@@ -16,6 +17,11 @@ import (
|
||||
"github.com/Dreamacro/clash/dns"
|
||||
)
|
||||
|
||||
const (
|
||||
defaultHealthCheckUrl = "https://www.gstatic.com/generate_204"
|
||||
defaultHealthCheckInterval = time.Hour
|
||||
)
|
||||
|
||||
var processors = []processor{
|
||||
patchOverride,
|
||||
patchGeneral,
|
||||
@@ -88,7 +94,13 @@ func patchProviders(cfg *config.RawConfig, profileDir string) error {
|
||||
|
||||
func patchProxyGroup(cfg *config.RawConfig, _ string) error {
|
||||
for _, g := range cfg.ProxyGroup {
|
||||
g["lazy"] = false
|
||||
if _, exist := g["url"]; !exist {
|
||||
g["url"] = defaultHealthCheckUrl
|
||||
}
|
||||
|
||||
if _, exist := g["interval"]; !exist {
|
||||
g["interval"] = int(defaultHealthCheckInterval.Seconds())
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
+6
-2
@@ -2,7 +2,11 @@
|
||||
|
||||
package config
|
||||
|
||||
import "github.com/Dreamacro/clash/config"
|
||||
import (
|
||||
"io"
|
||||
|
||||
"github.com/Dreamacro/clash/config"
|
||||
)
|
||||
|
||||
func forEachProviders(rawCfg *config.RawConfig, fun func(index int, total int, key string, provider map[string]interface{})) {
|
||||
total := len(rawCfg.ProxyProvider)
|
||||
@@ -17,6 +21,6 @@ func forEachProviders(rawCfg *config.RawConfig, fun func(index int, total int, k
|
||||
|
||||
func destroyProviders(cfg *config.Config) {
|
||||
for _, p := range cfg.Providers {
|
||||
_ = p.Destroy()
|
||||
_ = p.(io.Closer).Close()
|
||||
}
|
||||
}
|
||||
File renamed without changes.
@@ -2,7 +2,6 @@ package core
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net"
|
||||
"syscall"
|
||||
|
||||
"cfa/blob"
|
||||
@@ -41,34 +40,13 @@ func Init(home, versionName string, platformVersion int) {
|
||||
return pkg, nil
|
||||
}
|
||||
|
||||
dialer.DialerHook = func(dialer *net.Dialer) error {
|
||||
dialer.Control = func(network, address string, c syscall.RawConn) error {
|
||||
return c.Control(func(fd uintptr) {
|
||||
app.MarkSocket(int(fd))
|
||||
})
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
dialer.ListenPacketHook = func(lc *net.ListenConfig, address string) (string, error) {
|
||||
lc.Control = func(network, address string, c syscall.RawConn) error {
|
||||
return c.Control(func(fd uintptr) {
|
||||
app.MarkSocket(int(fd))
|
||||
})
|
||||
}
|
||||
|
||||
if platform.ShouldBlockConnection() {
|
||||
return "", errBlocked
|
||||
}
|
||||
|
||||
return address, nil
|
||||
}
|
||||
|
||||
dialer.DialHook = func(dialer *net.Dialer, network string, ip net.IP) error {
|
||||
dialer.DefaultSocketHook = func(network, address string, conn syscall.RawConn) error {
|
||||
if platform.ShouldBlockConnection() {
|
||||
return errBlocked
|
||||
}
|
||||
|
||||
return nil
|
||||
return conn.Control(func(fd uintptr) {
|
||||
app.MarkSocket(int(fd))
|
||||
})
|
||||
}
|
||||
}
|
||||
File renamed without changes.
@@ -6,8 +6,8 @@ require (
|
||||
cfa/blob v0.0.0 // local generated
|
||||
github.com/Dreamacro/clash v0.0.0 // local
|
||||
github.com/dlclark/regexp2 v1.4.0
|
||||
github.com/kr328/tun2socket v0.0.0 // local
|
||||
github.com/miekg/dns v1.1.42
|
||||
github.com/kr328/tun2socket-lwip v0.0.0-20210911023118-0b4947e2a9c1
|
||||
github.com/miekg/dns v1.1.43
|
||||
github.com/oschwald/geoip2-golang v1.5.0
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
|
||||
gopkg.in/yaml.v2 v2.4.0
|
||||
@@ -15,6 +15,4 @@ require (
|
||||
|
||||
replace github.com/Dreamacro/clash => ./clash
|
||||
|
||||
replace github.com/kr328/tun2socket => ./tun2socket
|
||||
|
||||
replace cfa/blob => ../../../build/intermediates/golang_blob
|
||||
@@ -0,0 +1,114 @@
|
||||
github.com/Dreamacro/go-shadowsocks2 v0.1.7 h1:8CtbE1HoPPMfrQZGXmlluq6dO2lL31W6WRRE8fabc4Q=
|
||||
github.com/Dreamacro/go-shadowsocks2 v0.1.7/go.mod h1:8p5G4cAj5ZlXwUR+Ww63gfSikr8kvw8uw3TDwLAJpUc=
|
||||
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=
|
||||
github.com/dlclark/regexp2 v1.4.0 h1:F1rxgk7p4uKjwIQxBs9oAXe5CqrXlCduYEJvrF4u93E=
|
||||
github.com/dlclark/regexp2 v1.4.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
|
||||
github.com/fanliao/go-promise v0.0.0-20141029170127-1890db352a72/go.mod h1:PjfxuH4FZdUyfMdtBio2lsRr1AKEaVPwelzuHuh8Lqc=
|
||||
github.com/go-chi/chi/v5 v5.0.4/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
|
||||
github.com/go-chi/cors v1.2.0/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vzc58=
|
||||
github.com/go-chi/render v1.0.1/go.mod h1:pq4Rr7HbnsdaeHagklXub+p6Wd16Af5l9koip1OvJns=
|
||||
github.com/gofrs/uuid v4.0.0+incompatible h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPhW6m+TnJw=
|
||||
github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
|
||||
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/hugelgupf/socketpair v0.0.0-20190730060125-05d35a94e714/go.mod h1:2Goc3h8EklBH5mspfHFxBnEoURQCGzQQH1ga9Myjvis=
|
||||
github.com/insomniacslk/dhcp v0.0.0-20210827173440-b95caade3eac h1:IO6EfdRnPhxgKOsk9DbewdtQZHKZKnGlW7QCUttvNys=
|
||||
github.com/insomniacslk/dhcp v0.0.0-20210827173440-b95caade3eac/go.mod h1:h+MxyHxRg9NH3terB1nfRIUaQEcI0XOVkdR9LNBlp8E=
|
||||
github.com/jsimonetti/rtnetlink v0.0.0-20190606172950-9527aa82566a/go.mod h1:Oz+70psSo5OFh8DBl0Zv2ACw7Esh6pPUphlvZG9x7uw=
|
||||
github.com/jsimonetti/rtnetlink v0.0.0-20200117123717-f846d4f6c1f4/go.mod h1:WGuG/smIU4J/54PblvSbh+xvCZmpJnFgr3ds6Z55XMQ=
|
||||
github.com/jsimonetti/rtnetlink v0.0.0-20201009170750-9c6f07d100c1/go.mod h1:hqoO/u39cqLeBLebZ8fWdE96O7FxrAsRYhnVOdgHxok=
|
||||
github.com/jsimonetti/rtnetlink v0.0.0-20201110080708-d2c240429e6c/go.mod h1:huN4d1phzjhlOsNIjFsw2SVRbwIHj3fJDMEU2SDPTmg=
|
||||
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
||||
github.com/kr328/tun2socket-lwip v0.0.0-20210911023118-0b4947e2a9c1 h1:NJ5NyPHIok6hqdELvrI4bps5lLyfJtFmm0K4c9CB/WM=
|
||||
github.com/kr328/tun2socket-lwip v0.0.0-20210911023118-0b4947e2a9c1/go.mod h1:JFkXLCpLkNVvLMRkq3gexTlRHfzbqcP5HfFOoq5Mj+g=
|
||||
github.com/mdlayher/ethernet v0.0.0-20190606142754-0394541c37b7/go.mod h1:U6ZQobyTjI/tJyq2HG+i/dfSoFUt8/aZCM+GKtmFk/Y=
|
||||
github.com/mdlayher/netlink v0.0.0-20190409211403-11939a169225/go.mod h1:eQB3mZE4aiYnlUsyGGCOpPETfdQq4Jhsgf1fk3cwQaA=
|
||||
github.com/mdlayher/netlink v1.0.0/go.mod h1:KxeJAFOFLG6AjpyDkQ/iIhxygIUKD+vcwqcnu43w/+M=
|
||||
github.com/mdlayher/netlink v1.1.0/go.mod h1:H4WCitaheIsdF9yOYu8CFmCgQthAPIWZmcKp9uZHgmY=
|
||||
github.com/mdlayher/netlink v1.1.1/go.mod h1:WTYpFb/WTvlRJAyKhZL5/uy69TDDpHHu2VZmb2XgV7o=
|
||||
github.com/mdlayher/raw v0.0.0-20190606142536-fef19f00fc18/go.mod h1:7EpbotpCmVZcu+KCX4g9WaRNuu11uyhiW7+Le1dKawg=
|
||||
github.com/mdlayher/raw v0.0.0-20191009151244-50f2db8cc065/go.mod h1:7EpbotpCmVZcu+KCX4g9WaRNuu11uyhiW7+Le1dKawg=
|
||||
github.com/miekg/dns v1.1.43 h1:JKfpVSCB84vrAmHzyrsxB5NAr5kLoMXZArPSw7Qlgyg=
|
||||
github.com/miekg/dns v1.1.43/go.mod h1:+evo5L0630/F6ca/Z9+GAqzhjGyn8/c+TBaOyfEl0V4=
|
||||
github.com/oschwald/geoip2-golang v1.5.0 h1:igg2yQIrrcRccB1ytFXqBfOHCjXWIoMv85lVJ1ONZzw=
|
||||
github.com/oschwald/geoip2-golang v1.5.0/go.mod h1:xdvYt5xQzB8ORWFqPnqMwZpCpgNagttWdoZLlJQzg7s=
|
||||
github.com/oschwald/maxminddb-golang v1.8.0 h1:Uh/DSnGoxsyp/KYbY1AuP0tYEwfs0sCph9p/UMXK/Hk=
|
||||
github.com/oschwald/maxminddb-golang v1.8.0/go.mod h1:RXZtst0N6+FY/3qCNmZMBApR19cdQj43/NM9VkrNAis=
|
||||
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.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
|
||||
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/u-root/uio v0.0.0-20210528114334-82958018845c h1:BFvcl34IGnw8yvJi8hlqLFo9EshRInwWBs2M5fGWzQA=
|
||||
github.com/u-root/uio v0.0.0-20210528114334-82958018845c/go.mod h1:LpEX5FO/cB+WF4TYGY1V5qktpaZLkKkSegbr0V4eYXA=
|
||||
go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE=
|
||||
go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210317152858-513c2a44f670/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
||||
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 h1:HWj/xjIHfjYU5nVXpTM0s39J9CbLn7Cc5a7IC5rwsMQ=
|
||||
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190419010253-1f3472d942ba/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20191007182048-72f939374954/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210903162142-ad29c8ab022f h1:w6wWR0H+nyVpbSAQbzVEIACVyr/h8l/BEkY6Sokc7Eg=
|
||||
golang.org/x/net v0.0.0-20210903162142-ad29c8ab022f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190411185658-b44545bcd369/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190418153312-f0ce4c0180be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190606122018-79a91cf218c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191224085550-c709ea063b76/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201009025420-dfb3f7c4e634/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201101102859-da207088b7d1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210525143221-35b2ab0089ea/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210906170528-6f6e22806c34 h1:GkvMjFtXUmahfDtashnc1mnrCtuBVcwse5QV2lUk/tI=
|
||||
golang.org/x/sys v0.0.0-20210906170528-6f6e22806c34/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/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/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -0,0 +1,40 @@
|
||||
package proxy
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/Dreamacro/clash/listener/http"
|
||||
"github.com/Dreamacro/clash/tunnel"
|
||||
)
|
||||
|
||||
var listener *http.Listener
|
||||
var lock sync.Mutex
|
||||
|
||||
func Start(listen string) (listenAt string, err error) {
|
||||
lock.Lock()
|
||||
defer lock.Unlock()
|
||||
|
||||
stopLocked()
|
||||
|
||||
listener, err = http.NewWithAuthenticate(listen, tunnel.TCPIn(), false)
|
||||
if err == nil {
|
||||
listenAt = listener.Listener().Addr().String()
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func Stop() {
|
||||
lock.Lock()
|
||||
defer lock.Unlock()
|
||||
|
||||
stopLocked()
|
||||
}
|
||||
|
||||
func stopLocked() {
|
||||
if listener != nil {
|
||||
listener.Close()
|
||||
}
|
||||
|
||||
listener = nil
|
||||
}
|
||||
File renamed without changes.
File renamed without changes.
@@ -11,8 +11,6 @@ import (
|
||||
"cfa/tun"
|
||||
|
||||
"golang.org/x/sync/semaphore"
|
||||
|
||||
"github.com/Dreamacro/clash/log"
|
||||
)
|
||||
|
||||
type remoteTun struct {
|
||||
@@ -23,7 +21,7 @@ type remoteTun struct {
|
||||
}
|
||||
|
||||
func (t *remoteTun) markSocket(fd int) {
|
||||
_ = t.limit.Acquire(context.Background(), 1)
|
||||
_ = t.limit.Acquire(context.TODO(), 1)
|
||||
defer t.limit.Release(1)
|
||||
|
||||
if t.closed {
|
||||
@@ -34,7 +32,7 @@ func (t *remoteTun) markSocket(fd int) {
|
||||
}
|
||||
|
||||
func (t *remoteTun) querySocketUid(protocol int, source, target string) int {
|
||||
_ = t.limit.Acquire(context.Background(), 1)
|
||||
_ = t.limit.Acquire(context.TODO(), 1)
|
||||
defer t.limit.Release(1)
|
||||
|
||||
if t.closed {
|
||||
@@ -50,26 +48,27 @@ func (t *remoteTun) stop() {
|
||||
|
||||
t.closed = true
|
||||
|
||||
C.release_object(t.callback)
|
||||
app.ApplyTunContext(nil, nil)
|
||||
|
||||
log.Infoln("Android tun device destroyed")
|
||||
C.release_object(t.callback)
|
||||
}
|
||||
|
||||
//export startTun
|
||||
func startTun(fd, mtu C.int, dns C.c_string, callback unsafe.Pointer) C.int {
|
||||
func startTun(fd, mtu C.int, gateway, dns C.c_string, callback unsafe.Pointer) C.int {
|
||||
f := int(fd)
|
||||
m := int(mtu)
|
||||
g := C.GoString(gateway)
|
||||
d := C.GoString(dns)
|
||||
|
||||
remote := &remoteTun{callback: callback, closed: false, limit: semaphore.NewWeighted(4)}
|
||||
|
||||
if tun.Start(f, m, d) != nil {
|
||||
return 1
|
||||
}
|
||||
|
||||
app.ApplyTunContext(remote.markSocket, remote.querySocketUid)
|
||||
|
||||
log.Infoln("Android tun device created")
|
||||
if tun.Start(f, m, g, d, remote.stop) != nil {
|
||||
app.ApplyTunContext(nil, nil)
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package tun
|
||||
|
||||
import (
|
||||
"net"
|
||||
|
||||
"github.com/Dreamacro/clash/component/resolver"
|
||||
D "github.com/miekg/dns"
|
||||
)
|
||||
|
||||
func shouldHijackDns(dns net.IP, target net.IP, targetPort int) bool {
|
||||
if targetPort != 53 {
|
||||
return false
|
||||
}
|
||||
|
||||
return net.IPv4zero.Equal(dns) || target.Equal(dns)
|
||||
}
|
||||
|
||||
func relayDns(payload []byte) ([]byte, error) {
|
||||
msg := &D.Msg{}
|
||||
if err := msg.Unpack(payload); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
r, err := resolver.ServeMsg(msg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
r.SetRcode(msg, r.Rcode)
|
||||
|
||||
return r.Pack()
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package tun
|
||||
|
||||
import "github.com/Dreamacro/clash/log"
|
||||
|
||||
func (a *adapter) rx() {
|
||||
log.Infoln("[ATUN] Device rx started")
|
||||
defer log.Infoln("[ATUN] Device rx exited")
|
||||
defer a.once.Do(a.stop)
|
||||
defer a.close()
|
||||
|
||||
buf := make([]byte, a.mtu)
|
||||
|
||||
for {
|
||||
n, err := a.device.Read(buf)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
_, _ = a.stack.Link().Write(buf[:n])
|
||||
}
|
||||
}
|
||||
|
||||
func (a *adapter) tx() {
|
||||
log.Infoln("[ATUN] Device tx started")
|
||||
defer log.Infoln("[ATUN] Device tx exited")
|
||||
defer a.once.Do(a.stop)
|
||||
defer a.close()
|
||||
|
||||
buf := make([]byte, a.mtu)
|
||||
|
||||
for {
|
||||
n, err := a.stack.Link().Read(buf)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
_, _ = a.device.Write(buf[:n])
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
package tun
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"io"
|
||||
"net"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
C "github.com/Dreamacro/clash/constant"
|
||||
"github.com/Dreamacro/clash/context"
|
||||
"github.com/Dreamacro/clash/log"
|
||||
"github.com/Dreamacro/clash/tunnel"
|
||||
)
|
||||
|
||||
const defaultDnsReadTimeout = time.Second * 30
|
||||
|
||||
func (a *adapter) tcp() {
|
||||
log.Infoln("[ATUN] TCP listener started")
|
||||
defer log.Infoln("[ATUN] TCP listener exited")
|
||||
defer a.stack.Close()
|
||||
|
||||
accept:
|
||||
for {
|
||||
conn, err := a.stack.TCP().Accept()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
sAddr := conn.LocalAddr().(*net.TCPAddr)
|
||||
tAddr := conn.RemoteAddr().(*net.TCPAddr)
|
||||
|
||||
// handle dns messages
|
||||
if a.hijackTCPDNS(conn, tAddr) {
|
||||
continue
|
||||
}
|
||||
|
||||
// drop all connections connect to blocking list
|
||||
for _, b := range a.blocking {
|
||||
if b.Contains(tAddr.IP) {
|
||||
_ = conn.Close()
|
||||
|
||||
continue accept
|
||||
}
|
||||
}
|
||||
|
||||
metadata := &C.Metadata{
|
||||
NetWork: C.TCP,
|
||||
Type: C.SOCKS5,
|
||||
SrcIP: sAddr.IP,
|
||||
DstIP: tAddr.IP,
|
||||
SrcPort: strconv.Itoa(sAddr.Port),
|
||||
DstPort: strconv.Itoa(tAddr.Port),
|
||||
AddrType: C.AtypIPv4,
|
||||
Host: "",
|
||||
RawSrcAddr: sAddr,
|
||||
RawDstAddr: tAddr,
|
||||
}
|
||||
|
||||
tunnel.TCPIn() <- context.NewConnContext(conn, metadata)
|
||||
}
|
||||
}
|
||||
|
||||
func (a *adapter) hijackTCPDNS(conn net.Conn, tAddr *net.TCPAddr) bool {
|
||||
if !shouldHijackDns(a.dns, tAddr.IP, tAddr.Port) {
|
||||
return false
|
||||
}
|
||||
|
||||
go func() {
|
||||
defer conn.Close()
|
||||
|
||||
for {
|
||||
if err := conn.SetReadDeadline(time.Now().Add(defaultDnsReadTimeout)); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
var length uint16
|
||||
if binary.Read(conn, binary.BigEndian, &length) != nil {
|
||||
return
|
||||
}
|
||||
|
||||
data := make([]byte, length)
|
||||
|
||||
_, err := io.ReadFull(conn, data)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
rb, err := relayDns(data)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if binary.Write(conn, binary.BigEndian, uint16(len(rb))) != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if _, err := conn.Write(rb); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
return true
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
package tun
|
||||
|
||||
import (
|
||||
"net"
|
||||
"os"
|
||||
"strings"
|
||||
"sync"
|
||||
"syscall"
|
||||
|
||||
"github.com/kr328/tun2socket-lwip"
|
||||
)
|
||||
|
||||
type adapter struct {
|
||||
device *os.File
|
||||
stack tun2socket.Stack
|
||||
blocking []*net.IPNet
|
||||
dns net.IP
|
||||
mtu int
|
||||
once sync.Once
|
||||
stop func()
|
||||
}
|
||||
|
||||
var lock sync.Mutex
|
||||
var instance *adapter
|
||||
|
||||
func (a *adapter) close() {
|
||||
_ = a.stack.Close()
|
||||
_ = a.device.Close()
|
||||
}
|
||||
|
||||
func Start(fd, mtu int, dns string, blocking string, stop func()) error {
|
||||
lock.Lock()
|
||||
defer lock.Unlock()
|
||||
|
||||
if instance != nil {
|
||||
instance.close()
|
||||
}
|
||||
|
||||
_ = syscall.SetNonblock(fd, true)
|
||||
|
||||
device := os.NewFile(uintptr(fd), "/dev/tun")
|
||||
stack, err := tun2socket.NewStack(mtu)
|
||||
if err != nil {
|
||||
_ = device.Close()
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
dn := net.ParseIP(dns)
|
||||
|
||||
var blk []*net.IPNet
|
||||
|
||||
for _, b := range strings.Split(blocking, ";") {
|
||||
_, n, err := net.ParseCIDR(b)
|
||||
if err != nil {
|
||||
device.Close()
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
blk = append(blk, n)
|
||||
}
|
||||
|
||||
instance = &adapter{
|
||||
device: device,
|
||||
stack: stack,
|
||||
blocking: blk,
|
||||
dns: dn,
|
||||
mtu: mtu,
|
||||
once: sync.Once{},
|
||||
stop: stop,
|
||||
}
|
||||
|
||||
go instance.rx()
|
||||
go instance.tx()
|
||||
go instance.tcp()
|
||||
go instance.udp()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func Stop() {
|
||||
lock.Lock()
|
||||
defer lock.Unlock()
|
||||
|
||||
if instance != nil {
|
||||
instance.close()
|
||||
}
|
||||
|
||||
instance = nil
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
package tun
|
||||
|
||||
import (
|
||||
"net"
|
||||
|
||||
"github.com/Dreamacro/clash/adapter/inbound"
|
||||
"github.com/Dreamacro/clash/common/pool"
|
||||
C "github.com/Dreamacro/clash/constant"
|
||||
"github.com/Dreamacro/clash/log"
|
||||
"github.com/Dreamacro/clash/transport/socks5"
|
||||
"github.com/Dreamacro/clash/tunnel"
|
||||
|
||||
"github.com/kr328/tun2socket-lwip"
|
||||
)
|
||||
|
||||
type packet struct {
|
||||
stack tun2socket.Stack
|
||||
local *net.UDPAddr
|
||||
data []byte
|
||||
}
|
||||
|
||||
func (pkt *packet) Data() []byte {
|
||||
return pkt.data
|
||||
}
|
||||
|
||||
func (pkt *packet) WriteBack(b []byte, addr net.Addr) (n int, err error) {
|
||||
return pkt.stack.UDP().WriteTo(b, pkt.local, addr)
|
||||
}
|
||||
|
||||
func (pkt *packet) Drop() {
|
||||
pool.Put(pkt.data)
|
||||
}
|
||||
|
||||
func (pkt *packet) LocalAddr() net.Addr {
|
||||
return &net.UDPAddr{
|
||||
IP: pkt.local.IP,
|
||||
Port: pkt.local.Port,
|
||||
Zone: "",
|
||||
}
|
||||
}
|
||||
|
||||
func (a *adapter) udp() {
|
||||
log.Infoln("[ATUN] UDP receiver started")
|
||||
defer log.Infoln("[ATUN] UDP receiver exited")
|
||||
defer a.stack.Close()
|
||||
|
||||
read:
|
||||
for {
|
||||
buf := pool.Get(a.mtu)
|
||||
|
||||
n, lAddr, rAddr, err := a.stack.UDP().ReadFrom(buf)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
sAddr := lAddr.(*net.UDPAddr)
|
||||
tAddr := rAddr.(*net.UDPAddr)
|
||||
|
||||
// handle dns messages
|
||||
if a.hijackUDPDNS(buf[:n], sAddr, tAddr) {
|
||||
continue
|
||||
}
|
||||
|
||||
// drop all packet send to blocking list
|
||||
for _, b := range a.blocking {
|
||||
if b.Contains(tAddr.IP) {
|
||||
continue read
|
||||
}
|
||||
}
|
||||
|
||||
pkt := &packet{
|
||||
stack: a.stack,
|
||||
local: sAddr,
|
||||
data: buf[:n],
|
||||
}
|
||||
|
||||
tunnel.UDPIn() <- inbound.NewPacket(socks5.ParseAddrToSocksAddr(tAddr), pkt, C.SOCKS5)
|
||||
}
|
||||
}
|
||||
|
||||
func (a *adapter) hijackUDPDNS(pkt []byte, sAddr, tAddr *net.UDPAddr) bool {
|
||||
if !shouldHijackDns(a.dns, tAddr.IP, tAddr.Port) {
|
||||
return false
|
||||
}
|
||||
|
||||
go func() {
|
||||
answer, err := relayDns(pkt)
|
||||
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
_, _ = a.stack.UDP().WriteTo(answer, sAddr, tAddr)
|
||||
|
||||
pool.Put(pkt)
|
||||
}()
|
||||
|
||||
return true
|
||||
}
|
||||
File renamed without changes.
File renamed without changes.
+4
-4
@@ -3,9 +3,9 @@ package tunnel
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/Dreamacro/clash/adapters/outbound"
|
||||
"github.com/Dreamacro/clash/adapters/outboundgroup"
|
||||
"github.com/Dreamacro/clash/adapters/provider"
|
||||
"github.com/Dreamacro/clash/adapter"
|
||||
"github.com/Dreamacro/clash/adapter/outboundgroup"
|
||||
"github.com/Dreamacro/clash/constant/provider"
|
||||
"github.com/Dreamacro/clash/log"
|
||||
"github.com/Dreamacro/clash/tunnel"
|
||||
)
|
||||
@@ -19,7 +19,7 @@ func HealthCheck(name string) {
|
||||
return
|
||||
}
|
||||
|
||||
g, ok := p.(*outbound.Proxy).ProxyAdapter.(outboundgroup.ProxyGroup)
|
||||
g, ok := p.(*adapter.Proxy).ProxyAdapter.(outboundgroup.ProxyGroup)
|
||||
if !ok {
|
||||
log.Warnln("Request health check for `%s`: invalid type %s", name, p.Type().String())
|
||||
|
||||
File renamed without changes.
@@ -7,7 +7,8 @@ import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/Dreamacro/clash/adapters/provider"
|
||||
P "github.com/Dreamacro/clash/adapter/provider"
|
||||
"github.com/Dreamacro/clash/constant/provider"
|
||||
"github.com/Dreamacro/clash/tunnel"
|
||||
)
|
||||
|
||||
@@ -38,7 +39,7 @@ func QueryProviders() []*Provider {
|
||||
for _, p := range providers {
|
||||
updatedAt := time.Time{}
|
||||
|
||||
if s, ok := p.(provider.UpdatableProvider); ok {
|
||||
if s, ok := p.(P.UpdatableProvider); ok {
|
||||
updatedAt = s.UpdatedAt()
|
||||
}
|
||||
|
||||
+1
-1
@@ -7,7 +7,7 @@ import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/Dreamacro/clash/adapters/provider"
|
||||
"github.com/Dreamacro/clash/adapter/provider"
|
||||
"github.com/Dreamacro/clash/log"
|
||||
"github.com/Dreamacro/clash/tunnel"
|
||||
)
|
||||
@@ -4,12 +4,12 @@ import (
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/Dreamacro/clash/adapter"
|
||||
"github.com/dlclark/regexp2"
|
||||
|
||||
"github.com/Dreamacro/clash/adapters/outbound"
|
||||
"github.com/Dreamacro/clash/adapters/outboundgroup"
|
||||
"github.com/Dreamacro/clash/adapters/provider"
|
||||
"github.com/Dreamacro/clash/adapter/outboundgroup"
|
||||
C "github.com/Dreamacro/clash/constant"
|
||||
"github.com/Dreamacro/clash/constant/provider"
|
||||
"github.com/Dreamacro/clash/log"
|
||||
"github.com/Dreamacro/clash/tunnel"
|
||||
)
|
||||
@@ -60,7 +60,7 @@ func QueryProxyGroupNames(excludeNotSelectable bool) []string {
|
||||
return []string{}
|
||||
}
|
||||
|
||||
global := tunnel.Proxies()["GLOBAL"].(*outbound.Proxy).ProxyAdapter.(outboundgroup.ProxyGroup)
|
||||
global := tunnel.Proxies()["GLOBAL"].(*adapter.Proxy).ProxyAdapter.(outboundgroup.ProxyGroup)
|
||||
proxies := global.Providers()[0].Proxies()
|
||||
result := make([]string, 0, len(proxies)+1)
|
||||
|
||||
@@ -69,7 +69,7 @@ func QueryProxyGroupNames(excludeNotSelectable bool) []string {
|
||||
}
|
||||
|
||||
for _, p := range proxies {
|
||||
if _, ok := p.(*outbound.Proxy).ProxyAdapter.(outboundgroup.ProxyGroup); ok {
|
||||
if _, ok := p.(*adapter.Proxy).ProxyAdapter.(outboundgroup.ProxyGroup); ok {
|
||||
if !excludeNotSelectable || p.Type() == C.Selector {
|
||||
result = append(result, p.Name())
|
||||
}
|
||||
@@ -88,7 +88,7 @@ func QueryProxyGroup(name string, sortMode SortMode, uiSubtitlePattern *regexp2.
|
||||
return nil
|
||||
}
|
||||
|
||||
g, ok := p.(*outbound.Proxy).ProxyAdapter.(outboundgroup.ProxyGroup)
|
||||
g, ok := p.(*adapter.Proxy).ProxyAdapter.(outboundgroup.ProxyGroup)
|
||||
if !ok {
|
||||
log.Warnln("Query group `%s`: invalid type %s", name, p.Type().String())
|
||||
|
||||
@@ -136,7 +136,7 @@ func PatchSelector(selector, name string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
g, ok := p.(*outbound.Proxy).ProxyAdapter.(outboundgroup.ProxyGroup)
|
||||
g, ok := p.(*adapter.Proxy).ProxyAdapter.(outboundgroup.ProxyGroup)
|
||||
if !ok {
|
||||
log.Warnln("Patch selector `%s`: invalid type %s", selector, p.Type().String())
|
||||
|
||||
@@ -171,7 +171,7 @@ func collectProviders(providers []provider.ProxyProvider, uiSubtitlePattern *reg
|
||||
subtitle := px.Type().String()
|
||||
|
||||
if uiSubtitlePattern != nil {
|
||||
if _, ok := px.(*outbound.Proxy).ProxyAdapter.(outboundgroup.ProxyGroup); !ok {
|
||||
if _, ok := px.(*adapter.Proxy).ProxyAdapter.(outboundgroup.ProxyGroup); !ok {
|
||||
runes := []rune(name)
|
||||
match, err := uiSubtitlePattern.FindRunesMatch(runes)
|
||||
if err == nil && match != nil {
|
||||
File renamed without changes.
File renamed without changes.
@@ -1,6 +1,6 @@
|
||||
package tunnel
|
||||
|
||||
import "github.com/Dreamacro/clash/adapters/provider"
|
||||
import "github.com/Dreamacro/clash/adapter/provider"
|
||||
|
||||
func Suspend(s bool) {
|
||||
provider.Suspend(s)
|
||||
File renamed without changes.
@@ -2,6 +2,9 @@ cmake_minimum_required(VERSION 3.0)
|
||||
|
||||
project(clash-bridge C)
|
||||
|
||||
set(CMAKE_POSITION_INDEPENDENT_CODE on)
|
||||
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -O3")
|
||||
|
||||
set(GO_OUTPUT_BASE ${GO_OUTPUT}/${FLAVOR_NAME})
|
||||
|
||||
if ("${CMAKE_BUILD_TYPE}" STREQUAL "Debug")
|
||||
@@ -16,7 +19,6 @@ endif ()
|
||||
|
||||
include_directories("${GO_OUTPUT_BASE}/${CMAKE_ANDROID_ARCH_ABI}")
|
||||
include_directories("${GO_SOURCE}")
|
||||
include_directories("${GO_SOURCE}/tun2socket/bridge/native")
|
||||
|
||||
link_directories("${GO_OUTPUT_BASE}/${CMAKE_ANDROID_ARCH_ABI}")
|
||||
|
||||
|
||||
@@ -96,15 +96,17 @@ Java_com_github_kr328_clash_core_bridge_Bridge_nativeNotifyInstalledAppChanged(J
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_com_github_kr328_clash_core_bridge_Bridge_nativeStartTun(JNIEnv *env, jobject thiz, jint fd,
|
||||
jint mtu, jstring dns,
|
||||
Java_com_github_kr328_clash_core_bridge_Bridge_nativeStartTun(JNIEnv *env, jobject thiz,
|
||||
jint fd, jint mtu,
|
||||
jstring dns, jstring blocking,
|
||||
jobject cb) {
|
||||
TRACE_METHOD();
|
||||
|
||||
scoped_string _blocking = get_string(blocking);
|
||||
scoped_string _dns = get_string(dns);
|
||||
jobject _interface = new_global(cb);
|
||||
|
||||
startTun(fd, mtu, _dns, _interface);
|
||||
startTun(fd, mtu, _dns, _blocking, _interface);
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
|
||||
Submodule core/src/main/golang/clash deleted from 043e34f700.
@@ -1,108 +0,0 @@
|
||||
package proxy
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"net"
|
||||
"net/http"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
adapters "github.com/Dreamacro/clash/adapters/inbound"
|
||||
"github.com/Dreamacro/clash/log"
|
||||
"github.com/Dreamacro/clash/tunnel"
|
||||
)
|
||||
|
||||
const (
|
||||
LocalHttpTimeout = time.Millisecond * 100
|
||||
)
|
||||
|
||||
var listener *httpListener
|
||||
var lock sync.Mutex
|
||||
|
||||
type httpListener struct {
|
||||
net.Listener
|
||||
|
||||
closed bool
|
||||
}
|
||||
|
||||
func Start(listen string) (listenAt string, err error) {
|
||||
lock.Lock()
|
||||
defer lock.Unlock()
|
||||
|
||||
stopLocked()
|
||||
|
||||
l, err := net.Listen("tcp", listen)
|
||||
if err != nil {
|
||||
log.Errorln("Listen HTTP proxy at: %s: %s", listen, err.Error())
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
h := &httpListener{
|
||||
Listener: l,
|
||||
closed: false,
|
||||
}
|
||||
|
||||
listener = h
|
||||
|
||||
go func() {
|
||||
for !h.closed {
|
||||
conn, err := h.Accept()
|
||||
if err != nil {
|
||||
log.Warnln("Accept connection: %s", err.Error())
|
||||
continue
|
||||
}
|
||||
|
||||
_ = conn.(*net.TCPConn).SetKeepAlive(false)
|
||||
|
||||
h.handleConn(conn)
|
||||
}
|
||||
}()
|
||||
|
||||
return h.Addr().String(), nil
|
||||
}
|
||||
|
||||
func Stop() {
|
||||
lock.Lock()
|
||||
defer lock.Unlock()
|
||||
|
||||
stopLocked()
|
||||
}
|
||||
|
||||
func stopLocked() {
|
||||
if listener != nil {
|
||||
listener.closed = true
|
||||
_ = listener.Close()
|
||||
}
|
||||
|
||||
listener = nil
|
||||
}
|
||||
|
||||
func (l *httpListener) handleConn(conn net.Conn) {
|
||||
_ = conn.SetReadDeadline(time.Now().Add(LocalHttpTimeout))
|
||||
|
||||
br := bufio.NewReader(conn)
|
||||
request, err := http.ReadRequest(br)
|
||||
|
||||
_ = conn.SetReadDeadline(time.Time{})
|
||||
|
||||
if err != nil || request.URL.Host == "" {
|
||||
if err != nil {
|
||||
log.Warnln("HTTP Connection closed: %s", err.Error())
|
||||
}
|
||||
|
||||
_ = conn.Close()
|
||||
return
|
||||
}
|
||||
|
||||
if request.Method == http.MethodConnect {
|
||||
_, err := conn.Write([]byte("HTTP/1.1 200 Connection established\r\n\r\n"))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
tunnel.Add(adapters.NewHTTPS(request, conn))
|
||||
return
|
||||
}
|
||||
|
||||
tunnel.Add(adapters.NewHTTP(request, conn))
|
||||
}
|
||||
@@ -1,98 +0,0 @@
|
||||
package tun
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"io"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/Dreamacro/clash/component/resolver"
|
||||
"github.com/kr328/tun2socket/bridge"
|
||||
|
||||
D "github.com/miekg/dns"
|
||||
)
|
||||
|
||||
const defaultDnsReadTimeout = time.Second * 30
|
||||
|
||||
func shouldHijackDns(dns net.IP, target net.IP, targetPort int) bool {
|
||||
if targetPort != 53 {
|
||||
return false
|
||||
}
|
||||
|
||||
return net.IPv4zero.Equal(dns) || target.Equal(dns)
|
||||
}
|
||||
|
||||
func hijackUDPDns(pkt []byte, lAddr, rAddr net.Addr, udp bridge.UDP) {
|
||||
go func() {
|
||||
answer, err := relayDnsPacket(pkt)
|
||||
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
_, _ = udp.WriteTo(answer, lAddr, rAddr)
|
||||
|
||||
recycleUDP(pkt)
|
||||
}()
|
||||
}
|
||||
|
||||
func hijackTCPDns(conn net.Conn) {
|
||||
go func() {
|
||||
defer conn.Close()
|
||||
|
||||
for {
|
||||
if err := conn.SetReadDeadline(time.Now().Add(defaultDnsReadTimeout)); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
var length uint16
|
||||
if binary.Read(conn, binary.BigEndian, &length) != nil {
|
||||
return
|
||||
}
|
||||
|
||||
data := make([]byte, length)
|
||||
|
||||
_, err := io.ReadFull(conn, data)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
rb, err := relayDnsPacket(data)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if binary.Write(conn, binary.BigEndian, uint16(len(rb))) != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if _, err := conn.Write(rb); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func relayDnsPacket(payload []byte) ([]byte, error) {
|
||||
msg := &D.Msg{}
|
||||
if err := msg.Unpack(payload); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
r, err := resolver.ServeMsg(msg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, ans := range r.Answer {
|
||||
header := ans.Header()
|
||||
|
||||
if header.Class == D.ClassINET && (header.Rrtype == D.TypeA || header.Rrtype == D.TypeAAAA) {
|
||||
header.Ttl = 1
|
||||
}
|
||||
}
|
||||
|
||||
r.SetRcode(msg, r.Rcode)
|
||||
|
||||
return r.Pack()
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
package tun
|
||||
|
||||
import (
|
||||
"net"
|
||||
"strconv"
|
||||
|
||||
C "github.com/Dreamacro/clash/constant"
|
||||
CTX "github.com/Dreamacro/clash/context"
|
||||
"github.com/Dreamacro/clash/tunnel"
|
||||
)
|
||||
|
||||
func handleTCP(conn net.Conn, source *net.TCPAddr, target *net.TCPAddr) {
|
||||
metadata := &C.Metadata{
|
||||
NetWork: C.TCP,
|
||||
Type: C.SOCKS,
|
||||
SrcIP: source.IP,
|
||||
DstIP: target.IP,
|
||||
SrcPort: strconv.Itoa(source.Port),
|
||||
DstPort: strconv.Itoa(target.Port),
|
||||
AddrType: C.AtypIPv4,
|
||||
Host: "",
|
||||
RawSrcAddr: source,
|
||||
RawDstAddr: target,
|
||||
}
|
||||
|
||||
tunnel.Add(CTX.NewConnContext(conn, metadata))
|
||||
}
|
||||
@@ -1,152 +0,0 @@
|
||||
package tun
|
||||
|
||||
import (
|
||||
"net"
|
||||
"os"
|
||||
"sync"
|
||||
|
||||
"github.com/kr328/tun2socket"
|
||||
)
|
||||
|
||||
type context struct {
|
||||
device *os.File
|
||||
stack tun2socket.Stack
|
||||
}
|
||||
|
||||
var lock sync.Mutex
|
||||
var tun *context
|
||||
|
||||
func (ctx *context) close() {
|
||||
_ = ctx.stack.Close()
|
||||
_ = ctx.device.Close()
|
||||
}
|
||||
|
||||
func Start(fd, mtu int, dns string) error {
|
||||
lock.Lock()
|
||||
defer lock.Unlock()
|
||||
|
||||
stopLocked()
|
||||
|
||||
dnsIP := net.ParseIP(dns)
|
||||
|
||||
device := os.NewFile(uintptr(fd), "/dev/tun")
|
||||
|
||||
stack, err := tun2socket.NewStack(mtu)
|
||||
if err != nil {
|
||||
_ = device.Close()
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
ctx := &context{
|
||||
device: device,
|
||||
stack: stack,
|
||||
}
|
||||
|
||||
go func() {
|
||||
// device -> lwip
|
||||
|
||||
defer ctx.close()
|
||||
|
||||
buf := make([]byte, mtu)
|
||||
|
||||
for {
|
||||
n, err := device.Read(buf)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
_, _ = stack.Link().Write(buf[:n])
|
||||
}
|
||||
}()
|
||||
|
||||
go func() {
|
||||
// lwip -> device
|
||||
|
||||
defer ctx.close()
|
||||
|
||||
buf := make([]byte, mtu)
|
||||
|
||||
for {
|
||||
n, err := stack.Link().Read(buf)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
_, _ = device.Write(buf[:n])
|
||||
}
|
||||
}()
|
||||
|
||||
go func() {
|
||||
// lwip tcp
|
||||
|
||||
defer ctx.close()
|
||||
|
||||
for {
|
||||
conn, err := stack.TCP().Accept()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
source := conn.LocalAddr().(*net.TCPAddr)
|
||||
target := conn.RemoteAddr().(*net.TCPAddr)
|
||||
|
||||
if shouldHijackDns(dnsIP, target.IP, target.Port) {
|
||||
hijackTCPDns(conn)
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
handleTCP(conn, source, target)
|
||||
}
|
||||
}()
|
||||
|
||||
go func() {
|
||||
// lwip udp
|
||||
|
||||
defer ctx.close()
|
||||
|
||||
for {
|
||||
buf := allocUDP(mtu)
|
||||
|
||||
n, lAddr, rAddr, err := stack.UDP().ReadFrom(buf)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
source := lAddr.(*net.UDPAddr)
|
||||
target := rAddr.(*net.UDPAddr)
|
||||
|
||||
if n == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
if shouldHijackDns(dnsIP, target.IP, target.Port) {
|
||||
hijackUDPDns(buf[:n], source, target, stack.UDP())
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
handleUDP(buf[:n], source, target, stack.UDP())
|
||||
}
|
||||
}()
|
||||
|
||||
tun = ctx
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func Stop() {
|
||||
lock.Lock()
|
||||
defer lock.Unlock()
|
||||
|
||||
stopLocked()
|
||||
}
|
||||
|
||||
func stopLocked() {
|
||||
if tun != nil {
|
||||
tun.close()
|
||||
}
|
||||
|
||||
tun = nil
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
package tun
|
||||
|
||||
import (
|
||||
"net"
|
||||
|
||||
"github.com/Dreamacro/clash/transport/socks5"
|
||||
"github.com/kr328/tun2socket/bridge"
|
||||
|
||||
adapters "github.com/Dreamacro/clash/adapters/inbound"
|
||||
"github.com/Dreamacro/clash/common/pool"
|
||||
C "github.com/Dreamacro/clash/constant"
|
||||
"github.com/Dreamacro/clash/tunnel"
|
||||
)
|
||||
|
||||
type udpPacket struct {
|
||||
source *net.UDPAddr
|
||||
data []byte
|
||||
udp bridge.UDP
|
||||
}
|
||||
|
||||
func (u *udpPacket) Data() []byte {
|
||||
return u.data
|
||||
}
|
||||
|
||||
func (u *udpPacket) WriteBack(b []byte, addr net.Addr) (n int, err error) {
|
||||
return u.udp.WriteTo(b, u.source, addr)
|
||||
}
|
||||
|
||||
func (u *udpPacket) Drop() {
|
||||
recycleUDP(u.data)
|
||||
}
|
||||
|
||||
func (u *udpPacket) LocalAddr() net.Addr {
|
||||
return &net.UDPAddr{
|
||||
IP: u.source.IP,
|
||||
Port: int(u.source.Port),
|
||||
Zone: "",
|
||||
}
|
||||
}
|
||||
|
||||
func handleUDP(payload []byte, source *net.UDPAddr, target *net.UDPAddr, udp bridge.UDP) {
|
||||
pkt := &udpPacket{
|
||||
source: source,
|
||||
data: payload,
|
||||
udp: udp,
|
||||
}
|
||||
|
||||
adapter := adapters.NewPacket(socks5.ParseAddrToSocksAddr(target), pkt, C.SOCKS)
|
||||
|
||||
tunnel.AddPacket(adapter)
|
||||
}
|
||||
|
||||
func allocUDP(size int) []byte {
|
||||
return pool.Get(size)
|
||||
}
|
||||
|
||||
func recycleUDP(payload []byte) {
|
||||
_ = pool.Put(payload)
|
||||
}
|
||||
Submodule core/src/main/golang/tun2socket deleted from 46a71ab799.
@@ -62,10 +62,11 @@ object Clash {
|
||||
fd: Int,
|
||||
mtu: Int,
|
||||
dns: String,
|
||||
blocking: String,
|
||||
markSocket: (Int) -> Boolean,
|
||||
querySocketUid: (protocol: Int, source: InetSocketAddress, target: InetSocketAddress) -> Int
|
||||
) {
|
||||
Bridge.nativeStartTun(fd, mtu, dns, object : TunInterface {
|
||||
Bridge.nativeStartTun(fd, mtu, dns, blocking, object : TunInterface {
|
||||
override fun markSocket(fd: Int) {
|
||||
markSocket(fd)
|
||||
}
|
||||
@@ -218,7 +219,7 @@ object Clash {
|
||||
return Channel<LogMessage>(32).apply {
|
||||
Bridge.nativeSubscribeLogcat(object : LogcatInterface {
|
||||
override fun received(jsonPayload: String) {
|
||||
offer(Json.decodeFromString(LogMessage.serializer(), jsonPayload))
|
||||
trySend(Json.decodeFromString(LogMessage.serializer(), jsonPayload))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ object Bridge {
|
||||
external fun nativeQueryTrafficTotal(): Long
|
||||
external fun nativeNotifyDnsChanged(dnsList: String)
|
||||
external fun nativeNotifyInstalledAppChanged(uidList: String)
|
||||
external fun nativeStartTun(fd: Int, mtu: Int, dns: String, cb: TunInterface)
|
||||
external fun nativeStartTun(fd: Int, mtu: Int, dns: String, blocking: String, cb: TunInterface)
|
||||
external fun nativeStopTun()
|
||||
external fun nativeStartHttp(listenAt: String): String?
|
||||
external fun nativeStopHttp()
|
||||
|
||||
@@ -80,7 +80,10 @@ data class ConfigurationOverride(
|
||||
var fakeIpFilter: List<String>? = null,
|
||||
|
||||
@SerialName("fallback-filter")
|
||||
val fallbackFilter: DnsFallbackFilter = DnsFallbackFilter()
|
||||
val fallbackFilter: DnsFallbackFilter = DnsFallbackFilter(),
|
||||
|
||||
@SerialName("nameserver-policy")
|
||||
var nameserverPolicy: Map<String, String>? = null,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
|
||||
+13
-61
@@ -1,70 +1,22 @@
|
||||
plugins {
|
||||
id("com.android.library")
|
||||
kotlin("android")
|
||||
kotlin("kapt")
|
||||
}
|
||||
|
||||
android {
|
||||
compileSdk = buildTargetSdkVersion
|
||||
|
||||
flavorDimensions(buildFlavor)
|
||||
|
||||
defaultConfig {
|
||||
minSdk = buildMinSdkVersion
|
||||
targetSdk = buildTargetSdkVersion
|
||||
|
||||
versionCode = buildVersionCode
|
||||
versionName = buildVersionName
|
||||
|
||||
consumerProguardFiles("consumer-rules.pro")
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
named("release") {
|
||||
isMinifyEnabled = false
|
||||
proguardFiles(
|
||||
getDefaultProguardFile("proguard-android-optimize.txt"),
|
||||
"proguard-rules.pro"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
productFlavors {
|
||||
create("foss") {
|
||||
dimension = "foss"
|
||||
}
|
||||
create("premium") {
|
||||
dimension = "premium"
|
||||
}
|
||||
}
|
||||
|
||||
buildFeatures {
|
||||
dataBinding = true
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility = JavaVersion.VERSION_1_8
|
||||
targetCompatibility = JavaVersion.VERSION_1_8
|
||||
}
|
||||
|
||||
kotlinOptions {
|
||||
jvmTarget = "1.8"
|
||||
}
|
||||
id("com.android.library")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
api(project(":common"))
|
||||
api(project(":core"))
|
||||
api(project(":service"))
|
||||
implementation(project(":common"))
|
||||
implementation(project(":core"))
|
||||
implementation(project(":service"))
|
||||
|
||||
implementation(kotlin("stdlib-jdk7"))
|
||||
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutineVersion")
|
||||
implementation("androidx.core:core-ktx:$ktxVersion")
|
||||
implementation("androidx.appcompat:appcompat:$appcompatVersion")
|
||||
implementation("androidx.activity:activity:$activityVersion")
|
||||
implementation("com.google.android.material:material:$materialVersion")
|
||||
implementation("androidx.coordinatorlayout:coordinatorlayout:$coordinatorlayoutVersion")
|
||||
implementation("androidx.recyclerview:recyclerview:$recyclerviewVersion")
|
||||
implementation("androidx.fragment:fragment:$fragmentVersion")
|
||||
implementation("androidx.viewpager2:viewpager2:$viewpagerVersion")
|
||||
implementation(deps.kotlin.coroutine)
|
||||
implementation(deps.androidx.core)
|
||||
implementation(deps.androidx.appcompat)
|
||||
implementation(deps.androidx.activity)
|
||||
implementation(deps.androidx.coordinator)
|
||||
implementation(deps.androidx.recyclerview)
|
||||
implementation(deps.androidx.fragment)
|
||||
implementation(deps.androidx.viewpager)
|
||||
implementation(deps.google.material)
|
||||
}
|
||||
@@ -236,7 +236,7 @@ class OverrideSettingsDesign(
|
||||
summary = R.string.sideload_geoip_summary
|
||||
) {
|
||||
clicked {
|
||||
requests.offer(Request.EditSideloadGeoip)
|
||||
requests.trySend(Request.EditSideloadGeoip)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -378,6 +378,15 @@ class OverrideSettingsDesign(
|
||||
configure = dnsDependencies::add,
|
||||
)
|
||||
|
||||
editableTextMap(
|
||||
value = configuration.dns::nameserverPolicy,
|
||||
keyAdapter = TextAdapter.String,
|
||||
valueAdapter = TextAdapter.String,
|
||||
title = R.string.name_server_policy,
|
||||
placeholder = R.string.dont_modify,
|
||||
configure = dnsDependencies::add,
|
||||
)
|
||||
|
||||
dns.listener?.onChanged()
|
||||
}
|
||||
|
||||
@@ -385,6 +394,6 @@ class OverrideSettingsDesign(
|
||||
}
|
||||
|
||||
fun requestClear() {
|
||||
requests.offer(Request.ResetOverride)
|
||||
requests.trySend(Request.ResetOverride)
|
||||
}
|
||||
}
|
||||
@@ -32,48 +32,48 @@ class ProxyMenu(
|
||||
R.id.not_selectable -> {
|
||||
uiStore.proxyExcludeNotSelectable = item.isChecked
|
||||
|
||||
requests.offer(ProxyDesign.Request.ReLaunch)
|
||||
requests.trySend(ProxyDesign.Request.ReLaunch)
|
||||
}
|
||||
R.id.single -> {
|
||||
uiStore.proxySingleLine = true
|
||||
|
||||
updateConfig()
|
||||
|
||||
requests.offer(ProxyDesign.Request.ReloadAll)
|
||||
requests.trySend(ProxyDesign.Request.ReloadAll)
|
||||
}
|
||||
R.id.multiple -> {
|
||||
uiStore.proxySingleLine = false
|
||||
|
||||
updateConfig()
|
||||
|
||||
requests.offer(ProxyDesign.Request.ReloadAll)
|
||||
requests.trySend(ProxyDesign.Request.ReloadAll)
|
||||
}
|
||||
R.id.default_ -> {
|
||||
uiStore.proxySort = ProxySort.Default
|
||||
|
||||
requests.offer(ProxyDesign.Request.ReloadAll)
|
||||
requests.trySend(ProxyDesign.Request.ReloadAll)
|
||||
}
|
||||
R.id.name -> {
|
||||
uiStore.proxySort = ProxySort.Title
|
||||
|
||||
requests.offer(ProxyDesign.Request.ReloadAll)
|
||||
requests.trySend(ProxyDesign.Request.ReloadAll)
|
||||
}
|
||||
R.id.delay -> {
|
||||
uiStore.proxySort = ProxySort.Delay
|
||||
|
||||
requests.offer(ProxyDesign.Request.ReloadAll)
|
||||
requests.trySend(ProxyDesign.Request.ReloadAll)
|
||||
}
|
||||
R.id.dont_modify -> {
|
||||
requests.offer(ProxyDesign.Request.PatchMode(null))
|
||||
requests.trySend(ProxyDesign.Request.PatchMode(null))
|
||||
}
|
||||
R.id.direct_mode -> {
|
||||
requests.offer(ProxyDesign.Request.PatchMode(TunnelState.Mode.Direct))
|
||||
requests.trySend(ProxyDesign.Request.PatchMode(TunnelState.Mode.Direct))
|
||||
}
|
||||
R.id.global_mode -> {
|
||||
requests.offer(ProxyDesign.Request.PatchMode(TunnelState.Mode.Global))
|
||||
requests.trySend(ProxyDesign.Request.PatchMode(TunnelState.Mode.Global))
|
||||
}
|
||||
R.id.rule_mode -> {
|
||||
requests.offer(ProxyDesign.Request.PatchMode(TunnelState.Mode.Rule))
|
||||
requests.trySend(ProxyDesign.Request.PatchMode(TunnelState.Mode.Rule))
|
||||
}
|
||||
else -> return false
|
||||
}
|
||||
|
||||
@@ -93,7 +93,7 @@ class AccessControlDesign(
|
||||
binding.surface = dialog.surface
|
||||
binding.mainList.applyLinearAdapter(context, adapter)
|
||||
binding.keywordView.addTextChangedListener {
|
||||
filter.offer(Unit)
|
||||
filter.trySend(Unit)
|
||||
}
|
||||
binding.closeView.setOnClickListener {
|
||||
dialog.dismiss()
|
||||
|
||||
@@ -38,7 +38,7 @@ class ApkBrokenDesign(context: Context) : Design<ApkBrokenDesign.Request>(contex
|
||||
summary = R.string.google_play_url
|
||||
) {
|
||||
clicked {
|
||||
requests.offer(Request(context.getString(R.string.google_play_url)))
|
||||
requests.trySend(Request(context.getString(R.string.google_play_url)))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,7 +47,7 @@ class ApkBrokenDesign(context: Context) : Design<ApkBrokenDesign.Request>(contex
|
||||
summary = R.string.github_releases_url
|
||||
) {
|
||||
clicked {
|
||||
requests.offer(Request(context.getString(R.string.github_releases_url)))
|
||||
requests.trySend(Request(context.getString(R.string.github_releases_url)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loaded 100 of 141 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user