Compare commits

...
54 Commits
Author SHA1 Message Date
kr328 981c27c9f1 Chore: allow run workflow only main branch 2021-09-11 18:33:51 +08:00
Kr328 11c4e19e0c Refactor: refactor project structure & update clash core (#1174)
* Refactor: refactor project structure

* Chore: remove apply

* Chore: update clash core

* Fix: fix versionName & versionCode patch

* Chore: update golang plugin

* Fix: fix workflow build
2021-09-11 14:53:09 +08:00
Kr328 fa23be76ed Chore: update tun2socket 2021-06-20 13:22:48 +08:00
kr328 8e50d35eba Fix: fix GLOBAL url testing 2021-06-19 19:37:19 +08:00
kr328 1112534be6 Chore: bump version 2021-06-19 14:00:26 +08:00
kr328 784f249d54 Chore: migrate to new clash core 2021-06-19 13:59:58 +08:00
kr328 d6ad21b402 Chore: update clash core 2021-06-19 13:59:10 +08:00
kr328 bd03cac80d Chore: remove system proxy timeout 2021-06-19 13:39:29 +08:00
yi_Xu da3ee71dfd Docs: update readme (#1044)
* docs: update README.md

- Fix the path and name of the release.
- Format the style with markdownlint.

* docs: fix apk name

* ci: add paths-ignore for workflow
2021-06-17 22:10:03 +08:00
kr328 62fb089e27 Fix: system proxy should handle connection in parallel 2021-06-13 18:21:51 +08:00
kr328 51dab21f6f Chore: bump version 2021-06-13 14:19:00 +08:00
kr328 9e89b3a201 Chore: update clash core 2021-06-13 14:19:00 +08:00
kr328 1c1e2b9f3d Chore: update dependencies 2021-06-13 14:19:00 +08:00
kr328 e2e0238dcd Chore: update tun2socket 2021-06-13 14:19:00 +08:00
Tragic Life 9745e11da4 Chore: update zh-TW translate (#1063) 2021-06-13 12:38:33 +08:00
kr328 8ae7ccbfc9 Chore: add coroutine-android proguard rules 2021-06-13 12:32:34 +08:00
kr328 5a2229596a Chore: use coroutine-android instead of core 2021-06-13 12:32:10 +08:00
kr328 a463d94480 Chore: bump version 2021-06-13 04:22:53 +08:00
kr328 750abc8c71 Improve: enable system proxy by default 2021-06-13 04:22:15 +08:00
kr328 8375fbd8b3 Fix: add http timeout & disable keep-alive 2021-06-13 02:45:20 +08:00
kr328 394e406a36 Chore: update tun2socket 2021-06-13 02:35:35 +08:00
kr328 2645af0d4c Chore: update tun2socket 2021-05-29 19:56:21 +08:00
Kr328 48222c22c8 Fix: should close connection if match blocking list 2021-05-29 00:35:04 +08:00
kr328 d6a71267c6 Chore: bump version 2021-05-28 23:05:09 +08:00
Kr328 0f4a46188c Chore: remove verbose logs 2021-05-28 11:36:29 +08:00
Kr328 5917b90837 Feature: add block loopback connections 2021-05-28 11:32:21 +08:00
Kr328 a222e90d1f Chore: update kaidl 2021-05-28 10:52:41 +08:00
Kr328 3f60d713f8 Chore: update dependencies 2021-05-28 10:52:35 +08:00
Kr328 9cb8433f3b Chore: update tun2socket 2021-05-28 10:43:00 +08:00
Goooler 428ca53532 Chore: use "implementation" & remove "exclude" for buildSrc dependencies (#1034) 2021-05-28 10:37:23 +08:00
kr328 6c4d7e537b Chore: bump version 2021-05-26 19:48:11 +08:00
kr328 58ab89736a Chore: change tun interface address/route 2021-05-26 16:03:43 +08:00
Kr328 5e34221a09 Fix: refactor tun implement 2021-05-26 15:33:59 +08:00
Kr328 ac35f2a5f4 Chore: update tun2socket 2021-05-26 03:17:30 +08:00
kr328 73992dca54 Chore: update tun2socket 2021-05-25 22:50:35 +08:00
kr328 53dc20109d Improve: migrate to latest dependencies 2021-05-25 22:49:03 +08:00
kr328 e7fef0a767 Chore: bump version 2021-05-25 22:30:38 +08:00
kr328 c73beabf7e Fix: launch mode of MainActivity should be singleTop 2021-05-25 20:30:07 +08:00
kr328 c7409d7ac6 Chore: cleanup code 2021-05-25 20:28:57 +08:00
kr328 5e238ab5d3 Chore: update tun2socket 2021-05-25 20:28:51 +08:00
kr328 a8f502ef4f Improve: merge ClashManager and ProfileService 2021-05-25 20:28:40 +08:00
kr328 c48ce82640 Fix: fix generated ksp search path 2021-05-25 19:40:44 +08:00
kr328 5594485bec Improve: enable -O3 for libbridge.so 2021-05-25 19:23:19 +08:00
kr328 f967bd299a Improve: enable CMAKE_POSITION_INDEPENDENT_CODE 2021-05-25 19:14:57 +08:00
kr328 a75c8f5dfc Chore: update tun2socket 2021-05-25 19:10:47 +08:00
kr328 271d56c01c Chore: update tun2socket 2021-05-25 14:11:17 +08:00
Kr328 12220789a3 Improve: clean tun2socket build on tasks["clean"] 2021-05-25 02:25:30 +08:00
kr328 ca85a688cc Chore: bump version 2021-05-24 19:45:09 +08:00
kr328 0e3ed548c9 Chore: update tun2socket 2021-05-24 19:44:41 +08:00
Kr328 27dc5395e0 Improve: migrate to latest dependencies 2021-05-24 16:15:06 +08:00
Kr328 4f05ba1ac6 Feature: Add workflow for push/PR checking (#1030)
* Chore: downgrade ndk version

* Fix: fix local.properties load

* Feature: add workflow for push/PR checking
2021-05-24 16:15:06 +08:00
kr328 1a36218c80 Feature: add nameserver-policy override 2021-05-24 16:15:06 +08:00
Goooler 3125b90efe Chore: update dependencies 2021-05-24 16:14:59 +08:00
Kr328 2a7425700a Chore: update tun2socket 2021-05-24 03:15:58 +08:00
141 changed files with 1534 additions and 1761 deletions

No files matched your search

+42
View File
@@ -0,0 +1,42 @@
name: Build Unsigned
on:
push:
branches:
- main
paths-ignore:
- '.github/**'
- '.idea/**'
- '.gitignore'
- '.gitmodules'
- '**.md'
- 'LICENSE'
- 'NOTICE'
pull_request:
paths-ignore:
- '.github/**'
- '.idea/**'
- '.gitignore'
- '.gitmodules'
- '**.md'
- 'LICENSE'
- 'NOTICE'
jobs:
BuildUnsigned:
runs-on: ubuntu-latest
steps:
- name: Checkout Repository
uses: actions/checkout@v2
with:
submodules: recursive
- name: Setup Java
uses: actions/setup-java@v1
with:
java-version: 11
- name: Setup Go
uses: actions/setup-go@v2
with:
go-version: 1.17
- name: Setup Android SDK
uses: android-actions/setup-android@v2
- name: Build
run: ./gradlew --no-daemon app:assembleFossRelease
+4 -2
View File
@@ -19,9 +19,11 @@ gradle-app.setting
# Ignore IDEA config
*.iml
/.idea/*
/core/src/main/golang/.idea/*
!/.idea/codeStyles
!/core/src/main/golang/.idea/codeStyles
/core/src/foss/go/.idea/*
!/core/src/foss/go/.idea/codeStyles
/core/src/premium/go/.idea/*
!/core/src/premium/go/.idea/codeStyles
# KeyStore
*.keystore
+2 -8
View File
@@ -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
+7 -13
View File
@@ -8,26 +8,20 @@ A Graphical user interface of [clash](https://github.com/Dreamacro/clash) for An
Fully feature of [clash](https://github.com/Dreamacro/clash) ~~(Exclude `external-controller`~~
### Requirement
* Android 5.0+ (minimum)
* Android 7.0+ (recommend)
* `armeabi-v7a` , `arm64-v8a`, `x86` or `x86_64` Architecture
- Android 5.0+ (minimum)
- Android 7.0+ (recommend)
- `armeabi-v7a` , `arm64-v8a`, `x86` or `x86_64` Architecture
### License
See also [LICENSE](./LICENSE) and [NOTICE](./NOTICE)
### Privacy Policy
### Privacy Policy
See also [PRIVACY_POLICY.md](./PRIVACY_POLICY.md)
### Build
1. Update submodules
@@ -38,7 +32,7 @@ See also [PRIVACY_POLICY.md](./PRIVACY_POLICY.md)
2. Install **OpenJDK 11**, **Android SDK**, **CMake** and **Golang**
3. Create `local.properties` in project root with
3. Create `local.properties` in project root with
```properties
sdk.dir=/path/to/android-sdk
@@ -51,7 +45,7 @@ See also [PRIVACY_POLICY.md](./PRIVACY_POLICY.md)
storePassword=<key store password>
keyAlias=<key alias>
keyPassword=<key password>
```
```
5. Build
@@ -59,4 +53,4 @@ See also [PRIVACY_POLICY.md](./PRIVACY_POLICY.md)
./gradlew app:assembleFossRelease
```
6. Pick `app-release-<arch>.apk` in `app/build/outputs/apks`
6. Pick `app-foss-<arch>-release-signed.apk` in `app/build/outputs/apk/foss/release/`
+19 -121
View File
@@ -1,133 +1,31 @@
import java.util.*
plugins {
id("com.android.application")
kotlin("android")
kotlin("kapt")
}
android {
compileSdk = buildTargetSdkVersion
flavorDimensions(buildFlavor)
defaultConfig {
applicationId = "com.github.kr328.clash"
minSdk = buildMinSdkVersion
targetSdk = buildTargetSdkVersion
versionCode = buildVersionCode
versionName = buildVersionName
resConfigs("zh-rCN", "zh-rHK", "zh-rTW")
resValue("string", "release_name", "v$buildVersionName")
resValue("integer", "release_code", "$buildVersionCode")
}
buildTypes {
named("release") {
isMinifyEnabled = true
isShrinkResources = true
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
}
}
productFlavors {
create("foss") {
dimension = "foss"
versionNameSuffix = ".foss"
applicationIdSuffix = ".foss"
}
create("premium") {
dimension = "premium"
versionNameSuffix = ".premium"
if (buildFlavor == "premium") {
val appCenterKey = rootProject.file("local.properties").inputStream()
.use { Properties().apply { load(it) } }
.getProperty("appcenter.key", null)
Objects.requireNonNull(appCenterKey)
buildConfigField("String", "APP_CENTER_KEY", "\"$appCenterKey\"")
}
}
}
val signingFile = rootProject.file("keystore.properties")
if (signingFile.exists()) {
val properties = Properties().apply {
signingFile.inputStream().use {
load(it)
}
}
signingConfigs {
create("release") {
storeFile = rootProject.file(properties.getProperty("storeFile")!!)
storePassword = properties.getProperty("storePassword")!!
keyAlias = properties.getProperty("keyAlias")!!
keyPassword = properties.getProperty("keyPassword")!!
}
}
buildTypes {
named("release") {
signingConfig = signingConfigs["release"]
}
}
}
buildFeatures {
dataBinding = true
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = "1.8"
}
splits {
abi {
isEnable = true
isUniversalApk = true
}
}
id("com.android.application")
}
dependencies {
val premiumImplementation by configurations
api(project(":core"))
api(project(":service"))
api(project(":design"))
api(project(":common"))
premiumImplementation("com.microsoft.appcenter:appcenter-analytics:$appcenterVersion")
premiumImplementation("com.microsoft.appcenter:appcenter-crashes:$appcenterVersion")
implementation(project(":core"))
implementation(project(":service"))
implementation(project(":design"))
implementation(project(":common"))
implementation(kotlin("stdlib-jdk7"))
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutineVersion")
implementation("androidx.core:core-ktx:$ktxVersion")
implementation("androidx.activity:activity:$activityVersion")
implementation("androidx.appcompat:appcompat:$appcompatVersion")
implementation("androidx.coordinatorlayout:coordinatorlayout:$coordinatorlayoutVersion")
implementation("androidx.recyclerview:recyclerview:$recyclerviewVersion")
implementation("androidx.fragment:fragment:$fragmentVersion")
implementation("com.google.android.material:material:$materialVersion")
implementation(deps.kotlin.coroutine)
implementation(deps.androidx.core)
implementation(deps.androidx.activity)
implementation(deps.androidx.fragment)
implementation(deps.androidx.appcompat)
implementation(deps.androidx.coordinator)
implementation(deps.androidx.recyclerview)
implementation(deps.google.material)
val premiumImplementation by configurations
premiumImplementation(deps.appcenter.analytics)
premiumImplementation(deps.appcenter.crashes)
}
task("cleanRelease", type = Delete::class) {
tasks.getByName("clean", type = Delete::class) {
delete(file("release"))
}
afterEvaluate {
tasks["clean"].dependsOn(tasks["cleanRelease"])
}
+26
View File
@@ -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;
}
+2 -1
View File
@@ -41,7 +41,8 @@
android:name=".MainActivity"
android:configChanges="uiMode"
android:exported="true"
android:label="@string/launch_name">
android:label="@string/launch_name"
android:launchMode="singleTop">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
@@ -24,8 +24,6 @@ class AppCrashedActivity : BaseActivity<AppCrashedDesign>() {
SystemLogcat.dumpCrash()
}
Tracker.uploadLogcat(logs)
design.setAppLogs(logs)
while (isActive) {
@@ -114,7 +114,7 @@ abstract class BaseActivity<D : Design<*>> :
Remote.broadcasts.addObserver(this)
events.offer(Event.ActivityStart)
events.trySend(Event.ActivityStart)
}
override fun onStop() {
@@ -124,7 +124,7 @@ abstract class BaseActivity<D : Design<*>> :
Remote.broadcasts.removeObserver(this)
events.offer(Event.ActivityStop)
events.trySend(Event.ActivityStop)
}
override fun onDestroy() {
@@ -174,23 +174,23 @@ abstract class BaseActivity<D : Design<*>> :
}
override fun onProfileChanged() {
events.offer(Event.ProfileChanged)
events.trySend(Event.ProfileChanged)
}
override fun onProfileLoaded() {
events.offer(Event.ProfileLoaded)
events.trySend(Event.ProfileLoaded)
}
override fun onServiceRecreated() {
events.offer(Event.ServiceRecreated)
events.trySend(Event.ServiceRecreated)
}
override fun onStarted() {
events.offer(Event.ClashStart)
events.trySend(Event.ClashStart)
}
override fun onStopped(cause: String?) {
events.offer(Event.ClashStop)
events.trySend(Event.ClashStop)
if (cause != null && activityStarted) {
launch {
@@ -141,7 +141,7 @@ class FilesActivity : BaseActivity<FilesDesign>() {
}
override fun onBackPressed() {
design?.requests?.offer(FilesDesign.Request.PopStack)
design?.requests?.trySend(FilesDesign.Request.PopStack)
}
private suspend fun FilesDesign.fetch(client: FilesClient, stack: Stack<String>, root: String) {
@@ -20,9 +20,9 @@ import com.github.kr328.clash.common.util.intent
import com.github.kr328.clash.core.model.LogMessage
import com.github.kr328.clash.log.LogcatCache
import com.github.kr328.clash.log.LogcatWriter
import com.github.kr328.clash.service.ClashManager
import com.github.kr328.clash.service.remote.IClashManager
import com.github.kr328.clash.service.RemoteService
import com.github.kr328.clash.service.remote.ILogObserver
import com.github.kr328.clash.service.remote.IRemoteService
import com.github.kr328.clash.service.remote.unwrap
import com.github.kr328.clash.util.logsDir
import kotlinx.coroutines.*
@@ -52,7 +52,7 @@ class LogcatService : Service(), CoroutineScope by CoroutineScope(Dispatchers.De
showNotification()
bindService(ClashManager::class.intent, connection, Context.BIND_AUTO_CREATE)
bindService(RemoteService::class.intent, connection, Context.BIND_AUTO_CREATE)
}
override fun onDestroy() {
@@ -88,7 +88,7 @@ class LogcatService : Service(), CoroutineScope by CoroutineScope(Dispatchers.De
return stopSelf()
launch(Dispatchers.IO) {
val service = binder.unwrap(IClashManager::class)
val service = binder.unwrap(IRemoteService::class).clash()
val channel = Channel<LogMessage>(CACHE_CAPACITY)
try {
@@ -97,7 +97,7 @@ class LogcatService : Service(), CoroutineScope by CoroutineScope(Dispatchers.De
LogcatWriter(this@LogcatService).use {
val observer = object : ILogObserver {
override fun newItem(log: LogMessage) {
channel.offer(log)
channel.trySend(log)
}
}
@@ -47,7 +47,7 @@ class LogsActivity : BaseActivity<LogsDesign>() {
deleteAllLogs()
}
events.offer(Event.ActivityStart)
events.trySend(Event.ActivityStart)
}
}
is LogsDesign.Request.OpenFile -> {
@@ -32,4 +32,8 @@ class MainApplication : Application() {
sendServiceRecreated()
}
}
fun finalize() {
Global.destroy()
}
}
@@ -73,7 +73,7 @@ class ProxyActivity : BaseActivity<ProxyDesign>() {
}
ProxyDesign.Request.ReloadAll -> {
names.indices.forEach { idx ->
design.requests.offer(ProxyDesign.Request.Reload(idx))
design.requests.trySend(ProxyDesign.Request.Reload(idx))
}
}
is ProxyDesign.Request.Reload -> {
@@ -8,7 +8,8 @@ object SystemLogcat {
"Go",
"DEBUG",
"AndroidRuntime",
"ClashForAndroid"
"ClashForAndroid",
"LwIP",
)
fun dumpCrash(): String {
@@ -10,13 +10,12 @@ import com.github.kr328.clash.store.AppStore
import com.github.kr328.clash.util.ApplicationObserver
import com.github.kr328.clash.util.verifyApk
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.launch
object Remote {
val broadcasts: Broadcasts = Broadcasts(Global.application)
val services: Services = Services(Global.application) {
val service: Service = Service(Global.application) {
ApplicationObserver.createdActivities.forEach { it.finish() }
val intent = AppCrashedActivity::class.intent
@@ -30,9 +29,9 @@ object Remote {
fun launch() {
ApplicationObserver.attach(Global.application)
ApplicationObserver.onVisibleChanged(visible::offer)
ApplicationObserver.onVisibleChanged { visible.trySend(it) }
GlobalScope.launch(Dispatchers.IO) {
Global.launch(Dispatchers.IO) {
run()
}
}
@@ -57,10 +56,10 @@ object Remote {
while (true) {
if (visible.receive()) {
services.bind()
service.bind()
broadcasts.register()
} else {
services.unbind()
service.unbind()
broadcasts.unregister()
}
}
@@ -0,0 +1,64 @@
package com.github.kr328.clash.remote
import android.app.Application
import android.content.ComponentName
import android.content.Context
import android.content.ServiceConnection
import android.os.IBinder
import com.github.kr328.clash.Tracker
import com.github.kr328.clash.common.log.Log
import com.github.kr328.clash.common.util.intent
import com.github.kr328.clash.log.SystemLogcat
import com.github.kr328.clash.service.RemoteService
import com.github.kr328.clash.service.remote.IRemoteService
import com.github.kr328.clash.service.remote.unwrap
import com.github.kr328.clash.util.unbindServiceSilent
import java.util.concurrent.TimeUnit
class Service(private val context: Application, val crashed: () -> Unit) {
val remote = Resource<IRemoteService>()
private val connection = object : ServiceConnection {
private var lastCrashed: Long = -1
override fun onServiceConnected(name: ComponentName?, service: IBinder) {
remote.set(service.unwrap(IRemoteService::class))
}
override fun onServiceDisconnected(name: ComponentName?) {
remote.set(null)
Tracker.uploadLogcat(SystemLogcat.dumpCrash())
if (System.currentTimeMillis() - lastCrashed < TOGGLE_CRASHED_INTERVAL) {
unbind()
crashed()
}
lastCrashed = System.currentTimeMillis()
Log.w("RemoteManager crashed")
}
}
fun bind() {
try {
context.bindService(RemoteService::class.intent, connection, Context.BIND_AUTO_CREATE)
} catch (e: Exception) {
unbind()
crashed()
}
}
fun unbind() {
context.unbindServiceSilent(connection)
remote.set(null)
}
companion object {
private val TOGGLE_CRASHED_INTERVAL = TimeUnit.SECONDS.toMillis(10)
}
}
@@ -1,88 +0,0 @@
package com.github.kr328.clash.remote
import android.app.Application
import android.content.ComponentName
import android.content.Context
import android.content.ServiceConnection
import android.os.IBinder
import com.github.kr328.clash.common.log.Log
import com.github.kr328.clash.common.util.intent
import com.github.kr328.clash.service.ClashManager
import com.github.kr328.clash.service.ProfileService
import com.github.kr328.clash.service.remote.IClashManager
import com.github.kr328.clash.service.remote.IProfileManager
import com.github.kr328.clash.service.remote.unwrap
import com.github.kr328.clash.util.unbindServiceSilent
import java.util.concurrent.TimeUnit
class Services(private val context: Application, val crashed: () -> Unit) {
val clash = Resource<IClashManager>()
val profile = Resource<IProfileManager>()
private val clashConnection = object : ServiceConnection {
private var lastCrashed: Long = -1
override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
clash.set(service?.unwrap(IClashManager::class))
}
override fun onServiceDisconnected(name: ComponentName?) {
clash.set(null)
if (System.currentTimeMillis() - lastCrashed < TOGGLE_CRASHED_INTERVAL) {
unbind()
crashed()
}
lastCrashed = System.currentTimeMillis()
Log.w("ClashManager crashed")
}
}
private val profileConnection = object : ServiceConnection {
private var lastCrashed: Long = -1
override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
profile.set(service?.unwrap(IProfileManager::class))
}
override fun onServiceDisconnected(name: ComponentName?) {
profile.set(null)
if (System.currentTimeMillis() - lastCrashed < TOGGLE_CRASHED_INTERVAL) {
unbind()
crashed()
}
lastCrashed = System.currentTimeMillis()
Log.w("ProfileService crashed")
}
}
fun bind() {
try {
context.bindService(ClashManager::class.intent, clashConnection, Context.BIND_AUTO_CREATE)
context.bindService(ProfileService::class.intent, profileConnection, Context.BIND_AUTO_CREATE)
} catch (e: Exception) {
unbind()
crashed()
}
}
fun unbind() {
context.unbindServiceSilent(clashConnection)
context.unbindServiceSilent(profileConnection)
clash.set(null)
profile.set(null)
}
companion object {
private val TOGGLE_CRASHED_INTERVAL = TimeUnit.SECONDS.toMillis(10)
}
}
@@ -14,14 +14,15 @@ suspend fun <T> withClash(
block: suspend IClashManager.() -> T
): T {
while (true) {
val client = Remote.services.clash.get()
val remote = Remote.service.remote.get()
val client = remote.clash()
try {
return withContext(context) { client.block() }
} catch (e: DeadObjectException) {
Log.w("Remote services panic")
Remote.services.clash.reset(client)
Remote.service.remote.reset(remote)
}
}
}
@@ -31,14 +32,15 @@ suspend fun <T> withProfile(
block: suspend IProfileManager.() -> T
): T {
while (true) {
val client = Remote.services.profile.get()
val remote = Remote.service.remote.get()
val client = remote.profile()
try {
return withContext(context) { client.block() }
} catch (e: DeadObjectException) {
Log.w("Remote services panic")
Remote.services.profile.reset(client)
Remote.service.remote.reset(remote)
}
}
}
+116
View File
@@ -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
}
}
}
}
}
-35
View File
@@ -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"
}
}
}
-17
View File
@@ -1,17 +0,0 @@
import org.gradle.api.Project
const val buildVersionCode = 204002
const val buildVersionName = "2.4.2"
const val buildMinSdkVersion = 21
const val buildTargetSdkVersion = 30
const val buildNdkVersion = "23.0.7123448"
val Project.buildFlavor: String
get() {
return if (project(":core").file("src/main/golang/clash/script/script.go").exists())
"premium"
else
"foss"
}
-13
View File
@@ -1,13 +0,0 @@
const val activityVersion = "1.2.3"
const val coroutineVersion = "1.4.3"
const val roomVersion = "2.3.0"
const val ktxVersion = "1.3.2"
const val appcompatVersion = "1.2.0"
const val muiltprocessVersion = "1.0.0"
const val appcenterVersion = "4.1.1"
const val serializationVersion = "1.2.1"
const val materialVersion = "1.3.0"
const val coordinatorlayoutVersion = "1.1.0"
const val recyclerviewVersion = "1.2.0"
const val fragmentVersion = "1.3.3"
const val viewpagerVersion = "1.0.0"
-8
View File
@@ -1,8 +0,0 @@
import org.gradle.api.Project
import java.io.File
val Project.golangSource: File
get() = file("src/main/golang")
val Project.golangBuild: File
get() = buildDir.resolve("intermediates/golang")
@@ -1,25 +0,0 @@
package com.github.kr328.clash.tools
import com.android.build.gradle.BaseExtension
import com.android.build.gradle.api.BaseVariant
import java.io.Serializable
data class BuildConfig(
val debug: Boolean,
val premium: Boolean,
val abis: List<NativeAbi>,
val minSdkVersion: Int,
) : Serializable {
companion object {
fun of(extension: BaseExtension, variant: BaseVariant): BuildConfig {
return BuildConfig(
debug = variant.buildType.isDebuggable,
premium = variant.flavorName == "premium",
abis = extension.defaultConfig.externalNativeBuild.cmake.abiFilters
.map { NativeAbi.parse(it) }
.distinct(),
minSdkVersion = extension.defaultConfig.minSdkVersion!!.apiLevel
)
}
}
}
@@ -1,45 +0,0 @@
package com.github.kr328.clash.tools
import com.android.build.gradle.LibraryExtension
import golangBuild
import golangSource
import org.gradle.api.Plugin
import org.gradle.api.Project
import java.util.*
class ClashBuildPlugin : Plugin<Project> {
override fun apply(target: Project) {
target.afterEvaluate {
target.extensions.getByType(LibraryExtension::class.java).apply {
libraryVariants.forEach { variant ->
val config = BuildConfig.of(this, variant)
val buildDir = target.golangBuild.resolve(variant.name)
val capitalize = variant.name.capitalize(Locale.getDefault())
val task = target.tasks.register(
"externalGolangBuild$capitalize",
ClashBuildTask::class.java
) {
it.config.set(config)
it.ndkDirectory.set(ndkDirectory)
it.inputDirectory.set(target.golangSource)
it.outputDirectory.set(buildDir)
}
sourceSets.named(variant.name) {
it.jniLibs {
srcDir(buildDir)
}
}
variant.externalNativeBuildProviders.forEach {
it.get().dependsOn(task)
}
target.tasks.filter { it.name.startsWith("buildCMake") }.forEach {
it.mustRunAfter(task)
}
}
}
}
}
}
@@ -1,60 +0,0 @@
package com.github.kr328.clash.tools
import org.gradle.api.DefaultTask
import org.gradle.api.file.DirectoryProperty
import org.gradle.api.provider.Property
import org.gradle.api.tasks.Input
import org.gradle.api.tasks.InputDirectory
import org.gradle.api.tasks.OutputDirectory
import org.gradle.api.tasks.TaskAction
import java.io.File
abstract class ClashBuildTask : DefaultTask() {
abstract val config: Property<BuildConfig>
@Input get
abstract val ndkDirectory: DirectoryProperty
@InputDirectory get
abstract val inputDirectory: DirectoryProperty
@InputDirectory get
abstract val outputDirectory: DirectoryProperty
@OutputDirectory get
@TaskAction
fun build() {
val input = inputDirectory.file
val output = outputDirectory.file
val config = config.get()
val environment = Environment(ndkDirectory.file, config.minSdkVersion)
val tags = listOf("without_gvisor", "without_system") +
(if (config.debug) listOf("debug") else emptyList()) +
(if (config.premium) listOf("premium") else emptyList())
Command.ofGoModuleTidy(input).exec()
config.abis.forEach {
Command.ofGoRun(
"make/make.go",
listOf("bridge", "native", "build", "android", it.goArch),
input.resolve("tun2socket/bridge"),
environment.ofLwipBuild(it)
).exec()
Command.ofGoBuild(
"c-shared",
output.resolve("${it.value}/libclash.so"),
tags,
!config.debug,
input,
environment.ofCoreBuild(it)
).exec()
}
}
private val DirectoryProperty.file: File
get() = get().asFile
}
@@ -1,84 +0,0 @@
package com.github.kr328.clash.tools
import org.gradle.api.GradleException
import java.io.File
import kotlin.concurrent.thread
class Command(
private val command: Array<String>,
workingDir: File,
environments: Map<String, String>
) {
private val processBuilder: ProcessBuilder = ProcessBuilder(*command)
.redirectErrorStream(true)
.directory(workingDir)
.apply { environment().putAll(environments) }
fun exec() {
val process = processBuilder.start()
thread {
process.inputStream.copyTo(System.out)
}
val result = process.waitFor()
if (result != 0) {
throw GradleException("exec ${command.joinToString(" ")}: exit with $result")
}
}
companion object {
fun ofGoModuleTidy(workingDir: File): Command {
return Command(arrayOf("go", "mod", "tidy"), workingDir, System.getenv())
}
fun ofGoBuild(
mode: String,
output: File,
tags: List<String>,
strip: Boolean,
workingDir: File,
environments: Map<String, String>
): Command {
val command = mutableListOf("go", "build")
// go build mode
command += "-buildmode"
command += mode
// output file
command += "-o"
command += output.absolutePath
// trim path prefix
command += "-trimpath"
if (tags.isNotEmpty()) {
command += "-tags"
command += tags.joinToString(",")
}
if (strip) {
command += "-ldflags"
command += "-s -w"
}
return Command(command.toTypedArray(), workingDir, environments)
}
fun ofGoRun(
file: String,
args: List<String>,
workingDir: File,
environments: Map<String, String>
): Command {
val command = mutableListOf("go", "run")
command += file
command += args
return Command(command.toTypedArray(), workingDir, environments)
}
}
}
@@ -1,44 +0,0 @@
package com.github.kr328.clash.tools
import org.apache.tools.ant.taskdefs.condition.Os
import org.gradle.api.GradleException
import java.io.File
class Environment(
private val ndkDirectory: File,
private val minSdkVersion: Int,
) {
fun ofCoreBuild(abi: NativeAbi): Map<String, String> {
val host = when {
Os.isFamily(Os.FAMILY_WINDOWS) ->
"windows"
Os.isFamily(Os.FAMILY_MAC) ->
"darwin"
Os.isFamily(Os.FAMILY_UNIX) ->
"linux"
else ->
throw GradleException("Unsupported host: ${System.getProperty("os.name")}")
}
val compiler = ndkDirectory.resolve("toolchains/llvm/prebuilt/$host-x86_64/bin")
.resolve("${abi.compiler}${minSdkVersion}-clang")
return mapOf(
"CC" to compiler.absolutePath,
"GOOS" to "android",
"GOARCH" to abi.goArch,
"GOARM" to abi.goArm,
"CGO_ENABLED" to "1",
"CFLAGS" to "-O3 -Werror",
)
}
fun ofLwipBuild(abi: NativeAbi): Map<String, String> {
return mapOf(
"CMAKE_SYSTEM_NAME" to "Android",
"CMAKE_ANDROID_NDK" to ndkDirectory.absolutePath,
"CMAKE_ANDROID_ARCH_ABI" to abi.value,
"CMAKE_SYSTEM_VERSION" to minSdkVersion.toString()
)
}
}
@@ -1,25 +0,0 @@
package com.github.kr328.clash.tools
enum class NativeAbi(
val value: String,
val compiler: String,
val goArch: String,
val goArm: String
) {
ArmeabiV7a("armeabi-v7a", "armv7a-linux-androideabi", "arm", "7"),
Arm64V8a("arm64-v8a", "aarch64-linux-android", "arm64", ""),
X86("x86", "i686-linux-android", "386", ""),
X64("x86_64", "x86_64-linux-android", "amd64", "");
companion object {
fun parse(value: String): NativeAbi {
return when (value) {
ArmeabiV7a.value -> ArmeabiV7a
Arm64V8a.value -> Arm64V8a
X86.value -> X86
X64.value -> X64
else -> throw IllegalArgumentException("unsupported abi $value")
}
}
}
}
+3 -41
View File
@@ -1,50 +1,12 @@
plugins {
id("com.android.library")
kotlin("android")
}
android {
compileSdk = buildTargetSdkVersion
defaultConfig {
minSdk = buildMinSdkVersion
targetSdk = buildTargetSdkVersion
versionCode = buildVersionCode
versionName = buildVersionName
consumerProguardFiles("consumer-rules.pro")
}
buildTypes {
named("release") {
isMinifyEnabled = false
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = "1.8"
}
id("com.android.library")
}
dependencies {
compileOnly(project(":hideapi"))
implementation(kotlin("stdlib-jdk7"))
implementation("androidx.core:core-ktx:$ktxVersion")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutineVersion")
}
repositories {
mavenCentral()
google()
implementation(deps.kotlin.coroutine)
implementation(deps.androidx.core)
}
@@ -1,8 +1,11 @@
package com.github.kr328.clash.common
import android.app.Application
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.cancel
object Global {
object Global : CoroutineScope by CoroutineScope(Dispatchers.IO) {
val application: Application
get() = application_
@@ -11,4 +14,8 @@ object Global {
fun init(application: Application) {
this.application_ = application
}
fun destroy() {
cancel()
}
}
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="receive_clash_broadcasts">接收 Clash 廣播</string>
<string name="receive_broadcasts_of_clash">接收來自 Clash 內部的廣播</string>
</resources>
+28 -54
View File
@@ -1,12 +1,13 @@
import com.github.kr328.golang.GolangPlugin
import java.io.FileOutputStream
import java.net.URL
import java.time.Duration
plugins {
id("com.android.library")
kotlin("android")
id("com.android.library")
id("kotlinx-serialization")
id("clash-build")
id("golang-android")
}
val geoipDatabaseUrl =
@@ -14,60 +15,31 @@ val geoipDatabaseUrl =
val geoipInvalidate = Duration.ofDays(7)!!
val geoipOutput = buildDir.resolve("intermediates/golang_blob")
android {
compileSdk = buildTargetSdkVersion
ndkVersion = buildNdkVersion
flavorDimensions(buildFlavor)
defaultConfig {
minSdk = buildMinSdkVersion
targetSdk = buildTargetSdkVersion
versionCode = buildVersionCode
versionName = buildVersionName
consumerProguardFiles("consumer-rules.pro")
externalNativeBuild {
cmake {
abiFilters("arm64-v8a", "armeabi-v7a", "x86", "x86_64")
arguments(
"-DGO_SOURCE:STRING=$golangSource",
"-DGO_OUTPUT:STRING=$golangBuild",
"-DFLAVOR_NAME=$buildFlavor",
)
}
}
}
buildTypes {
named("release") {
isMinifyEnabled = false
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
}
}
productFlavors {
golang {
sourceSets {
create("foss") {
dimension = "foss"
srcDir.set(file("src/foss/go"))
}
create("premium") {
dimension = "premium"
srcDir.set(file("src/premium/go"))
}
all {
fileName.set("libclash.so")
}
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = "1.8"
android {
productFlavors {
all {
externalNativeBuild {
cmake {
arguments("-DGO_SOURCE:STRING=${golang.sourceSets.getByName(name).srcDir.asFile.get()}")
arguments("-DGO_OUTPUT:STRING=${GolangPlugin.outputDirOf(project, null, null)}")
arguments("-DFLAVOR_NAME:STRING=$name")
}
}
}
}
externalNativeBuild {
@@ -78,12 +50,12 @@ android {
}
dependencies {
api(project(":common"))
implementation(project(":common"))
implementation(kotlin("stdlib-jdk7"))
implementation("androidx.core:core-ktx:$ktxVersion")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutineVersion")
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:$serializationVersion")
implementation(deps.androidx.core)
implementation(deps.kotlin.coroutine)
implementation(deps.kotlin.serialization.json)
}
repositories {
@@ -108,6 +80,8 @@ task("downloadGeoipDatabase") {
var GeoipDatabase []byte
""".trimIndent()
outputs.dir(geoipOutput)
onlyIf {
System.currentTimeMillis() - databaseFile.lastModified() > geoipInvalidate.toMillis()
}
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -25,13 +25,12 @@ type Status struct {
var client = &http.Client{
Transport: &http.Transport{
// from http.DefaultTransport
MaxIdleConns: 100,
IdleConnTimeout: 90 * time.Second,
DisableKeepAlives: true,
TLSHandshakeTimeout: 10 * time.Second,
ExpectContinueTimeout: 1 * time.Second,
DialContext: dialer.DefaultTunnelDialer,
},
Timeout: 60 * time.Second,
}
func openUrl(url string) (io.ReadCloser, error) {
File renamed without changes.
File renamed without changes.
@@ -5,6 +5,7 @@ import (
"errors"
"fmt"
"strings"
"time"
"github.com/Dreamacro/clash/log"
"github.com/dlclark/regexp2"
@@ -16,6 +17,11 @@ import (
"github.com/Dreamacro/clash/dns"
)
const (
defaultHealthCheckUrl = "https://www.gstatic.com/generate_204"
defaultHealthCheckInterval = time.Hour
)
var processors = []processor{
patchOverride,
patchGeneral,
@@ -88,7 +94,13 @@ func patchProviders(cfg *config.RawConfig, profileDir string) error {
func patchProxyGroup(cfg *config.RawConfig, _ string) error {
for _, g := range cfg.ProxyGroup {
g["lazy"] = false
if _, exist := g["url"]; !exist {
g["url"] = defaultHealthCheckUrl
}
if _, exist := g["interval"]; !exist {
g["interval"] = int(defaultHealthCheckInterval.Seconds())
}
}
return nil
@@ -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))
})
}
}
File renamed without changes.
@@ -6,8 +6,8 @@ require (
cfa/blob v0.0.0 // local generated
github.com/Dreamacro/clash v0.0.0 // local
github.com/dlclark/regexp2 v1.4.0
github.com/kr328/tun2socket v0.0.0 // local
github.com/miekg/dns v1.1.42
github.com/kr328/tun2socket-lwip v0.0.0-20210911023118-0b4947e2a9c1
github.com/miekg/dns v1.1.43
github.com/oschwald/geoip2-golang v1.5.0
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
gopkg.in/yaml.v2 v2.4.0
@@ -15,6 +15,4 @@ require (
replace github.com/Dreamacro/clash => ./clash
replace github.com/kr328/tun2socket => ./tun2socket
replace cfa/blob => ../../../build/intermediates/golang_blob
+114
View 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=
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
+40
View 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
}
File renamed without changes.
File renamed without changes.
@@ -11,8 +11,6 @@ import (
"cfa/tun"
"golang.org/x/sync/semaphore"
"github.com/Dreamacro/clash/log"
)
type remoteTun struct {
@@ -23,7 +21,7 @@ type remoteTun struct {
}
func (t *remoteTun) markSocket(fd int) {
_ = t.limit.Acquire(context.Background(), 1)
_ = t.limit.Acquire(context.TODO(), 1)
defer t.limit.Release(1)
if t.closed {
@@ -34,7 +32,7 @@ func (t *remoteTun) markSocket(fd int) {
}
func (t *remoteTun) querySocketUid(protocol int, source, target string) int {
_ = t.limit.Acquire(context.Background(), 1)
_ = t.limit.Acquire(context.TODO(), 1)
defer t.limit.Release(1)
if t.closed {
@@ -50,26 +48,27 @@ func (t *remoteTun) stop() {
t.closed = true
C.release_object(t.callback)
app.ApplyTunContext(nil, nil)
log.Infoln("Android tun device destroyed")
C.release_object(t.callback)
}
//export startTun
func startTun(fd, mtu C.int, dns C.c_string, callback unsafe.Pointer) C.int {
func startTun(fd, mtu C.int, gateway, dns C.c_string, callback unsafe.Pointer) C.int {
f := int(fd)
m := int(mtu)
g := C.GoString(gateway)
d := C.GoString(dns)
remote := &remoteTun{callback: callback, closed: false, limit: semaphore.NewWeighted(4)}
if tun.Start(f, m, d) != nil {
return 1
}
app.ApplyTunContext(remote.markSocket, remote.querySocketUid)
log.Infoln("Android tun device created")
if tun.Start(f, m, g, d, remote.stop) != nil {
app.ApplyTunContext(nil, nil)
return 1
}
return 0
}
+32
View 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
View 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
View 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
View 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
View 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
}
File renamed without changes.
File renamed without changes.
@@ -3,9 +3,9 @@ package tunnel
import (
"sync"
"github.com/Dreamacro/clash/adapters/outbound"
"github.com/Dreamacro/clash/adapters/outboundgroup"
"github.com/Dreamacro/clash/adapters/provider"
"github.com/Dreamacro/clash/adapter"
"github.com/Dreamacro/clash/adapter/outboundgroup"
"github.com/Dreamacro/clash/constant/provider"
"github.com/Dreamacro/clash/log"
"github.com/Dreamacro/clash/tunnel"
)
@@ -19,7 +19,7 @@ func HealthCheck(name string) {
return
}
g, ok := p.(*outbound.Proxy).ProxyAdapter.(outboundgroup.ProxyGroup)
g, ok := p.(*adapter.Proxy).ProxyAdapter.(outboundgroup.ProxyGroup)
if !ok {
log.Warnln("Request health check for `%s`: invalid type %s", name, p.Type().String())
File renamed without changes.
@@ -7,7 +7,8 @@ import (
"fmt"
"time"
"github.com/Dreamacro/clash/adapters/provider"
P "github.com/Dreamacro/clash/adapter/provider"
"github.com/Dreamacro/clash/constant/provider"
"github.com/Dreamacro/clash/tunnel"
)
@@ -38,7 +39,7 @@ func QueryProviders() []*Provider {
for _, p := range providers {
updatedAt := time.Time{}
if s, ok := p.(provider.UpdatableProvider); ok {
if s, ok := p.(P.UpdatableProvider); ok {
updatedAt = s.UpdatedAt()
}
@@ -7,7 +7,7 @@ import (
"fmt"
"time"
"github.com/Dreamacro/clash/adapters/provider"
"github.com/Dreamacro/clash/adapter/provider"
"github.com/Dreamacro/clash/log"
"github.com/Dreamacro/clash/tunnel"
)
@@ -4,12 +4,12 @@ import (
"sort"
"strings"
"github.com/Dreamacro/clash/adapter"
"github.com/dlclark/regexp2"
"github.com/Dreamacro/clash/adapters/outbound"
"github.com/Dreamacro/clash/adapters/outboundgroup"
"github.com/Dreamacro/clash/adapters/provider"
"github.com/Dreamacro/clash/adapter/outboundgroup"
C "github.com/Dreamacro/clash/constant"
"github.com/Dreamacro/clash/constant/provider"
"github.com/Dreamacro/clash/log"
"github.com/Dreamacro/clash/tunnel"
)
@@ -60,7 +60,7 @@ func QueryProxyGroupNames(excludeNotSelectable bool) []string {
return []string{}
}
global := tunnel.Proxies()["GLOBAL"].(*outbound.Proxy).ProxyAdapter.(outboundgroup.ProxyGroup)
global := tunnel.Proxies()["GLOBAL"].(*adapter.Proxy).ProxyAdapter.(outboundgroup.ProxyGroup)
proxies := global.Providers()[0].Proxies()
result := make([]string, 0, len(proxies)+1)
@@ -69,7 +69,7 @@ func QueryProxyGroupNames(excludeNotSelectable bool) []string {
}
for _, p := range proxies {
if _, ok := p.(*outbound.Proxy).ProxyAdapter.(outboundgroup.ProxyGroup); ok {
if _, ok := p.(*adapter.Proxy).ProxyAdapter.(outboundgroup.ProxyGroup); ok {
if !excludeNotSelectable || p.Type() == C.Selector {
result = append(result, p.Name())
}
@@ -88,7 +88,7 @@ func QueryProxyGroup(name string, sortMode SortMode, uiSubtitlePattern *regexp2.
return nil
}
g, ok := p.(*outbound.Proxy).ProxyAdapter.(outboundgroup.ProxyGroup)
g, ok := p.(*adapter.Proxy).ProxyAdapter.(outboundgroup.ProxyGroup)
if !ok {
log.Warnln("Query group `%s`: invalid type %s", name, p.Type().String())
@@ -136,7 +136,7 @@ func PatchSelector(selector, name string) bool {
return false
}
g, ok := p.(*outbound.Proxy).ProxyAdapter.(outboundgroup.ProxyGroup)
g, ok := p.(*adapter.Proxy).ProxyAdapter.(outboundgroup.ProxyGroup)
if !ok {
log.Warnln("Patch selector `%s`: invalid type %s", selector, p.Type().String())
@@ -171,7 +171,7 @@ func collectProviders(providers []provider.ProxyProvider, uiSubtitlePattern *reg
subtitle := px.Type().String()
if uiSubtitlePattern != nil {
if _, ok := px.(*outbound.Proxy).ProxyAdapter.(outboundgroup.ProxyGroup); !ok {
if _, ok := px.(*adapter.Proxy).ProxyAdapter.(outboundgroup.ProxyGroup); !ok {
runes := []rune(name)
match, err := uiSubtitlePattern.FindRunesMatch(runes)
if err == nil && match != nil {
File renamed without changes.
@@ -1,6 +1,6 @@
package tunnel
import "github.com/Dreamacro/clash/adapters/provider"
import "github.com/Dreamacro/clash/adapter/provider"
func Suspend(s bool) {
provider.Suspend(s)
File renamed without changes.
+3 -1
View File
@@ -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}")
+5 -3
View File
@@ -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
-108
View File
@@ -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))
}
-98
View File
@@ -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()
}
-27
View File
@@ -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))
}
-152
View File
@@ -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
}
-59
View File
@@ -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)
}
@@ -62,10 +62,11 @@ object Clash {
fd: Int,
mtu: Int,
dns: String,
blocking: String,
markSocket: (Int) -> Boolean,
querySocketUid: (protocol: Int, source: InetSocketAddress, target: InetSocketAddress) -> Int
) {
Bridge.nativeStartTun(fd, mtu, dns, object : TunInterface {
Bridge.nativeStartTun(fd, mtu, dns, blocking, object : TunInterface {
override fun markSocket(fd: Int) {
markSocket(fd)
}
@@ -218,7 +219,7 @@ object Clash {
return Channel<LogMessage>(32).apply {
Bridge.nativeSubscribeLogcat(object : LogcatInterface {
override fun received(jsonPayload: String) {
offer(Json.decodeFromString(LogMessage.serializer(), jsonPayload))
trySend(Json.decodeFromString(LogMessage.serializer(), jsonPayload))
}
})
}
@@ -17,7 +17,7 @@ object Bridge {
external fun nativeQueryTrafficTotal(): Long
external fun nativeNotifyDnsChanged(dnsList: String)
external fun nativeNotifyInstalledAppChanged(uidList: String)
external fun nativeStartTun(fd: Int, mtu: Int, dns: String, cb: TunInterface)
external fun nativeStartTun(fd: Int, mtu: Int, dns: String, blocking: String, cb: TunInterface)
external fun nativeStopTun()
external fun nativeStartHttp(listenAt: String): String?
external fun nativeStopHttp()
@@ -80,7 +80,10 @@ data class ConfigurationOverride(
var fakeIpFilter: List<String>? = null,
@SerialName("fallback-filter")
val fallbackFilter: DnsFallbackFilter = DnsFallbackFilter()
val fallbackFilter: DnsFallbackFilter = DnsFallbackFilter(),
@SerialName("nameserver-policy")
var nameserverPolicy: Map<String, String>? = null,
)
@Serializable
+13 -61
View File
@@ -1,70 +1,22 @@
plugins {
id("com.android.library")
kotlin("android")
kotlin("kapt")
}
android {
compileSdk = buildTargetSdkVersion
flavorDimensions(buildFlavor)
defaultConfig {
minSdk = buildMinSdkVersion
targetSdk = buildTargetSdkVersion
versionCode = buildVersionCode
versionName = buildVersionName
consumerProguardFiles("consumer-rules.pro")
}
buildTypes {
named("release") {
isMinifyEnabled = false
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
}
}
productFlavors {
create("foss") {
dimension = "foss"
}
create("premium") {
dimension = "premium"
}
}
buildFeatures {
dataBinding = true
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = "1.8"
}
id("com.android.library")
}
dependencies {
api(project(":common"))
api(project(":core"))
api(project(":service"))
implementation(project(":common"))
implementation(project(":core"))
implementation(project(":service"))
implementation(kotlin("stdlib-jdk7"))
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutineVersion")
implementation("androidx.core:core-ktx:$ktxVersion")
implementation("androidx.appcompat:appcompat:$appcompatVersion")
implementation("androidx.activity:activity:$activityVersion")
implementation("com.google.android.material:material:$materialVersion")
implementation("androidx.coordinatorlayout:coordinatorlayout:$coordinatorlayoutVersion")
implementation("androidx.recyclerview:recyclerview:$recyclerviewVersion")
implementation("androidx.fragment:fragment:$fragmentVersion")
implementation("androidx.viewpager2:viewpager2:$viewpagerVersion")
implementation(deps.kotlin.coroutine)
implementation(deps.androidx.core)
implementation(deps.androidx.appcompat)
implementation(deps.androidx.activity)
implementation(deps.androidx.coordinator)
implementation(deps.androidx.recyclerview)
implementation(deps.androidx.fragment)
implementation(deps.androidx.viewpager)
implementation(deps.google.material)
}
@@ -236,7 +236,7 @@ class OverrideSettingsDesign(
summary = R.string.sideload_geoip_summary
) {
clicked {
requests.offer(Request.EditSideloadGeoip)
requests.trySend(Request.EditSideloadGeoip)
}
}
@@ -378,6 +378,15 @@ class OverrideSettingsDesign(
configure = dnsDependencies::add,
)
editableTextMap(
value = configuration.dns::nameserverPolicy,
keyAdapter = TextAdapter.String,
valueAdapter = TextAdapter.String,
title = R.string.name_server_policy,
placeholder = R.string.dont_modify,
configure = dnsDependencies::add,
)
dns.listener?.onChanged()
}
@@ -385,6 +394,6 @@ class OverrideSettingsDesign(
}
fun requestClear() {
requests.offer(Request.ResetOverride)
requests.trySend(Request.ResetOverride)
}
}
@@ -32,48 +32,48 @@ class ProxyMenu(
R.id.not_selectable -> {
uiStore.proxyExcludeNotSelectable = item.isChecked
requests.offer(ProxyDesign.Request.ReLaunch)
requests.trySend(ProxyDesign.Request.ReLaunch)
}
R.id.single -> {
uiStore.proxySingleLine = true
updateConfig()
requests.offer(ProxyDesign.Request.ReloadAll)
requests.trySend(ProxyDesign.Request.ReloadAll)
}
R.id.multiple -> {
uiStore.proxySingleLine = false
updateConfig()
requests.offer(ProxyDesign.Request.ReloadAll)
requests.trySend(ProxyDesign.Request.ReloadAll)
}
R.id.default_ -> {
uiStore.proxySort = ProxySort.Default
requests.offer(ProxyDesign.Request.ReloadAll)
requests.trySend(ProxyDesign.Request.ReloadAll)
}
R.id.name -> {
uiStore.proxySort = ProxySort.Title
requests.offer(ProxyDesign.Request.ReloadAll)
requests.trySend(ProxyDesign.Request.ReloadAll)
}
R.id.delay -> {
uiStore.proxySort = ProxySort.Delay
requests.offer(ProxyDesign.Request.ReloadAll)
requests.trySend(ProxyDesign.Request.ReloadAll)
}
R.id.dont_modify -> {
requests.offer(ProxyDesign.Request.PatchMode(null))
requests.trySend(ProxyDesign.Request.PatchMode(null))
}
R.id.direct_mode -> {
requests.offer(ProxyDesign.Request.PatchMode(TunnelState.Mode.Direct))
requests.trySend(ProxyDesign.Request.PatchMode(TunnelState.Mode.Direct))
}
R.id.global_mode -> {
requests.offer(ProxyDesign.Request.PatchMode(TunnelState.Mode.Global))
requests.trySend(ProxyDesign.Request.PatchMode(TunnelState.Mode.Global))
}
R.id.rule_mode -> {
requests.offer(ProxyDesign.Request.PatchMode(TunnelState.Mode.Rule))
requests.trySend(ProxyDesign.Request.PatchMode(TunnelState.Mode.Rule))
}
else -> return false
}
@@ -93,7 +93,7 @@ class AccessControlDesign(
binding.surface = dialog.surface
binding.mainList.applyLinearAdapter(context, adapter)
binding.keywordView.addTextChangedListener {
filter.offer(Unit)
filter.trySend(Unit)
}
binding.closeView.setOnClickListener {
dialog.dismiss()
@@ -38,7 +38,7 @@ class ApkBrokenDesign(context: Context) : Design<ApkBrokenDesign.Request>(contex
summary = R.string.google_play_url
) {
clicked {
requests.offer(Request(context.getString(R.string.google_play_url)))
requests.trySend(Request(context.getString(R.string.google_play_url)))
}
}
@@ -47,7 +47,7 @@ class ApkBrokenDesign(context: Context) : Design<ApkBrokenDesign.Request>(contex
summary = R.string.github_releases_url
) {
clicked {
requests.offer(Request(context.getString(R.string.github_releases_url)))
requests.trySend(Request(context.getString(R.string.github_releases_url)))
}
}
}
Loaded 100 of 141 files, more files were not shown because too many files have changed in this diff. Show more