mirror of
https://github.com/wgtunnel/android.git
synced 2026-06-02 00:29:08 +02:00
fix: kill switch mode tunnel bug, restore/app bootstrap logic for killswitch and tunnels
This commit is contained in:
@@ -171,7 +171,7 @@ androidComponents {
|
||||
} else {
|
||||
"${baseFileName}.apk"
|
||||
}
|
||||
|
||||
|
||||
output.outputFileName.set(outputFileName)
|
||||
}
|
||||
}
|
||||
|
||||
+46
-2
@@ -4,26 +4,56 @@ import com.zaneschepke.logcatter.LogReader
|
||||
import com.zaneschepke.tunnel.backend.Backend
|
||||
import com.zaneschepke.tunnel.model.DnsBoostrapConfig
|
||||
import com.zaneschepke.tunnel.model.DnsBoostrapMode
|
||||
import com.zaneschepke.wireguardautotunnel.core.tunnel.TunnelProvider
|
||||
import com.zaneschepke.wireguardautotunnel.domain.enums.DnsProtocol
|
||||
import com.zaneschepke.wireguardautotunnel.domain.enums.TunnelMode
|
||||
import com.zaneschepke.wireguardautotunnel.domain.repository.DnsSettingsRepository
|
||||
import com.zaneschepke.wireguardautotunnel.domain.repository.GeneralSettingRepository
|
||||
import com.zaneschepke.wireguardautotunnel.domain.repository.LockdownSettingsRepository
|
||||
import com.zaneschepke.wireguardautotunnel.domain.repository.MonitoringSettingsRepository
|
||||
import com.zaneschepke.wireguardautotunnel.domain.repository.TunnelRepository
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.awaitAll
|
||||
import kotlinx.coroutines.coroutineScope
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.flow.distinctUntilChangedBy
|
||||
import kotlinx.coroutines.launch
|
||||
import timber.log.Timber
|
||||
|
||||
class AppBoostrapCoordinator(
|
||||
private val monitoringRepository: MonitoringSettingsRepository,
|
||||
private val settingsRepository: GeneralSettingRepository,
|
||||
private val dnsRepository: DnsSettingsRepository,
|
||||
private val tunnelRepository: TunnelRepository,
|
||||
private val lockdownRepository: LockdownSettingsRepository,
|
||||
private val tunnelProvider: TunnelProvider,
|
||||
private val backend: Backend,
|
||||
private val logReader: LogReader,
|
||||
) {
|
||||
|
||||
private val _isReady = MutableStateFlow(false)
|
||||
val isReady: StateFlow<Boolean> = _isReady.asStateFlow()
|
||||
|
||||
suspend fun bootstrap() = coroutineScope {
|
||||
launch { bootstrapDns() }
|
||||
launch { bootstrapLogging() }
|
||||
launch { ensureGlobalConfig() }
|
||||
|
||||
val criticalTasks =
|
||||
listOf(
|
||||
async { bootstrapDns() },
|
||||
async { ensureGlobalConfig() },
|
||||
async { restoreLockdown() },
|
||||
)
|
||||
|
||||
try {
|
||||
criticalTasks.awaitAll()
|
||||
_isReady.value = true
|
||||
Timber.d("App bootstrap completed successfully")
|
||||
} catch (e: Exception) {
|
||||
Timber.e(e, "One or more critical bootstrap tasks failed")
|
||||
_isReady.value = true
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun bootstrapDns() {
|
||||
@@ -58,4 +88,18 @@ class AppBoostrapCoordinator(
|
||||
private suspend fun ensureGlobalConfig() {
|
||||
tunnelRepository.ensureGlobalConfigExists()
|
||||
}
|
||||
|
||||
private suspend fun restoreLockdown() {
|
||||
val settings = settingsRepository.getGeneralSettings()
|
||||
|
||||
when (settings.tunnelMode) {
|
||||
TunnelMode.LOCK_DOWN -> {
|
||||
val lockdownSettings = lockdownRepository.getLockdownSettings()
|
||||
tunnelProvider.setLockDown(lockdownSettings).onFailure {
|
||||
Timber.w(it, "Failed to restore lockdown/kill-switch on startup")
|
||||
}
|
||||
}
|
||||
else -> Unit
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+2
-5
@@ -10,16 +10,13 @@ class AutoTunnelCoordinator(
|
||||
private val autoTunnelStateHolder: AutoTunnelStateHolder,
|
||||
) {
|
||||
|
||||
suspend fun shouldTakeOverBoot(): Boolean {
|
||||
suspend fun shouldRestore(): Boolean {
|
||||
val settings = repository.getAutoTunnelSettings()
|
||||
return settings.startOnBoot && settings.isAutoTunnelEnabled
|
||||
}
|
||||
|
||||
suspend fun restoreIfNeeded(): Boolean {
|
||||
if (!shouldTakeOverBoot()) return false
|
||||
|
||||
fun start() {
|
||||
serviceManager.startAutoTunnelService()
|
||||
return true
|
||||
}
|
||||
|
||||
suspend fun enable() {
|
||||
|
||||
+11
-28
@@ -1,53 +1,36 @@
|
||||
package com.zaneschepke.wireguardautotunnel.core.orchestration
|
||||
|
||||
import com.zaneschepke.wireguardautotunnel.core.service.ServiceManager
|
||||
import com.zaneschepke.wireguardautotunnel.core.tunnel.TunnelProvider
|
||||
import com.zaneschepke.wireguardautotunnel.domain.enums.TunnelMode
|
||||
import com.zaneschepke.wireguardautotunnel.domain.repository.GeneralSettingRepository
|
||||
import com.zaneschepke.wireguardautotunnel.domain.repository.LockdownSettingsRepository
|
||||
import com.zaneschepke.wireguardautotunnel.domain.repository.TunnelRepository
|
||||
import kotlinx.coroutines.flow.first
|
||||
|
||||
class StartupCoordinator(
|
||||
private val tunnelCoordinator: TunnelCoordinator,
|
||||
private val tunnelProvider: TunnelProvider,
|
||||
private val settingsRepository: GeneralSettingRepository,
|
||||
private val autoTunnelCoordinator: AutoTunnelCoordinator,
|
||||
private val tunnelRepository: TunnelRepository,
|
||||
private val lockdownRepository: LockdownSettingsRepository,
|
||||
private val serviceManager: ServiceManager,
|
||||
private val bootstrapCoordinator: AppBoostrapCoordinator,
|
||||
) {
|
||||
|
||||
suspend fun applyStartupPolicy(): Result<Unit> {
|
||||
|
||||
suspend fun applyStartupPolicy(): Result<Unit> = runCatching {
|
||||
val shouldRestoreAutoTunnel = autoTunnelCoordinator.shouldRestore()
|
||||
val settings = settingsRepository.getGeneralSettings()
|
||||
val shouldRestoreDefaultTunnel = settings.isRestoreOnBootEnabled
|
||||
|
||||
if (!settings.isRestoreOnBootEnabled) {
|
||||
if (shouldRestoreAutoTunnel || shouldRestoreDefaultTunnel) {
|
||||
// Wait for app critical bootstrap to finish
|
||||
bootstrapCoordinator.isReady.first { it }
|
||||
} else {
|
||||
return Result.success(Unit)
|
||||
}
|
||||
|
||||
val autoTunnelTookOver = autoTunnelCoordinator.restoreIfNeeded()
|
||||
|
||||
if (autoTunnelTookOver) {
|
||||
if (shouldRestoreAutoTunnel) {
|
||||
autoTunnelCoordinator.start()
|
||||
return Result.success(Unit)
|
||||
}
|
||||
|
||||
val mode = settings.tunnelMode
|
||||
|
||||
if (mode == TunnelMode.VPN && !serviceManager.hasVpnPermission()) {
|
||||
return Result.failure(IllegalStateException("VPN permission missing"))
|
||||
}
|
||||
|
||||
if (mode == TunnelMode.LOCK_DOWN) {
|
||||
val lockdownSettings = lockdownRepository.getLockdownSettings()
|
||||
tunnelProvider.setLockDown(lockdownSettings).getOrElse {
|
||||
return Result.failure(it)
|
||||
}
|
||||
}
|
||||
|
||||
val defaultTunnel = tunnelRepository.getDefaultTunnel() ?: return Result.success(Unit)
|
||||
|
||||
tunnelCoordinator.startTunnel(defaultTunnel)
|
||||
|
||||
return Result.success(Unit)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
package com.zaneschepke.hevtunnel
|
||||
|
||||
import android.content.Context
|
||||
import androidx.annotation.Keep
|
||||
import java.io.File
|
||||
import java.io.FileOutputStream
|
||||
import java.io.IOException
|
||||
|
||||
@Keep
|
||||
object TProxyService {
|
||||
private const val HEV_CONFIG_FILE_NAME: String = "tproxy.conf"
|
||||
private const val TASK_STACK_SIZE = 24576
|
||||
@@ -42,8 +40,7 @@ object TProxyService {
|
||||
"""
|
||||
.trimIndent()
|
||||
|
||||
FileOutputStream(tproxyFile, false).use { fos -> fos.write(hevConf.toByteArray()) }
|
||||
|
||||
FileOutputStream(tproxyFile, false).use { it.write(hevConf.toByteArray()) }
|
||||
return tproxyFile
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,7 +27,8 @@ interface Backend {
|
||||
|
||||
suspend fun setBootstrapDnsMode(mode: DnsBoostrapMode)
|
||||
|
||||
suspend fun stopAllOfType(modeClass: KClass<out BackendMode>): Result<Unit>
|
||||
// Emergency synchronous teardown to be called only from Service.onDestroy()
|
||||
fun emergencyStopAllOfTypeSync(modeClass: KClass<out BackendMode>)
|
||||
|
||||
suspend fun stopAllActiveTunnels(): Result<Unit>
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ import com.zaneschepke.tunnel.model.KillSwitchConfig
|
||||
internal interface KillSwitch {
|
||||
fun setKillSwitch(config: KillSwitchConfig?)
|
||||
|
||||
fun startHevSocks5Bridge()
|
||||
fun startHevSocks5Bridge(port: Int, pass: String)
|
||||
|
||||
fun stopHevSocks5Bridge()
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@ import com.zaneschepke.tunnel.state.TunnelRuntimeState
|
||||
import com.zaneschepke.tunnel.util.RootShellException
|
||||
import com.zaneschepke.tunnel.util.buildResolvedPeers
|
||||
import com.zaneschepke.tunnel.util.exponentialBackoffForever
|
||||
import kotlin.reflect.KClass
|
||||
import kotlin.time.Duration.Companion.seconds
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
@@ -623,6 +624,27 @@ internal class TunnelActor(
|
||||
}
|
||||
}
|
||||
|
||||
fun emergencyStop(tunnelId: Int) {
|
||||
val runtime = _state.value.byTunnelId[tunnelId] ?: return
|
||||
val handle = runtime.running.handle
|
||||
val mode = runtime.running.mode
|
||||
|
||||
Timber.d("Emergency stop tunnel $tunnelId (handle=$handle, mode=$mode)")
|
||||
|
||||
engine.stop(handle, mode)
|
||||
|
||||
// Immediately clean up actor state
|
||||
stopTunnel(tunnelId, handle)
|
||||
}
|
||||
|
||||
// Convenience method for services
|
||||
fun emergencyStopAllOfType(modeClass: KClass<out BackendMode>) {
|
||||
_state.value.byTunnelId
|
||||
.filter { (_, runtime) -> modeClass.isInstance(runtime.running.mode) }
|
||||
.keys
|
||||
.forEach { emergencyStop(it) }
|
||||
}
|
||||
|
||||
suspend fun resolvePeers(runningTunnel: RunningTunnel): Map<PublicKey, DnsBootstrapResult> {
|
||||
|
||||
val peersToResolve = runningTunnel.mode.config.peers.filter { !it.isStaticallyConfigured }
|
||||
|
||||
@@ -209,15 +209,9 @@ class TunnelBackend(
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun stopAllOfType(modeClass: KClass<out BackendMode>): Result<Unit> =
|
||||
runCatching {
|
||||
val idsToStop =
|
||||
_status.value.activeTunnels
|
||||
.filter { (_, activeTunnel) -> modeClass.isInstance(activeTunnel.mode) }
|
||||
.keys
|
||||
|
||||
idsToStop.forEach { id -> stop(id) }
|
||||
}
|
||||
override fun emergencyStopAllOfTypeSync(modeClass: KClass<out BackendMode>) {
|
||||
actor.emergencyStopAllOfType(modeClass)
|
||||
}
|
||||
|
||||
override suspend fun stopAllActiveTunnels(): Result<Unit> = runCatching {
|
||||
_status.value.activeTunnels.forEach { (id, _) -> stop(id) }
|
||||
|
||||
@@ -14,9 +14,9 @@ internal interface TunnelEngine {
|
||||
val status: Flow<NativeTunnelStatus>
|
||||
val state: Flow<EngineState>
|
||||
|
||||
suspend fun start(tunnel: Tunnel, mode: BackendMode): EngineStartResult
|
||||
fun start(tunnel: Tunnel, mode: BackendMode): EngineStartResult
|
||||
|
||||
suspend fun stop(handle: Int, mode: BackendMode)
|
||||
fun stop(handle: Int, mode: BackendMode)
|
||||
|
||||
suspend fun updatePeers(handle: Int, mode: BackendMode, peers: List<PeerSection>)
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ import com.zaneschepke.tunnel.Tunnel
|
||||
import com.zaneschepke.tunnel.VpnBackend
|
||||
import com.zaneschepke.tunnel.model.BackendMode
|
||||
import com.zaneschepke.tunnel.model.ProxyConfig
|
||||
import com.zaneschepke.tunnel.service.VpnService
|
||||
import com.zaneschepke.tunnel.state.EngineStartResult
|
||||
import com.zaneschepke.tunnel.state.EngineState
|
||||
import com.zaneschepke.tunnel.state.NativeTunnelStatus
|
||||
@@ -22,13 +23,11 @@ internal class WireGuardTunnelEngine(
|
||||
stateProvider: EngineStateProvider,
|
||||
) : TunnelEngine {
|
||||
|
||||
private val proxyPass = UUID.randomUUID().toString()
|
||||
|
||||
override val status: Flow<NativeTunnelStatus> = serviceHolder.nativeStatuses
|
||||
|
||||
override val state: Flow<EngineState> = stateProvider.state
|
||||
|
||||
override suspend fun start(tunnel: Tunnel, mode: BackendMode): EngineStartResult {
|
||||
override fun start(tunnel: Tunnel, mode: BackendMode): EngineStartResult {
|
||||
|
||||
val ifName = WGT_INTERFACE_PREFIX + tunnel.id
|
||||
|
||||
@@ -80,8 +79,8 @@ internal class WireGuardTunnelEngine(
|
||||
socks5 =
|
||||
ProxyConfig.Socks5(
|
||||
port = getAvailablePort(),
|
||||
username = LOCKDOWN_USER,
|
||||
password = proxyPass,
|
||||
username = VpnService.LOCKDOWN_USERNAME,
|
||||
password = UUID.randomUUID().toString(),
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -111,8 +110,8 @@ internal class WireGuardTunnelEngine(
|
||||
@Throws(IOException::class)
|
||||
private fun getAvailablePort(): Int {
|
||||
ServerSocket(0).use { socket ->
|
||||
socket.setReuseAddress(true)
|
||||
return socket.getLocalPort()
|
||||
socket.reuseAddress = true
|
||||
return socket.localPort
|
||||
}
|
||||
}
|
||||
|
||||
@@ -121,7 +120,7 @@ internal class WireGuardTunnelEngine(
|
||||
return peer.copy(endpoint = null)
|
||||
}
|
||||
|
||||
override suspend fun stop(handle: Int, mode: BackendMode) {
|
||||
override fun stop(handle: Int, mode: BackendMode) {
|
||||
when (mode) {
|
||||
is BackendMode.Proxy -> {
|
||||
ProxyBackend.awgTurnProxyTunnelOff(handle)
|
||||
@@ -179,7 +178,17 @@ internal class WireGuardTunnelEngine(
|
||||
}
|
||||
|
||||
if (withBridge) {
|
||||
serviceHolder.getVpnService().startHevSocks5Bridge()
|
||||
val port =
|
||||
proxyConfig.socks5?.port
|
||||
?: throw BackendException.InternalError(
|
||||
"Bridge port not set for kill switch proxy config"
|
||||
)
|
||||
val pass =
|
||||
proxyConfig.socks5.password
|
||||
?: throw BackendException.InternalError(
|
||||
"Bridge pass not set for kill switch proxy config"
|
||||
)
|
||||
serviceHolder.getVpnService().startHevSocks5Bridge(port, pass)
|
||||
}
|
||||
|
||||
return handle
|
||||
@@ -194,7 +203,6 @@ internal class WireGuardTunnelEngine(
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val LOCKDOWN_USER = "local"
|
||||
const val WGT_INTERFACE_PREFIX = "wgtun"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,6 @@ import com.zaneschepke.tunnel.backend.Backend
|
||||
import com.zaneschepke.tunnel.backend.ServiceHolder
|
||||
import com.zaneschepke.tunnel.backend.ServiceHolder.Companion.alwaysOnCallback
|
||||
import com.zaneschepke.tunnel.model.BackendMode
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.koin.java.KoinJavaComponent.inject
|
||||
import timber.log.Timber
|
||||
|
||||
@@ -42,7 +41,7 @@ class TunnelService : LifecycleService() {
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
runBlocking { backend.stopAllOfType(BackendMode.Proxy.Standard::class) }
|
||||
backend.emergencyStopAllOfTypeSync(BackendMode.Proxy.Standard::class)
|
||||
|
||||
ServiceCompat.stopForeground(this, ServiceCompat.STOP_FOREGROUND_REMOVE)
|
||||
serviceHolder.clear(this)
|
||||
|
||||
@@ -4,7 +4,6 @@ import android.content.Intent
|
||||
import android.os.Build
|
||||
import android.os.ParcelFileDescriptor
|
||||
import android.system.OsConstants
|
||||
import androidx.annotation.Keep
|
||||
import androidx.core.app.ServiceCompat
|
||||
import com.zaneschepke.hevtunnel.HevTunnelConfig
|
||||
import com.zaneschepke.hevtunnel.TProxyService
|
||||
@@ -23,15 +22,13 @@ import com.zaneschepke.tunnel.util.parseDns
|
||||
import com.zaneschepke.tunnel.util.parseInetNetwork
|
||||
import com.zaneschepke.wireguardautotunnel.parser.Config
|
||||
import java.io.IOException
|
||||
import java.net.ServerSocket
|
||||
import java.util.UUID
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.SupervisorJob
|
||||
import kotlinx.coroutines.cancel
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.koin.java.KoinJavaComponent.inject
|
||||
import timber.log.Timber
|
||||
|
||||
@@ -40,8 +37,6 @@ class VpnService : android.net.VpnService(), KillSwitch, SocketProtector {
|
||||
private val backend: Backend by inject(Backend::class.java)
|
||||
private val serviceHolder: ServiceHolder by inject(ServiceHolder::class.java)
|
||||
|
||||
private val defaultPass = UUID.randomUUID().toString()
|
||||
|
||||
private val serviceScope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
|
||||
private var hevBridgeJob: Job? = null
|
||||
private var fd: ParcelFileDescriptor? = null
|
||||
@@ -79,10 +74,10 @@ class VpnService : android.net.VpnService(), KillSwitch, SocketProtector {
|
||||
|
||||
serviceScope.cancel()
|
||||
|
||||
runBlocking {
|
||||
backend.stopAllOfType(BackendMode.Vpn::class)
|
||||
backend.stopAllOfType(BackendMode.Proxy.KillSwitchPrimary::class)
|
||||
}
|
||||
backend.emergencyStopAllOfTypeSync(BackendMode.Vpn::class)
|
||||
backend.emergencyStopAllOfTypeSync(BackendMode.Proxy.KillSwitchPrimary::class)
|
||||
|
||||
stopHevSocks5Bridge()
|
||||
|
||||
serviceHolder.clear(this)
|
||||
|
||||
@@ -105,40 +100,61 @@ class VpnService : android.net.VpnService(), KillSwitch, SocketProtector {
|
||||
return START_STICKY
|
||||
}
|
||||
|
||||
private fun startHevBridge(): Job {
|
||||
private fun startHevBridge(port: Int, pass: String): Job {
|
||||
val job = serviceScope.launch {
|
||||
try {
|
||||
val port = getAvailablePort()
|
||||
val fd = fd ?: throw IOException("No VPN interface fd available")
|
||||
val config =
|
||||
HevTunnelConfig(
|
||||
port = port,
|
||||
mtu = DEFAULT_MTU,
|
||||
ipv4 = IPV4_INTERFACE_ADDRESS,
|
||||
ipv6 = IPV6_INTERFACE_ADDRESS,
|
||||
address = LOCALHOST,
|
||||
username = DEFAULT_USERNAME,
|
||||
password = defaultPass,
|
||||
)
|
||||
val hevConfigFile = TProxyService.createHevTunnelConfig(config, this@VpnService)
|
||||
TProxyService.TProxyStartService(hevConfigFile.absolutePath, fd.fd)
|
||||
} catch (e: IOException) {
|
||||
Timber.e(e)
|
||||
val vpnFd = fd ?: throw IOException("No VPN interface fd available")
|
||||
|
||||
repeat(60) { attempt ->
|
||||
try {
|
||||
java.net.Socket().use { socket ->
|
||||
socket.connect(java.net.InetSocketAddress(LOCALHOST, port), 800)
|
||||
}
|
||||
|
||||
Timber.d(
|
||||
"SOCKS5 proxy is ready on port $port, starting HEV bridge (attempt ${attempt + 1})"
|
||||
)
|
||||
|
||||
val config =
|
||||
HevTunnelConfig(
|
||||
port = port,
|
||||
mtu = DEFAULT_MTU,
|
||||
ipv4 = IPV4_INTERFACE_ADDRESS,
|
||||
ipv6 = IPV6_INTERFACE_ADDRESS,
|
||||
address = LOCALHOST,
|
||||
username = LOCKDOWN_USERNAME,
|
||||
password = pass,
|
||||
)
|
||||
val hevConfigFile =
|
||||
TProxyService.createHevTunnelConfig(config, this@VpnService)
|
||||
TProxyService.TProxyStartService(hevConfigFile.absolutePath, vpnFd.fd)
|
||||
|
||||
Timber.d("HEV bridge started successfully - coroutine can now exit")
|
||||
return@launch // safe to exit as hev handles own threading internally
|
||||
} catch (e: Exception) {
|
||||
Timber.w(e, "SOCKS5 connect failed (attempt ${attempt + 1})")
|
||||
if (attempt % 5 == 0) {
|
||||
Timber.d("SOCKS5 not ready yet, retrying...")
|
||||
}
|
||||
delay(300)
|
||||
}
|
||||
}
|
||||
Timber.e("Timed out waiting for SOCKS5 proxy to be ready")
|
||||
} catch (e: Exception) {
|
||||
Timber.e(e, "Failed to start HEV bridge")
|
||||
}
|
||||
}
|
||||
job.invokeOnCompletion {
|
||||
TProxyService.TProxyStopService()
|
||||
|
||||
// stop HEV when the job is canceled from stopHevSocks5Bridge or onDestroy
|
||||
job.invokeOnCompletion { cause ->
|
||||
if (cause != null) { // canceled or failed
|
||||
Timber.d("HEV bridge job stopped - shutting down native HEV")
|
||||
TProxyService.TProxyStopService()
|
||||
}
|
||||
hevBridgeJob = null
|
||||
}
|
||||
return job
|
||||
}
|
||||
|
||||
@Throws(IOException::class)
|
||||
private fun getAvailablePort(): Int {
|
||||
ServerSocket(0).use { socket ->
|
||||
socket.setReuseAddress(true)
|
||||
return socket.getLocalPort()
|
||||
}
|
||||
return job
|
||||
}
|
||||
|
||||
private fun disableKillSwitch() {
|
||||
@@ -164,11 +180,15 @@ class VpnService : android.net.VpnService(), KillSwitch, SocketProtector {
|
||||
}
|
||||
}
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||
builder.setMetered(config.metered)
|
||||
setMetered(config.metered)
|
||||
}
|
||||
addRoute(IPV6_DEFAULT_ROUTE, 0)
|
||||
setMtu(DEFAULT_MTU)
|
||||
addDnsServer(DEFAULT_DNS_SERVER)
|
||||
|
||||
// TODO could add an options to kill switch settings for this for ping
|
||||
// sorts/update checks, etc to bypass killswitch
|
||||
// addDisallowedApplication(this@VpnService.packageName)
|
||||
}
|
||||
.establish()
|
||||
}
|
||||
@@ -216,14 +236,20 @@ class VpnService : android.net.VpnService(), KillSwitch, SocketProtector {
|
||||
.establish()
|
||||
}
|
||||
|
||||
override fun startHevSocks5Bridge() {
|
||||
override fun startHevSocks5Bridge(port: Int, pass: String) {
|
||||
if (hevBridgeJob != null) return
|
||||
hevBridgeJob = startHevBridge()
|
||||
hevBridgeJob = startHevBridge(port, pass)
|
||||
}
|
||||
|
||||
override fun stopHevSocks5Bridge() {
|
||||
hevBridgeJob?.cancel()
|
||||
hevBridgeJob = null
|
||||
|
||||
try {
|
||||
TProxyService.TProxyStopService()
|
||||
} catch (e: Exception) {
|
||||
Timber.w(e, "TProxyStopService failed, may already be stopped")
|
||||
}
|
||||
}
|
||||
|
||||
override fun bypass(fd: Int): Int {
|
||||
@@ -248,7 +274,7 @@ class VpnService : android.net.VpnService(), KillSwitch, SocketProtector {
|
||||
private const val LOCALHOST = "127.0.0.1"
|
||||
private const val IPV4_INTERFACE_ADDRESS = "10.0.0.1"
|
||||
private const val IPV6_INTERFACE_ADDRESS = "2001:db8::1"
|
||||
private const val DEFAULT_USERNAME = "local"
|
||||
const val LOCKDOWN_USERNAME = "local"
|
||||
private const val IPV4_DEFAULT_ROUTE = "0.0.0.0"
|
||||
private const val IPV6_DEFAULT_ROUTE = "::"
|
||||
private const val DEFAULT_DNS_SERVER = "1.1.1.1"
|
||||
|
||||
Reference in New Issue
Block a user