mirror of
https://github.com/MetaCubeX/ClashMetaForAndroid.git
synced 2026-05-09 18:11:26 +08:00
Compare commits
47 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 |
28
.github/workflows/build-unsigned.yaml
vendored
28
.github/workflows/build-unsigned.yaml
vendored
@@ -1,5 +1,25 @@
|
||||
name: Build Unsigned
|
||||
on: [push, pull_request]
|
||||
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
|
||||
@@ -15,10 +35,8 @@ jobs:
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: 1.16
|
||||
go-version: 1.17
|
||||
- name: Setup Android SDK
|
||||
uses: android-actions/setup-android@v2
|
||||
- name: Setup Cmake & Ninja
|
||||
uses: lukka/get-cmake@latest
|
||||
- name: Build
|
||||
run: ./gradlew --no-daemon app:assembleRelease
|
||||
run: ./gradlew --no-daemon app:assembleFossRelease
|
||||
6
.gitignore
vendored
6
.gitignore
vendored
@@ -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
|
||||
|
||||
10
.gitmodules
vendored
10
.gitmodules
vendored
@@ -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
|
||||
|
||||
20
README.md
20
README.md
@@ -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/`
|
||||
|
||||
@@ -1,140 +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 localFile = rootProject.file("local.properties")
|
||||
if (localFile.exists()) {
|
||||
val appCenterKey = localFile.inputStream()
|
||||
.use { Properties().apply { load(it) } }
|
||||
.getProperty("appcenter.key", null)
|
||||
|
||||
if (appCenterKey != null) {
|
||||
buildConfigField("String", "APP_CENTER_KEY", "\"$appCenterKey\"")
|
||||
} else {
|
||||
buildConfigField("String", "APP_CENTER_KEY", "null")
|
||||
}
|
||||
} else {
|
||||
buildConfigField("String", "APP_CENTER_KEY", "null")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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:$coreVersion")
|
||||
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"])
|
||||
}
|
||||
26
app/proguard-rules.pro
vendored
26
app/proguard-rules.pro
vendored
@@ -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) {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -8,7 +8,8 @@ object SystemLogcat {
|
||||
"Go",
|
||||
"DEBUG",
|
||||
"AndroidRuntime",
|
||||
"ClashForAndroid"
|
||||
"ClashForAndroid",
|
||||
"LwIP",
|
||||
)
|
||||
|
||||
fun dumpCrash(): String {
|
||||
|
||||
@@ -15,7 +15,7 @@ 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
|
||||
@@ -56,10 +56,10 @@ object Remote {
|
||||
|
||||
while (true) {
|
||||
if (visible.receive()) {
|
||||
services.bind()
|
||||
service.bind()
|
||||
broadcasts.register()
|
||||
} else {
|
||||
services.unbind()
|
||||
service.unbind()
|
||||
broadcasts.unregister()
|
||||
}
|
||||
}
|
||||
|
||||
64
app/src/main/java/com/github/kr328/clash/remote/Service.kt
Normal file
64
app/src/main/java/com/github/kr328/clash/remote/Service.kt
Normal file
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
116
build.gradle.kts
116
build.gradle.kts
@@ -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 = 204003
|
||||
const val buildVersionName = "2.4.3"
|
||||
|
||||
const val buildMinSdkVersion = 21
|
||||
const val buildTargetSdkVersion = 30
|
||||
|
||||
const val buildNdkVersion = "22.1.7171670"
|
||||
|
||||
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.5.0"
|
||||
const val roomVersion = "2.3.0"
|
||||
const val coreVersion = "1.5.0"
|
||||
const val appcompatVersion = "1.3.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.4"
|
||||
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")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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:$coreVersion")
|
||||
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutineVersion")
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
google()
|
||||
implementation(deps.kotlin.coroutine)
|
||||
implementation(deps.androidx.core)
|
||||
}
|
||||
|
||||
5
common/src/main/res/values-zh-rTW/strings.xml
Normal file
5
common/src/main/res/values-zh-rTW/strings.xml
Normal file
@@ -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>
|
||||
@@ -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:$coreVersion")
|
||||
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()
|
||||
}
|
||||
|
||||
1
core/src/foss/go/clash
Submodule
1
core/src/foss/go/clash
Submodule
Submodule core/src/foss/go/clash added at a918699140
@@ -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) {
|
||||
@@ -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
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
@@ -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))
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
114
core/src/foss/go/go.sum
Normal file
114
core/src/foss/go/go.sum
Normal file
@@ -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=
|
||||
40
core/src/foss/go/proxy/http.go
Normal file
40
core/src/foss/go/proxy/http.go
Normal file
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
32
core/src/foss/go/tun/dns.go
Normal file
32
core/src/foss/go/tun/dns.go
Normal file
@@ -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()
|
||||
}
|
||||
39
core/src/foss/go/tun/link.go
Normal file
39
core/src/foss/go/tun/link.go
Normal file
@@ -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])
|
||||
}
|
||||
}
|
||||
105
core/src/foss/go/tun/tcp.go
Normal file
105
core/src/foss/go/tun/tcp.go
Normal file
@@ -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
|
||||
}
|
||||
91
core/src/foss/go/tun/tun.go
Normal file
91
core/src/foss/go/tun/tun.go
Normal file
@@ -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
|
||||
}
|
||||
99
core/src/foss/go/tun/udp.go
Normal file
99
core/src/foss/go/tun/udp.go
Normal file
@@ -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
|
||||
}
|
||||
@@ -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())
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
@@ -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)
|
||||
@@ -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 958ecb352f
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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:$coreVersion")
|
||||
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)
|
||||
}
|
||||
|
||||
@@ -70,6 +70,13 @@ class NetworkSettingsDesign(
|
||||
configure = vpnDependencies::add,
|
||||
)
|
||||
|
||||
switch(
|
||||
value = srvStore::blockLoopback,
|
||||
title = R.string.block_loopback,
|
||||
summary = R.string.block_loopback_summary,
|
||||
configure = vpnDependencies::add,
|
||||
)
|
||||
|
||||
if (Build.VERSION.SDK_INT >= 29) {
|
||||
switch(
|
||||
value = srvStore::systemProxy,
|
||||
|
||||
@@ -211,4 +211,6 @@
|
||||
<string name="sources">源代碼</string>
|
||||
<string name="clash_core">Clash 核心</string>
|
||||
<string name="name_server_policy">Name Server 策略</string>
|
||||
<string name="block_loopback">阻止本地迴環</string>
|
||||
<string name="block_loopback_summary">阻止本地迴環連接</string>
|
||||
</resources>
|
||||
@@ -1,95 +1,95 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="access_control_packages_summary">為應用配置訪問權限</string>
|
||||
<string name="access_control_packages_summary">為軟體設定訪問權限</string>
|
||||
<string name="about">關於</string>
|
||||
<string name="access_control_mode">訪問控制模式</string>
|
||||
<string name="access_control_packages">訪問控制應用包列表</string>
|
||||
<string name="append_system_dns">追加系統 DNS</string>
|
||||
<string name="application_broken">應用損壞</string>
|
||||
<string name="access_control_packages">訪問控制軟體套件清單</string>
|
||||
<string name="append_system_dns">追加作業系統 DNS</string>
|
||||
<string name="application_broken">軟體損毀</string>
|
||||
<string name="application_name">Clash for Android</string>
|
||||
<string name="auto_update">自動更新</string>
|
||||
<string name="behavior">行為</string>
|
||||
<string name="bypass_private_network">繞過私有網絡</string>
|
||||
<string name="bypass_private_network_summary">繞過私有網絡地址</string>
|
||||
<string name="bypass_private_network">繞過私有網路</string>
|
||||
<string name="bypass_private_network_summary">繞過私有網路位址</string>
|
||||
<string name="cancel">取消</string>
|
||||
<string name="clash_logcat">Clash 日誌捕捉工具</string>
|
||||
<string name="create_profile">創建配置</string>
|
||||
<string name="dark_mode">暗黑模式</string>
|
||||
<string name="default_">默認</string>
|
||||
<string name="delay">延遲</string>
|
||||
<string name="clash_logcat">Clash 日誌檔採集工具</string>
|
||||
<string name="create_profile">建立設定檔</string>
|
||||
<string name="dark_mode">深色模式</string>
|
||||
<string name="default_">預設</string>
|
||||
<string name="delay">延時</string>
|
||||
<string name="delete">刪除</string>
|
||||
<string name="delete_all_logs">刪除所有日誌</string>
|
||||
<string name="delete_all_logs_warn">所有歷史日誌將丟失</string>
|
||||
<string name="detail">詳情</string>
|
||||
<string name="delete_all_logs">刪除所有日誌檔</string>
|
||||
<string name="delete_all_logs_warn">所有歷史日誌檔將丟失</string>
|
||||
<string name="detail">內容</string>
|
||||
<string name="direct_mode">直連模式</string>
|
||||
<string name="disabled">已禁用</string>
|
||||
<string name="dns_hijacking">DNS 劫持</string>
|
||||
<string name="dns_hijacking_summary">處理所有 DNS 數據包</string>
|
||||
<string name="dns_hijacking_summary">處理所有 DNS 封包</string>
|
||||
<string name="duplicate">複製</string>
|
||||
<string name="edit">編輯</string>
|
||||
<string name="empty_name">空名稱</string>
|
||||
<string name="exit_without_save">退出而不保存</string>
|
||||
<string name="empty_name">空白名稱</string>
|
||||
<string name="exit_without_save">退出而不儲存</string>
|
||||
<string name="exit_without_save_warning">所有變更將會丟失</string>
|
||||
<string name="export">導出</string>
|
||||
<string name="export">匯出</string>
|
||||
<string name="external">外部</string>
|
||||
<string name="file">文件</string>
|
||||
<string name="file_exported">文件已導出</string>
|
||||
<string name="file">檔案</string>
|
||||
<string name="file_exported">檔案已匯出</string>
|
||||
<string name="format_minutes">%d 分鐘</string>
|
||||
<string name="format_profile_activated">%s 已激活</string>
|
||||
<string name="format_profile_activated">%s 已啟用</string>
|
||||
<string name="format_traffic_forwarded">%s 已轉發</string>
|
||||
<string name="global_mode">全局模式</string>
|
||||
<string name="global_mode">全域性模式</string>
|
||||
<string name="history">歷史</string>
|
||||
<string name="import_from_file">從文件導入</string>
|
||||
<string name="import_from_url">從 URL 導入</string>
|
||||
<string name="interface_">界面</string>
|
||||
<string name="invalid_url">無效的 URL</string>
|
||||
<string name="import_from_file">從檔案匯入</string>
|
||||
<string name="import_from_url">從 URL 匯入</string>
|
||||
<string name="interface_">介面</string>
|
||||
<string name="invalid_url">無效 URL</string>
|
||||
<string name="launch_name">Clash</string>
|
||||
<string name="logcat">Logcat</string>
|
||||
<string name="logs">日誌</string>
|
||||
<string name="mode">模式</string>
|
||||
<string name="name">名稱</string>
|
||||
<string name="network">網絡</string>
|
||||
<string name="new_profile">新配置</string>
|
||||
<string name="not_selected">未選擇</string>
|
||||
<string name="network">網路</string>
|
||||
<string name="new_profile">新設定檔</string>
|
||||
<string name="not_selected">未選取</string>
|
||||
<string name="ok">確認</string>
|
||||
<string name="profile">配置</string>
|
||||
<string name="profile_name">配置名稱</string>
|
||||
<string name="profiles">配置</string>
|
||||
<string name="profile">設定檔</string>
|
||||
<string name="profile_name">設定檔名稱</string>
|
||||
<string name="profiles">設定檔</string>
|
||||
<string name="properties">參數</string>
|
||||
<string name="proxy">代理</string>
|
||||
<string name="recently">近期</string>
|
||||
<string name="route_system_traffic">自動路由系統流量</string>
|
||||
<string name="routing_via_vpn_service">通過 VpnService 自動路由所有系統流量</string>
|
||||
<string name="proxy">Proxy</string>
|
||||
<string name="recently">最近</string>
|
||||
<string name="route_system_traffic">自動轉發作業系統流量</string>
|
||||
<string name="routing_via_vpn_service">通過 VpnService 自動轉發所有作業系統流量</string>
|
||||
<string name="rule_mode">規則模式</string>
|
||||
<string name="running">運行中</string>
|
||||
<string name="settings">設置</string>
|
||||
<string name="running">運作中</string>
|
||||
<string name="settings">設定</string>
|
||||
<string name="show_traffic">顯示流量</string>
|
||||
<string name="show_traffic_summary">在通知中自動刷新流量</string>
|
||||
<string name="allow_clash_auto_restart">允許 Clash 自動重啟</string>
|
||||
<string name="auto_restart">自動重啟</string>
|
||||
<string name="show_traffic_summary">在通知中自動重新整理流量</string>
|
||||
<string name="allow_clash_auto_restart">允許 Clash 自動重新啟動</string>
|
||||
<string name="auto_restart">自動重新啟動</string>
|
||||
<string name="stopped">已停止</string>
|
||||
<string name="help">幫助</string>
|
||||
<string name="tap_to_start">點此啟動</string>
|
||||
<string name="update">更新</string>
|
||||
<string name="url">URL</string>
|
||||
<string name="vpn_service_options">VpnService 選項</string>
|
||||
<string name="options_unavailable">選項在 Clash 運行時不可用</string>
|
||||
<string name="search">查找</string>
|
||||
<string name="system_apps">系統應用</string>
|
||||
<string name="options_unavailable">選項在 Clash 運作時不可用</string>
|
||||
<string name="search">尋找</string>
|
||||
<string name="system_apps">作業系統軟體</string>
|
||||
<string name="update_time">更新時間</string>
|
||||
<string name="package_name">應用包名稱</string>
|
||||
<string name="package_name">軟體套件名稱</string>
|
||||
<string name="install_time">安裝時間</string>
|
||||
<string name="clash_for_android">Clash for Android</string>
|
||||
<string name="feedback">反饋</string>
|
||||
<string name="feedback">回饋</string>
|
||||
<string name="github_issues">Github Issues</string>
|
||||
<string name="tips_properties"><![CDATA[僅接受 <strong>Clash 配置文件</strong>(包含<strong>代理</strong>/<strong>規則</strong>)]]></string>
|
||||
<string name="tips_properties"><![CDATA[僅接受 <strong>Clash 設定檔</strong>(包含<strong>Proxy</strong>/<strong>規則</strong>)]]></string>
|
||||
<string name="loading">載入中</string>
|
||||
<string name="tips_help"><![CDATA[Clash for Android 是一個<strong>免費軟件</strong>並且我們<strong>不</strong>為其提供任何服務, <strong>請務必不要反饋非應用自身引起的問題</strong>]]></string>
|
||||
<string name="donate">捐贈</string>
|
||||
<string name="allow_all_apps">允許所有應用</string>
|
||||
<string name="allow_selected_apps">僅允許已選擇的應用</string>
|
||||
<string name="deny_selected_apps">不允許已選擇的應用</string>
|
||||
<string name="no_profile_selected">沒有選擇配置文件</string>
|
||||
<string name="tips_help"><![CDATA[Clash for Android 是一個<strong>免費軟體</strong>並且我們<strong>不</strong>為其提供任何服務, <strong>請務必不要回報非軟體自身引起的問題</strong>]]></string>
|
||||
<string name="donate">抖內</string>
|
||||
<string name="allow_all_apps">允許所有軟體</string>
|
||||
<string name="allow_selected_apps">僅允許已選取的軟體</string>
|
||||
<string name="deny_selected_apps">不允許已選取的軟體</string>
|
||||
<string name="no_profile_selected">沒有選取設定檔</string>
|
||||
<string name="copied">已複製</string>
|
||||
<string name="script_mode">腳本模式</string>
|
||||
<string name="google_play">Google Play</string>
|
||||
@@ -97,22 +97,22 @@
|
||||
<string name="select_all">全選</string>
|
||||
<string name="select_invert">反選</string>
|
||||
<string name="select_none">清除</string>
|
||||
<string name="app">應用</string>
|
||||
<string name="follow_system_android_10">跟隨系統 (Android 10+)</string>
|
||||
<string name="always_dark">總是暗黑模式</string>
|
||||
<string name="always_light">總是明亮模式</string>
|
||||
<string name="app">軟體</string>
|
||||
<string name="follow_system_android_10">跟隨作業系統 (Android 10+)</string>
|
||||
<string name="always_dark">總是深色模式</string>
|
||||
<string name="always_light">總是淺色模式</string>
|
||||
<string name="service">服務</string>
|
||||
<string name="accept_http_content">僅接受 http(s) 和 content 類型</string>
|
||||
<string name="at_least_15_minutes">至少 15 分鐘</string>
|
||||
<string name="override">覆寫</string>
|
||||
<string name="general">常規</string>
|
||||
<string name="general">一般</string>
|
||||
<string name="dns">DNS</string>
|
||||
<string name="http_port">HTTP 端口</string>
|
||||
<string name="socks_port">Socks 端口</string>
|
||||
<string name="mixed_port">複合端口</string>
|
||||
<string name="allow_lan">允許來自局域網的連接</string>
|
||||
<string name="bind_address">監聽地址</string>
|
||||
<string name="log_level">日誌級別</string>
|
||||
<string name="http_port">HTTP 埠</string>
|
||||
<string name="socks_port">Socks 埠</string>
|
||||
<string name="mixed_port">複合埠</string>
|
||||
<string name="allow_lan">允許來自區域網路的連結</string>
|
||||
<string name="bind_address">監聽位址</string>
|
||||
<string name="log_level">日誌檔級別</string>
|
||||
<string name="ipv6">IPv6</string>
|
||||
<string name="hosts">Hosts</string>
|
||||
<string name="enabled">已啟用</string>
|
||||
@@ -130,85 +130,87 @@
|
||||
<string name="fakeip_filter">FakeIP 過濾器</string>
|
||||
<string name="geoip_fallback">GeoIP Fallback</string>
|
||||
<string name="ipcidr_fallback">IPCIDR Fallback</string>
|
||||
<string name="use_built_in">使用內置</string>
|
||||
<string name="use_built_in">使用內建</string>
|
||||
<string name="mapping">Real-IP 至 域名映射</string>
|
||||
<string name="fakeip">Fake-IP 至 域名映射</string>
|
||||
<string name="sort">排序</string>
|
||||
<string name="layout">佈局</string>
|
||||
<string name="single">單列</string>
|
||||
<string name="multiple">多列</string>
|
||||
<string name="not_selectable">不可選擇</string>
|
||||
<string name="single">單欄</string>
|
||||
<string name="multiple">多欄</string>
|
||||
<string name="not_selectable">不可選取</string>
|
||||
<string name="providers">外部資源</string>
|
||||
<string name="unavailable">不可用</string>
|
||||
<string name="_new">新建</string>
|
||||
<string name="_new">新增</string>
|
||||
<string name="value">值</string>
|
||||
<string name="listen">監聽</string>
|
||||
<string name="files">文件</string>
|
||||
<string name="browse_files">瀏覽文件</string>
|
||||
<string name="browse_configuration_providers">瀏覽配置文件和外部資源</string>
|
||||
<string name="rename">重命名</string>
|
||||
<string name="file_name">文件名</string>
|
||||
<string name="format_type_unsaved">%s (未保存)</string>
|
||||
<string name="files">檔案</string>
|
||||
<string name="browse_files">瀏覽檔案</string>
|
||||
<string name="browse_configuration_providers">瀏覽設定檔和外部資源</string>
|
||||
<string name="rename">重新命名</string>
|
||||
<string name="file_name">檔名</string>
|
||||
<string name="format_type_unsaved">%s (未儲存)</string>
|
||||
<string name="format_minutes_ago">%d 分鐘前</string>
|
||||
<string name="format_hours_ago">%d 小時前</string>
|
||||
<string name="format_days_ago">%d 天前</string>
|
||||
<string name="format_months_ago">%d 月前</string>
|
||||
<string name="format_years_ago">%d 年前</string>
|
||||
<string name="system_proxy">系統代理</string>
|
||||
<string name="system_proxy_summary">為 VpnService 附加 HTTP 代理</string>
|
||||
<string name="system_proxy">作業系統 Proxy</string>
|
||||
<string name="system_proxy_summary">為 VpnService 附加 HTTP Proxy</string>
|
||||
<string name="dont_modify">不修改</string>
|
||||
<string name="redirect_port">Redirect 端口</string>
|
||||
<string name="tproxy_port">TProxy 端口</string>
|
||||
<string name="reset">重置</string>
|
||||
<string name="redirect_port">Redirect 埠</string>
|
||||
<string name="tproxy_port">TProxy 埠</string>
|
||||
<string name="reset">重設</string>
|
||||
<string name="use_hosts">使用 Hosts</string>
|
||||
<string name="authentication">認證</string>
|
||||
<string name="domain_fallback">域名 Fallback</string>
|
||||
<string name="empty">置空</string>
|
||||
<string name="import_from_clipboard">從剪切板導入</string>
|
||||
<string name="export_to_clipboard">導出至剪切板</string>
|
||||
<string name="import_from_clipboard">從剪貼簿匯入</string>
|
||||
<string name="export_to_clipboard">匯出至剪貼簿</string>
|
||||
<string name="auto_update_minutes">自動更新 (分鐘)</string>
|
||||
<string name="profile_url">配置 URL</string>
|
||||
<string name="profile_url">設定檔 URL</string>
|
||||
<string name="should_not_be_blank">不能為空</string>
|
||||
<string name="format_fetching_configuration">正在從 %s 下載配置文件</string>
|
||||
<string name="format_fetching_configuration">正在從 %s 下載設定檔</string>
|
||||
<string name="format_fetching_provider">正在下載外部資源 %s</string>
|
||||
<string name="initializing">正在初始化</string>
|
||||
<string name="verifying">正在校驗</string>
|
||||
<string name="sideload_geoip">旁加載 GEOIP</string>
|
||||
<string name="sideload_geoip_summary">外部 GEOIP 數據庫</string>
|
||||
<string name="sideload_geoip">旁載入 GEOIP</string>
|
||||
<string name="sideload_geoip_summary">外部 GEOIP 資料庫</string>
|
||||
<string name="force_enable">強制啟用</string>
|
||||
<string name="document">文檔</string>
|
||||
<string name="document">文件</string>
|
||||
<string name="clash_wiki">Clash Wiki</string>
|
||||
<string name="invalid_file_name">非法的文件名稱</string>
|
||||
<string name="reset_override_settings">重置覆寫設置</string>
|
||||
<string name="reset_override_settings_message">所有的覆寫設置將會被擦除</string>
|
||||
<string name="invalid_file_name">不規範檔案名稱</string>
|
||||
<string name="reset_override_settings">重置覆寫設定</string>
|
||||
<string name="reset_override_settings_message">所有的覆寫設定將會被抹除</string>
|
||||
<string name="key">鍵</string>
|
||||
<string name="more">更多</string>
|
||||
<string name="save">保存</string>
|
||||
<string name="delay_test">延遲測試</string>
|
||||
<string name="save">儲存</string>
|
||||
<string name="delay_test">延時測試</string>
|
||||
<string name="format_provider_type">%1$s(%2$s)</string>
|
||||
<string name="rule">規則</string>
|
||||
<string name="http">HTTP</string>
|
||||
<string name="compatible">兼容</string>
|
||||
<string name="compatible">相容</string>
|
||||
<string name="format_update_provider_failure">更新 %1$s: %2$s</string>
|
||||
<string name="update_all">更新全部</string>
|
||||
<string name="proxy_empty_tips">沒有可以顯示的組</string>
|
||||
<string name="proxy_empty_tips">沒有可以顯示的分組</string>
|
||||
<string name="reverse">反轉</string>
|
||||
<string name="close">關閉</string>
|
||||
<string name="keyword">關鍵詞</string>
|
||||
<string name="invalid_log_file">無效的日誌文件</string>
|
||||
<string name="application_crashed">應用崩潰</string>
|
||||
<string name="application_broken_tips">應用缺少必要的運行組件,這通常由於下載不完整的 apk 導致。</string>
|
||||
<string name="keyword">關鍵字</string>
|
||||
<string name="invalid_log_file">無效的日誌檔</string>
|
||||
<string name="application_crashed">軟體崩潰</string>
|
||||
<string name="application_broken_tips">軟體缺少必要的運作元件,這通常是由於下載了不完整的 apk 而導致。</string>
|
||||
<string name="reinstall">重新安裝</string>
|
||||
<string name="github_releases">Github Releases</string>
|
||||
<string name="unable_to_start_vpn">無法啟動 VPN 組件</string>
|
||||
<string name="request_donate_tips">如果您覺得本應用對您有幫助歡迎在 [幫助] 中給予開發者一點捐贈</string>
|
||||
<string name="request_donate">捐贈</string>
|
||||
<string name="version_updated">應用已更新</string>
|
||||
<string name="version_updated_tips">設置已被清除,舊的配置文件需要再次保存。</string>
|
||||
<string name="active_unsaved_tips">配置文件需要在激活之前保存</string>
|
||||
<string name="mode_switch_tips">僅在本次會話中有效</string>
|
||||
<string name="import_">導入</string>
|
||||
<string name="sources">源代碼</string>
|
||||
<string name="unable_to_start_vpn">無法啟動 VPN 元件</string>
|
||||
<string name="request_donate_tips">如果您覺得本軟體對您有幫助歡迎在 [幫助] 中給予開發者一點抖內</string>
|
||||
<string name="request_donate">抖內</string>
|
||||
<string name="version_updated">軟體已更新</string>
|
||||
<string name="version_updated_tips">設定已被清除,舊版設定檔需要再次儲存。</string>
|
||||
<string name="active_unsaved_tips">設定檔需要在啟用之前儲存</string>
|
||||
<string name="mode_switch_tips">僅在本次工作階段中有效</string>
|
||||
<string name="import_">匯入</string>
|
||||
<string name="sources">原始碼</string>
|
||||
<string name="clash_core">Clash 核心</string>
|
||||
<string name="name_server_policy">Name Server 策略</string>
|
||||
</resources>
|
||||
<string name="block_loopback">阻止本地迴環</string>
|
||||
<string name="block_loopback_summary">阻止本地迴環連結</string>
|
||||
</resources>
|
||||
|
||||
@@ -211,4 +211,6 @@
|
||||
<string name="sources">源代码</string>
|
||||
<string name="clash_core">Clash 核心</string>
|
||||
<string name="name_server_policy">Name Server 策略</string>
|
||||
<string name="block_loopback">阻止本地回环</string>
|
||||
<string name="block_loopback_summary">阻止本地回环连接</string>
|
||||
</resources>
|
||||
@@ -119,6 +119,8 @@
|
||||
<string name="bypass_private_network_summary">Bypass private network addresses</string>
|
||||
<string name="dns_hijacking">DNS Hijacking</string>
|
||||
<string name="dns_hijacking_summary">Handle all dns packet</string>
|
||||
<string name="block_loopback">Block Loopback</string>
|
||||
<string name="block_loopback_summary">Block loopback connections</string>
|
||||
<string name="system_proxy">System Proxy</string>
|
||||
<string name="system_proxy_summary">Attach http proxy to VpnService</string>
|
||||
<string name="access_control_mode">Access Control Mode</string>
|
||||
|
||||
@@ -238,7 +238,7 @@ class OverrideSettingsDesign(
|
||||
summary = R.string.sideload_geoip_summary
|
||||
) {
|
||||
clicked {
|
||||
requests.offer(Request.EditSideloadGeoip)
|
||||
requests.trySend(Request.EditSideloadGeoip)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -379,7 +379,7 @@ class OverrideSettingsDesign(
|
||||
placeholder = R.string.dont_modify,
|
||||
configure = dnsDependencies::add,
|
||||
)
|
||||
|
||||
|
||||
editableTextMap(
|
||||
value = configuration.dns::nameserverPolicy,
|
||||
keyAdapter = TextAdapter.String,
|
||||
@@ -396,6 +396,6 @@ class OverrideSettingsDesign(
|
||||
}
|
||||
|
||||
fun requestClear() {
|
||||
requests.offer(Request.ResetOverride)
|
||||
requests.trySend(Request.ResetOverride)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,51 +32,51 @@ 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))
|
||||
}
|
||||
R.id.script_mode -> {
|
||||
requests.offer(ProxyDesign.Request.PatchMode(TunnelState.Mode.Script))
|
||||
requests.trySend(ProxyDesign.Request.PatchMode(TunnelState.Mode.Script))
|
||||
}
|
||||
else -> return false
|
||||
}
|
||||
|
||||
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Binary file not shown.
4
gradle/wrapper/gradle-wrapper.properties
vendored
4
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -1,6 +1,6 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-all.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-all.zip
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionSha256Sum=13bf8d3cf8eeeb5770d19741a59bde9bd966dd78d17f1bbad787a05ef19d1c2d
|
||||
distributionSha256Sum=a8da5b02437a60819cad23e10fc7e9cf32bcb57029d9cb277e26eeff76ce014b
|
||||
269
gradlew
vendored
269
gradlew
vendored
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env sh
|
||||
#!/bin/sh
|
||||
|
||||
#
|
||||
# Copyright 2015 the original author or authors.
|
||||
# Copyright ? 2015-2021 the original authors.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
@@ -17,67 +17,101 @@
|
||||
#
|
||||
|
||||
##############################################################################
|
||||
##
|
||||
## Gradle start up script for UN*X
|
||||
##
|
||||
#
|
||||
# Gradle start up script for POSIX generated by Gradle.
|
||||
#
|
||||
# Important for running:
|
||||
#
|
||||
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
|
||||
# noncompliant, but you have some other compliant shell such as ksh or
|
||||
# bash, then to run this script, type that shell name before the whole
|
||||
# command line, like:
|
||||
#
|
||||
# ksh Gradle
|
||||
#
|
||||
# Busybox and similar reduced shells will NOT work, because this script
|
||||
# requires all of these POSIX shell features:
|
||||
# * functions;
|
||||
# * expansions ?$var?, ?${var}?, ?${var:-default}?, ?${var+SET}?,
|
||||
# ?${var#prefix}?, ?${var%suffix}?, and ?$( cmd )?;
|
||||
# * compound commands having a testable exit status, especially ?case?;
|
||||
# * various built-in commands including ?command?, ?set?, and ?ulimit?.
|
||||
#
|
||||
# Important for patching:
|
||||
#
|
||||
# (2) This script targets any POSIX shell, so it avoids extensions provided
|
||||
# by Bash, Ksh, etc; in particular arrays are avoided.
|
||||
#
|
||||
# The "traditional" practice of packing multiple parameters into a
|
||||
# space-separated string is a well documented source of bugs and security
|
||||
# problems, so this is (mostly) avoided, by progressively accumulating
|
||||
# options in "$@", and eventually passing that to Java.
|
||||
#
|
||||
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
|
||||
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
|
||||
# see the in-line comments for details.
|
||||
#
|
||||
# There are tweaks for specific operating systems such as AIX, CygWin,
|
||||
# Darwin, MinGW, and NonStop.
|
||||
#
|
||||
# (3) This script is generated from the Groovy template
|
||||
# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||
# within the Gradle project.
|
||||
#
|
||||
# You can find Gradle at https://github.com/gradle/gradle/.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
# Attempt to set APP_HOME
|
||||
|
||||
# Resolve links: $0 may be a link
|
||||
PRG="$0"
|
||||
# Need this for relative symlinks.
|
||||
while [ -h "$PRG" ] ; do
|
||||
ls=`ls -ld "$PRG"`
|
||||
link=`expr "$ls" : '.*-> \(.*\)$'`
|
||||
if expr "$link" : '/.*' > /dev/null; then
|
||||
PRG="$link"
|
||||
else
|
||||
PRG=`dirname "$PRG"`"/$link"
|
||||
fi
|
||||
app_path=$0
|
||||
|
||||
# Need this for daisy-chained symlinks.
|
||||
while
|
||||
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
|
||||
[ -h "$app_path" ]
|
||||
do
|
||||
ls=$( ls -ld "$app_path" )
|
||||
link=${ls#*' -> '}
|
||||
case $link in #(
|
||||
/*) app_path=$link ;; #(
|
||||
*) app_path=$APP_HOME$link ;;
|
||||
esac
|
||||
done
|
||||
SAVED="`pwd`"
|
||||
cd "`dirname \"$PRG\"`/" >/dev/null
|
||||
APP_HOME="`pwd -P`"
|
||||
cd "$SAVED" >/dev/null
|
||||
|
||||
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
|
||||
|
||||
APP_NAME="Gradle"
|
||||
APP_BASE_NAME=`basename "$0"`
|
||||
APP_BASE_NAME=${0##*/}
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD="maximum"
|
||||
MAX_FD=maximum
|
||||
|
||||
warn () {
|
||||
echo "$*"
|
||||
}
|
||||
} >&2
|
||||
|
||||
die () {
|
||||
echo
|
||||
echo "$*"
|
||||
echo
|
||||
exit 1
|
||||
}
|
||||
} >&2
|
||||
|
||||
# OS specific support (must be 'true' or 'false').
|
||||
cygwin=false
|
||||
msys=false
|
||||
darwin=false
|
||||
nonstop=false
|
||||
case "`uname`" in
|
||||
CYGWIN* )
|
||||
cygwin=true
|
||||
;;
|
||||
Darwin* )
|
||||
darwin=true
|
||||
;;
|
||||
MINGW* )
|
||||
msys=true
|
||||
;;
|
||||
NONSTOP* )
|
||||
nonstop=true
|
||||
;;
|
||||
case "$( uname )" in #(
|
||||
CYGWIN* ) cygwin=true ;; #(
|
||||
Darwin* ) darwin=true ;; #(
|
||||
MSYS* | MINGW* ) msys=true ;; #(
|
||||
NONSTOP* ) nonstop=true ;;
|
||||
esac
|
||||
|
||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||
@@ -87,9 +121,9 @@ CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||
if [ -n "$JAVA_HOME" ] ; then
|
||||
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||
# IBM's JDK on AIX uses strange locations for the executables
|
||||
JAVACMD="$JAVA_HOME/jre/sh/java"
|
||||
JAVACMD=$JAVA_HOME/jre/sh/java
|
||||
else
|
||||
JAVACMD="$JAVA_HOME/bin/java"
|
||||
JAVACMD=$JAVA_HOME/bin/java
|
||||
fi
|
||||
if [ ! -x "$JAVACMD" ] ; then
|
||||
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||
@@ -98,7 +132,7 @@ Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
else
|
||||
JAVACMD="java"
|
||||
JAVACMD=java
|
||||
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
@@ -106,80 +140,95 @@ location of your Java installation."
|
||||
fi
|
||||
|
||||
# Increase the maximum file descriptors if we can.
|
||||
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
|
||||
MAX_FD_LIMIT=`ulimit -H -n`
|
||||
if [ $? -eq 0 ] ; then
|
||||
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
|
||||
MAX_FD="$MAX_FD_LIMIT"
|
||||
fi
|
||||
ulimit -n $MAX_FD
|
||||
if [ $? -ne 0 ] ; then
|
||||
warn "Could not set maximum file descriptor limit: $MAX_FD"
|
||||
fi
|
||||
else
|
||||
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
|
||||
fi
|
||||
fi
|
||||
|
||||
# For Darwin, add options to specify how the application appears in the dock
|
||||
if $darwin; then
|
||||
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
||||
fi
|
||||
|
||||
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
|
||||
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
||||
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
||||
|
||||
JAVACMD=`cygpath --unix "$JAVACMD"`
|
||||
|
||||
# We build the pattern for arguments to be converted via cygpath
|
||||
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
|
||||
SEP=""
|
||||
for dir in $ROOTDIRSRAW ; do
|
||||
ROOTDIRS="$ROOTDIRS$SEP$dir"
|
||||
SEP="|"
|
||||
done
|
||||
OURCYGPATTERN="(^($ROOTDIRS))"
|
||||
# Add a user-defined pattern to the cygpath arguments
|
||||
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
|
||||
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
|
||||
fi
|
||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||
i=0
|
||||
for arg in "$@" ; do
|
||||
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
|
||||
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
|
||||
|
||||
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
|
||||
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
|
||||
else
|
||||
eval `echo args$i`="\"$arg\""
|
||||
fi
|
||||
i=`expr $i + 1`
|
||||
done
|
||||
case $i in
|
||||
0) set -- ;;
|
||||
1) set -- "$args0" ;;
|
||||
2) set -- "$args0" "$args1" ;;
|
||||
3) set -- "$args0" "$args1" "$args2" ;;
|
||||
4) set -- "$args0" "$args1" "$args2" "$args3" ;;
|
||||
5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
|
||||
6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
|
||||
7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
|
||||
8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
|
||||
9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
|
||||
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
||||
case $MAX_FD in #(
|
||||
max*)
|
||||
MAX_FD=$( ulimit -H -n ) ||
|
||||
warn "Could not query maximum file descriptor limit"
|
||||
esac
|
||||
case $MAX_FD in #(
|
||||
'' | soft) :;; #(
|
||||
*)
|
||||
ulimit -n "$MAX_FD" ||
|
||||
warn "Could not set maximum file descriptor limit to $MAX_FD"
|
||||
esac
|
||||
fi
|
||||
|
||||
# Escape application args
|
||||
save () {
|
||||
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
|
||||
echo " "
|
||||
}
|
||||
APP_ARGS=`save "$@"`
|
||||
# Collect all arguments for the java command, stacking in reverse order:
|
||||
# * args from the command line
|
||||
# * the main class name
|
||||
# * -classpath
|
||||
# * -D...appname settings
|
||||
# * --module-path (only if needed)
|
||||
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
|
||||
|
||||
# Collect all arguments for the java command, following the shell quoting and substitution rules
|
||||
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
|
||||
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||
if "$cygwin" || "$msys" ; then
|
||||
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
|
||||
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
|
||||
|
||||
JAVACMD=$( cygpath --unix "$JAVACMD" )
|
||||
|
||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||
for arg do
|
||||
if
|
||||
case $arg in #(
|
||||
-*) false ;; # don't mess with options #(
|
||||
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
|
||||
[ -e "$t" ] ;; #(
|
||||
*) false ;;
|
||||
esac
|
||||
then
|
||||
arg=$( cygpath --path --ignore --mixed "$arg" )
|
||||
fi
|
||||
# Roll the args list around exactly as many times as the number of
|
||||
# args, so each arg winds up back in the position where it started, but
|
||||
# possibly modified.
|
||||
#
|
||||
# NB: a `for` loop captures its iteration list before it begins, so
|
||||
# changing the positional parameters here affects neither the number of
|
||||
# iterations, nor the values presented in `arg`.
|
||||
shift # remove old arg
|
||||
set -- "$@" "$arg" # push replacement arg
|
||||
done
|
||||
fi
|
||||
|
||||
# Collect all arguments for the java command;
|
||||
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
|
||||
# shell script including quotes and variable substitutions, so put them in
|
||||
# double quotes to make sure that they get re-expanded; and
|
||||
# * put everything else in single quotes, so that it's not re-expanded.
|
||||
|
||||
set -- \
|
||||
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
||||
-classpath "$CLASSPATH" \
|
||||
org.gradle.wrapper.GradleWrapperMain \
|
||||
"$@"
|
||||
|
||||
# Use "xargs" to parse quoted args.
|
||||
#
|
||||
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
|
||||
#
|
||||
# In Bash we could simply go:
|
||||
#
|
||||
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
|
||||
# set -- "${ARGS[@]}" "$@"
|
||||
#
|
||||
# but POSIX shell has neither arrays nor command substitution, so instead we
|
||||
# post-process each arg (as a line of input to sed) to backslash-escape any
|
||||
# character that might be a shell metacharacter, then use eval to reverse
|
||||
# that process (while maintaining the separation between arguments), and wrap
|
||||
# the whole thing up as a single "set" statement.
|
||||
#
|
||||
# This will of course break if any of these variables contains a newline or
|
||||
# an unmatched quote.
|
||||
#
|
||||
|
||||
eval "set -- $(
|
||||
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
|
||||
xargs -n1 |
|
||||
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
|
||||
tr '\n' ' '
|
||||
)" '"$@"'
|
||||
|
||||
exec "$JAVACMD" "$@"
|
||||
|
||||
@@ -1,31 +1,3 @@
|
||||
plugins {
|
||||
id("com.android.library")
|
||||
}
|
||||
|
||||
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"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user