Skip to content

Commit

Permalink
Fix windows admin auto launch issues
Browse files Browse the repository at this point in the history
Add android vpn options

Support proxies icon configuration

Optimize android immersion display

Fix some issues
  • Loading branch information
chen08209 committed Oct 8, 2024
1 parent 23e3baf commit 8d4931c
Show file tree
Hide file tree
Showing 80 changed files with 3,910 additions and 2,442 deletions.
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
package com.follow.clash

import com.follow.clash.models.Props
import com.follow.clash.models.TunProps

import com.follow.clash.models.VpnOptions

interface BaseServiceInterface {
fun start(port: Int, props: Props?): TunProps?
fun start(options: VpnOptions): Int
fun stop()
fun startForeground(title: String, content: String)
}
20 changes: 20 additions & 0 deletions android/app/src/main/kotlin/com/follow/clash/extensions/Ext.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import android.system.OsConstants.IPPROTO_TCP
import android.system.OsConstants.IPPROTO_UDP
import android.util.Base64
import androidx.core.graphics.drawable.toBitmap
import com.follow.clash.models.CIDR
import com.follow.clash.models.Metadata
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
Expand All @@ -33,6 +34,25 @@ fun Metadata.getProtocol(): Int? {
return null
}

fun String.toCIDR(): CIDR {
val parts = split("/")
if (parts.size != 2) {
throw IllegalArgumentException("Invalid CIDR format")
}
val ipAddress = parts[0]
val prefixLength = parts[1].toIntOrNull()
?: throw IllegalArgumentException("Invalid prefix length")

val address = InetAddress.getByName(ipAddress)

val maxPrefix = if (address.address.size == 4) 32 else 128
if (prefixLength < 0 || prefixLength > maxPrefix) {
throw IllegalArgumentException("Invalid prefix length for IP version")
}

return CIDR(address, prefixLength)
}


