summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorDavid Lönnhager <david.l@mullvad.net>2022-05-09 14:38:43 +0200
committerDavid Lönnhager <david.l@mullvad.net>2022-05-09 14:38:43 +0200
commit3d62b802f449ddae32c78d8922d3b4aaea3c84a9 (patch)
treeda2938eb7c4c63586d31cce8efe83d952b156312
parentdfeaebfd02ac6745657f0e14194d5ba4edc34e3a (diff)
parent243ca7ce48cd67834b6a7d49fb1ffad5a50d7aee (diff)
downloadmullvadvpn-3d62b802f449ddae32c78d8922d3b4aaea3c84a9.tar.xz
mullvadvpn-3d62b802f449ddae32c78d8922d3b4aaea3c84a9.zip
Merge branch 'refactor-tunnel-params-gen'
-rw-r--r--mullvad-daemon/src/device/mod.rs2
-rw-r--r--mullvad-daemon/src/lib.rs308
-rw-r--r--mullvad-daemon/src/tunnel.rs286
-rw-r--r--talpid-core/src/tunnel_state_machine/connecting_state.rs9
-rw-r--r--talpid-core/src/tunnel_state_machine/mod.rs4
5 files changed, 329 insertions, 280 deletions
diff --git a/mullvad-daemon/src/device/mod.rs b/mullvad-daemon/src/device/mod.rs
index 753c963cca..dc43ce9ee1 100644
--- a/mullvad-daemon/src/device/mod.rs
+++ b/mullvad-daemon/src/device/mod.rs
@@ -170,7 +170,7 @@ impl From<PrivateDeviceEvent> for DeviceEvent {
}
impl PrivateDeviceEvent {
- fn data(&self) -> Option<&PrivateAccountAndDevice> {
+ pub fn data(&self) -> Option<&PrivateAccountAndDevice> {
match self {
PrivateDeviceEvent::Login(config) => Some(config),
PrivateDeviceEvent::Updated(config) => Some(config),
diff --git a/mullvad-daemon/src/lib.rs b/mullvad-daemon/src/lib.rs
index 04300018fd..0a063613e2 100644
--- a/mullvad-daemon/src/lib.rs
+++ b/mullvad-daemon/src/lib.rs
@@ -21,6 +21,7 @@ pub mod rpc_uniqueness_check;
pub mod runtime;
pub mod settings;
mod target_state;
+mod tunnel;
pub mod version;
mod version_check;
@@ -34,15 +35,14 @@ use futures::{
use mullvad_api::availability::ApiAvailabilityHandle;
use mullvad_relay_selector::{
updater::{RelayListUpdater, RelayListUpdaterHandle},
- RelaySelector, SelectedBridge, SelectedObfuscator, SelectedRelay, SelectorConfig,
+ RelaySelector, SelectorConfig,
};
use mullvad_types::{
account::{AccountData, AccountToken, VoucherSubmission},
device::{AccountAndDevice, Device, DeviceEvent, DeviceId, RemoveDeviceEvent},
- endpoint::MullvadEndpoint,
location::GeoIpLocation,
relay_constraints::{BridgeSettings, BridgeState, ObfuscationSettings, RelaySettingsUpdate},
- relay_list::{Relay, RelayList},
+ relay_list::RelayList,
settings::{DnsOptions, Settings},
states::{TargetState, TunnelState},
version::{AppVersion, AppVersionInfo},
@@ -60,22 +60,20 @@ use std::{
mem,
path::PathBuf,
pin::Pin,
- sync::{mpsc as sync_mpsc, Arc, Weak},
+ sync::{Arc, Weak},
time::Duration,
};
#[cfg(any(target_os = "linux", windows))]
use talpid_core::split_tunnel;
use talpid_core::{
mpsc::Sender,
- tunnel_state_machine::{self, TunnelCommand, TunnelParametersGenerator},
+ tunnel_state_machine::{self, TunnelCommand},
};
#[cfg(target_os = "android")]
use talpid_types::android::AndroidContext;
-#[cfg(not(target_os = "android"))]
-use talpid_types::net::openvpn;
use talpid_types::{
- net::{wireguard, TunnelEndpoint, TunnelParameters, TunnelType},
- tunnel::{ErrorStateCause, ParameterGenerationError, TunnelStateTransition},
+ net::{TunnelEndpoint, TunnelType},
+ tunnel::{ErrorStateCause, TunnelStateTransition},
ErrorExt,
};
#[cfg(not(target_os = "android"))]
@@ -140,15 +138,6 @@ pub enum Error {
#[error(display = "An account is already set")]
AlreadyLoggedIn,
- #[error(display = "No wireguard private key available")]
- NoKeyAvailable,
-
- #[error(display = "No bridge available")]
- NoBridgeAvailable,
-
- #[error(display = "No matching entry relay was found")]
- NoEntryRelayAvailable,
-
#[error(display = "No account token is set")]
NoAccountToken,
@@ -328,11 +317,6 @@ pub enum DaemonCommand {
pub(crate) enum InternalDaemonEvent {
/// Tunnel has changed state.
TunnelStateTransition(TunnelStateTransition),
- /// Request from the `MullvadTunnelParametersGenerator` to obtain a new relay.
- GenerateTunnelParameters(
- sync_mpsc::Sender<Result<TunnelParameters, ParameterGenerationError>>,
- u32,
- ),
/// A command sent to the daemon.
Command(DaemonCommand),
/// Daemon shutdown triggered by a signal, ctrl-c or similar.
@@ -567,7 +551,7 @@ pub struct Daemon<L: EventListener> {
version_updater_handle: version_check::VersionUpdaterHandle,
relay_selector: RelaySelector,
relay_list_updater: RelayListUpdaterHandle,
- last_generated_relays: Option<LastSelectedRelays>,
+ parameters_generator: tunnel::ParametersGenerator,
app_version_info: Option<AppVersionInfo>,
shutdown_tasks: Vec<Pin<Box<dyn Future<Output = ()>>>>,
tunnel_state_machine_handle: tunnel_state_machine::JoinHandle,
@@ -691,9 +675,11 @@ where
let initial_api_endpoint =
api::get_allowed_endpoint(api_runtime.address_cache.get_address().await);
- let tunnel_parameters_generator = MullvadTunnelParametersGenerator {
- tx: internal_event_tx.clone(),
- };
+ let parameters_generator = tunnel::ParametersGenerator::new(
+ account_manager.clone(),
+ relay_selector.clone(),
+ settings.tunnel_options.clone(),
+ );
let (offline_state_tx, offline_state_rx) = mpsc::unbounded();
#[cfg(target_os = "windows")]
let (volume_update_tx, volume_update_rx) = mpsc::unbounded();
@@ -707,7 +693,7 @@ where
#[cfg(windows)]
exclude_paths,
},
- tunnel_parameters_generator,
+ parameters_generator.clone(),
log_dir,
resource_dir.clone(),
internal_event_tx.to_specialized_sender(),
@@ -773,7 +759,7 @@ where
version_updater_handle,
relay_selector,
relay_list_updater,
- last_generated_relays: None,
+ parameters_generator,
app_version_info,
shutdown_tasks: vec![],
tunnel_state_machine_handle,
@@ -868,10 +854,6 @@ where
TunnelStateTransition(transition) => {
self.handle_tunnel_state_transition(transition).await
}
- GenerateTunnelParameters(tunnel_parameters_tx, retry_attempt) => {
- self.handle_generate_tunnel_parameters(&tunnel_parameters_tx, retry_attempt)
- .await
- }
Command(command) => self.handle_command(command).await,
TriggerShutdown => self.trigger_shutdown_event(),
NewAppVersionInfo(app_version_info) => {
@@ -897,11 +879,11 @@ where
TunnelStateTransition::Disconnected => TunnelState::Disconnected,
TunnelStateTransition::Connecting(endpoint) => TunnelState::Connecting {
endpoint,
- location: self.build_location_from_relay(),
+ location: self.parameters_generator.get_last_location(),
},
TunnelStateTransition::Connected(endpoint) => TunnelState::Connected {
endpoint,
- location: self.build_location_from_relay(),
+ location: self.parameters_generator.get_last_location(),
},
TunnelStateTransition::Disconnecting(after_disconnect) => {
TunnelState::Disconnecting(after_disconnect)
@@ -966,144 +948,6 @@ where
};
}
- async fn handle_generate_tunnel_parameters(
- &mut self,
- tunnel_parameters_tx: &sync_mpsc::Sender<
- Result<TunnelParameters, ParameterGenerationError>,
- >,
- retry_attempt: u32,
- ) {
- let data = match self.account_manager.data().await {
- Ok(Some(data)) => data,
- _ => {
- log::error!("No account token configured");
- return;
- }
- };
-
- let result = match self.relay_selector.get_relay(retry_attempt) {
- Ok((SelectedRelay::Custom(custom_relay), _bridge, _obfsucator)) => {
- custom_relay
- // TODO(emilsp): generate proxy settings for custom tunnels
- .to_tunnel_parameters(self.settings.tunnel_options.clone(), None)
- .map_err(|e| {
- log::error!("Failed to resolve hostname for custom tunnel config: {}", e);
- ParameterGenerationError::CustomTunnelHostResultionError
- })
- }
- Ok((SelectedRelay::Normal(constraints), bridge, obfuscator)) => {
- let result = self
- .create_tunnel_parameters(
- &constraints.exit_relay,
- &constraints.entry_relay,
- constraints.endpoint,
- bridge,
- obfuscator,
- data,
- )
- .await;
- result.map_err(|error| match error {
- Error::NoKeyAvailable => ParameterGenerationError::NoWireguardKey,
- Error::NoBridgeAvailable => ParameterGenerationError::NoMatchingBridgeRelay,
- error => {
- log::error!(
- "{}",
- error.display_chain_with_msg("Failed to generate tunnel parameters")
- );
- ParameterGenerationError::NoMatchingRelay
- }
- })
- }
- Err(mullvad_relay_selector::Error::NoBridge) => {
- Err(ParameterGenerationError::NoMatchingBridgeRelay)
- }
- Err(_error) => Err(ParameterGenerationError::NoMatchingRelay),
- };
- if tunnel_parameters_tx.send(result).is_err() {
- log::error!("Failed to send tunnel parameters");
- }
- }
-
- #[cfg_attr(target_os = "android", allow(unused_variables))]
- async fn create_tunnel_parameters(
- &mut self,
- relay: &Relay,
- entry_relay: &Option<Relay>,
- endpoint: MullvadEndpoint,
- bridge: Option<SelectedBridge>,
- obfuscator: Option<SelectedObfuscator>,
- device: PrivateAccountAndDevice,
- ) -> Result<TunnelParameters, Error> {
- let tunnel_options = self.settings.tunnel_options.clone();
- match endpoint {
- #[cfg(not(target_os = "android"))]
- MullvadEndpoint::OpenVpn(endpoint) => {
- let (bridge_settings, bridge_relay) = match bridge {
- Some(SelectedBridge::Normal(bridge)) => {
- (Some(bridge.settings), Some(bridge.relay))
- }
- Some(SelectedBridge::Custom(settings)) => (Some(settings), None),
- None => (None, None),
- };
-
- self.last_generated_relays = Some(LastSelectedRelays::OpenVpn {
- relay: relay.clone(),
- bridge: bridge_relay,
- });
-
- Ok(openvpn::TunnelParameters {
- config: openvpn::ConnectionConfig::new(
- endpoint,
- device.account_token,
- "-".to_string(),
- ),
- options: tunnel_options.openvpn,
- generic_options: tunnel_options.generic,
- proxy: bridge_settings,
- }
- .into())
- }
- #[cfg(target_os = "android")]
- MullvadEndpoint::OpenVpn(endpoint) => {
- unreachable!("OpenVPN is not supported on Android");
- }
- MullvadEndpoint::Wireguard(endpoint) => {
- let tunnel = wireguard::TunnelConfig {
- private_key: device.device.wg_data.private_key,
- addresses: vec![
- device.device.wg_data.addresses.ipv4_address.ip().into(),
- device.device.wg_data.addresses.ipv6_address.ip().into(),
- ],
- };
-
- let (obfuscator_relay, obfuscator_config) = match obfuscator {
- Some(obfuscator) => (Some(obfuscator.relay), Some(obfuscator.config)),
- None => (None, None),
- };
-
- self.last_generated_relays = Some(LastSelectedRelays::WireGuard {
- wg_entry: entry_relay.clone(),
- wg_exit: relay.clone(),
- obfuscator: obfuscator_relay,
- });
-
- Ok(wireguard::TunnelParameters {
- connection: wireguard::ConnectionConfig {
- tunnel,
- peer: endpoint.peer,
- exit_peer: endpoint.exit_peer,
- ipv4_gateway: endpoint.ipv4_gateway,
- ipv6_gateway: Some(endpoint.ipv6_gateway),
- },
- options: tunnel_options.wireguard.options,
- generic_options: tunnel_options.generic,
- obfuscation: obfuscator_config,
- }
- .into())
- }
- }
- }
-
fn schedule_reconnect(&mut self, delay: Duration) {
self.unschedule_reconnect();
@@ -1353,9 +1197,11 @@ where
Connecting { location, .. } => {
Self::oneshot_send(tx, location.clone(), "current location")
}
- Disconnecting(..) => {
- Self::oneshot_send(tx, self.build_location_from_relay(), "current location")
- }
+ Disconnecting(..) => Self::oneshot_send(
+ tx,
+ self.parameters_generator.get_last_location(),
+ "current location",
+ ),
Connected { location, .. } => {
let relay_location = location.clone();
let location_future = self.get_geo_location().await;
@@ -1390,53 +1236,6 @@ where
}
}
- fn build_location_from_relay(&self) -> Option<GeoIpLocation> {
- let relays = self.last_generated_relays.as_ref()?;
- let hostname;
- let bridge_hostname;
- let entry_hostname;
- let obfuscator_hostname;
- let location;
- let take_hostname =
- |relay: &Option<Relay>| relay.as_ref().map(|relay| relay.hostname.clone());
-
- match relays {
- LastSelectedRelays::WireGuard {
- wg_entry: entry,
- wg_exit: exit,
- obfuscator,
- } => {
- entry_hostname = take_hostname(entry);
- hostname = exit.hostname.clone();
- obfuscator_hostname = take_hostname(obfuscator);
- bridge_hostname = None;
- location = exit.location.as_ref().cloned().unwrap();
- }
- #[cfg(not(target_os = "android"))]
- LastSelectedRelays::OpenVpn { relay, bridge } => {
- hostname = relay.hostname.clone();
- bridge_hostname = take_hostname(bridge);
- entry_hostname = None;
- obfuscator_hostname = None;
- location = relay.location.as_ref().cloned().unwrap();
- }
- };
-
- Some(GeoIpLocation {
- ipv4: None,
- ipv6: None,
- country: location.country,
- city: Some(location.city),
- latitude: location.latitude,
- longitude: location.longitude,
- mullvad_exit_ip: true,
- hostname: Some(hostname),
- bridge_hostname,
- entry_hostname,
- obfuscator_hostname,
- })
- }
-
async fn on_create_new_account(&mut self, tx: ResponseTx<String, Error>) {
let account_manager = self.account_manager.clone();
tokio::spawn(async move {
@@ -1930,6 +1729,8 @@ where
Ok(settings_changed) => {
Self::oneshot_send(tx, Ok(()), "use_wireguard_nt response");
if settings_changed {
+ self.parameters_generator
+ .set_tunnel_options(&self.settings.tunnel_options);
self.event_listener
.notify_settings(self.settings.to_settings());
if let Some(TunnelType::Wireguard) = self.get_connected_tunnel_type() {
@@ -2079,6 +1880,8 @@ where
Ok(settings_changed) => {
Self::oneshot_send(tx, Ok(()), "set_openvpn_mssfix response");
if settings_changed {
+ self.parameters_generator
+ .set_tunnel_options(&self.settings.tunnel_options);
self.event_listener
.notify_settings(self.settings.to_settings());
if let Some(TunnelType::OpenVpn) = self.get_connected_tunnel_type() {
@@ -2186,6 +1989,8 @@ where
Ok(settings_changed) => {
Self::oneshot_send(tx, Ok(()), "set_enable_ipv6 response");
if settings_changed {
+ self.parameters_generator
+ .set_tunnel_options(&self.settings.tunnel_options);
self.event_listener
.notify_settings(self.settings.to_settings());
log::info!("Initiating tunnel restart because the enable IPv6 setting changed");
@@ -2212,6 +2017,8 @@ where
let settings = self.settings.to_settings();
let resolvers =
dns::addresses_from_options(&settings.tunnel_options.dns_options);
+ self.parameters_generator
+ .set_tunnel_options(&settings.tunnel_options);
self.event_listener.notify_settings(settings);
self.send_tunnel_command(TunnelCommand::Dns(resolvers));
}
@@ -2233,6 +2040,8 @@ where
Ok(settings_changed) => {
Self::oneshot_send(tx, Ok(()), "set_wireguard_mtu response");
if settings_changed {
+ self.parameters_generator
+ .set_tunnel_options(&self.settings.tunnel_options);
self.event_listener
.notify_settings(self.settings.to_settings());
if let Some(TunnelType::Wireguard) = self.get_connected_tunnel_type() {
@@ -2273,6 +2082,8 @@ where
error.display_chain_with_msg("Failed to update rotation interval")
);
}
+ self.parameters_generator
+ .set_tunnel_options(&self.settings.tunnel_options);
self.event_listener
.notify_settings(self.settings.to_settings());
}
@@ -2509,57 +2320,6 @@ impl DaemonShutdownHandle {
}
}
-struct MullvadTunnelParametersGenerator {
- tx: DaemonEventSender,
-}
-
-impl TunnelParametersGenerator for MullvadTunnelParametersGenerator {
- fn generate(
- &mut self,
- retry_attempt: u32,
- ) -> Result<TunnelParameters, ParameterGenerationError> {
- let (response_tx, response_rx) = sync_mpsc::channel();
- if self
- .tx
- .send(InternalDaemonEvent::GenerateTunnelParameters(
- response_tx,
- retry_attempt,
- ))
- .is_err()
- {
- log::error!("Failed to send daemon command to generate tunnel parameters!");
- return Err(ParameterGenerationError::NoMatchingRelay);
- }
-
- match response_rx.recv() {
- Ok(result) => result,
- Err(_) => {
- log::error!("Failed to receive tunnel parameter generation result!");
- Err(ParameterGenerationError::NoMatchingRelay)
- }
- }
- }
-}
-
-/// Contains all relays that were selected last time when tunnel parameters were generated.
-enum LastSelectedRelays {
- /// Represents all relays generated for a WireGuard tunnel.
- /// The traffic flow can look like this:
- /// client -> obfuscator -> entry -> exit -> internet
- /// But for most users, it will look like this:
- /// client -> entry -> internet
- WireGuard {
- wg_entry: Option<Relay>,
- wg_exit: Relay,
- obfuscator: Option<Relay>,
- },
- /// Represents all relays generated for an OpenVPN tunnel.
- /// The traffic flows like this:
- /// client -> bridge -> relay -> internet
- #[cfg(not(target_os = "android"))]
- OpenVpn { relay: Relay, bridge: Option<Relay> },
-}
-
fn new_selector_config(settings: &Settings) -> SelectorConfig {
SelectorConfig {
relay_settings: settings.get_relay_settings(),
diff --git a/mullvad-daemon/src/tunnel.rs b/mullvad-daemon/src/tunnel.rs
new file mode 100644
index 0000000000..9e8919a41b
--- /dev/null
+++ b/mullvad-daemon/src/tunnel.rs
@@ -0,0 +1,286 @@
+use std::{
+ future::Future,
+ pin::Pin,
+ sync::{Arc, Mutex},
+};
+
+use mullvad_relay_selector::{RelaySelector, SelectedBridge, SelectedObfuscator, SelectedRelay};
+use mullvad_types::{
+ endpoint::MullvadEndpoint, location::GeoIpLocation, relay_list::Relay, settings::TunnelOptions,
+};
+use talpid_core::tunnel_state_machine::TunnelParametersGenerator;
+use talpid_types::{
+ net::{wireguard, TunnelParameters},
+ tunnel::ParameterGenerationError,
+ ErrorExt,
+};
+
+#[cfg(not(target_os = "android"))]
+use talpid_types::net::openvpn;
+
+use crate::device::{AccountManagerHandle, PrivateAccountAndDevice};
+
+#[derive(err_derive::Error, Debug)]
+pub enum Error {
+ #[error(display = "Not logged in on a valid device")]
+ NoAuthDetails,
+
+ #[error(display = "No relay available")]
+ NoRelayAvailable,
+
+ #[error(display = "No bridge available")]
+ NoBridgeAvailable,
+
+ #[error(display = "Failed to resolve hostname for custom relay")]
+ ResolveCustomHostnameError,
+}
+
+#[derive(Clone)]
+pub(crate) struct ParametersGenerator(Arc<Mutex<InnerParametersGenerator>>);
+
+struct InnerParametersGenerator {
+ relay_selector: RelaySelector,
+ tunnel_options: TunnelOptions,
+ account_manager: AccountManagerHandle,
+
+ // TODO: Move this to `RelaySelector`?
+ last_generated_relays: Option<LastSelectedRelays>,
+}
+
+impl ParametersGenerator {
+ /// Constructs a new tunnel parameters generator.
+ pub fn new(
+ account_manager: AccountManagerHandle,
+ relay_selector: RelaySelector,
+ tunnel_options: TunnelOptions,
+ ) -> Self {
+ Self(Arc::new(Mutex::new(InnerParametersGenerator {
+ tunnel_options,
+ relay_selector,
+
+ account_manager,
+
+ last_generated_relays: None,
+ })))
+ }
+
+ /// Sets the tunnel options to use when generating new tunnel parameters.
+ pub fn set_tunnel_options(&self, tunnel_options: &TunnelOptions) {
+ self.0.lock().unwrap().tunnel_options = tunnel_options.clone();
+ }
+
+ /// Gets the location associated with the last generated tunnel parameters.
+ pub fn get_last_location(&self) -> Option<GeoIpLocation> {
+ let inner = self.0.lock().unwrap();
+
+ let relays = inner.last_generated_relays.as_ref()?;
+
+ let hostname;
+ let bridge_hostname;
+ let entry_hostname;
+ let obfuscator_hostname;
+ let location;
+ let take_hostname =
+ |relay: &Option<Relay>| relay.as_ref().map(|relay| relay.hostname.clone());
+
+ match relays {
+ LastSelectedRelays::WireGuard {
+ wg_entry: entry,
+ wg_exit: exit,
+ obfuscator,
+ } => {
+ entry_hostname = take_hostname(entry);
+ hostname = exit.hostname.clone();
+ obfuscator_hostname = take_hostname(obfuscator);
+ bridge_hostname = None;
+ location = exit.location.as_ref().cloned().unwrap();
+ }
+ #[cfg(not(target_os = "android"))]
+ LastSelectedRelays::OpenVpn { relay, bridge } => {
+ hostname = relay.hostname.clone();
+ bridge_hostname = take_hostname(bridge);
+ entry_hostname = None;
+ obfuscator_hostname = None;
+ location = relay.location.as_ref().cloned().unwrap();
+ }
+ };
+
+ Some(GeoIpLocation {
+ ipv4: None,
+ ipv6: None,
+ country: location.country,
+ city: Some(location.city),
+ latitude: location.latitude,
+ longitude: location.longitude,
+ mullvad_exit_ip: true,
+ hostname: Some(hostname),
+ bridge_hostname,
+ entry_hostname,
+ obfuscator_hostname,
+ })
+ }
+}
+
+impl InnerParametersGenerator {
+ async fn generate(&mut self, retry_attempt: u32) -> Result<TunnelParameters, Error> {
+ let _data = self.device().await?;
+ match self.relay_selector.get_relay(retry_attempt) {
+ Ok((SelectedRelay::Custom(custom_relay), _bridge, _obfsucator)) => {
+ custom_relay
+ // TODO: generate proxy settings for custom tunnels
+ .to_tunnel_parameters(self.tunnel_options.clone(), None)
+ .map_err(|e| {
+ log::error!("Failed to resolve hostname for custom tunnel config: {}", e);
+ Error::ResolveCustomHostnameError
+ })
+ }
+ Ok((SelectedRelay::Normal(constraints), bridge, obfuscator)) => {
+ self.create_tunnel_parameters(
+ &constraints.exit_relay,
+ &constraints.entry_relay,
+ constraints.endpoint,
+ bridge,
+ obfuscator,
+ )
+ .await
+ }
+ Err(mullvad_relay_selector::Error::NoBridge) => Err(Error::NoBridgeAvailable),
+ Err(_error) => Err(Error::NoRelayAvailable),
+ }
+ }
+
+ #[cfg_attr(target_os = "android", allow(unused_variables))]
+ async fn create_tunnel_parameters(
+ &mut self,
+ relay: &Relay,
+ entry_relay: &Option<Relay>,
+ endpoint: MullvadEndpoint,
+ bridge: Option<SelectedBridge>,
+ obfuscator: Option<SelectedObfuscator>,
+ ) -> Result<TunnelParameters, Error> {
+ let data = self.device().await?;
+ match endpoint {
+ #[cfg(not(target_os = "android"))]
+ MullvadEndpoint::OpenVpn(endpoint) => {
+ let (bridge_settings, bridge_relay) = match bridge {
+ Some(SelectedBridge::Normal(bridge)) => {
+ (Some(bridge.settings), Some(bridge.relay))
+ }
+ Some(SelectedBridge::Custom(settings)) => (Some(settings), None),
+ None => (None, None),
+ };
+
+ self.last_generated_relays = Some(LastSelectedRelays::OpenVpn {
+ relay: relay.clone(),
+ bridge: bridge_relay,
+ });
+
+ Ok(openvpn::TunnelParameters {
+ config: openvpn::ConnectionConfig::new(
+ endpoint,
+ data.account_token,
+ "-".to_string(),
+ ),
+ options: self.tunnel_options.openvpn.clone(),
+ generic_options: self.tunnel_options.generic.clone(),
+ proxy: bridge_settings,
+ }
+ .into())
+ }
+ #[cfg(target_os = "android")]
+ MullvadEndpoint::OpenVpn(endpoint) => {
+ unreachable!("OpenVPN is not supported on Android");
+ }
+ MullvadEndpoint::Wireguard(endpoint) => {
+ let tunnel = wireguard::TunnelConfig {
+ private_key: data.device.wg_data.private_key,
+ addresses: vec![
+ data.device.wg_data.addresses.ipv4_address.ip().into(),
+ data.device.wg_data.addresses.ipv6_address.ip().into(),
+ ],
+ };
+
+ let (obfuscator_relay, obfuscator_config) = match obfuscator {
+ Some(obfuscator) => (Some(obfuscator.relay), Some(obfuscator.config)),
+ None => (None, None),
+ };
+
+ self.last_generated_relays = Some(LastSelectedRelays::WireGuard {
+ wg_entry: entry_relay.clone(),
+ wg_exit: relay.clone(),
+ obfuscator: obfuscator_relay,
+ });
+
+ Ok(wireguard::TunnelParameters {
+ connection: wireguard::ConnectionConfig {
+ tunnel,
+ peer: endpoint.peer,
+ exit_peer: endpoint.exit_peer,
+ ipv4_gateway: endpoint.ipv4_gateway,
+ ipv6_gateway: Some(endpoint.ipv6_gateway),
+ },
+ options: self.tunnel_options.wireguard.options.clone(),
+ generic_options: self.tunnel_options.generic.clone(),
+ obfuscation: obfuscator_config,
+ }
+ .into())
+ }
+ }
+ }
+
+ async fn device(&self) -> Result<PrivateAccountAndDevice, Error> {
+ self.account_manager
+ .data()
+ .await
+ .ok()
+ .flatten()
+ .ok_or(Error::NoAuthDetails)
+ }
+}
+
+impl TunnelParametersGenerator for ParametersGenerator {
+ fn generate(
+ &mut self,
+ retry_attempt: u32,
+ ) -> Pin<Box<dyn Future<Output = Result<TunnelParameters, ParameterGenerationError>>>> {
+ let generator = self.0.clone();
+ Box::pin(async move {
+ let mut inner = generator.lock().unwrap();
+ inner
+ .generate(retry_attempt)
+ .await
+ .map_err(|error| match error {
+ Error::NoBridgeAvailable => ParameterGenerationError::NoMatchingBridgeRelay,
+ Error::ResolveCustomHostnameError => {
+ ParameterGenerationError::CustomTunnelHostResultionError
+ }
+ error => {
+ log::error!(
+ "{}",
+ error.display_chain_with_msg("Failed to generate tunnel parameters")
+ );
+ ParameterGenerationError::NoMatchingRelay
+ }
+ })
+ })
+ }
+}
+
+/// Contains all relays that were selected last time when tunnel parameters were generated.
+enum LastSelectedRelays {
+ /// Represents all relays generated for a WireGuard tunnel.
+ /// The traffic flow can look like this:
+ /// client -> obfuscator -> entry -> exit -> internet
+ /// But for most users, it will look like this:
+ /// client -> entry -> internet
+ WireGuard {
+ wg_entry: Option<Relay>,
+ wg_exit: Relay,
+ obfuscator: Option<Relay>,
+ },
+ /// Represents all relays generated for an OpenVPN tunnel.
+ /// The traffic flows like this:
+ /// client -> bridge -> relay -> internet
+ #[cfg(not(target_os = "android"))]
+ OpenVpn { relay: Relay, bridge: Option<Relay> },
+}
diff --git a/talpid-core/src/tunnel_state_machine/connecting_state.rs b/talpid-core/src/tunnel_state_machine/connecting_state.rs
index f79c6ab35d..7e22bd6aa0 100644
--- a/talpid-core/src/tunnel_state_machine/connecting_state.rs
+++ b/talpid-core/src/tunnel_state_machine/connecting_state.rs
@@ -528,10 +528,11 @@ impl TunnelState for ConnectingState {
if shared_values.is_offline {
return ErrorState::enter(shared_values, ErrorStateCause::IsOffline);
}
- match shared_values
- .tunnel_parameters_generator
- .generate(retry_attempt)
- {
+ match shared_values.runtime.block_on(
+ shared_values
+ .tunnel_parameters_generator
+ .generate(retry_attempt),
+ ) {
Err(err) => {
ErrorState::enter(shared_values, ErrorStateCause::TunnelParameterError(err))
}
diff --git a/talpid-core/src/tunnel_state_machine/mod.rs b/talpid-core/src/tunnel_state_machine/mod.rs
index f6fa472983..6c388a4680 100644
--- a/talpid-core/src/tunnel_state_machine/mod.rs
+++ b/talpid-core/src/tunnel_state_machine/mod.rs
@@ -32,9 +32,11 @@ use futures::{
use std::os::unix::io::RawFd;
use std::{
collections::HashSet,
+ future::Future,
io,
net::IpAddr,
path::PathBuf,
+ pin::Pin,
sync::{Arc, Mutex},
time::Duration,
};
@@ -366,7 +368,7 @@ pub trait TunnelParametersGenerator: Send + 'static {
fn generate(
&mut self,
retry_attempt: u32,
- ) -> Result<TunnelParameters, ParameterGenerationError>;
+ ) -> Pin<Box<dyn Future<Output = Result<TunnelParameters, ParameterGenerationError>>>>;
}
/// Values that are common to all tunnel states.