first working version. tunnel establish
This commit is contained in:
@@ -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()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -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,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Вызываем вашу функцию-обертку
|
// Вызываем вашу функцию-обертку
|
||||||
|
|||||||
Reference in New Issue
Block a user