diff --git a/mullvad-cli/src/cmds/custom_list.rs b/mullvad-cli/src/cmds/custom_list.rs index 47e48c0c5f2e..5651ff2d16a9 100644 --- a/mullvad-cli/src/cmds/custom_list.rs +++ b/mullvad-cli/src/cmds/custom_list.rs @@ -31,6 +31,7 @@ pub enum CustomList { /// A custom list name: String, }, + // TODO: Add `clear` command? } #[derive(Subcommand, Debug)] diff --git a/mullvad-daemon/src/custom_list.rs b/mullvad-daemon/src/custom_list.rs index c15fd14a701d..ed507468acc5 100644 --- a/mullvad-daemon/src/custom_list.rs +++ b/mullvad-daemon/src/custom_list.rs @@ -56,7 +56,7 @@ where self.relay_selector .set_config(new_selector_config(&self.settings)); - if self.change_should_cause_reconnect(id) { + if self.change_should_cause_reconnect(Some(id)) { log::info!("Initiating tunnel restart because a selected custom list was deleted"); self.reconnect_tunnel(); } @@ -100,7 +100,7 @@ where self.relay_selector .set_config(new_selector_config(&self.settings)); - if self.change_should_cause_reconnect(id) { + if self.change_should_cause_reconnect(Some(id)) { log::info!("Initiating tunnel restart because a selected custom list changed"); self.reconnect_tunnel(); } @@ -110,48 +110,77 @@ where Ok(()) } - fn change_should_cause_reconnect(&self, custom_list_id: Id) -> bool { + pub async fn clear_custom_lists(&mut self) -> Result<(), Error> { + let settings_changed = self + .settings + .update(|settings| { + settings.custom_lists.clear(); + }) + .await + .map_err(Error::SettingsError); + + if let Ok(true) = settings_changed { + self.relay_selector + .set_config(new_selector_config(&self.settings)); + + if self.change_should_cause_reconnect(None) { + log::info!("Initiating tunnel restart because a selected custom list was deleted"); + self.reconnect_tunnel(); + } + } + + settings_changed?; + Ok(()) + } + + /// Check whether we need to reconnect after changing custom lists. + /// + /// If `custom_list_id` is `Some`, only changes to that custom list will trigger a reconnect. + fn change_should_cause_reconnect(&self, custom_list_id: Option) -> bool { use mullvad_types::states::TunnelState; let mut need_to_reconnect = false; - if let RelaySettings::Normal(relay_settings) = &self.settings.relay_settings { - if let Constraint::Only(LocationConstraint::CustomList { list_id }) = - &relay_settings.location - { - need_to_reconnect |= list_id == &custom_list_id; - } + let RelaySettings::Normal(relay_settings) = &self.settings.relay_settings else { + return false; + }; - if let TunnelState::Connecting { - endpoint, - location: _, - } - | TunnelState::Connected { - endpoint, - location: _, - } = &self.tunnel_state - { - match endpoint.tunnel_type { - TunnelType::Wireguard => { - if relay_settings.wireguard_constraints.multihop() { - if let Constraint::Only(LocationConstraint::CustomList { list_id }) = - &relay_settings.wireguard_constraints.entry_location - { - need_to_reconnect |= list_id == &custom_list_id; - } + if let Constraint::Only(LocationConstraint::CustomList { list_id }) = + &relay_settings.location + { + need_to_reconnect |= custom_list_id.map(|id| &id == list_id).unwrap_or(true); + } + + if let TunnelState::Connecting { + endpoint, + location: _, + } + | TunnelState::Connected { + endpoint, + location: _, + } = &self.tunnel_state + { + match endpoint.tunnel_type { + TunnelType::Wireguard => { + if relay_settings.wireguard_constraints.multihop() { + if let Constraint::Only(LocationConstraint::CustomList { list_id }) = + &relay_settings.wireguard_constraints.entry_location + { + need_to_reconnect |= + custom_list_id.map(|id| &id == list_id).unwrap_or(true); } } + } - TunnelType::OpenVpn => { - if !matches!(self.settings.bridge_state, BridgeState::Off) { - if let Ok(ResolvedBridgeSettings::Normal(bridge_settings)) = - self.settings.bridge_settings.resolve() + TunnelType::OpenVpn => { + if !matches!(self.settings.bridge_state, BridgeState::Off) { + if let Ok(ResolvedBridgeSettings::Normal(bridge_settings)) = + self.settings.bridge_settings.resolve() + { + if let Constraint::Only(LocationConstraint::CustomList { list_id }) = + &bridge_settings.location { - if let Constraint::Only(LocationConstraint::CustomList { - list_id, - }) = &bridge_settings.location - { - need_to_reconnect |= list_id == &custom_list_id; - } + need_to_reconnect |= + custom_list_id.map(|id| &id == list_id).unwrap_or(true); } } } diff --git a/mullvad-daemon/src/lib.rs b/mullvad-daemon/src/lib.rs index da02cb6ffc95..365e82efeba1 100644 --- a/mullvad-daemon/src/lib.rs +++ b/mullvad-daemon/src/lib.rs @@ -279,6 +279,8 @@ pub enum DaemonCommand { DeleteCustomList(ResponseTx<(), Error>, mullvad_types::custom_list::Id), /// Update a custom list with a given id UpdateCustomList(ResponseTx<(), Error>, CustomList), + /// Remove all custom lists + ClearCustomLists(ResponseTx<(), Error>), /// Add API access methods AddApiAccessMethod( ResponseTx, @@ -1255,6 +1257,7 @@ where CreateCustomList(tx, name) => self.on_create_custom_list(tx, name).await, DeleteCustomList(tx, id) => self.on_delete_custom_list(tx, id).await, UpdateCustomList(tx, update) => self.on_update_custom_list(tx, update).await, + ClearCustomLists(tx) => self.on_clear_custom_lists(tx).await, GetVersionInfo(tx) => self.on_get_version_info(tx), AddApiAccessMethod(tx, name, enabled, access_method) => { self.on_add_access_method(tx, name, enabled, access_method) @@ -2435,6 +2438,11 @@ where Self::oneshot_send(tx, result, "update_custom_list response"); } + async fn on_clear_custom_lists(&mut self, tx: ResponseTx<(), Error>) { + let result = self.clear_custom_lists().await; + Self::oneshot_send(tx, result, "clear_custom_lists response"); + } + async fn on_add_access_method( &mut self, tx: ResponseTx, diff --git a/mullvad-daemon/src/management_interface.rs b/mullvad-daemon/src/management_interface.rs index 52c2c50d4cc2..55573f87a7d1 100644 --- a/mullvad-daemon/src/management_interface.rs +++ b/mullvad-daemon/src/management_interface.rs @@ -598,6 +598,16 @@ impl ManagementService for ManagementServiceImpl { .map_err(map_daemon_error) } + async fn clear_custom_lists(&self, _: Request<()>) -> ServiceResult<()> { + log::debug!("clear_custom_lists"); + let (tx, rx) = oneshot::channel(); + self.send_command_to_daemon(DaemonCommand::ClearCustomLists(tx))?; + self.wait_for_result(rx) + .await? + .map(Response::new) + .map_err(map_daemon_error) + } + // Access Methods async fn add_api_access_method( diff --git a/mullvad-management-interface/proto/management_interface.proto b/mullvad-management-interface/proto/management_interface.proto index cd3e6afaac0c..8af34aa8afe3 100644 --- a/mullvad-management-interface/proto/management_interface.proto +++ b/mullvad-management-interface/proto/management_interface.proto @@ -73,6 +73,7 @@ service ManagementService { rpc CreateCustomList(google.protobuf.StringValue) returns (google.protobuf.StringValue) {} rpc DeleteCustomList(google.protobuf.StringValue) returns (google.protobuf.Empty) {} rpc UpdateCustomList(CustomList) returns (google.protobuf.Empty) {} + rpc ClearCustomLists(google.protobuf.Empty) returns (google.protobuf.Empty) {} // Access methods rpc AddApiAccessMethod(NewAccessMethodSetting) returns (UUID) {} diff --git a/mullvad-management-interface/src/client.rs b/mullvad-management-interface/src/client.rs index 1e9b287f46ac..2244480ba2c2 100644 --- a/mullvad-management-interface/src/client.rs +++ b/mullvad-management-interface/src/client.rs @@ -535,6 +535,14 @@ impl MullvadProxyClient { Ok(()) } + pub async fn clear_custom_lists(&mut self) -> Result<()> { + self.0 + .clear_custom_lists(()) + .await + .map_err(map_custom_list_error)?; + Ok(()) + } + pub async fn add_access_method( &mut self, name: String, diff --git a/mullvad-types/src/custom_list.rs b/mullvad-types/src/custom_list.rs index b92f128a3bd3..89febe81abad 100644 --- a/mullvad-types/src/custom_list.rs +++ b/mullvad-types/src/custom_list.rs @@ -98,6 +98,11 @@ impl CustomListsSettings { pub fn remove(&mut self, index: usize) { self.custom_lists.remove(index); } + + /// Remove all custom lists + pub fn clear(&mut self) { + self.custom_lists.clear(); + } } impl IntoIterator for CustomListsSettings { diff --git a/test/test-manager/src/tests/mod.rs b/test/test-manager/src/tests/mod.rs index e99300232b9f..f4c4563bde08 100644 --- a/test/test-manager/src/tests/mod.rs +++ b/test/test-manager/src/tests/mod.rs @@ -171,12 +171,11 @@ pub async fn cleanup_after_test(mullvad_client: &mut MullvadProxyClient) -> anyh .await .context("Could not clear PQ options in cleanup")?; - for custom_list in custom_lists { - mullvad_client - .delete_custom_list(custom_list.name) - .await - .context("Could not remove custom list")?; - } + let _ = custom_lists; + mullvad_client + .clear_custom_lists() + .await + .context("Could not remove custom list")?; Ok(()) }