fun ConnectivityManager.resolveDns(network: Network?): List<String> {
val properties = getLinkProperties(network) ?: return listOf()
Expand Down
28 changes: 13 additions & 15 deletions android/app/src/main/kotlin/com/follow/clash/models/Props.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package com.follow.clash.models

import java.net.InetAddress

enum class AccessControlMode {
acceptSelected,
rejectSelected,
Expand All @@ -11,20 +13,16 @@ data class AccessControl(
val rejectList: List<String>,
)

data class Props(
val enable: Boolean?,
val accessControl: AccessControl?,
val allowBypass: Boolean?,
val systemProxy: Boolean?,
val ipv6: Boolean?,
)
data class CIDR(val address: InetAddress, val prefixLength: Int)

data class TunProps(
val fd: Int,
val gateway: String,
val gateway6: String,
val portal: String,
val portal6: String,
val dns: String,
val dns6: String
data class VpnOptions(
val enable: Boolean,
val port: Int,
val accessControl: AccessControl?,
val allowBypass: Boolean,
val systemProxy: Boolean,
val bypassDomain: List<String>,
val ipv4Address: String,
val ipv6Address: String,
val dnsServerAddress: String,
)
20 changes: 8 additions & 12 deletions android/app/src/main/kotlin/com/follow/clash/plugins/VpnPlugin.kt
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,6 @@ import com.follow.clash.GlobalState
import com.follow.clash.RunState
import com.follow.clash.extensions.getProtocol
import com.follow.clash.extensions.resolveDns
import com.follow.clash.models.Props
import com.follow.clash.models.TunProps
import com.follow.clash.services.FlClashService
import com.follow.clash.services.FlClashVpnService
import com.google.gson.Gson
Expand All @@ -31,14 +29,14 @@ import kotlinx.coroutines.withContext
import java.net.InetSocketAddress
import kotlin.concurrent.withLock
import com.follow.clash.models.Process
import com.follow.clash.models.VpnOptions


class VpnPlugin : FlutterPlugin, MethodChannel.MethodCallHandler {
private lateinit var flutterMethodChannel: MethodChannel
private lateinit var context: Context
private var flClashService: BaseServiceInterface? = null
private var port: Int = 7890
private var props: Props? = null
private lateinit var options: VpnOptions
private lateinit var scope: CoroutineScope

private val connectivity by lazy {
Expand Down Expand Up @@ -78,11 +76,9 @@ class VpnPlugin : FlutterPlugin, MethodChannel.MethodCallHandler {
override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) {
when (call.method) {
"start" -> {
port = call.argument<Int>("port")!!
val args = call.argument<String>("args")
props =
if (args != null) Gson().fromJson(args, Props::class.java) else null
when (props?.enable == true) {
val data = call.argument<String>("data")
options = Gson().fromJson(data, VpnOptions::class.java)
when (options.enable) {
true -> handleStartVpn()
false -> start()
}
Expand Down Expand Up @@ -241,10 +237,10 @@ class VpnPlugin : FlutterPlugin, MethodChannel.MethodCallHandler {
GlobalState.runLock.withLock {
if (GlobalState.runState.value == RunState.START) return
GlobalState.runState.value = RunState.START
val tunProps = flClashService?.start(port, props)
val fd = flClashService?.start(options)
flutterMethodChannel.invokeMethod(
"started",
Gson().toJson(tunProps, TunProps::class.java)
fd
)
}
}
Expand All @@ -259,7 +255,7 @@ class VpnPlugin : FlutterPlugin, MethodChannel.MethodCallHandler {
}

private fun bindService() {
val intent = when (props?.enable == true) {
val intent = when (options.enable) {
true -> Intent(context, FlClashVpnService::class.java)
false -> Intent(context, FlClashService::class.java)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,7 @@ import android.os.IBinder
import androidx.core.app.NotificationCompat
import com.follow.clash.BaseServiceInterface
import com.follow.clash.MainActivity
import com.follow.clash.models.Props

import com.follow.clash.models.VpnOptions


class FlClashService : Service(), BaseServiceInterface {
Expand Down Expand Up @@ -72,7 +71,7 @@ class FlClashService : Service(), BaseServiceInterface {
}
}

override fun start(port: Int, props: Props?) = null
override fun start(options: VpnOptions) = 0

override fun stop() {
stopSelf()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,72 +21,35 @@ import com.follow.clash.GlobalState
import com.follow.clash.MainActivity
import com.follow.clash.R
import com.follow.clash.TempActivity
import com.follow.clash.extensions.toCIDR
import com.follow.clash.models.AccessControlMode
import com.follow.clash.models.Props
import com.follow.clash.models.TunProps
import com.follow.clash.models.VpnOptions
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch


class FlClashVpnService : VpnService(), BaseServiceInterface {

companion object {
private val passList = listOf(
"*zhihu.com",
"*zhimg.com",
"*jd.com",
"100ime-iat-api.xfyun.cn",
"*360buyimg.com",
"localhost",
"*.local",
"127.*",
"10.*",
"172.16.*",
"172.17.*",
"172.18.*",
"172.19.*",
"172.2*",
"172.30.*",
"172.31.*",
"192.168.*"
)
private const val TUN_MTU = 9000
private const val TUN_SUBNET_PREFIX = 30
private const val TUN_GATEWAY = "172.19.0.1"
private const val TUN_SUBNET_PREFIX6 = 126
private const val TUN_GATEWAY6 = "fdfe:dcba:9876::1"
private const val TUN_PORTAL = "172.19.0.2"
private const val TUN_PORTAL6 = "fdfe:dcba:9876::2"
private const val TUN_DNS = TUN_PORTAL
private const val TUN_DNS6 = TUN_PORTAL6
private const val NET_ANY = "0.0.0.0"
private const val NET_ANY6 = "::"
}

override fun onCreate() {
super.onCreate()
GlobalState.initServiceEngine(applicationContext)
}

override fun start(port: Int, props: Props?): TunProps {
override fun start(options: VpnOptions): Int {
return with(Builder()) {
addAddress(TUN_GATEWAY, TUN_SUBNET_PREFIX)
addRoute(NET_ANY, 0)
addDnsServer(TUN_DNS)


if (props?.ipv6 == true) {
try {
addAddress(TUN_GATEWAY6, TUN_SUBNET_PREFIX6)
addRoute(NET_ANY6, 0)
addDnsServer(TUN_DNS6)
} catch (_: Exception) {

}
if (options.ipv4Address.isNotEmpty()) {
val cidr = options.ipv4Address.toCIDR()
addAddress(cidr.address, cidr.prefixLength)
addRoute("0.0.0.0", 0)
}
setMtu(TUN_MTU)
props?.accessControl?.let { accessControl ->
if (options.ipv6Address.isNotEmpty()) {
val cidr = options.ipv6Address.toCIDR()
addAddress(cidr.address, cidr.prefixLength)
addRoute("::", 0)
}
addDnsServer(options.dnsServerAddress)
setMtu(9000)
options.accessControl?.let { accessControl ->
when (accessControl.mode) {
AccessControlMode.acceptSelected -> {
(accessControl.acceptList + packageName).forEach {
Expand All @@ -106,28 +69,20 @@ class FlClashVpnService : VpnService(), BaseServiceInterface {
if (Build.VERSION.SDK_INT >= 29) {
setMetered(false)
}
if (props?.allowBypass == true) {
if (options.allowBypass) {
allowBypass()
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q && props?.systemProxy == true) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q && options.systemProxy) {
setHttpProxy(
ProxyInfo.buildDirectProxy(
"127.0.0.1",
port,
passList
options.port,
options.bypassDomain
)
)
}
TunProps(
fd = establish()?.detachFd()
?: throw NullPointerException("Establish VPN rejected by system"),
gateway = "$TUN_GATEWAY/$TUN_SUBNET_PREFIX",
gateway6 = if (props?.ipv6 == true) "$TUN_GATEWAY6/$TUN_SUBNET_PREFIX6" else "",
portal = TUN_PORTAL,
portal6 = if (props?.ipv6 == true) TUN_PORTAL6 else "",
dns = TUN_DNS,
dns6 = if (props?.ipv6 == true) TUN_DNS6 else ""
)
establish()?.detachFd()
?: throw NullPointerException("Establish VPN rejected by system")
}
}

Expand Down
Loading

0 comments on commit 8d4931c

Please sign in to comment.