Compare commits

...

79 Commits

Author SHA1 Message Date
Kr328
c4c070cb97 Chore: bump version 2021-09-24 11:15:54 +08:00
Kr328
7928f4321a Chore: update clash core 2021-09-24 11:11:47 +08:00
Kr328
3b62367003 Chore: bump version 2021-09-17 11:21:21 +08:00
Kr328
c8994d8488 Chore: add version to output apks name 2021-09-17 11:19:03 +08:00
kr328
4033994111 Chore: bump version 2021-09-14 01:50:06 +08:00
kr328
2050fb56d0 Chore: update clash core 2021-09-14 01:49:45 +08:00
kr328
eeea9d0fce Fix: add zhihu to http proxy black list 2021-09-14 01:07:55 +08:00
kr328
4b1f7a76c7 Chore: update dependencies 2021-09-13 22:44:01 +08:00
kr328
db2a7feb34 Chore: remove dependencies update detect plugin 2021-09-13 22:41:19 +08:00
Goooler
8b91b2eeb1 Chore: add .gitattributes (#1191)
* Add .editorconfig

* Add .gitattributes

* Remove indent_style

* Remove .editorconfig
2021-09-13 18:34:34 +08:00
Goooler
899803739b Chore: remove unused dependencies & improve gradle properties & code style (#1189)
* Inline version strings

* Optimize gradle

* Remove kotlin-stdlib-jdk7

* Add jvmargs in gradle.properties

* Revert "Inline version strings"

This reverts commit ad2ab2bb

* Revert asserts
2021-09-13 17:49:23 +08:00
Goooler
1ea3a5b682 Chore: bump setup-java action & Remove setup-android action (#1188)
* Bump setup-java action to v2

* Remove setup-android action

* Remove ignore rules under github folder
2021-09-13 17:45:46 +08:00
kr328
f21cf86cc4 Chore: allow run workflow only main branch 2021-09-12 18:31:16 +08:00
kr328
bb1052bbc7 Chore: update README.md 2021-09-12 18:25:09 +08:00
kr328
7dad08346c Chore: update dependencies 2021-09-12 18:02:30 +08:00
kr328
b7c56e58ae Chore: add hideapi to app module to avoid r8 class missing 2021-09-12 17:50:48 +08:00
kr328
a6f6da5bc5 Chore: remove unused check 2021-09-12 17:39:04 +08:00
kr328
f271ba3d97 Feature: add dns.geoip-code support 2021-09-12 17:38:52 +08:00
kr328
66d02c7e2d Chore: bump version 2021-09-12 13:19:55 +08:00
kr328
0406573317 Refactor: refactor properties 2021-09-12 13:19:40 +08:00
kr328
4a8b6b2704 Refactor: refactor design structure 2021-09-12 13:01:08 +08:00
kr328
603cec113c Chore: update clash patch 2021-09-12 12:40:15 +08:00
kr328
a579c7bb86 Fix: should not delay before patch system dns 2021-09-12 12:24:09 +08:00
kr328
654c488ed8 Fix: fix time zone 2021-09-12 11:46:20 +08:00
kr328
ecfb339680 Chore: fix code style 2021-09-12 11:26:27 +08:00
kr328
ecbe222a50 Chore: update dependencies 2021-09-12 11:14:45 +08:00
kr328
aacf62c259 Chore: update dependencies 2021-09-12 11:04:26 +08:00
kr328
3973fb2706 Fix: fix premium build 2021-09-12 01:33:52 +08:00
kr328
07ff81f4db Fix: should add main golang source as input 2021-09-12 00:43:32 +08:00
kr328
c0e1e45b72 Refactor: refactor clash core building 2021-09-12 00:39:59 +08:00
Kr328
b07f70ab0b Improve: should not reload profile on network changed 2021-09-11 23:37:54 +08:00
Kr328
e1a35f8b2d Chore: bump go version to 1.17 2021-09-11 19:49:26 +08:00
kr328
e364af393a Refactor: refactor golang flavor 2021-09-11 18:29:05 +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
134 changed files with 2826 additions and 2470 deletions

4
.gitattributes vendored Normal file
View File

@@ -0,0 +1,4 @@
* text=auto eol=lf
*.bat text eol=crlf
*.jar binary

View File

@@ -1,5 +1,25 @@
name: Build Unsigned
on: [push, pull_request]
on:
push:
branches:
- main
paths-ignore:
- '.idea/**'
- '.gitattributes'
- '.gitignore'
- '.gitmodules'
- '**.md'
- 'LICENSE'
- 'NOTICE'
pull_request:
paths-ignore:
- '.idea/**'
- '.gitattributes'
- '.gitignore'
- '.gitmodules'
- '**.md'
- 'LICENSE'
- 'NOTICE'
jobs:
BuildUnsigned:
runs-on: ubuntu-latest
@@ -9,16 +29,13 @@ jobs:
with:
submodules: recursive
- name: Setup Java
uses: actions/setup-java@v1
uses: actions/setup-java@v2
with:
distribution: 'adopt'
java-version: 11
- name: Setup Go
uses: actions/setup-go@v2
with:
go-version: 1.16
- name: Setup Android SDK
uses: android-actions/setup-android@v2
- name: Setup Cmake & Ninja
uses: lukka/get-cmake@latest
go-version: 1.17
- name: Build
run: ./gradlew --no-daemon app:assembleRelease
run: ./gradlew --no-daemon app:assembleFossRelease

12
.gitignore vendored
View File

@@ -19,11 +19,16 @@ gradle-app.setting
# Ignore IDEA config
*.iml
/.idea/*
/core/src/main/golang/.idea/*
!/.idea/codeStyles
/core/src/main/golang/.idea/*
!/core/src/main/golang/.idea/codeStyles
/core/src/foss/golang/.idea/*
!/core/src/foss/golang/.idea/codeStyles
/core/src/premium/golang/.idea/*
!/core/src/premium/golang/.idea/codeStyles
# KeyStore
signing.properties
*.keystore
*.jks
@@ -33,8 +38,9 @@ cmake-build-*
# local.properties
local.properties
# keystore
keystore.properties
# tracker
tracker.properties
# vscode
.vscode

13
.gitmodules vendored
View File

@@ -1,9 +1,6 @@
[submodule "core/src/main/golang/clash"]
path = core/src/main/golang/clash
[submodule "clash-foss"]
path = core/src/foss/golang/clash
url = https://github.com/Kr328/clash.git
[submodule "clash-premium"]
path = core/src/premium/golang/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

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,20 +32,20 @@ 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
```
4. Create `keystore.properties` in project root with
4. Create `signing.properties` in project root with
```properties
storeFile=/path/to/keystore/file
storePassword=<key store password>
keyAlias=<key alias>
keyPassword=<key password>
```
keystore.path=/path/to/keystore/file
keystore.password=<key store password>
key.alias=<key alias>
key.password=<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.apk` in `app/build/outputs/apk/foss/release/`

View File

@@ -1,140 +1,32 @@
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 {
compileOnly(project(":hideapi"))
implementation(project(":core"))
implementation(project(":service"))
implementation(project(":design"))
implementation(project(":common"))
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
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(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")
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"])
}

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;
}

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" />

View File

@@ -24,8 +24,6 @@ class AppCrashedActivity : BaseActivity<AppCrashedDesign>() {
SystemLogcat.dumpCrash()
}
Tracker.uploadLogcat(logs)
design.setAppLogs(logs)
while (isActive) {

View File

@@ -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 {

View File

@@ -8,7 +8,8 @@ object SystemLogcat {
"Go",
"DEBUG",
"AndroidRuntime",
"ClashForAndroid"
"ClashForAndroid",
"LwIP",
)
fun dumpCrash(): String {

View File

@@ -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()
}
}

View 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)
}
}

View File

@@ -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)
}
}

View File

@@ -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)
}
}
}

View File

@@ -8,7 +8,7 @@ import com.microsoft.appcenter.crashes.ingestion.models.ErrorAttachmentLog
object Tracker {
fun initialize(application: Application) {
if (BuildConfig.APP_CENTER_KEY != null && !BuildConfig.DEBUG) {
if (!BuildConfig.DEBUG) {
AppCenter.start(
application,
BuildConfig.APP_CENTER_KEY,
@@ -18,7 +18,7 @@ object Tracker {
}
fun uploadLogcat(logcat: String) {
if (BuildConfig.APP_CENTER_KEY != null && !BuildConfig.DEBUG) {
if (!BuildConfig.DEBUG) {
if (logcat.isNotBlank()) {
Crashes.trackError(
RuntimeException(),

View File

@@ -1,11 +1,163 @@
@file:Suppress("UNUSED_VARIABLE")
import com.android.build.gradle.AppExtension
import com.android.build.gradle.BaseExtension
import java.net.URL
import java.util.*
buildscript {
repositories {
mavenCentral()
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()
google()
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> {
val minSdkVersion = 21
val targetSdkVersion = 30
val buildVersionCode = 204013
val buildVersionName = "2.4.13"
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
resValue("string", "release_name", "v$buildVersionName")
resValue("integer", "release_code", "$buildVersionCode")
externalNativeBuild {
cmake {
abiFilters("arm64-v8a", "armeabi-v7a", "x86", "x86_64")
}
}
if (!isApp) {
consumerProguardFiles("consumer-rules.pro")
} else {
setProperty("archivesBaseName", "cfa-$versionName")
}
}
if (isApp) {
packagingOptions {
excludes.add("DebugProbesKt.bin")
}
}
productFlavors {
flavorDimensions(defaultDimension)
create("foss") {
dimension = defaultDimension
versionNameSuffix = ".foss"
buildConfigField("boolean", "PREMIUM", "Boolean.parseBoolean(\"false\")")
if (isApp) {
applicationIdSuffix = ".foss"
}
}
create("premium") {
dimension = defaultDimension
versionNameSuffix = ".premium"
buildConfigField("boolean", "PREMIUM", "Boolean.parseBoolean(\"true\")")
val tracker = rootProject.file("tracker.properties")
if (tracker.exists()) {
val prop = Properties().apply {
tracker.inputStream().use(this::load)
}
buildConfigField(
"String",
"APP_CENTER_KEY",
"\"${prop.getProperty("appcenter.key")!!}\""
)
}
}
}
signingConfigs {
val keystore = rootProject.file("signing.properties")
if (keystore.exists()) {
create("release") {
val prop = Properties().apply {
keystore.inputStream().use(this::load)
}
storeFile = rootProject.file(prop.getProperty("keystore.path")!!)
storePassword = prop.getProperty("keystore.password")!!
keyAlias = prop.getProperty("key.alias")!!
keyPassword = prop.getProperty("key.password")!!
}
}
}
buildTypes {
named("release") {
isMinifyEnabled = isApp
isShrinkResources = isApp
signingConfig = signingConfigs.findByName("release")
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
}
named("debug") {
versionNameSuffix = ".debug"
}
}
buildFeatures.apply {
dataBinding {
isEnabled = name != "hideapi"
}
}
if (isApp) {
this as AppExtension
splits {
abi {
isEnable = true
isUniversalApk = true
}
}
}
}
}
@@ -13,7 +165,7 @@ task("clean", type = Delete::class) {
delete(rootProject.buildDir)
}
tasks.named<Wrapper>("wrapper") {
tasks.wrapper {
distributionType = Wrapper.DistributionType.ALL
doLast {

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"
}
}
}

View File

@@ -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"
}

View File

@@ -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"

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")

View File

@@ -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
)
}
}
}

View File

@@ -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)
}
}
}
}
}
}

View File

@@ -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
}

View File

@@ -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)
}
}
}

View File

@@ -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()
)
}
}

View File

@@ -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")
}
}
}
}

View File

@@ -1,50 +1,11 @@
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)
}

View 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>

View File

@@ -1,73 +1,50 @@
import com.github.kr328.golang.GolangBuildTask
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 =
"https://github.com/Dreamacro/maxmind-geoip/releases/latest/download/Country.mmdb"
val geoipInvalidate = Duration.ofDays(7)!!
val geoipOutput = buildDir.resolve("intermediates/golang_blob")
val golangSource = file("src/main/golang/native")
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"
tags.set(listOf("foss"))
srcDir.set(file("src/foss/golang"))
}
create("premium") {
dimension = "premium"
tags.set(listOf("premium", "without_gvisor", "without_system"))
srcDir.set(file("src/premium/golang"))
}
all {
fileName.set("libclash.so")
packageName.set("cfa/native")
}
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = "1.8"
android {
productFlavors {
all {
externalNativeBuild {
cmake {
arguments("-DGO_SOURCE:STRING=${golangSource}")
arguments("-DGO_OUTPUT:STRING=${GolangPlugin.outputDirOf(project, null, null)}")
arguments("-DFLAVOR_NAME:STRING=$name")
}
}
}
}
externalNativeBuild {
@@ -78,16 +55,17 @@ 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 {
mavenCentral()
afterEvaluate {
tasks.withType(GolangBuildTask::class.java).forEach {
it.inputs.dir(golangSource)
}
}
task("downloadGeoipDatabase") {
@@ -108,6 +86,8 @@ task("downloadGeoipDatabase") {
var GeoipDatabase []byte
""".trimIndent()
outputs.dir(geoipOutput)
onlyIf {
System.currentTimeMillis() - databaseFile.lastModified() > geoipInvalidate.toMillis()
}

View File

@@ -0,0 +1,34 @@
module foss
go 1.17
require cfa v0.0.0
require (
cfa/blob v0.0.0 // indirect
github.com/Dreamacro/clash v0.0.0 // indirect
github.com/Dreamacro/go-shadowsocks2 v0.1.7 // indirect
github.com/dlclark/regexp2 v1.4.0 // indirect
github.com/gofrs/uuid v4.0.0+incompatible // indirect
github.com/gorilla/websocket v1.4.2 // indirect
github.com/insomniacslk/dhcp v0.0.0-20210827173440-b95caade3eac // indirect
github.com/kr328/tun2socket-lwip v0.0.0-20210911023118-0b4947e2a9c1 // indirect
github.com/miekg/dns v1.1.43 // indirect
github.com/oschwald/geoip2-golang v1.5.0 // indirect
github.com/oschwald/maxminddb-golang v1.8.0 // indirect
github.com/sirupsen/logrus v1.8.1 // indirect
github.com/u-root/uio v0.0.0-20210528114334-82958018845c // indirect
go.uber.org/atomic v1.9.0 // indirect
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 // indirect
golang.org/x/net v0.0.0-20210903162142-ad29c8ab022f // indirect
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect
golang.org/x/sys v0.0.0-20210906170528-6f6e22806c34 // indirect
golang.org/x/text v0.3.6 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
)
replace cfa => ../../main/golang
replace github.com/Dreamacro/clash => ./clash
replace cfa/blob => ../../../build/intermediates/golang_blob

114
core/src/foss/golang/go.sum Normal file
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=

View File

@@ -0,0 +1,5 @@
package golang
import (
_ "cfa/native/all"
)

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}")

View File

@@ -84,6 +84,16 @@ Java_com_github_kr328_clash_core_bridge_Bridge_nativeNotifyDnsChanged(JNIEnv *en
notifyDnsChanged(_dns_list);
}
JNIEXPORT void JNICALL
Java_com_github_kr328_clash_core_bridge_Bridge_nativeNotifyTimeZoneChanged(JNIEnv *env, jobject thiz,
jstring name, jint offset) {
TRACE_METHOD();
scoped_string _name = get_string(name);
notifyTimeZoneChanged(_name, offset);
}
JNIEXPORT void JNICALL
Java_com_github_kr328_clash_core_bridge_Bridge_nativeNotifyInstalledAppChanged(JNIEnv *env,
jobject thiz,
@@ -96,15 +106,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

View File

@@ -1,15 +0,0 @@
package app
import "strings"
var systemDns []string
func NotifyDnsChanged(dnsList string) {
dns := strings.Split(dnsList, ",")
systemDns = dns
}
func SystemDns() []string {
return systemDns
}

View File

@@ -1,20 +1,3 @@
module cfa
go 1.16
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/oschwald/geoip2-golang v1.5.0
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
gopkg.in/yaml.v2 v2.4.0
)
replace github.com/Dreamacro/clash => ./clash
replace github.com/kr328/tun2socket => ./tun2socket
replace cfa/blob => ../../../build/intermediates/golang_blob
go 1.17

View File

View File

@@ -0,0 +1,16 @@
package all
import (
_ "cfa/native/app"
_ "cfa/native/common"
_ "cfa/native/config"
_ "cfa/native/delegate"
_ "cfa/native/platform"
_ "cfa/native/proxy"
_ "cfa/native/tun"
_ "cfa/native/tunnel"
_ "golang.org/x/sync/semaphore"
_ "github.com/Dreamacro/clash/log"
)

View File

@@ -7,7 +7,7 @@ import (
"errors"
"unsafe"
"cfa/app"
"cfa/native/app"
"github.com/Dreamacro/clash/log"
)
@@ -43,6 +43,12 @@ func notifyInstalledAppsChanged(uids C.c_string) {
app.NotifyInstallAppsChanged(u)
}
//export notifyTimeZoneChanged
func notifyTimeZoneChanged(name C.c_string, offset C.int) {
app.NotifyTimeZoneChanged(C.GoString(name), int(offset))
}
//export queryConfiguration
func queryConfiguration() *C.char {
response := &struct{}{}

View File

@@ -3,6 +3,7 @@ package app
import (
"strconv"
"strings"
"time"
)
var appVersionName string
@@ -46,3 +47,7 @@ func NotifyInstallAppsChanged(uidList string) {
func QueryAppByUid(uid int) string {
return installedAppsUid[uid]
}
func NotifyTimeZoneChanged(name string, offset int) {
time.Local = time.FixedZone(name, offset)
}

View File

@@ -0,0 +1,20 @@
package app
import (
"strings"
"github.com/Dreamacro/clash/dns"
)
func NotifyDnsChanged(dnsList string) {
dL := strings.Split(dnsList, ",")
ns := make([]dns.NameServer, 0, len(dnsList))
for _, d := range dL {
ns = append(ns, dns.NameServer{Addr: d})
}
dns.UpdateSystemDNS(dL)
dns.FlushCacheWithDefaultResolver()
}

View File

@@ -1,10 +1,11 @@
package app
import (
"cfa/platform"
"net"
"strings"
"syscall"
"cfa/native/platform"
)
var markSocketImpl func(fd int)

View File

@@ -7,7 +7,7 @@ import (
"runtime"
"unsafe"
"cfa/config"
"cfa/native/config"
)
type remoteValidCallback struct {

View File

@@ -11,8 +11,7 @@ import (
"runtime"
"time"
"cfa/app"
"cfa/native/app"
"github.com/Dreamacro/clash/component/dialer"
)
@@ -25,13 +24,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) {

View File

@@ -3,12 +3,12 @@ package config
import (
"io/ioutil"
P "path"
"runtime"
"strings"
"gopkg.in/yaml.v2"
"cfa/app"
"cfa/native/app"
"github.com/Dreamacro/clash/constant"
"github.com/Dreamacro/clash/log"
@@ -81,6 +81,8 @@ func Load(path string) error {
app.ApplySubtitlePattern(rawCfg.ClashForAndroid.UiSubtitlePattern)
runtime.GC()
return nil
}

View File

@@ -5,17 +5,22 @@ import (
"errors"
"fmt"
"strings"
"time"
"github.com/Dreamacro/clash/log"
"github.com/dlclark/regexp2"
"cfa/app"
"cfa/common"
"cfa/native/common"
"github.com/Dreamacro/clash/log"
"github.com/Dreamacro/clash/config"
"github.com/Dreamacro/clash/dns"
)
const (
defaultHealthCheckUrl = "https://www.gstatic.com/generate_204"
defaultHealthCheckInterval = time.Hour
)
var processors = []processor{
patchOverride,
patchGeneral,
@@ -70,7 +75,7 @@ func patchDns(cfg *config.RawConfig, _ string) error {
}
if cfg.ClashForAndroid.AppendSystemDNS {
cfg.DNS.NameServer = append(cfg.DNS.NameServer, app.SystemDns()...)
cfg.DNS.NameServer = append(cfg.DNS.NameServer, "dhcp://" + dns.SystemDNSPlaceholder)
}
return nil
@@ -88,7 +93,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

View File

@@ -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()
}
}

View File

@@ -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) + len(rawCfg.RuleProvider)
@@ -23,10 +27,10 @@ func forEachProviders(rawCfg *config.RawConfig, fun func(index int, total int, k
func destroyProviders(cfg *config.Config) {
for _, p := range cfg.ProxyProviders {
_ = p.Destroy()
_ = p.(io.Closer).Close()
}
for _, p := range cfg.RuleProviders {
_ = p.Destroy()
_ = p.(io.Closer).Close()
}
}

View File

@@ -1,15 +1,13 @@
package core
package delegate
import (
"errors"
"net"
"syscall"
"cfa/blob"
"cfa/app"
"cfa/platform"
"cfa/native/app"
"cfa/native/platform"
"github.com/Dreamacro/clash/component/process"
"github.com/Dreamacro/clash/log"
@@ -41,34 +39,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))
})
}
}

View File

@@ -10,9 +10,9 @@ import "C"
import (
"runtime"
"cfa/config"
"cfa/core"
"cfa/tunnel"
"cfa/native/config"
"cfa/native/delegate"
"cfa/native/tunnel"
"github.com/Dreamacro/clash/log"
)
@@ -27,7 +27,7 @@ func coreInit(home, versionName C.c_string, sdkVersion C.int) {
v := C.GoString(versionName)
s := int(sdkVersion)
core.Init(h, v, s)
delegate.Init(h, v, s)
reset()
}

View File

@@ -3,7 +3,9 @@ package main
//#include "bridge.h"
import "C"
import "cfa/proxy"
import (
"cfa/native/proxy"
)
//export startHttp
func startHttp(listenAt C.c_string) *C.char {

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
}

View File

@@ -7,12 +7,10 @@ import (
"context"
"unsafe"
"cfa/app"
"cfa/tun"
"golang.org/x/sync/semaphore"
"github.com/Dreamacro/clash/log"
"cfa/native/app"
"cfa/native/tun"
)
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
}

View File

@@ -0,0 +1,33 @@
package tun
import (
"net"
"github.com/Dreamacro/clash/dns"
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 := dns.ServeDNSWithDefaultServer(msg)
if err != nil {
return nil, err
}
r.SetRcode(msg, r.Rcode)
return r.Pack()
}

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])
}
}

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
}

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
}

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
}

View File

@@ -6,8 +6,8 @@ import "C"
import (
"unsafe"
"cfa/app"
"cfa/tunnel"
"cfa/native/app"
"cfa/native/tunnel"
)
//export queryTunnelState

View File

@@ -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())

View File

@@ -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()
}

View File

@@ -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/log"
"github.com/Dreamacro/clash/tunnel"
)
@@ -48,7 +49,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()
}

View File

@@ -6,10 +6,11 @@ import (
"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"
"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 +61,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 +70,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 +89,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 +137,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 +172,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 {

View File

@@ -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)

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))
}

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()
}

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))
}

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
}

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)
}

View File

@@ -52,6 +52,10 @@ object Clash {
Bridge.nativeNotifyDnsChanged(dns.joinToString(separator = ","))
}
fun notifyTimeZoneChanged(name: String, offset: Int) {
Bridge.nativeNotifyTimeZoneChanged(name, offset)
}
fun notifyInstalledAppsChanged(uids: List<Pair<Int, String>>) {
val uidList = uids.joinToString(separator = ",") { "${it.first}:${it.second}" }
@@ -62,10 +66,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)
}

View File

@@ -16,8 +16,9 @@ object Bridge {
external fun nativeQueryTrafficNow(): Long
external fun nativeQueryTrafficTotal(): Long
external fun nativeNotifyDnsChanged(dnsList: String)
external fun nativeNotifyTimeZoneChanged(name: String, offset: Int)
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()

View File

@@ -91,6 +91,9 @@ data class ConfigurationOverride(
@SerialName("geoip")
var geoIp: Boolean? = null,
@SerialName("geoip-code")
var geoIpCode: String? = null,
@SerialName("ipcidr")
var ipcidr: List<String>? = null,

View File

@@ -0,0 +1,44 @@
module premium
go 1.17
require cfa v0.0.0
require (
cfa/blob v0.0.0 // indirect
github.com/Dreamacro/clash v0.0.0 // indirect
github.com/Dreamacro/go-shadowsocks2 v0.1.7 // indirect
github.com/avast/apkparser v0.0.0-20210223100516-186f320f9bfc // indirect
github.com/avast/apkverifier v0.0.0-20210524110121-dfe686b45d88 // indirect
github.com/dlclark/regexp2 v1.4.0 // indirect
github.com/gofrs/uuid v4.0.0+incompatible // indirect
github.com/google/btree v1.0.1 // indirect
github.com/gorilla/websocket v1.4.2 // indirect
github.com/insomniacslk/dhcp v0.0.0-20210827173440-b95caade3eac // indirect
github.com/klauspost/compress v1.11.13 // indirect
github.com/kr328/tun2socket-lwip v0.0.0-20210911023118-0b4947e2a9c1 // indirect
github.com/miekg/dns v1.1.43 // indirect
github.com/oschwald/geoip2-golang v1.5.0 // indirect
github.com/oschwald/maxminddb-golang v1.8.0 // indirect
github.com/sirupsen/logrus v1.8.1 // indirect
github.com/u-root/uio v0.0.0-20210528114334-82958018845c // indirect
go.starlark.net v0.0.0-20210901212718-87f333178d59 // indirect
go.uber.org/atomic v1.9.0 // indirect
go4.org/intern v0.0.0-20210108033219-3eb7198706b2 // indirect
go4.org/unsafe/assume-no-moving-gc v0.0.0-20201222180813-1025295fd063 // indirect
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 // indirect
golang.org/x/net v0.0.0-20210903162142-ad29c8ab022f // indirect
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect
golang.org/x/sys v0.0.0-20210906170528-6f6e22806c34 // indirect
golang.org/x/text v0.3.6 // indirect
golang.org/x/time v0.0.0-20191024005414-555d28b269f0 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gvisor.dev/gvisor v0.0.0-20210904021812-0d58674c658a // indirect
inet.af/netaddr v0.0.0-20210903134321-85fa6c94624e // indirect
)
replace cfa => ../../main/golang
replace github.com/Dreamacro/clash => ./clash
replace cfa/blob => ../../../build/intermediates/golang_blob

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,3 @@
package main
import _ "cfa/native/all"

View File

@@ -1,70 +1,21 @@
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)
}

View File

@@ -1,56 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:title="@string/filter">
<menu>
<item
android:id="@+id/not_selectable"
android:checkable="true"
android:title="@string/not_selectable" />
</menu>
</item>
<item android:title="@string/mode">
<menu>
<group android:checkableBehavior="single">
<item
android:id="@+id/dont_modify"
android:title="@string/dont_modify" />
<item
android:id="@+id/direct_mode"
android:title="@string/direct_mode" />
<item
android:id="@+id/global_mode"
android:title="@string/global_mode" />
<item
android:id="@+id/rule_mode"
android:title="@string/rule_mode" />
</group>
</menu>
</item>
<item android:title="@string/layout">
<menu>
<group android:checkableBehavior="single">
<item
android:id="@+id/single"
android:title="@string/single" />
<item
android:id="@+id/multiple"
android:title="@string/multiple" />
</group>
</menu>
</item>
<item android:title="@string/sort">
<menu>
<group android:checkableBehavior="single">
<item
android:id="@+id/default_"
android:title="@string/default_" />
<item
android:id="@+id/name"
android:title="@string/name" />
<item
android:id="@+id/delay"
android:title="@string/delay" />
</group>
</menu>
</item>
</menu>

Some files were not shown because too many files have changed in this diff Show More