Optimize V2RayVpnService add Tun2SocksManager
This commit is contained in:
@@ -163,6 +163,7 @@ object AppConfig {
|
|||||||
|
|
||||||
/** Give a good name to this, IDK*/
|
/** Give a good name to this, IDK*/
|
||||||
const val VPN = "VPN"
|
const val VPN = "VPN"
|
||||||
|
const val VPN_MTU = 1500
|
||||||
|
|
||||||
// Google API rule constants
|
// Google API rule constants
|
||||||
const val GOOGLEAPIS_CN_DOMAIN = "domain:googleapis.cn"
|
const val GOOGLEAPIS_CN_DOMAIN = "domain:googleapis.cn"
|
||||||
|
|||||||
@@ -0,0 +1,129 @@
|
|||||||
|
package com.v2ray.ang.service
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.net.LocalSocket
|
||||||
|
import android.net.LocalSocketAddress
|
||||||
|
import android.os.ParcelFileDescriptor
|
||||||
|
import android.util.Log
|
||||||
|
import com.v2ray.ang.AppConfig
|
||||||
|
import com.v2ray.ang.AppConfig.VPN_MTU
|
||||||
|
import com.v2ray.ang.handler.MmkvManager
|
||||||
|
import com.v2ray.ang.handler.SettingsManager
|
||||||
|
import com.v2ray.ang.util.Utils
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Manages the tun2socks process that handles VPN traffic
|
||||||
|
*/
|
||||||
|
class Tun2SocksManager(
|
||||||
|
private val context: Context,
|
||||||
|
private val vpnInterface: ParcelFileDescriptor,
|
||||||
|
private val isRunningProvider: () -> Boolean,
|
||||||
|
private val restartCallback: () -> Unit
|
||||||
|
) {
|
||||||
|
companion object {
|
||||||
|
private const val TUN2SOCKS = "libtun2socks.so"
|
||||||
|
}
|
||||||
|
|
||||||
|
private lateinit var process: Process
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Starts the tun2socks process with the appropriate parameters.
|
||||||
|
*/
|
||||||
|
fun startTun2Socks() {
|
||||||
|
Log.i(AppConfig.TAG, "Start run $TUN2SOCKS")
|
||||||
|
val socksPort = SettingsManager.getSocksPort()
|
||||||
|
val vpnConfig = SettingsManager.getCurrentVpnInterfaceAddressConfig()
|
||||||
|
val cmd = arrayListOf(
|
||||||
|
File(context.applicationInfo.nativeLibraryDir, TUN2SOCKS).absolutePath,
|
||||||
|
"--netif-ipaddr", vpnConfig.ipv4Router,
|
||||||
|
"--netif-netmask", "255.255.255.252",
|
||||||
|
"--socks-server-addr", "${AppConfig.LOOPBACK}:${socksPort}",
|
||||||
|
"--tunmtu", VPN_MTU.toString(),
|
||||||
|
"--sock-path", "sock_path",
|
||||||
|
"--enable-udprelay",
|
||||||
|
"--loglevel", "notice"
|
||||||
|
)
|
||||||
|
|
||||||
|
if (MmkvManager.decodeSettingsBool(AppConfig.PREF_PREFER_IPV6)) {
|
||||||
|
cmd.add("--netif-ip6addr")
|
||||||
|
cmd.add(vpnConfig.ipv6Router)
|
||||||
|
}
|
||||||
|
if (MmkvManager.decodeSettingsBool(AppConfig.PREF_LOCAL_DNS_ENABLED)) {
|
||||||
|
val localDnsPort = Utils.parseInt(
|
||||||
|
MmkvManager.decodeSettingsString(AppConfig.PREF_LOCAL_DNS_PORT),
|
||||||
|
AppConfig.PORT_LOCAL_DNS.toInt()
|
||||||
|
)
|
||||||
|
cmd.add("--dnsgw")
|
||||||
|
cmd.add("${AppConfig.LOOPBACK}:${localDnsPort}")
|
||||||
|
}
|
||||||
|
Log.i(AppConfig.TAG, cmd.toString())
|
||||||
|
|
||||||
|
try {
|
||||||
|
val proBuilder = ProcessBuilder(cmd)
|
||||||
|
proBuilder.redirectErrorStream(true)
|
||||||
|
process = proBuilder
|
||||||
|
.directory(context.filesDir)
|
||||||
|
.start()
|
||||||
|
Thread {
|
||||||
|
Log.i(AppConfig.TAG, "$TUN2SOCKS check")
|
||||||
|
process.waitFor()
|
||||||
|
Log.i(AppConfig.TAG, "$TUN2SOCKS exited")
|
||||||
|
if (isRunningProvider()) {
|
||||||
|
Log.i(AppConfig.TAG, "$TUN2SOCKS restart")
|
||||||
|
restartCallback()
|
||||||
|
}
|
||||||
|
}.start()
|
||||||
|
Log.i(AppConfig.TAG, "$TUN2SOCKS process info: $process")
|
||||||
|
|
||||||
|
sendFd()
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e(AppConfig.TAG, "Failed to start $TUN2SOCKS process", e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends the file descriptor to the tun2socks process.
|
||||||
|
* Attempts to send the file descriptor multiple times if necessary.
|
||||||
|
*/
|
||||||
|
private fun sendFd() {
|
||||||
|
val fd = vpnInterface.fileDescriptor
|
||||||
|
val path = File(context.filesDir, "sock_path").absolutePath
|
||||||
|
Log.i(AppConfig.TAG, "LocalSocket path: $path")
|
||||||
|
|
||||||
|
CoroutineScope(Dispatchers.IO).launch {
|
||||||
|
var tries = 0
|
||||||
|
while (true) try {
|
||||||
|
Thread.sleep(50L shl tries)
|
||||||
|
Log.i(AppConfig.TAG, "LocalSocket sendFd tries: $tries")
|
||||||
|
LocalSocket().use { localSocket ->
|
||||||
|
localSocket.connect(LocalSocketAddress(path, LocalSocketAddress.Namespace.FILESYSTEM))
|
||||||
|
localSocket.setFileDescriptorsForSend(arrayOf(fd))
|
||||||
|
localSocket.outputStream.write(42)
|
||||||
|
}
|
||||||
|
break
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e(AppConfig.TAG, "Failed to send file descriptor, try: $tries", e)
|
||||||
|
if (tries > 5) break
|
||||||
|
tries += 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stops the tun2socks process
|
||||||
|
*/
|
||||||
|
fun stopTun2Socks() {
|
||||||
|
try {
|
||||||
|
Log.i(AppConfig.TAG, "$TUN2SOCKS destroy")
|
||||||
|
if (::process.isInitialized) {
|
||||||
|
process.destroy()
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e(AppConfig.TAG, "Failed to destroy $TUN2SOCKS process", e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,8 +5,6 @@ import android.content.Context
|
|||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.content.pm.PackageManager
|
import android.content.pm.PackageManager
|
||||||
import android.net.ConnectivityManager
|
import android.net.ConnectivityManager
|
||||||
import android.net.LocalSocket
|
|
||||||
import android.net.LocalSocketAddress
|
|
||||||
import android.net.Network
|
import android.net.Network
|
||||||
import android.net.NetworkCapabilities
|
import android.net.NetworkCapabilities
|
||||||
import android.net.NetworkRequest
|
import android.net.NetworkRequest
|
||||||
@@ -19,26 +17,18 @@ import android.util.Log
|
|||||||
import androidx.annotation.RequiresApi
|
import androidx.annotation.RequiresApi
|
||||||
import com.v2ray.ang.AppConfig
|
import com.v2ray.ang.AppConfig
|
||||||
import com.v2ray.ang.AppConfig.LOOPBACK
|
import com.v2ray.ang.AppConfig.LOOPBACK
|
||||||
|
import com.v2ray.ang.AppConfig.VPN_MTU
|
||||||
import com.v2ray.ang.BuildConfig
|
import com.v2ray.ang.BuildConfig
|
||||||
import com.v2ray.ang.handler.MmkvManager
|
import com.v2ray.ang.handler.MmkvManager
|
||||||
import com.v2ray.ang.handler.SettingsManager
|
import com.v2ray.ang.handler.SettingsManager
|
||||||
import com.v2ray.ang.util.MyContextWrapper
|
import com.v2ray.ang.util.MyContextWrapper
|
||||||
import com.v2ray.ang.util.Utils
|
import com.v2ray.ang.util.Utils
|
||||||
import kotlinx.coroutines.CoroutineScope
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import java.io.File
|
|
||||||
import java.lang.ref.SoftReference
|
import java.lang.ref.SoftReference
|
||||||
|
|
||||||
class V2RayVpnService : VpnService(), ServiceControl {
|
class V2RayVpnService : VpnService(), ServiceControl {
|
||||||
companion object {
|
|
||||||
private const val VPN_MTU = 1500
|
|
||||||
private const val TUN2SOCKS = "libtun2socks.so"
|
|
||||||
}
|
|
||||||
|
|
||||||
private lateinit var mInterface: ParcelFileDescriptor
|
private lateinit var mInterface: ParcelFileDescriptor
|
||||||
private var isRunning = false
|
private var isRunning = false
|
||||||
private lateinit var process: Process
|
private var tun2SocksManager: Tun2SocksManager? = null
|
||||||
|
|
||||||
/**destroy
|
/**destroy
|
||||||
* Unfortunately registerDefaultNetworkCallback is going to return our VPN interface: https://android.googlesource.com/platform/frameworks/base/+/dda156ab0c5d66ad82bdcf76cda07cbc0a9c8a2e
|
* Unfortunately registerDefaultNetworkCallback is going to return our VPN interface: https://android.googlesource.com/platform/frameworks/base/+/dda156ab0c5d66ad82bdcf76cda07cbc0a9c8a2e
|
||||||
@@ -281,79 +271,13 @@ class V2RayVpnService : VpnService(), ServiceControl {
|
|||||||
* Starts the tun2socks process with the appropriate parameters.
|
* Starts the tun2socks process with the appropriate parameters.
|
||||||
*/
|
*/
|
||||||
private fun runTun2socks() {
|
private fun runTun2socks() {
|
||||||
Log.i(AppConfig.TAG, "Start run $TUN2SOCKS")
|
tun2SocksManager = Tun2SocksManager(
|
||||||
val socksPort = SettingsManager.getSocksPort()
|
context = applicationContext,
|
||||||
val vpnConfig = SettingsManager.getCurrentVpnInterfaceAddressConfig()
|
vpnInterface = mInterface,
|
||||||
val cmd = arrayListOf(
|
isRunningProvider = { isRunning },
|
||||||
File(applicationContext.applicationInfo.nativeLibraryDir, TUN2SOCKS).absolutePath,
|
restartCallback = { runTun2socks() }
|
||||||
"--netif-ipaddr", vpnConfig.ipv4Router,
|
).also {
|
||||||
"--netif-netmask", "255.255.255.252",
|
it.startTun2Socks()
|
||||||
"--socks-server-addr", "$LOOPBACK:${socksPort}",
|
|
||||||
"--tunmtu", VPN_MTU.toString(),
|
|
||||||
"--sock-path", "sock_path",//File(applicationContext.filesDir, "sock_path").absolutePath,
|
|
||||||
"--enable-udprelay",
|
|
||||||
"--loglevel", "notice"
|
|
||||||
)
|
|
||||||
|
|
||||||
if (MmkvManager.decodeSettingsBool(AppConfig.PREF_PREFER_IPV6)) {
|
|
||||||
cmd.add("--netif-ip6addr")
|
|
||||||
cmd.add(vpnConfig.ipv6Router)
|
|
||||||
}
|
|
||||||
if (MmkvManager.decodeSettingsBool(AppConfig.PREF_LOCAL_DNS_ENABLED)) {
|
|
||||||
val localDnsPort = Utils.parseInt(MmkvManager.decodeSettingsString(AppConfig.PREF_LOCAL_DNS_PORT), AppConfig.PORT_LOCAL_DNS.toInt())
|
|
||||||
cmd.add("--dnsgw")
|
|
||||||
cmd.add("$LOOPBACK:${localDnsPort}")
|
|
||||||
}
|
|
||||||
Log.i(AppConfig.TAG, cmd.toString())
|
|
||||||
|
|
||||||
try {
|
|
||||||
val proBuilder = ProcessBuilder(cmd)
|
|
||||||
proBuilder.redirectErrorStream(true)
|
|
||||||
process = proBuilder
|
|
||||||
.directory(applicationContext.filesDir)
|
|
||||||
.start()
|
|
||||||
Thread {
|
|
||||||
Log.i(AppConfig.TAG, "$TUN2SOCKS check")
|
|
||||||
process.waitFor()
|
|
||||||
Log.i(AppConfig.TAG, "$TUN2SOCKS exited")
|
|
||||||
if (isRunning) {
|
|
||||||
Log.i(AppConfig.TAG, "$TUN2SOCKS restart")
|
|
||||||
runTun2socks()
|
|
||||||
}
|
|
||||||
}.start()
|
|
||||||
Log.i(AppConfig.TAG, "$TUN2SOCKS process info : ${process.toString()}")
|
|
||||||
|
|
||||||
sendFd()
|
|
||||||
} catch (e: Exception) {
|
|
||||||
Log.e(AppConfig.TAG, "Failed to start $TUN2SOCKS process", e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sends the file descriptor to the tun2socks process.
|
|
||||||
* Attempts to send the file descriptor multiple times if necessary.
|
|
||||||
*/
|
|
||||||
private fun sendFd() {
|
|
||||||
val fd = mInterface.fileDescriptor
|
|
||||||
val path = File(applicationContext.filesDir, "sock_path").absolutePath
|
|
||||||
Log.i(AppConfig.TAG, "LocalSocket path : $path")
|
|
||||||
|
|
||||||
CoroutineScope(Dispatchers.IO).launch {
|
|
||||||
var tries = 0
|
|
||||||
while (true) try {
|
|
||||||
Thread.sleep(50L shl tries)
|
|
||||||
Log.i(AppConfig.TAG, "LocalSocket sendFd tries: $tries")
|
|
||||||
LocalSocket().use { localSocket ->
|
|
||||||
localSocket.connect(LocalSocketAddress(path, LocalSocketAddress.Namespace.FILESYSTEM))
|
|
||||||
localSocket.setFileDescriptorsForSend(arrayOf(fd))
|
|
||||||
localSocket.outputStream.write(42)
|
|
||||||
}
|
|
||||||
break
|
|
||||||
} catch (e: Exception) {
|
|
||||||
Log.e(AppConfig.TAG, "Failed to send file descriptor, try: $tries", e)
|
|
||||||
if (tries > 5) break
|
|
||||||
tries += 1
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -375,12 +299,8 @@ class V2RayVpnService : VpnService(), ServiceControl {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
tun2SocksManager?.stopTun2Socks()
|
||||||
Log.i(AppConfig.TAG, "$TUN2SOCKS destroy")
|
tun2SocksManager = null
|
||||||
process.destroy()
|
|
||||||
} catch (e: Exception) {
|
|
||||||
Log.e(AppConfig.TAG, "Failed to destroy $TUN2SOCKS process", e)
|
|
||||||
}
|
|
||||||
|
|
||||||
V2RayServiceManager.stopCoreLoop()
|
V2RayServiceManager.stopCoreLoop()
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user