first working version. tunnel establish

This commit is contained in:
2026-03-12 21:39:03 +07:00
parent dfc24b8da3
commit d5acfe7448
3 changed files with 126 additions and 14 deletions
@@ -5,7 +5,7 @@
<application> <application>
<service <service
android:name=".VpnService" android:name=".TunVpnService"
android:permission="android.permission.BIND_VPN_SERVICE" android:permission="android.permission.BIND_VPN_SERVICE"
android:exported="true"> android:exported="true">
<intent-filter> <intent-filter>
@@ -5,6 +5,7 @@ import android.content.Intent
import android.net.VpnService import android.net.VpnService
import android.os.ParcelFileDescriptor import android.os.ParcelFileDescriptor
import android.os.Parcelable import android.os.Parcelable
import android.util.Log
import kotlinx.parcelize.Parcelize import kotlinx.parcelize.Parcelize
import app.tauri.plugin.Plugin import app.tauri.plugin.Plugin
@@ -12,6 +13,9 @@ import app.tauri.plugin.Invoke
import app.tauri.annotation.InvokeArg import app.tauri.annotation.InvokeArg
import app.tauri.annotation.Command import app.tauri.annotation.Command
import app.tauri.annotation.TauriPlugin import app.tauri.annotation.TauriPlugin
import app.tauri.annotation.ActivityCallback
import androidx.activity.result.ActivityResult
import uniffi.netrunner_client.SessionManager import uniffi.netrunner_client.SessionManager
import uniffi.netrunner_client.Session import uniffi.netrunner_client.Session
@@ -19,9 +23,7 @@ import uniffi.netrunner_client.Session
import com.fasterxml.jackson.annotation.JsonCreator import com.fasterxml.jackson.annotation.JsonCreator
import com.fasterxml.jackson.annotation.JsonProperty import com.fasterxml.jackson.annotation.JsonProperty
// ------------------------------------------------ private const val TAG = "VPN_DEBUG"
// DATA
// ------------------------------------------------
@Parcelize @Parcelize
data class TunnelConfig @JsonCreator constructor( data class TunnelConfig @JsonCreator constructor(
@@ -40,8 +42,15 @@ class TunVpnService : VpnService() {
private var activeSession: Session? = null private var activeSession: Session? = null
private var vpnInterface: ParcelFileDescriptor? = null private var vpnInterface: ParcelFileDescriptor? = null
override fun onCreate() {
super.onCreate()
Log.d(TAG, "TunVpnService created")
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
Log.d(TAG, "onStartCommand called action=${intent?.action}")
when (intent?.action) { when (intent?.action) {
"START_VPN" -> { "START_VPN" -> {
@@ -49,6 +58,8 @@ class TunVpnService : VpnService() {
val ip = intent.getStringExtra("ip") ?: "" val ip = intent.getStringExtra("ip") ?: ""
val port = intent.getIntExtra("port", 0) val port = intent.getIntExtra("port", 0)
Log.d(TAG, "START_VPN received ip=$ip port=$port")
val config = if (android.os.Build.VERSION.SDK_INT >= 33) { val config = if (android.os.Build.VERSION.SDK_INT >= 33) {
intent.getParcelableExtra("config", TunnelConfig::class.java) intent.getParcelableExtra("config", TunnelConfig::class.java)
} else { } else {
@@ -56,15 +67,23 @@ class TunVpnService : VpnService() {
intent.getParcelableExtra("config") intent.getParcelableExtra("config")
} }
if (config != null) { if (config == null) {
Log.e(TAG, "TunnelConfig is NULL")
} else {
Log.d(TAG, "Config received: $config")
startVpn(config, ip, port) startVpn(config, ip, port)
} }
} }
"STOP_VPN" -> { "STOP_VPN" -> {
Log.d(TAG, "STOP_VPN received")
stopVpn() stopVpn()
stopSelf() stopSelf()
} }
else -> {
Log.w(TAG, "Unknown action ${intent?.action}")
}
} }
return START_STICKY return START_STICKY
@@ -72,35 +91,75 @@ class TunVpnService : VpnService() {
private fun startVpn(config: TunnelConfig, ip: String, port: Int) { private fun startVpn(config: TunnelConfig, ip: String, port: Int) {
Log.d(TAG, "startVpn called")
val builder = Builder() val builder = Builder()
.addAddress(config.address, config.prefix) .addAddress(config.address, config.prefix)
.addDnsServer(config.dns)
.addRoute("0.0.0.0", 0) .addRoute("0.0.0.0", 0)
.setMtu(config.mtu) .setMtu(config.mtu)
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
try {
builder.addDisallowedApplication("com.netrunner.vpn")
} catch (e: Exception) {
Log.d(TAG, "Can't exclude ${e.message}")
}
}
Log.d(TAG, "VPN builder configured")
vpnInterface = builder.establish() vpnInterface = builder.establish()
val fd = vpnInterface?.detachFd() ?: return if (vpnInterface == null) {
Log.e(TAG, "builder.establish() returned null")
return
}
Log.d(TAG, "VPN interface established")
val fd = vpnInterface?.detachFd() ?: run {
Log.e(TAG, "detachFd failed")
return
}
Log.d(TAG, "TUN fd=$fd")
try { try {
val manager = SessionManager() val manager = SessionManager()
Log.d(TAG, "SessionManager created")
activeSession = manager.start("$ip:$port", fd) activeSession = manager.start("$ip:$port", fd)
Log.d(TAG, "Session started to $ip:$port")
} catch (e: Exception) { } catch (e: Exception) {
android.util.Log.e("VPN_DEBUG", "manager.start failed", e)
Log.e(TAG, "manager.start failed", e)
} }
} }
private fun stopVpn() { private fun stopVpn() {
Log.d(TAG, "stopVpn called")
activeSession?.stop() activeSession?.stop()
activeSession = null activeSession = null
try { try {
vpnInterface?.close() vpnInterface?.close()
} catch (_: Exception) {} Log.d(TAG, "vpnInterface closed")
} catch (e: Exception) {
Log.e(TAG, "Error closing interface", e)
}
vpnInterface = null vpnInterface = null
} }
override fun onDestroy() { override fun onDestroy() {
Log.d(TAG, "TunVpnService destroyed")
stopVpn() stopVpn()
super.onDestroy() super.onDestroy()
} }
@@ -114,7 +173,9 @@ class TunVpnService : VpnService() {
class VpnPlugin(private val activity: Activity) : Plugin(activity) { class VpnPlugin(private val activity: Activity) : Plugin(activity) {
init { init {
Log.d(TAG, "VpnPlugin init")
System.loadLibrary("netrunner_client") System.loadLibrary("netrunner_client")
Log.d(TAG, "Rust library loaded")
} }
@InvokeArg @InvokeArg
@@ -129,10 +190,16 @@ class VpnPlugin(private val activity: Activity) : Plugin(activity) {
@Command @Command
fun startVpn(invoke: Invoke) { fun startVpn(invoke: Invoke) {
Log.d(TAG, "startVpn command invoked")
val args = invoke.parseArgs(VpnArgs::class.java) val args = invoke.parseArgs(VpnArgs::class.java)
Log.d(TAG, "Args parsed ip=${args.ip} port=${args.port}")
val config = args.config val config = args.config
if (config == null) { if (config == null) {
Log.e(TAG, "Config is null")
invoke.reject("Config is null") invoke.reject("Config is null")
return return
} }
@@ -141,13 +208,22 @@ class VpnPlugin(private val activity: Activity) : Plugin(activity) {
if (permissionIntent != null) { if (permissionIntent != null) {
pendingArgs = args Log.d(TAG, "VPN permission required")
activity.startActivityForResult(permissionIntent, 100)
invoke.resolve() // не reject pendingArgs = args
startActivityForResult(
invoke,
permissionIntent,
"onVpnPermissionResult"
)
invoke.resolve()
return return
} }
Log.d(TAG, "Permission already granted")
startVpnService(args) startVpnService(args)
invoke.resolve() invoke.resolve()
@@ -155,6 +231,8 @@ class VpnPlugin(private val activity: Activity) : Plugin(activity) {
private fun startVpnService(args: VpnArgs) { private fun startVpnService(args: VpnArgs) {
Log.d(TAG, "Starting TunVpnService")
val intent = Intent(activity, TunVpnService::class.java).apply { val intent = Intent(activity, TunVpnService::class.java).apply {
action = "START_VPN" action = "START_VPN"
putExtra("ip", args.ip) putExtra("ip", args.ip)
@@ -162,18 +240,52 @@ class VpnPlugin(private val activity: Activity) : Plugin(activity) {
putExtra("config", args.config) putExtra("config", args.config)
} }
activity.startService(intent) val result = activity.startService(intent)
if (result == null) {
Log.e(TAG, "startService returned null")
} else {
Log.d(TAG, "TunVpnService start requested")
}
}
@ActivityCallback
fun onVpnPermissionResult(invoke: Invoke, result: ActivityResult) {
Log.d(TAG, "VPN permission result received")
if (result.resultCode == Activity.RESULT_OK) {
Log.d(TAG, "VPN permission granted")
pendingArgs?.let {
startVpnService(it)
invoke.resolve()
}
} else {
Log.w(TAG, "VPN permission denied")
invoke.reject("Permission denied")
}
pendingArgs = null
} }
@Command @Command
fun stopVpn(invoke: Invoke) { fun stopVpn(invoke: Invoke) {
Log.d(TAG, "stopVpn command invoked")
val intent = Intent(activity, TunVpnService::class.java).apply { val intent = Intent(activity, TunVpnService::class.java).apply {
action = "STOP_VPN" action = "STOP_VPN"
} }
activity.startService(intent) activity.startService(intent)
Log.d(TAG, "STOP_VPN intent sent")
invoke.resolve() invoke.resolve()
} }
} }
+2 -2
View File
@@ -19,8 +19,8 @@ export function VpnControl() {
const config: TunnelConfig = { const config: TunnelConfig = {
address: "10.0.0.1", address: "10.0.0.1",
prefix: 24, prefix: 24,
dns: "8.8.8.8", dns: "10.0.0.2",
mtu: 1400, mtu: 1500,
}; };
// Вызываем вашу функцию-обертку // Вызываем вашу функцию-обертку