summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--android/src/main/kotlin/net/mullvad/talpid/TalpidVpnService.kt59
-rw-r--r--mullvad-jni/src/talpid_vpn_service.rs23
-rw-r--r--talpid-core/src/tunnel/tun_provider/android/mod.rs159
3 files changed, 179 insertions, 62 deletions
diff --git a/android/src/main/kotlin/net/mullvad/talpid/TalpidVpnService.kt b/android/src/main/kotlin/net/mullvad/talpid/TalpidVpnService.kt
index 24bcd16b30..8f095641a1 100644
--- a/android/src/main/kotlin/net/mullvad/talpid/TalpidVpnService.kt
+++ b/android/src/main/kotlin/net/mullvad/talpid/TalpidVpnService.kt
@@ -1,12 +1,22 @@
package net.mullvad.talpid
import android.net.VpnService
+import android.os.ParcelFileDescriptor
import java.net.Inet4Address
import java.net.Inet6Address
import java.net.InetAddress
+import kotlin.properties.Delegates.observable
import net.mullvad.talpid.tun_provider.TunConfig
open class TalpidVpnService : VpnService() {
+ private var activeTunDevice by observable<Int?>(null) { _, oldTunDevice, _ ->
+ oldTunDevice?.let { oldTunFd ->
+ ParcelFileDescriptor.adoptFd(oldTunFd).close()
+ }
+ }
+
+ private var currentTunConfig = defaultTunConfig()
+
val connectivityListener = ConnectivityListener()
override fun onCreate() {
@@ -17,7 +27,53 @@ open class TalpidVpnService : VpnService() {
connectivityListener.unregister(this)
}
- fun createTun(config: TunConfig): Int {
+ fun getTun(config: TunConfig): Int {
+ synchronized(this) {
+ val tunDevice = activeTunDevice
+
+ if (config == currentTunConfig && tunDevice != null) {
+ return tunDevice
+ } else {
+ val newTunDevice = createTun(config)
+
+ currentTunConfig = config
+ activeTunDevice = newTunDevice
+
+ return newTunDevice
+ }
+ }
+ }
+
+ fun createTun() {
+ synchronized(this) {
+ activeTunDevice = createTun(currentTunConfig)
+ }
+ }
+
+ fun createTunIfClosed() {
+ synchronized(this) {
+ if (activeTunDevice == null) {
+ activeTunDevice = createTun(currentTunConfig)
+ }
+ }
+ }
+
+ fun recreateTunIfOpen(config: TunConfig) {
+ synchronized(this) {
+ if (activeTunDevice != null) {
+ currentTunConfig = config
+ activeTunDevice = createTun(config)
+ }
+ }
+ }
+
+ fun closeTun() {
+ synchronized(this) {
+ activeTunDevice = null
+ }
+ }
+
+ private fun createTun(config: TunConfig): Int {
if (VpnService.prepare(this) != null) {
// VPN permission wasn't granted
return -1
@@ -63,5 +119,6 @@ open class TalpidVpnService : VpnService() {
}
}
+ private external fun defaultTunConfig(): TunConfig
private external fun waitForTunnelUp(tunFd: Int, isIpv6Enabled: Boolean)
}
diff --git a/mullvad-jni/src/talpid_vpn_service.rs b/mullvad-jni/src/talpid_vpn_service.rs
index 0e266970ba..e315b55e15 100644
--- a/mullvad-jni/src/talpid_vpn_service.rs
+++ b/mullvad-jni/src/talpid_vpn_service.rs
@@ -1,8 +1,11 @@
use ipnetwork::IpNetwork;
-use jnix::jni::{
- objects::JObject,
- sys::{jboolean, jint, JNI_FALSE},
- JNIEnv,
+use jnix::{
+ jni::{
+ objects::JObject,
+ sys::{jboolean, jint, JNI_FALSE},
+ JNIEnv,
+ },
+ IntoJava, JnixEnv,
};
use nix::sys::{
select::{pselect, FdSet},
@@ -15,6 +18,7 @@ use std::{
os::unix::io::RawFd,
time::{Duration, Instant},
};
+use talpid_core::tunnel::TunConfig;
use talpid_types::ErrorExt;
#[derive(Debug, err_derive::Error)]
@@ -31,6 +35,17 @@ enum Error {
#[no_mangle]
#[allow(non_snake_case)]
+pub extern "system" fn Java_net_mullvad_talpid_TalpidVpnService_defaultTunConfig<'env>(
+ env: JNIEnv<'env>,
+ _this: JObject<'_>,
+) -> JObject<'env> {
+ let env = JnixEnv::from(env);
+
+ TunConfig::default().into_java(&env).forget()
+}
+
+#[no_mangle]
+#[allow(non_snake_case)]
pub extern "system" fn Java_net_mullvad_talpid_TalpidVpnService_waitForTunnelUp(
_: JNIEnv<'_>,
_this: JObject<'_>,
diff --git a/talpid-core/src/tunnel/tun_provider/android/mod.rs b/talpid-core/src/tunnel/tun_provider/android/mod.rs
index 3cf6f16cd5..e4e8f4fbac 100644
--- a/talpid-core/src/tunnel/tun_provider/android/mod.rs
+++ b/talpid-core/src/tunnel/tun_provider/android/mod.rs
@@ -12,12 +12,11 @@ use jnix::{
IntoJava, JnixEnv,
};
use std::{
- fs::File,
net::{IpAddr, Ipv4Addr, Ipv6Addr},
- os::unix::io::{AsRawFd, FromRawFd, RawFd},
+ os::unix::io::{AsRawFd, RawFd},
sync::Arc,
};
-use talpid_types::android::AndroidContext;
+use talpid_types::{android::AndroidContext, ErrorExt};
/// Errors that occur while setting up VpnService tunnel.
@@ -58,7 +57,6 @@ pub struct AndroidTunProvider {
jvm: Arc<JavaVM>,
class: GlobalRef,
object: GlobalRef,
- active_tun: Option<File>,
last_tun_config: TunConfig,
allow_lan: bool,
}
@@ -78,7 +76,6 @@ impl AndroidTunProvider {
jvm: context.jvm,
class: talpid_vpn_service_class,
object: context.vpn_service,
- active_tun: None,
last_tun_config: TunConfig::default(),
allow_lan,
}
@@ -87,10 +84,7 @@ impl AndroidTunProvider {
pub fn set_allow_lan(&mut self, allow_lan: bool) -> Result<(), Error> {
if self.allow_lan != allow_lan {
self.allow_lan = allow_lan;
-
- if self.active_tun.is_some() {
- self.create_tun()?;
- }
+ self.recreate_tun_if_open()?;
}
Ok(())
@@ -98,7 +92,9 @@ impl AndroidTunProvider {
/// Retrieve a tunnel device with the provided configuration.
pub fn get_tun(&mut self, config: TunConfig) -> Result<VpnServiceTun, Error> {
- let tun_fd = self.get_tun_fd(config)?;
+ let tun_fd = self.get_tun_fd(config.clone())?;
+
+ self.last_tun_config = config;
let jvm = unsafe { JavaVM::from_raw(self.jvm.get_java_vm_pointer()) }
.map_err(Error::CloneJavaVm)?;
@@ -116,34 +112,98 @@ impl AndroidTunProvider {
/// Will open a new tunnel if there is already an active tunnel. The previous tunnel will be
/// closed.
pub fn create_tun(&mut self) -> Result<(), Error> {
- self.open_tun(self.last_tun_config.clone())
+ let result = self.call_method(
+ "createTun",
+ "()V",
+ JavaType::Primitive(Primitive::Void),
+ &[],
+ )?;
+
+ match result {
+ JValue::Void => Ok(()),
+ value => Err(Error::InvalidMethodResult(
+ "createTun",
+ format!("{:?}", value),
+ )),
+ }
}
/// Open a tunnel device using the previous or the default configuration if there is no
/// currently active tunnel.
pub fn create_tun_if_closed(&mut self) -> Result<(), Error> {
- if self.active_tun.is_none() {
- self.create_tun()?;
- }
+ let result = self.call_method(
+ "createTunIfClosed",
+ "()V",
+ JavaType::Primitive(Primitive::Void),
+ &[],
+ )?;
- Ok(())
+ match result {
+ JValue::Void => Ok(()),
+ value => Err(Error::InvalidMethodResult(
+ "createTunIfClosed",
+ format!("{:?}", value),
+ )),
+ }
}
/// Close currently active tunnel device.
pub fn close_tun(&mut self) {
- self.active_tun = None;
+ let result = self.call_method("closeTun", "()V", JavaType::Primitive(Primitive::Void), &[]);
+
+ let error = match result {
+ Ok(JValue::Void) => None,
+ Ok(value) => Some(Error::InvalidMethodResult(
+ "closeTun",
+ format!("{:?}", value),
+ )),
+ Err(error) => Some(error),
+ };
+
+ if let Some(error) = error {
+ log::error!(
+ "{}",
+ error.display_chain_with_msg("Failed to close the tunnel")
+ );
+ }
}
fn get_tun_fd(&mut self, config: TunConfig) -> Result<RawFd, Error> {
- if self.active_tun.is_none() || self.last_tun_config != config {
- self.open_tun(config)?;
+ let env = self.env()?;
+ let actual_config = self.prepare_tun_config(config);
+ let java_config = actual_config.into_java(&env);
+
+ let result = self.call_method(
+ "getTun",
+ "(Lnet/mullvad/talpid/tun_provider/TunConfig;)I",
+ JavaType::Primitive(Primitive::Int),
+ &[JValue::Object(java_config.as_obj())],
+ )?;
+
+ match result {
+ JValue::Int(0) => Err(Error::TunnelDeviceError),
+ JValue::Int(-1) => Err(Error::PermissionDenied),
+ JValue::Int(fd) => Ok(fd),
+ value => Err(Error::InvalidMethodResult("getTun", format!("{:?}", value))),
}
+ }
+
+ fn recreate_tun_if_open(&mut self) -> Result<(), Error> {
+ let env = self.env()?;
+ let actual_config = self.prepare_tun_config(self.last_tun_config.clone());
+ let java_config = actual_config.into_java(&env);
+
+ let result = self.call_method(
+ "recreateTunIfOpen",
+ "(Lnet/mullvad/talpid/tun_provider/TunConfig;)V",
+ JavaType::Primitive(Primitive::Void),
+ &[JValue::Object(java_config.as_obj())],
+ )?;
- Ok(self
- .active_tun
- .as_ref()
- .expect("Tunnel should be configured")
- .as_raw_fd())
+ match result {
+ JValue::Void => Ok(()),
+ value => Err(Error::InvalidMethodResult("getTun", format!("{:?}", value))),
+ }
}
fn prepare_tun_config(&self, config: TunConfig) -> TunConfig {
@@ -189,48 +249,33 @@ impl AndroidTunProvider {
}
}
- fn open_tun(&mut self, config: TunConfig) -> Result<(), Error> {
- let actual_config = self.prepare_tun_config(config.clone());
-
+ fn call_method(
+ &self,
+ name: &'static str,
+ signature: &str,
+ return_type: JavaType,
+ parameters: &[JValue<'_>],
+ ) -> Result<JValue<'_>, Error> {
let env = JnixEnv::from(
self.jvm
.attach_current_thread_as_daemon()
.map_err(Error::AttachJvmToThread)?,
);
- let create_tun_method = env
- .get_method_id(
- &self.class,
- "createTun",
- "(Lnet/mullvad/talpid/tun_provider/TunConfig;)I",
- )
- .map_err(|cause| Error::FindMethod("createTun", cause))?;
+ let method_id = env
+ .get_method_id(&self.class, name, signature)
+ .map_err(|cause| Error::FindMethod(name, cause))?;
- let java_config = actual_config.clone().into_java(&env);
- let result = env
- .call_method_unchecked(
- self.object.as_obj(),
- create_tun_method,
- JavaType::Primitive(Primitive::Int),
- &[JValue::Object(java_config.as_obj())],
- )
- .map_err(|cause| Error::CallMethod("createTun", cause))?;
-
- match result {
- JValue::Int(0) => Err(Error::TunnelDeviceError),
- JValue::Int(-1) => Err(Error::PermissionDenied),
- JValue::Int(fd) => {
- let tun = unsafe { File::from_raw_fd(fd) };
+ env.call_method_unchecked(self.object.as_obj(), method_id, return_type, parameters)
+ .map_err(|cause| Error::CallMethod(name, cause))
+ }
- self.active_tun = Some(tun);
- self.last_tun_config = config;
+ fn env(&self) -> Result<JnixEnv<'_>, Error> {
+ let jni_env = self
+ .jvm
+ .attach_current_thread_as_daemon()
+ .map_err(Error::AttachJvmToThread)?;
- Ok(())
- }
- value => Err(Error::InvalidMethodResult(
- "createTun",
- format!("{:?}", value),
- )),
- }
+ Ok(JnixEnv::from(jni_env))
}
}