Skip to content

Commit 8d4931c

Browse files
committed
Fix windows admin auto launch issues
Add android vpn options Support proxies icon configuration Optimize android immersion display Fix some issues
1 parent 23e3baf commit 8d4931c

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

80 files changed

+3910
-2442
lines changed
Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
package com.follow.clash
22

3-
import com.follow.clash.models.Props
4-
import com.follow.clash.models.TunProps
3+
4+
import com.follow.clash.models.VpnOptions
55

66
interface BaseServiceInterface {
7-
fun start(port: Int, props: Props?): TunProps?
7+
fun start(options: VpnOptions): Int
88
fun stop()
99
fun startForeground(title: String, content: String)
1010
}

android/app/src/main/kotlin/com/follow/clash/extensions/Ext.kt

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import android.system.OsConstants.IPPROTO_TCP
88
import android.system.OsConstants.IPPROTO_UDP
99
import android.util.Base64
1010
import androidx.core.graphics.drawable.toBitmap
11+
import com.follow.clash.models.CIDR
1112
import com.follow.clash.models.Metadata
1213
import kotlinx.coroutines.Dispatchers
1314
import kotlinx.coroutines.withContext
@@ -33,6 +34,25 @@ fun Metadata.getProtocol(): Int? {
3334
return null
3435
}
3536

37+
fun String.toCIDR(): CIDR {
38+
val parts = split("/")
39+
if (parts.size != 2) {
40+
throw IllegalArgumentException("Invalid CIDR format")
41+
}
42+
val ipAddress = parts[0]
43+
val prefixLength = parts[1].toIntOrNull()
44+
?: throw IllegalArgumentException("Invalid prefix length")
45+
46+
val address = InetAddress.getByName(ipAddress)
47+
48+
val maxPrefix = if (address.address.size == 4) 32 else 128
49+
if (prefixLength < 0 || prefixLength > maxPrefix) {
50+
throw IllegalArgumentException("Invalid prefix length for IP version")
51+
}
52+
53+
return CIDR(address, prefixLength)
54+
}
55+
3656

