summaryrefslogtreecommitdiffhomepage
path: root/mullvad-daemon/src
diff options
context:
space:
mode:
authorMarkus Pettersson <markus.pettersson@mullvad.net>2023-09-19 13:43:53 +0200
committerDavid Lönnhager <david.l@mullvad.net>2023-10-09 14:40:06 +0200
commitfc477f4e4df6db2973ff88fc4b6819d38d64d8cc (patch)
tree069b709abb09599b37f32716de679298095bc1c1 /mullvad-daemon/src
parentbe5e93c32f3b3e9ec59da18215fad80457dd4d49 (diff)
downloadmullvadvpn-fc477f4e4df6db2973ff88fc4b6819d38d64d8cc.tar.xz
mullvadvpn-fc477f4e4df6db2973ff88fc4b6819d38d64d8cc.zip
Add `mullvad proxy use`
Allow for settings a specific Access Method to use
Diffstat (limited to 'mullvad-daemon/src')
-rw-r--r--mullvad-daemon/src/access_methods.rs29
-rw-r--r--mullvad-daemon/src/api.rs72
-rw-r--r--mullvad-daemon/src/lib.rs28
-rw-r--r--mullvad-daemon/src/management_interface.rs15
4 files changed, 118 insertions, 26 deletions
diff --git a/mullvad-daemon/src/access_methods.rs b/mullvad-daemon/src/access_methods.rs
index 79afdae054..a0c168c026 100644
--- a/mullvad-daemon/src/access_methods.rs
+++ b/mullvad-daemon/src/access_methods.rs
@@ -86,22 +86,29 @@ where
.map_err(Error::Settings)
}
+ pub async fn set_api_access_method(
+ &mut self,
+ access_method: AccessMethod,
+ ) -> Result<(), Error> {
+ {
+ let mut connection_modes = self.connection_modes.lock().unwrap();
+ connection_modes.set_access_method(access_method);
+ }
+ // Force a rotation of Acces Methods.
+ let _ = self.api_handle.service().next_api_endpoint();
+ Ok(())
+ }
+
/// If settings were changed due to an update, notify all listeners.
fn notify_on_change(&mut self, settings_changed: MadeChanges) {
if settings_changed {
self.event_listener
.notify_settings(self.settings.to_settings());
- };
- // TODO: Could this be replaced by message passing? Yes plz.
- let mut connection_modes = self.connection_modes.lock().unwrap();
- *connection_modes = self
- .settings
- .api_access_methods
- .api_access_methods
- .clone()
- .into_iter()
- .map(|x| (x, 1))
- .collect();
+ // TODO: Could this be replaced by message passing? Yes plz.
+ let mut connection_modes = self.connection_modes.lock().unwrap();
+ connection_modes
+ .update_access_methods(self.settings.api_access_methods.api_access_methods.clone())
+ };
}
}
diff --git a/mullvad-daemon/src/api.rs b/mullvad-daemon/src/api.rs
index e8568ac684..f96dea9484 100644
--- a/mullvad-daemon/src/api.rs
+++ b/mullvad-daemon/src/api.rs
@@ -44,6 +44,58 @@ pub struct ApiConnectionModeProvider {
relay_selector: RelaySelector,
retry_attempt: u32,
current_task: Option<Pin<Box<dyn Future<Output = ApiConnectionMode> + Send>>>,
+ connection_modes: Arc<Mutex<ConnectionModesIterator>>,
+}
+
+/// An iterator which will always produce an [`AccessMethod`].
+///
+/// Safety: It is always safe to [`unwrap`] after calling [`next`] on a
+/// [`std::iter::Cycle`], so thereby it is safe to always call [`unwrap`] on a
+/// [`ConnectionModesIterator`]
+///
+/// [`unwrap`]: Option::unwrap
+/// [`next`]: std::iter::Iterator::next
+pub struct ConnectionModesIterator {
+ available_modes: Box<dyn Iterator<Item = AccessMethod> + Send>,
+ next: Option<AccessMethod>,
+}
+
+impl ConnectionModesIterator {
+ pub fn new(modes: Vec<AccessMethod>) -> ConnectionModesIterator {
+ Self {
+ next: None,
+ available_modes: Self::get_filtered_access_methods(modes),
+ }
+ }
+
+ /// Set the next [`AccessMethod`] to be returned from this iterator.
+ pub fn set_access_method(&mut self, next: AccessMethod) {
+ self.next = Some(next);
+ }
+ /// Update the collection of [`AccessMethod`] which this iterator will
+ /// return.
+ pub fn update_access_methods(&mut self, access_methods: Vec<AccessMethod>) {
+ self.available_modes = Self::get_filtered_access_methods(access_methods);
+ }
+
+ fn get_filtered_access_methods(
+ modes: Vec<AccessMethod>,
+ ) -> Box<dyn Iterator<Item = AccessMethod> + Send> {
+ Box::new(
+ modes
+ .into_iter()
+ .filter(|access_method| access_method.enabled())
+ .cycle(),
+ )
+ }
+}
+
+impl Iterator for ConnectionModesIterator {
+ type Item = AccessMethod;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ self.next.take().or_else(|| self.available_modes.next())
+ }
}
impl Stream for ApiConnectionModeProvider {
@@ -83,23 +135,22 @@ impl Stream for ApiConnectionModeProvider {
}
impl ApiConnectionModeProvider {
- pub(crate) fn new(cache_dir: PathBuf, relay_selector: RelaySelector) -> Self {
+ pub(crate) fn new(
+ cache_dir: PathBuf,
+ relay_selector: RelaySelector,
+ connection_modes: Vec<AccessMethod>,
+ ) -> Self {
Self {
cache_dir,
relay_selector,
retry_attempt: 0,
current_task: None,
+ connection_modes: Arc::new(Mutex::new(ConnectionModesIterator::new(connection_modes))),
}
}
- fn should_use_bridge(retry_attempt: u32) -> bool {
- retry_attempt % 3 > 0
- }
-
- fn should_use_direct(retry_attempt: u32) -> bool {
- // TODO: Change back before comitting!
- false
- // !Self::should_use_bridge(retry_attempt)
+ pub(crate) fn handle(&self) -> Arc<Mutex<ConnectionModesIterator>> {
+ self.connection_modes.clone()
}
/// Return a new connection mode to be used for the API connection.
@@ -112,10 +163,9 @@ impl ApiConnectionModeProvider {
log::debug!("Rotating Access mode!");
let access_method = {
let mut access_methods_picker = self.connection_modes.lock().unwrap();
- // Rotate through the cycle of access methods.
- // Safety: It is always safe to unwrap after calling `next` on a [`std::iter::Cycle`]
access_methods_picker.next().unwrap()
};
+
let connection_mode = self.from(&access_method);
log::info!("New API connection mode selected: {}", connection_mode);
connection_mode
diff --git a/mullvad-daemon/src/lib.rs b/mullvad-daemon/src/lib.rs
index 8427b1ea6b..3c822fdf4e 100644
--- a/mullvad-daemon/src/lib.rs
+++ b/mullvad-daemon/src/lib.rs
@@ -65,7 +65,7 @@ use std::{
mem,
path::PathBuf,
pin::Pin,
- sync::{Arc, Weak},
+ sync::{Arc, Mutex, Weak},
time::Duration,
};
#[cfg(any(target_os = "linux", windows))]
@@ -273,6 +273,8 @@ pub enum DaemonCommand {
ReplaceApiAccessMethod(ResponseTx<(), Error>, ApiAccessMethodReplace),
/// Toggle the status of an API access method
ToggleApiAccessMethod(ResponseTx<(), Error>, ApiAccessMethodToggle),
+ /// Set the API access method to use
+ SetApiAccessMethod(ResponseTx<(), Error>, AccessMethod),
/// Get information about the currently running and latest app versions
GetVersionInfo(oneshot::Sender<Option<AppVersionInfo>>),
/// Return whether the daemon is performing post-upgrade tasks
@@ -572,6 +574,7 @@ pub struct Daemon<L: EventListener> {
account_history: account_history::AccountHistory,
device_checker: device::TunnelStateChangeHandler,
account_manager: device::AccountManagerHandle,
+ connection_modes: Arc<Mutex<api::ConnectionModesIterator>>,
api_runtime: mullvad_api::Runtime,
api_handle: mullvad_api::rest::MullvadRestHandle,
version_updater_handle: version_check::VersionUpdaterHandle,
@@ -634,8 +637,15 @@ where
let initial_selector_config = new_selector_config(&settings);
let relay_selector = RelaySelector::new(initial_selector_config, &resource_dir, &cache_dir);
- let proxy_provider =
- api::ApiConnectionModeProvider::new(cache_dir.clone(), relay_selector.clone());
+ // TODO: Should ApiConnectionModeProvider be an Actor instead of sharing a datastructure-behind-locks with the daemon with the daemon?
+ let proxy_provider = api::ApiConnectionModeProvider::new(
+ cache_dir.clone(),
+ relay_selector.clone(),
+ settings.api_access_methods.api_access_methods.clone(),
+ );
+
+ let connection_modes = proxy_provider.handle();
+
let api_handle = api_runtime
.mullvad_rest_handle(proxy_provider, endpoint_updater.callback())
.await;
@@ -772,6 +782,7 @@ where
account_history,
device_checker: device::TunnelStateChangeHandler::new(account_manager.clone()),
account_manager,
+ connection_modes,
api_runtime,
api_handle,
version_updater_handle,
@@ -1055,6 +1066,7 @@ where
self.on_replace_api_access_method(tx, method).await
}
ToggleApiAccessMethod(tx, method) => self.on_toggle_api_access_method(tx, method).await,
+ SetApiAccessMethod(tx, method) => self.on_set_api_access_method(tx, method).await,
IsPerformingPostUpgrade(tx) => self.on_is_performing_post_upgrade(tx),
GetCurrentVersion(tx) => self.on_get_current_version(tx),
#[cfg(not(target_os = "android"))]
@@ -2275,7 +2287,15 @@ where
.toggle_api_access_method(method)
.await
.map_err(Error::AccessMethodError);
- Self::oneshot_send(tx, result, "edit_api_access_method response");
+ Self::oneshot_send(tx, result, "toggle_api_access_method response");
+ }
+
+ async fn on_set_api_access_method(&mut self, tx: ResponseTx<(), Error>, method: AccessMethod) {
+ let result = self
+ .set_api_access_method(method)
+ .await
+ .map_err(Error::AccessMethodError);
+ Self::oneshot_send(tx, result, "set_api_access_method response");
}
fn on_get_settings(&self, tx: oneshot::Sender<Settings>) {
diff --git a/mullvad-daemon/src/management_interface.rs b/mullvad-daemon/src/management_interface.rs
index 91be9352ec..2b6481207a 100644
--- a/mullvad-daemon/src/management_interface.rs
+++ b/mullvad-daemon/src/management_interface.rs
@@ -639,6 +639,21 @@ impl ManagementService for ManagementServiceImpl {
.map_err(map_daemon_error)
}
+ async fn set_api_access_method(
+ &self,
+ request: Request<types::ApiAccessMethod>,
+ ) -> ServiceResult<()> {
+ log::debug!("set_api_access_method");
+ let access_method =
+ mullvad_types::api_access_method::AccessMethod::try_from(request.into_inner())?;
+ let (tx, rx) = oneshot::channel();
+ self.send_command_to_daemon(DaemonCommand::SetApiAccessMethod(tx, access_method))?;
+ self.wait_for_result(rx)
+ .await?
+ .map(Response::new)
+ .map_err(map_daemon_error)
+ }
+
// Split tunneling
//