3757
fun ConnectivityManager.resolveDns(network: Network?): List<String> {
3858
val properties = getLinkProperties(network) ?: return listOf()
Lines changed: 13 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package com.follow.clash.models
22

3+
import java.net.InetAddress
4+
35
enum class AccessControlMode {
46
acceptSelected,
57
rejectSelected,
@@ -11,20 +13,16 @@ data class AccessControl(
1113
val rejectList: List<String>,
1214
)
1315

14-
data class Props(
15-
val enable: Boolean?,
16-
val accessControl: AccessControl?,
17-
val allowBypass: Boolean?,
18-
val systemProxy: Boolean?,
19-
val ipv6: Boolean?,
20-
)
16+
data class CIDR(val address: InetAddress, val prefixLength: Int)
2117

22-
data class TunProps(
23-
val fd: Int,
24-
val gateway: String,
25-
val gateway6: String,
26-
val portal: String,
27-
val portal6: String,
28-
val dns: String,
29-
val dns6: String
18+
data class VpnOptions(
19+
val enable: Boolean,
20+
val port: Int,
21+
val accessControl: AccessControl?,
22+
val allowBypass: Boolean,
23+
val systemProxy: Boolean,
24+
val bypassDomain: List<String>,
25+
val ipv4Address: String,
26+
val ipv6Address: String,
27+
val dnsServerAddress: String,
3028
)

android/app/src/main/kotlin/com/follow/clash/plugins/VpnPlugin.kt

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,6 @@ import com.follow.clash.GlobalState
1616
import com.follow.clash.RunState
1717
import com.follow.clash.extensions.getProtocol
1818
import com.follow.clash.extensions.resolveDns
19-
import com.follow.clash.models.Props
20-
import com.follow.clash.models.TunProps
2119
import com.follow.clash.services.FlClashService
2220
import com.follow.clash.services.FlClashVpnService
2321
import com.google.gson.Gson
@@ -31,14 +29,14 @@ import kotlinx.coroutines.withContext
3129
import java.net.InetSocketAddress
3230
import kotlin.concurrent.withLock
3331
import com.follow.clash.models.Process
32+
import com.follow.clash.models.VpnOptions
3433

3534

3635
class VpnPlugin : FlutterPlugin, MethodChannel.MethodCallHandler {
3736
private lateinit var flutterMethodChannel: MethodChannel
3837
private lateinit var context: Context
3938
private var flClashService: BaseServiceInterface? = null
40-
private var port: Int = 7890
41-
private var props: Props? = null
39+
private lateinit var options: VpnOptions
4240
private lateinit var scope: CoroutineScope
4341

4442
private val connectivity by lazy {
@@ -78,11 +76,9 @@ class VpnPlugin : FlutterPlugin, MethodChannel.MethodCallHandler {
7876
override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) {
7977
when (call.method) {
8078
"start" -> {
81-
port = call.argument<Int>("port")!!
82-
val args = call.argument<String>("args")
83-
props =
84-
if (args != null) Gson().fromJson(args, Props::class.java) else null
85-
when (props?.enable == true) {
79+
val data = call.argument<String>("data")
80+
options = Gson().fromJson(data, VpnOptions::class.java)
81+
when (options.enable) {
8682
true -> handleStartVpn()
8783
false -> start()
8884
}
@@ -241,10 +237,10 @@ class VpnPlugin : FlutterPlugin, MethodChannel.MethodCallHandler {
241237
GlobalState.runLock.withLock {
242238
if (GlobalState.runState.value == RunState.START) return
243239
GlobalState.runState.value = RunState.START
244-
val tunProps = flClashService?.start(port, props)
240+
val fd = flClashService?.start(options)
245241
flutterMethodChannel.invokeMethod(
246242
"started",
247-
Gson().toJson(tunProps, TunProps::class.java)
243+
fd
248244
)
249245
}
250246
}
@@ -259,7 +255,7 @@ class VpnPlugin : FlutterPlugin, MethodChannel.MethodCallHandler {
259255
}
260256

261257
private fun bindService() {
262-
val intent = when (props?.enable == true) {
258+
val intent = when (options.enable) {
263259
true -> Intent(context, FlClashVpnService::class.java)
264260
false -> Intent(context, FlClashService::class.java)
265261
}

android/app/src/main/kotlin/com/follow/clash/services/FlClashService.kt

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,7 @@ import android.os.IBinder
1414
import androidx.core.app.NotificationCompat
1515
import com.follow.clash.BaseServiceInterface
1616
import com.follow.clash.MainActivity
17-
import com.follow.clash.models.Props
18-
17+
import com.follow.clash.models.VpnOptions
1918

2019

2120
class FlClashService : Service(), BaseServiceInterface {
@@ -72,7 +71,7 @@ class FlClashService : Service(), BaseServiceInterface {
7271
}
7372
}
7473

75-
override fun start(port: Int, props: Props?) = null
74+
override fun start(options: VpnOptions) = 0
7675

7776
override fun stop() {
7877
stopSelf()

android/app/src/main/kotlin/com/follow/clash/services/FlClashVpnService.kt

Lines changed: 21 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -21,72 +21,35 @@ import com.follow.clash.GlobalState
2121
import com.follow.clash.MainActivity
2222
import com.follow.clash.R
2323
import com.follow.clash.TempActivity
24+
import com.follow.clash.extensions.toCIDR
2425
import com.follow.clash.models.AccessControlMode
25-
import com.follow.clash.models.Props
26-
import com.follow.clash.models.TunProps
26+
import com.follow.clash.models.VpnOptions
2727
import kotlinx.coroutines.CoroutineScope
2828
import kotlinx.coroutines.Dispatchers
2929
import kotlinx.coroutines.launch
3030

3131

3232
class FlClashVpnService : VpnService(), BaseServiceInterface {
33-
34-
companion object {
35-
private val passList = listOf(
36-
"*zhihu.com",
37-
"*zhimg.com",
38-
"*jd.com",
39-
"100ime-iat-api.xfyun.cn",
40-
"*360buyimg.com",
41-
"localhost",
42-
"*.local",
43-
"127.*",
44-
"10.*",
45-
"172.16.*",
46-
"172.17.*",
47-
"172.18.*",
48-
"172.19.*",
49-
"172.2*",
50-
"172.30.*",
51-
"172.31.*",
52-
"192.168.*"
53-
)
54-
private const val TUN_MTU = 9000
55-
private const val TUN_SUBNET_PREFIX = 30
56-
private const val TUN_GATEWAY = "172.19.0.1"
57-
private const val TUN_SUBNET_PREFIX6 = 126
58-
private const val TUN_GATEWAY6 = "fdfe:dcba:9876::1"
59-
private const val TUN_PORTAL = "172.19.0.2"
60-
private const val TUN_PORTAL6 = "fdfe:dcba:9876::2"
61-
private const val TUN_DNS = TUN_PORTAL
62-
private const val TUN_DNS6 = TUN_PORTAL6
63-
private const val NET_ANY = "0.0.0.0"
64-
private const val NET_ANY6 = "::"
65-
}
66-
6733
override fun onCreate() {
6834
super.onCreate()
6935
GlobalState.initServiceEngine(applicationContext)
7036
}
7137

72-
override fun start(port: Int, props: Props?): TunProps {
38+
override fun start(options: VpnOptions): Int {
7339
return with(Builder()) {
74-
addAddress(TUN_GATEWAY, TUN_SUBNET_PREFIX)
75-
addRoute(NET_ANY, 0)
76-
addDnsServer(TUN_DNS)
77-
78-
79-
if (props?.ipv6 == true) {
80-
try {
81-
addAddress(TUN_GATEWAY6, TUN_SUBNET_PREFIX6)
82-
addRoute(NET_ANY6, 0)
83-
addDnsServer(TUN_DNS6)
84-
} catch (_: Exception) {
85-
86-
}
40+
if (options.ipv4Address.isNotEmpty()) {
41+
val cidr = options.ipv4Address.toCIDR()
42+
addAddress(cidr.address, cidr.prefixLength)
43+
addRoute("0.0.0.0", 0)
8744
}
88-
setMtu(TUN_MTU)
89-
props?.accessControl?.let { accessControl ->
45+
if (options.ipv6Address.isNotEmpty()) {
46+
val cidr = options.ipv6Address.toCIDR()
47+
addAddress(cidr.address, cidr.prefixLength)
48+
addRoute("::", 0)
49+
}
50+
addDnsServer(options.dnsServerAddress)
51+
setMtu(9000)
52+
options.accessControl?.let { accessControl ->
9053
when (accessControl.mode) {
9154
AccessControlMode.acceptSelected -> {
9255
(accessControl.acceptList + packageName).forEach {
@@ -106,28 +69,20 @@ class FlClashVpnService : VpnService(), BaseServiceInterface {
10669
if (Build.VERSION.SDK_INT >= 29) {
10770
setMetered(false)
10871
}
109-
if (props?.allowBypass == true) {
72+
if (options.allowBypass) {
11073
allowBypass()
11174
}
112-
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q && props?.systemProxy == true) {
75+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q && options.systemProxy) {
11376
setHttpProxy(
11477
ProxyInfo.buildDirectProxy(
11578
"127.0.0.1",
116-
port,
117-
passList
79+
options.port,
80+
options.bypassDomain
11881
)
11982
)
12083
}
121-
TunProps(
122-
fd = establish()?.detachFd()
123-
?: throw NullPointerException("Establish VPN rejected by system"),
124-
gateway = "$TUN_GATEWAY/$TUN_SUBNET_PREFIX",
125-
gateway6 = if (props?.ipv6 == true) "$TUN_GATEWAY6/$TUN_SUBNET_PREFIX6" else "",
126-
portal = TUN_PORTAL,
127-
portal6 = if (props?.ipv6 == true) TUN_PORTAL6 else "",
128-
dns = TUN_DNS,
129-
dns6 = if (props?.ipv6 == true) TUN_DNS6 else ""
130-
)
84+
establish()?.detachFd()
85+
?: throw NullPointerException("Establish VPN rejected by system")
13186
}
13287
}
13388

0 commit comments

Comments
 (0)