summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorDavid Lönnhager <david.l@mullvad.net>2023-09-21 00:37:07 +0200
committerDavid Lönnhager <david.l@mullvad.net>2023-09-27 10:25:35 +0200
commit52cdf0bfbed50179b1da5ccad80fa0245a524c1e (patch)
treea0193fc10ede1818c5f9987c005c16f56d058322
parent8030071e86bcae8bfde27ead730d0414b1bd7605 (diff)
downloadmullvadvpn-52cdf0bfbed50179b1da5ccad80fa0245a524c1e.tar.xz
mullvadvpn-52cdf0bfbed50179b1da5ccad80fa0245a524c1e.zip
Refactor custom list implementation
-rw-r--r--mullvad-cli/src/cmds/bridge.rs3
-rw-r--r--mullvad-cli/src/cmds/custom_list.rs (renamed from mullvad-cli/src/cmds/custom_lists.rs)52
-rw-r--r--mullvad-cli/src/cmds/mod.rs2
-rw-r--r--mullvad-cli/src/cmds/relay.rs10
-rw-r--r--mullvad-cli/src/main.rs2
-rw-r--r--mullvad-daemon/src/custom_list.rs178
-rw-r--r--mullvad-daemon/src/custom_lists.rs324
-rw-r--r--mullvad-daemon/src/lib.rs97
-rw-r--r--mullvad-daemon/src/management_interface.rs116
-rw-r--r--mullvad-management-interface/proto/management_interface.proto26
-rw-r--r--mullvad-management-interface/src/client.rs65
-rw-r--r--mullvad-management-interface/src/lib.rs5
-rw-r--r--mullvad-management-interface/src/types/conversions/custom_list.rs138
-rw-r--r--mullvad-management-interface/src/types/conversions/relay_constraints.rs7
-rw-r--r--mullvad-management-interface/src/types/conversions/settings.rs4
-rw-r--r--mullvad-types/src/custom_list.rs137
-rw-r--r--mullvad-types/src/relay_constraints.rs21
17 files changed, 447 insertions, 740 deletions
diff --git a/mullvad-cli/src/cmds/bridge.rs b/mullvad-cli/src/cmds/bridge.rs
index 2ee7d9a4f0..50c242cefd 100644
--- a/mullvad-cli/src/cmds/bridge.rs
+++ b/mullvad-cli/src/cmds/bridge.rs
@@ -172,7 +172,8 @@ impl Bridge {
Self::update_bridge_settings(&mut rpc, Some(location), None, None).await
}
SetCommands::CustomList { custom_list_name } => {
- let list = rpc.get_custom_list(custom_list_name).await?;
+ let list =
+ super::custom_list::find_list_by_name(&mut rpc, &custom_list_name).await?;
let location =
Constraint::Only(LocationConstraint::CustomList { list_id: list.id });
Self::update_bridge_settings(&mut rpc, Some(location), None, None).await
diff --git a/mullvad-cli/src/cmds/custom_lists.rs b/mullvad-cli/src/cmds/custom_list.rs
index d30f29f62d..d70a96a28a 100644
--- a/mullvad-cli/src/cmds/custom_lists.rs
+++ b/mullvad-cli/src/cmds/custom_list.rs
@@ -2,11 +2,10 @@ use super::{
relay::{find_relay_by_hostname, get_filtered_relays},
relay_constraints::LocationArgs,
};
-use anyhow::Result;
+use anyhow::{anyhow, Result};
use clap::Subcommand;
use mullvad_management_interface::MullvadProxyClient;
use mullvad_types::{
- custom_list::CustomListLocationUpdate,
relay_constraints::{Constraint, GeographicLocationConstraint},
relay_list::RelayList,
};
@@ -86,7 +85,7 @@ impl CustomList {
async fn list() -> Result<()> {
let mut rpc = MullvadProxyClient::new().await?;
let cache = rpc.get_relay_locations().await?;
- for custom_list in rpc.list_custom_lists().await? {
+ for custom_list in rpc.get_settings().await?.custom_lists {
Self::print_custom_list(&custom_list, &cache)
}
Ok(())
@@ -96,7 +95,7 @@ impl CustomList {
/// If the list does not exist, print an error.
async fn get(name: String) -> Result<()> {
let mut rpc = MullvadProxyClient::new().await?;
- let custom_list = rpc.get_custom_list(name).await?;
+ let custom_list = find_list_by_name(&mut rpc, &name).await?;
let cache = rpc.get_relay_locations().await?;
Self::print_custom_list_content(&custom_list, &cache);
Ok(())
@@ -111,30 +110,47 @@ impl CustomList {
async fn add_location(name: String, location_args: LocationArgs) -> Result<()> {
let countries = get_filtered_relays().await?;
let location = find_relay_by_hostname(&countries, &location_args.country)
- .map_or(Constraint::from(location_args), Constraint::Only);
- let update = CustomListLocationUpdate::Add { name, location };
+ .map_or(Constraint::from(location_args), Constraint::Only)
+ .option()
+ .ok_or(anyhow!("\"any\" is not a valid location"))?;
+
let mut rpc = MullvadProxyClient::new().await?;
- rpc.update_custom_list_location(update).await?;
+
+ let mut list = find_list_by_name(&mut rpc, &name).await?;
+ list.locations.insert(location);
+ rpc.update_custom_list(list).await?;
+
Ok(())
}
async fn remove_location(name: String, location_args: LocationArgs) -> Result<()> {
- let location = Constraint::<GeographicLocationConstraint>::from(location_args);
- let update = CustomListLocationUpdate::Remove { name, location };
+ let location = Constraint::<GeographicLocationConstraint>::from(location_args)
+ .option()
+ .ok_or(anyhow!("\"any\" is not a valid location"))?;
+
let mut rpc = MullvadProxyClient::new().await?;
- rpc.update_custom_list_location(update).await?;
+
+ let mut list = find_list_by_name(&mut rpc, &name).await?;
+ list.locations.remove(&location);
+ rpc.update_custom_list(list).await?;
+
Ok(())
}
async fn delete_list(name: String) -> Result<()> {
let mut rpc = MullvadProxyClient::new().await?;
- rpc.delete_custom_list(name).await?;
+ let list = find_list_by_name(&mut rpc, &name).await?;
+ rpc.delete_custom_list(list.id.to_string()).await?;
Ok(())
}
async fn rename_list(name: String, new_name: String) -> Result<()> {
let mut rpc = MullvadProxyClient::new().await?;
- rpc.rename_custom_list(name, new_name).await?;
+
+ let mut list = find_list_by_name(&mut rpc, &name).await?;
+ list.name = new_name;
+ rpc.update_custom_list(list).await?;
+
Ok(())
}
@@ -217,3 +233,15 @@ impl<'a> std::fmt::Display for GeographicLocationConstraintFormatter<'a> {
}
}
}
+
+pub async fn find_list_by_name(
+ rpc: &mut MullvadProxyClient,
+ name: &str,
+) -> Result<mullvad_types::custom_list::CustomList> {
+ rpc.get_settings()
+ .await?
+ .custom_lists
+ .into_iter()
+ .find(|list| list.name == name)
+ .ok_or(anyhow!("List not found"))
+}
diff --git a/mullvad-cli/src/cmds/mod.rs b/mullvad-cli/src/cmds/mod.rs
index 8b7ca1a6be..c63a981133 100644
--- a/mullvad-cli/src/cmds/mod.rs
+++ b/mullvad-cli/src/cmds/mod.rs
@@ -5,7 +5,7 @@ pub mod account;
pub mod auto_connect;
pub mod beta_program;
pub mod bridge;
-pub mod custom_lists;
+pub mod custom_list;
pub mod dns;
pub mod lan;
pub mod lockdown;
diff --git a/mullvad-cli/src/cmds/relay.rs b/mullvad-cli/src/cmds/relay.rs
index 78f7d1f3c4..2bccbbc24e 100644
--- a/mullvad-cli/src/cmds/relay.rs
+++ b/mullvad-cli/src/cmds/relay.rs
@@ -516,7 +516,9 @@ impl Relay {
async fn set_custom_list(custom_list_name: String) -> Result<()> {
let mut rpc = MullvadProxyClient::new().await?;
- let list_id = rpc.get_custom_list(custom_list_name).await?.id;
+ let list_id = super::custom_list::find_list_by_name(&mut rpc, &custom_list_name)
+ .await?
+ .id;
rpc.update_relay_settings(RelaySettingsUpdate::Normal(RelayConstraintsUpdate {
location: Some(Constraint::Only(LocationConstraint::CustomList { list_id })),
..Default::default()
@@ -617,9 +619,11 @@ impl Relay {
};
}
Some(EntryLocation::CustomList { custom_list_name }) => {
- let list = rpc.get_custom_list(custom_list_name).await?;
+ let list_id = super::custom_list::find_list_by_name(&mut rpc, &custom_list_name)
+ .await?
+ .id;
wireguard_constraints.entry_location =
- Constraint::Only(LocationConstraint::CustomList { list_id: list.id });
+ Constraint::Only(LocationConstraint::CustomList { list_id });
}
None => (),
}
diff --git a/mullvad-cli/src/main.rs b/mullvad-cli/src/main.rs
index 566b91e3f0..41f1643970 100644
--- a/mullvad-cli/src/main.rs
+++ b/mullvad-cli/src/main.rs
@@ -115,7 +115,7 @@ enum Cli {
/// Manage custom lists
#[clap(subcommand)]
- CustomList(custom_lists::CustomList),
+ CustomList(custom_list::CustomList),
}
#[tokio::main]
diff --git a/mullvad-daemon/src/custom_list.rs b/mullvad-daemon/src/custom_list.rs
new file mode 100644
index 0000000000..8d244765cc
--- /dev/null
+++ b/mullvad-daemon/src/custom_list.rs
@@ -0,0 +1,178 @@
+use crate::{new_selector_config, Daemon, Error, EventListener};
+use mullvad_types::{
+ custom_list::{CustomList, Id},
+ relay_constraints::{
+ BridgeSettings, BridgeState, Constraint, LocationConstraint, RelaySettings,
+ },
+};
+use talpid_types::net::TunnelType;
+
+impl<L> Daemon<L>
+where
+ L: EventListener + Clone + Send + 'static,
+{
+ pub async fn create_custom_list(&mut self, name: String) -> Result<Id, crate::Error> {
+ if self
+ .settings
+ .custom_lists
+ .iter()
+ .any(|list| list.name == name)
+ {
+ return Err(Error::CustomListExists);
+ }
+
+ let new_list = CustomList::new(name);
+ let id = new_list.id;
+
+ let settings_changed = self
+ .settings
+ .update(|settings| {
+ settings.custom_lists.add(new_list);
+ })
+ .await
+ .map_err(Error::SettingsError);
+
+ if let Ok(true) = settings_changed {
+ self.event_listener
+ .notify_settings(self.settings.to_settings());
+ self.relay_selector
+ .set_config(new_selector_config(&self.settings));
+ }
+
+ settings_changed?;
+ Ok(id)
+ }
+
+ pub async fn delete_custom_list(&mut self, id: Id) -> Result<(), Error> {
+ let Some(list_index) = self
+ .settings
+ .custom_lists
+ .iter()
+ .position(|elem| elem.id == id)
+ else {
+ return Err(Error::CustomListNotFound);
+ };
+ let settings_changed = self
+ .settings
+ .update(|settings| {
+ // NOTE: Not using swap remove because it would make user output slightly
+ // more confusing and the cost is so small.
+ settings.custom_lists.remove(list_index);
+ })
+ .await
+ .map_err(Error::SettingsError);
+
+ if let Ok(true) = settings_changed {
+ self.event_listener
+ .notify_settings(self.settings.to_settings());
+ self.relay_selector
+ .set_config(new_selector_config(&self.settings));
+
+ if self.change_should_cause_reconnect(id) {
+ log::info!("Initiating tunnel restart because a selected custom list was deleted");
+ self.reconnect_tunnel();
+ }
+ }
+
+ settings_changed?;
+ Ok(())
+ }
+
+ pub async fn update_custom_list(&mut self, new_list: CustomList) -> Result<(), Error> {
+ let Some((list_index, old_list)) = self
+ .settings
+ .custom_lists
+ .iter()
+ .enumerate()
+ .find(|elem| elem.1.id == new_list.id)
+ else {
+ return Err(Error::CustomListNotFound);
+ };
+ let id = old_list.id;
+
+ if old_list.name != new_list.name
+ && self
+ .settings
+ .custom_lists
+ .iter()
+ .any(|list| list.name == new_list.name)
+ {
+ return Err(Error::CustomListExists);
+ }
+
+ let settings_changed = self
+ .settings
+ .update(|settings| {
+ settings.custom_lists[list_index] = new_list;
+ })
+ .await
+ .map_err(Error::SettingsError);
+
+ if let Ok(true) = settings_changed {
+ self.event_listener
+ .notify_settings(self.settings.to_settings());
+ self.relay_selector
+ .set_config(new_selector_config(&self.settings));
+
+ if self.change_should_cause_reconnect(id) {
+ log::info!("Initiating tunnel restart because a selected custom list changed");
+ self.reconnect_tunnel();
+ }
+ }
+
+ settings_changed?;
+ Ok(())
+ }
+
+ fn change_should_cause_reconnect(&self, custom_list_id: Id) -> 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;
+ }
+
+ if let TunnelState::Connecting {
+ endpoint,
+ location: _,
+ }
+ | TunnelState::Connected {
+ endpoint,
+ location: _,
+ } = &self.tunnel_state
+ {
+ match endpoint.tunnel_type {
+ TunnelType::Wireguard => {
+ if relay_settings.wireguard_constraints.use_multihop {
+ if let Constraint::Only(LocationConstraint::CustomList { list_id }) =
+ &relay_settings.wireguard_constraints.entry_location
+ {
+ need_to_reconnect |= list_id == &custom_list_id;
+ }
+ }
+ }
+
+ TunnelType::OpenVpn => {
+ if !matches!(self.settings.bridge_state, BridgeState::Off) {
+ if let BridgeSettings::Normal(bridge_settings) =
+ &self.settings.bridge_settings
+ {
+ if let Constraint::Only(LocationConstraint::CustomList {
+ list_id,
+ }) = &bridge_settings.location
+ {
+ need_to_reconnect |= list_id == &custom_list_id;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ need_to_reconnect
+ }
+}
diff --git a/mullvad-daemon/src/custom_lists.rs b/mullvad-daemon/src/custom_lists.rs
deleted file mode 100644
index 4627df58ae..0000000000
--- a/mullvad-daemon/src/custom_lists.rs
+++ /dev/null
@@ -1,324 +0,0 @@
-use crate::{new_selector_config, settings, Daemon, EventListener};
-use mullvad_types::{
- custom_list::{CustomList, CustomListLocationUpdate, Id},
- relay_constraints::{
- BridgeSettings, BridgeState, Constraint, LocationConstraint, RelaySettings,
- },
-};
-use talpid_types::net::TunnelType;
-
-#[derive(err_derive::Error, Debug)]
-pub enum Error {
- /// Custom list already exists
- #[error(display = "A list with that name already exists")]
- ListExists,
- /// Custom list does not exist
- #[error(display = "A list with that name does not exist")]
- ListNotFound,
- /// Can not add any to a custom list
- #[error(display = "Can not add or remove 'any' to or from a custom list")]
- CannotAddOrRemoveAny,
- /// Location already exists in the custom list
- #[error(display = "Location already exists in the custom list")]
- LocationExists,
- /// Location was not found in the list
- #[error(display = "Location was not found in the list")]
- LocationNotFoundInlist,
- /// Custom list settings error
- #[error(display = "Settings error")]
- Settings(#[error(source)] settings::Error),
-}
-
-impl<L> Daemon<L>
-where
- L: EventListener + Clone + Send + 'static,
-{
- pub async fn delete_custom_list(&mut self, name: String) -> Result<(), Error> {
- let custom_list = self.settings.custom_lists.get_custom_list_with_name(&name);
- match &custom_list {
- None => Err(Error::ListNotFound),
- Some(custom_list) => {
- let id = custom_list.id.clone();
-
- let settings_changed = self
- .settings
- .update(|settings| {
- let index = settings
- .custom_lists
- .custom_lists
- .iter()
- .position(|custom_list| custom_list.id == id)
- .unwrap();
- // NOTE: Not using swap remove because it would make user output slightly
- // more confusing and the cost is so small.
- settings.custom_lists.custom_lists.remove(index);
- })
- .await
- .map_err(Error::Settings);
-
- if let Ok(true) = settings_changed {
- let need_to_reconnect = self.change_should_cause_reconnect(&id);
-
- self.event_listener
- .notify_settings(self.settings.to_settings());
- self.relay_selector
- .set_config(new_selector_config(&self.settings));
-
- if need_to_reconnect {
- log::info!(
- "Initiating tunnel restart because a selected custom list was deleted"
- );
- self.reconnect_tunnel();
- }
- }
-
- settings_changed.map(|_| ())
- }
- }
- }
-
- pub async fn create_custom_list(&mut self, name: String) -> Result<(), Error> {
- if self
- .settings
- .custom_lists
- .get_custom_list_with_name(&name)
- .is_some()
- {
- return Err(Error::ListExists);
- }
-
- let settings_changed = self
- .settings
- .update(|settings| {
- let custom_list = CustomList::new(name);
- settings.custom_lists.custom_lists.push(custom_list);
- })
- .await
- .map_err(Error::Settings);
-
- if let Ok(true) = settings_changed {
- self.event_listener
- .notify_settings(self.settings.to_settings());
- self.relay_selector
- .set_config(new_selector_config(&self.settings));
- }
-
- settings_changed.map(|_| ())
- }
-
- pub async fn update_custom_list_location(
- &mut self,
- update: CustomListLocationUpdate,
- ) -> Result<(), Error> {
- match update {
- CustomListLocationUpdate::Add {
- name,
- location: new_location,
- } => {
- if new_location.is_any() {
- return Err(Error::CannotAddOrRemoveAny);
- }
-
- if let Some(custom_list) =
- self.settings.custom_lists.get_custom_list_with_name(&name)
- {
- let id = custom_list.id.clone();
- let new_location = new_location.unwrap();
-
- let settings_changed = self
- .settings
- .update(|settings| {
- let locations = &mut settings
- .custom_lists
- .custom_lists
- .iter_mut()
- .find(|custom_list| custom_list.id == id)
- .unwrap()
- .locations;
-
- if !locations.iter().any(|location| new_location == *location) {
- locations.push(new_location);
- }
- })
- .await
- .map_err(Error::Settings);
-
- if let Ok(true) = settings_changed {
- let should_reconnect = self.change_should_cause_reconnect(&id);
-
- self.event_listener
- .notify_settings(self.settings.to_settings());
- self.relay_selector
- .set_config(new_selector_config(&self.settings));
-
- if should_reconnect {
- log::info!(
- "Initiating tunnel restart because a selected custom list changed"
- );
- self.reconnect_tunnel();
- }
- } else if let Ok(false) = settings_changed {
- return Err(Error::LocationExists);
- }
-
- settings_changed.map(|_| ())
- } else {
- Err(Error::ListNotFound)
- }
- }
- CustomListLocationUpdate::Remove {
- name,
- location: location_to_remove,
- } => {
- if location_to_remove.is_any() {
- return Err(Error::CannotAddOrRemoveAny);
- }
-
- if let Some(custom_list) =
- self.settings.custom_lists.get_custom_list_with_name(&name)
- {
- let id = custom_list.id.clone();
- let location_to_remove = location_to_remove.unwrap();
-
- let settings_changed = self
- .settings
- .update(|settings| {
- let locations = &mut settings
- .custom_lists
- .custom_lists
- .iter_mut()
- .find(|custom_list| custom_list.id == id)
- .unwrap()
- .locations;
- if let Some(index) = locations
- .iter()
- .position(|location| location == &location_to_remove)
- {
- locations.remove(index);
- }
- })
- .await
- .map_err(Error::Settings);
-
- if let Ok(true) = settings_changed {
- let should_reconnect = self.change_should_cause_reconnect(&id);
-
- self.event_listener
- .notify_settings(self.settings.to_settings());
- self.relay_selector
- .set_config(new_selector_config(&self.settings));
-
- if should_reconnect {
- log::info!(
- "Initiating tunnel restart because a selected custom list changed"
- );
- self.reconnect_tunnel();
- }
- } else if let Ok(false) = settings_changed {
- return Err(Error::LocationNotFoundInlist);
- }
-
- settings_changed.map(|_| ())
- } else {
- Err(Error::ListNotFound)
- }
- }
- }
- }
-
- pub async fn rename_custom_list(
- &mut self,
- name: String,
- new_name: String,
- ) -> Result<(), Error> {
- if self
- .settings
- .custom_lists
- .get_custom_list_with_name(&new_name)
- .is_some()
- {
- Err(Error::ListExists)
- } else {
- match self.settings.custom_lists.get_custom_list_with_name(&name) {
- Some(custom_list) => {
- let id = custom_list.id.clone();
-
- let settings_changed = self
- .settings
- .update(|settings| {
- settings
- .custom_lists
- .custom_lists
- .iter_mut()
- .find(|custom_list| custom_list.id == id)
- .unwrap()
- .name = new_name;
- })
- .await;
-
- if let Ok(true) = settings_changed {
- self.event_listener
- .notify_settings(self.settings.to_settings());
- self.relay_selector
- .set_config(new_selector_config(&self.settings));
- }
-
- Ok(())
- }
- None => Err(Error::ListNotFound),
- }
- }
- }
-
- fn change_should_cause_reconnect(&self, custom_list_id: &Id) -> 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;
- }
-
- if let TunnelState::Connecting {
- endpoint,
- location: _,
- }
- | TunnelState::Connected {
- endpoint,
- location: _,
- } = &self.tunnel_state
- {
- match endpoint.tunnel_type {
- TunnelType::Wireguard => {
- if relay_settings.wireguard_constraints.use_multihop {
- if let Constraint::Only(LocationConstraint::CustomList { list_id }) =
- &relay_settings.wireguard_constraints.entry_location
- {
- need_to_reconnect |= list_id == custom_list_id;
- }
- }
- }
-
- TunnelType::OpenVpn => {
- if !matches!(self.settings.bridge_state, BridgeState::Off) {
- if let BridgeSettings::Normal(bridge_settings) =
- &self.settings.bridge_settings
- {
- if let Constraint::Only(LocationConstraint::CustomList {
- list_id,
- }) = &bridge_settings.location
- {
- need_to_reconnect |= list_id == custom_list_id;
- }
- }
- }
- }
- }
- }
- }
-
- need_to_reconnect
- }
-}
diff --git a/mullvad-daemon/src/lib.rs b/mullvad-daemon/src/lib.rs
index e757258c73..81ebe1050c 100644
--- a/mullvad-daemon/src/lib.rs
+++ b/mullvad-daemon/src/lib.rs
@@ -5,7 +5,7 @@ pub mod account_history;
mod api;
#[cfg(not(target_os = "android"))]
mod cleanup;
-mod custom_lists;
+mod custom_list;
pub mod device;
mod dns;
pub mod exception_logging;
@@ -40,7 +40,7 @@ use mullvad_relay_selector::{
use mullvad_types::{
account::{AccountData, AccountToken, VoucherSubmission},
auth_failed::AuthFailed,
- custom_list::{CustomList, CustomListLocationUpdate},
+ custom_list::CustomList,
device::{Device, DeviceEvent, DeviceEventCause, DeviceId, DeviceState, RemoveDeviceEvent},
location::GeoIpLocation,
relay_constraints::{BridgeSettings, BridgeState, ObfuscationSettings, RelaySettingsUpdate},
@@ -162,8 +162,13 @@ pub enum Error {
#[error(display = "Tunnel state machine error")]
TunnelError(#[error(source)] tunnel_state_machine::Error),
- #[error(display = "Custom list error")]
- CustomListError(#[error(source)] custom_lists::Error),
+ /// Custom list already exists
+ #[error(display = "A list with that name already exists")]
+ CustomListExists,
+
+ /// Custom list does not exist
+ #[error(display = "A list with that name does not exist")]
+ CustomListNotFound,
#[cfg(target_os = "macos")]
#[error(display = "Failed to set exclusion group")]
@@ -244,18 +249,12 @@ pub enum DaemonCommand {
RotateWireguardKey(ResponseTx<(), Error>),
/// Return a public key of the currently set wireguard private key, if there is one
GetWireguardKey(ResponseTx<Option<PublicKey>, Error>),
- /// List custom lists
- ListCustomLists(ResponseTx<Vec<CustomList>, Error>),
- /// Get custom list
- GetCustomList(ResponseTx<CustomList, Error>, String),
/// Create custom list
- CreateCustomList(ResponseTx<(), Error>, String),
+ CreateCustomList(ResponseTx<mullvad_types::custom_list::Id, Error>, String),
/// Delete custom list
- DeleteCustomList(ResponseTx<(), Error>, String),
- /// Update a custom list by adding or removing a location
- UpdateCustomListLocation(ResponseTx<(), Error>, CustomListLocationUpdate),
- /// Rename a custom list from the old name to a new name
- RenameCustomList(ResponseTx<(), Error>, String, String),
+ DeleteCustomList(ResponseTx<(), Error>, mullvad_types::custom_list::Id),
+ /// Update a custom list with a given id
+ UpdateCustomList(ResponseTx<(), Error>, CustomList),
/// Get information about the currently running and latest app versions
GetVersionInfo(oneshot::Sender<Option<AppVersionInfo>>),
/// Return whether the daemon is performing post-upgrade tasks
@@ -1027,16 +1026,9 @@ where
GetSettings(tx) => self.on_get_settings(tx),
RotateWireguardKey(tx) => self.on_rotate_wireguard_key(tx),
GetWireguardKey(tx) => self.on_get_wireguard_key(tx).await,
- ListCustomLists(tx) => self.on_list_custom_lists(tx),
- GetCustomList(tx, name) => self.on_get_custom_list(tx, name),
CreateCustomList(tx, name) => self.on_create_custom_list(tx, name).await,
- DeleteCustomList(tx, name) => self.on_delete_custom_list(tx, name).await,
- UpdateCustomListLocation(tx, update) => {
- self.on_update_custom_list_location(tx, update).await
- }
- RenameCustomList(tx, name, new_name) => {
- self.on_rename_custom_list(tx, name, new_name).await
- }
+ DeleteCustomList(tx, id) => self.on_delete_custom_list(tx, id).await,
+ UpdateCustomList(tx, update) => self.on_update_custom_list(tx, update).await,
GetVersionInfo(tx) => self.on_get_version_info(tx),
IsPerformingPostUpgrade(tx) => self.on_is_performing_post_upgrade(tx),
GetCurrentVersion(tx) => self.on_get_current_version(tx),
@@ -2189,60 +2181,27 @@ where
Self::oneshot_send(tx, result, "get_wireguard_key response");
}
- fn on_list_custom_lists(&mut self, tx: ResponseTx<Vec<CustomList>, Error>) {
- let result = self.settings.custom_lists.custom_lists.clone();
- Self::oneshot_send(tx, Ok(result), "list_custom_lists response");
- }
-
- fn on_get_custom_list(&mut self, tx: ResponseTx<CustomList, Error>, name: String) {
- let result = self
- .settings
- .custom_lists
- .get_custom_list_with_name(&name)
- .cloned()
- .ok_or(Error::CustomListError(custom_lists::Error::ListNotFound));
- Self::oneshot_send(tx, result, "create_custom_list response");
- }
-
- async fn on_create_custom_list(&mut self, tx: ResponseTx<(), Error>, name: String) {
- let result = self
- .create_custom_list(name)
- .await
- .map_err(Error::CustomListError);
+ async fn on_create_custom_list(
+ &mut self,
+ tx: ResponseTx<mullvad_types::custom_list::Id, Error>,
+ name: String,
+ ) {
+ let result = self.create_custom_list(name).await;
Self::oneshot_send(tx, result, "create_custom_list response");
}
- async fn on_delete_custom_list(&mut self, tx: ResponseTx<(), Error>, name: String) {
- let result = self
- .delete_custom_list(name)
- .await
- .map_err(Error::CustomListError);
- Self::oneshot_send(tx, result, "delete_custom_list response");
- }
-
- async fn on_update_custom_list_location(
+ async fn on_delete_custom_list(
&mut self,
tx: ResponseTx<(), Error>,
- update: CustomListLocationUpdate,
+ id: mullvad_types::custom_list::Id,
) {
- let result = self
- .update_custom_list_location(update)
- .await
- .map_err(Error::CustomListError);
- Self::oneshot_send(tx, result, "update_custom_list_location response");
+ let result = self.delete_custom_list(id).await;
+ Self::oneshot_send(tx, result, "delete_custom_list response");
}
- async fn on_rename_custom_list(
- &mut self,
- tx: ResponseTx<(), Error>,
- name: String,
- new_name: String,
- ) {
- let result = self
- .rename_custom_list(name, new_name)
- .await
- .map_err(Error::CustomListError);
- Self::oneshot_send(tx, result, "rename_custom_list response");
+ async fn on_update_custom_list(&mut self, tx: ResponseTx<(), Error>, new_list: CustomList) {
+ let result = self.update_custom_list(new_list).await;
+ Self::oneshot_send(tx, result, "update_custom_list 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 23f2578f58..5358628640 100644
--- a/mullvad-daemon/src/management_interface.rs
+++ b/mullvad-daemon/src/management_interface.rs
@@ -1,7 +1,4 @@
-use crate::{
- account_history, custom_lists, device, settings, DaemonCommand, DaemonCommandSender,
- EventListener,
-};
+use crate::{account_history, device, settings, DaemonCommand, DaemonCommandSender, EventListener};
use futures::{
channel::{mpsc, oneshot},
StreamExt,
@@ -27,6 +24,7 @@ use mullvad_types::{
use std::path::PathBuf;
use std::{
convert::{TryFrom, TryInto},
+ str::FromStr,
sync::{Arc, Mutex},
time::Duration,
};
@@ -586,82 +584,35 @@ impl ManagementService for ManagementServiceImpl {
// Custom lists
//
- async fn list_custom_lists(
- &self,
- _: Request<()>,
- ) -> ServiceResult<mullvad_management_interface::types::CustomLists> {
- log::debug!("list_custom_lists");
- let (tx, rx) = oneshot::channel();
- self.send_command_to_daemon(DaemonCommand::ListCustomLists(tx))?;
- self.wait_for_result(rx)
- .await?
- .map(|custom_lists| {
- Response::new(mullvad_management_interface::types::CustomLists::from(
- custom_lists,
- ))
- })
- .map_err(map_daemon_error)
- }
-
- async fn get_custom_list(
- &self,
- request: Request<String>,
- ) -> ServiceResult<mullvad_management_interface::types::CustomList> {
- log::debug!("get_custom_list");
- let (tx, rx) = oneshot::channel();
- self.send_command_to_daemon(DaemonCommand::GetCustomList(tx, request.into_inner()))?;
- self.wait_for_result(rx)
- .await?
- .map(|custom_list| {
- Response::new(mullvad_management_interface::types::CustomList::from(
- custom_list,
- ))
- })
- .map_err(map_daemon_error)
- }
-
- async fn create_custom_list(&self, request: Request<String>) -> ServiceResult<()> {
+ async fn create_custom_list(&self, request: Request<String>) -> ServiceResult<String> {
log::debug!("create_custom_list");
let (tx, rx) = oneshot::channel();
self.send_command_to_daemon(DaemonCommand::CreateCustomList(tx, request.into_inner()))?;
self.wait_for_result(rx)
.await?
- .map(Response::new)
+ .map(|response| Response::new(response.to_string()))
.map_err(map_daemon_error)
}
async fn delete_custom_list(&self, request: Request<String>) -> ServiceResult<()> {
log::debug!("delete_custom_list");
let (tx, rx) = oneshot::channel();
- self.send_command_to_daemon(DaemonCommand::DeleteCustomList(tx, request.into_inner()))?;
+ self.send_command_to_daemon(DaemonCommand::DeleteCustomList(
+ tx,
+ mullvad_types::custom_list::Id::from_str(&request.into_inner())
+ .map_err(|_| Status::invalid_argument("invalid ID"))?,
+ ))?;
self.wait_for_result(rx)
.await?
.map(Response::new)
.map_err(map_daemon_error)
}
- async fn update_custom_list_location(
- &self,
- request: Request<types::CustomListLocationUpdate>,
- ) -> ServiceResult<()> {
- log::debug!("update_custom_list_location");
- let custom_list =
- mullvad_types::custom_list::CustomListLocationUpdate::try_from(request.into_inner())?;
- let (tx, rx) = oneshot::channel();
- self.send_command_to_daemon(DaemonCommand::UpdateCustomListLocation(tx, custom_list))?;
- self.wait_for_result(rx)
- .await?
- .map(Response::new)
- .map_err(map_daemon_error)
- }
- async fn rename_custom_list(
- &self,
- request: Request<types::CustomListRename>,
- ) -> ServiceResult<()> {
- log::debug!("rename_custom_list");
- let names: (String, String) = From::from(request.into_inner());
+ async fn update_custom_list(&self, request: Request<types::CustomList>) -> ServiceResult<()> {
+ log::debug!("update_custom_list");
+ let custom_list = mullvad_types::custom_list::CustomList::try_from(request.into_inner())?;
let (tx, rx) = oneshot::channel();
- self.send_command_to_daemon(DaemonCommand::RenameCustomList(tx, names.0, names.1))?;
+ self.send_command_to_daemon(DaemonCommand::UpdateCustomList(tx, custom_list))?;
self.wait_for_result(rx)
.await?
.map(Response::new)
@@ -1006,7 +957,16 @@ fn map_daemon_error(error: crate::Error) -> Status {
DaemonError::NoAccountToken | DaemonError::NoAccountTokenHistory => {
Status::unauthenticated(error.to_string())
}
- DaemonError::CustomListError(error) => map_custom_list_error(error),
+ DaemonError::CustomListExists => Status::with_details(
+ Code::AlreadyExists,
+ error.to_string(),
+ mullvad_management_interface::CUSTOM_LIST_LIST_EXISTS_DETAILS.into(),
+ ),
+ DaemonError::CustomListNotFound => Status::with_details(
+ Code::NotFound,
+ error.to_string(),
+ mullvad_management_interface::CUSTOM_LIST_LIST_NOT_FOUND_DETAILS.into(),
+ ),
error => Status::unknown(error.to_string()),
}
}
@@ -1087,36 +1047,6 @@ fn map_account_history_error(error: account_history::Error) -> Status {
}
}
-/// Converts an instance of [`mullvad_daemon::account_history::Error`] into a tonic status.
-fn map_custom_list_error(error: custom_lists::Error) -> Status {
- match error {
- custom_lists::Error::ListExists => Status::with_details(
- Code::AlreadyExists,
- error.to_string(),
- mullvad_management_interface::CUSTOM_LIST_LIST_EXISTS_DETAILS.into(),
- ),
- custom_lists::Error::ListNotFound => Status::with_details(
- Code::NotFound,
- error.to_string(),
- mullvad_management_interface::CUSTOM_LIST_LIST_NOT_FOUND_DETAILS.into(),
- ),
- custom_lists::Error::CannotAddOrRemoveAny => {
- Status::new(Code::InvalidArgument, error.to_string())
- }
- custom_lists::Error::LocationExists => Status::with_details(
- Code::AlreadyExists,
- error.to_string(),
- mullvad_management_interface::CUSTOM_LIST_LOCATION_EXISTS_DETAILS.into(),
- ),
- custom_lists::Error::LocationNotFoundInlist => Status::with_details(
- Code::NotFound,
- error.to_string(),
- mullvad_management_interface::CUSTOM_LIST_LOCATION_NOT_FOUND_DETAILS.into(),
- ),
- custom_lists::Error::Settings(error) => map_settings_error(error),
- }
-}
-
fn map_protobuf_type_err(err: types::FromProtobufTypeError) -> Status {
match err {
types::FromProtobufTypeError::InvalidArgument(err) => Status::invalid_argument(err),
diff --git a/mullvad-management-interface/proto/management_interface.proto b/mullvad-management-interface/proto/management_interface.proto
index 0337e0775f..f4b3eb961d 100644
--- a/mullvad-management-interface/proto/management_interface.proto
+++ b/mullvad-management-interface/proto/management_interface.proto
@@ -69,12 +69,9 @@ service ManagementService {
rpc GetWireguardKey(google.protobuf.Empty) returns (PublicKey) {}
// Custom lists
- rpc ListCustomLists(google.protobuf.Empty) returns (CustomLists) {}
- rpc GetCustomList(google.protobuf.StringValue) returns (CustomList) {}
- rpc CreateCustomList(google.protobuf.StringValue) returns (google.protobuf.Empty) {}
+ rpc CreateCustomList(google.protobuf.StringValue) returns (google.protobuf.StringValue) {}
rpc DeleteCustomList(google.protobuf.StringValue) returns (google.protobuf.Empty) {}
- rpc UpdateCustomListLocation(CustomListLocationUpdate) returns (google.protobuf.Empty) {}
- rpc RenameCustomList(CustomListRename) returns (google.protobuf.Empty) {}
+ rpc UpdateCustomList(CustomList) returns (google.protobuf.Empty) {}
// Split tunneling (Linux)
rpc GetSplitTunnelProcesses(google.protobuf.Empty) returns (stream google.protobuf.Int32Value) {}
@@ -287,7 +284,7 @@ message BridgeSettings {
message LocationConstraint {
oneof type {
- string customList = 1;
+ string custom_list = 1;
RelayLocation location = 2;
}
}
@@ -319,29 +316,12 @@ message ObfuscationSettings {
Udp2TcpObfuscationSettings udp2tcp = 2;
}
-message CustomListRename {
- string name = 1;
- string new_name = 2;
-}
-
-message CustomListLocationUpdate {
- enum State {
- ADD = 0;
- REMOVE = 1;
- }
- State state = 1;
- string name = 2;
- RelayLocation location = 3;
-}
-
message CustomList {
string id = 1;
string name = 2;
repeated RelayLocation locations = 3;
}
-message CustomLists { repeated CustomList custom_lists = 1; }
-
message CustomListSettings { repeated CustomList custom_lists = 1; }
message Settings {
diff --git a/mullvad-management-interface/src/client.rs b/mullvad-management-interface/src/client.rs
index e9acad1337..a1ddc5e39a 100644
--- a/mullvad-management-interface/src/client.rs
+++ b/mullvad-management-interface/src/client.rs
@@ -4,7 +4,7 @@ use crate::types;
use futures::{Stream, StreamExt};
use mullvad_types::{
account::{AccountData, AccountToken, VoucherSubmission},
- custom_list::{CustomList, CustomListLocationUpdate},
+ custom_list::{CustomList, Id},
device::{Device, DeviceEvent, DeviceId, DeviceState, RemoveDeviceEvent},
location::GeoIpLocation,
relay_constraints::{BridgeSettings, BridgeState, ObfuscationSettings, RelaySettingsUpdate},
@@ -16,6 +16,7 @@ use mullvad_types::{
};
#[cfg(target_os = "windows")]
use std::path::Path;
+use std::str::FromStr;
#[cfg(target_os = "windows")]
use talpid_types::split_tunnel::ExcludedProcess;
use tonic::{Code, Status};
@@ -430,60 +431,27 @@ impl MullvadProxyClient {
PublicKey::try_from(key).map_err(Error::InvalidResponse)
}
- pub async fn list_custom_lists(&mut self) -> Result<Vec<CustomList>> {
- let result = self
- .0
- .list_custom_lists(())
- .await
- .map_err(map_custom_list_error)?
- .into_inner()
- .try_into()
- .map_err(Error::InvalidResponse)?;
- Ok(result)
- }
-
- pub async fn get_custom_list(&mut self, name: String) -> Result<CustomList> {
- let result = self
+ pub async fn create_custom_list(&mut self, name: String) -> Result<Id> {
+ let id = self
.0
- .get_custom_list(name)
- .await
- .map_err(map_custom_list_error)?
- .into_inner()
- .try_into()
- .map_err(Error::InvalidResponse)?;
- Ok(result)
- }
-
- pub async fn create_custom_list(&mut self, name: String) -> Result<()> {
- self.0
.create_custom_list(name)
.await
- .map_err(map_custom_list_error)?;
- Ok(())
- }
-
- pub async fn delete_custom_list(&mut self, name: String) -> Result<()> {
- self.0
- .delete_custom_list(name)
- .await
- .map_err(map_custom_list_error)?;
- Ok(())
+ .map_err(map_custom_list_error)?
+ .into_inner();
+ Id::from_str(&id).map_err(|_| Error::CustomListListNotFound)
}
- pub async fn update_custom_list_location(
- &mut self,
- custom_list_update: CustomListLocationUpdate,
- ) -> Result<()> {
+ pub async fn delete_custom_list(&mut self, id: String) -> Result<()> {
self.0
- .update_custom_list_location(types::CustomListLocationUpdate::from(custom_list_update))
+ .delete_custom_list(id)
.await
.map_err(map_custom_list_error)?;
Ok(())
}
- pub async fn rename_custom_list(&mut self, name: String, new_name: String) -> Result<()> {
+ pub async fn update_custom_list(&mut self, custom_list: CustomList) -> Result<()> {
self.0
- .rename_custom_list(types::CustomListRename::from((name, new_name)))
+ .update_custom_list(types::CustomList::from(custom_list))
.await
.map_err(map_custom_list_error)?;
Ok(())
@@ -605,26 +573,19 @@ fn map_location_error(status: Status) -> Error {
fn map_custom_list_error(status: Status) -> Error {
match status.code() {
Code::NotFound => {
- let details = status.details();
- if details == crate::CUSTOM_LIST_LOCATION_NOT_FOUND_DETAILS {
- Error::LocationNotFoundInCustomlist
- } else if details == crate::CUSTOM_LIST_LIST_NOT_FOUND_DETAILS {
+ if status.details() == crate::CUSTOM_LIST_LIST_NOT_FOUND_DETAILS {
Error::CustomListListNotFound
} else {
Error::Rpc(status)
}
}
Code::AlreadyExists => {
- let details = status.details();
- if details == crate::CUSTOM_LIST_LOCATION_EXISTS_DETAILS {
- Error::LocationExistsInCustomList
- } else if details == crate::CUSTOM_LIST_LIST_EXISTS_DETAILS {
+ if status.details() == crate::CUSTOM_LIST_LIST_EXISTS_DETAILS {
Error::CustomListExists
} else {
Error::Rpc(status)
}
}
- Code::InvalidArgument => Error::CustomListCannotAddOrRemoveAny,
_other => Error::Rpc(status),
}
}
diff --git a/mullvad-management-interface/src/lib.rs b/mullvad-management-interface/src/lib.rs
index b2ca7a4079..cf1a798878 100644
--- a/mullvad-management-interface/src/lib.rs
+++ b/mullvad-management-interface/src/lib.rs
@@ -26,9 +26,7 @@ use once_cell::sync::Lazy;
static MULLVAD_MANAGEMENT_SOCKET_GROUP: Lazy<Option<String>> =
Lazy::new(|| env::var("MULLVAD_MANAGEMENT_SOCKET_GROUP").ok());
-pub const CUSTOM_LIST_LOCATION_NOT_FOUND_DETAILS: &[u8] = b"custom_list_location_not_found";
pub const CUSTOM_LIST_LIST_NOT_FOUND_DETAILS: &[u8] = b"custom_list_list_not_found";
-pub const CUSTOM_LIST_LOCATION_EXISTS_DETAILS: &[u8] = b"custom_list_location_exists";
pub const CUSTOM_LIST_LIST_EXISTS_DETAILS: &[u8] = b"custom_list_list_exists";
#[derive(err_derive::Error, Debug)]
@@ -100,9 +98,6 @@ pub enum Error {
#[error(display = "A custom list with that name does not exist")]
CustomListListNotFound,
- #[error(display = "Can not add or remove 'any' to or from a custom list")]
- CustomListCannotAddOrRemoveAny,
-
#[error(display = "Location already exists in the custom list")]
LocationExistsInCustomList,
diff --git a/mullvad-management-interface/src/types/conversions/custom_list.rs b/mullvad-management-interface/src/types/conversions/custom_list.rs
index 63bb73652b..62799168f7 100644
--- a/mullvad-management-interface/src/types/conversions/custom_list.rs
+++ b/mullvad-management-interface/src/types/conversions/custom_list.rs
@@ -1,30 +1,15 @@
-use crate::types::{proto, FromProtobufTypeError};
-use mullvad_types::{custom_list::Id, relay_constraints::GeographicLocationConstraint};
-use proto::RelayLocation;
-
-impl From<(String, String)> for proto::CustomListRename {
- fn from(names: (String, String)) -> Self {
- proto::CustomListRename {
- name: names.0,
- new_name: names.1,
- }
- }
-}
+use std::{collections::BTreeSet, str::FromStr};
-impl From<proto::CustomListRename> for (String, String) {
- fn from(names: proto::CustomListRename) -> Self {
- (names.name, names.new_name)
- }
-}
+use crate::types::{proto, FromProtobufTypeError};
+use mullvad_types::{
+ custom_list::{CustomList, Id},
+ relay_constraints::GeographicLocationConstraint,
+};
-impl From<&mullvad_types::custom_list::CustomListsSettings> for proto::CustomListSettings {
- fn from(settings: &mullvad_types::custom_list::CustomListsSettings) -> Self {
+impl From<mullvad_types::custom_list::CustomListsSettings> for proto::CustomListSettings {
+ fn from(settings: mullvad_types::custom_list::CustomListsSettings) -> Self {
Self {
- custom_lists: settings
- .custom_lists
- .iter()
- .map(|custom_list| proto::CustomList::from(custom_list.clone()))
- .collect(),
+ custom_lists: settings.into_iter().map(proto::CustomList::from).collect(),
}
}
}
@@ -33,72 +18,13 @@ impl TryFrom<proto::CustomListSettings> for mullvad_types::custom_list::CustomLi
type Error = FromProtobufTypeError;
fn try_from(settings: proto::CustomListSettings) -> Result<Self, Self::Error> {
- Ok(Self {
- custom_lists: settings
+ Ok(Self::from(
+ settings
.custom_lists
.into_iter()
.map(mullvad_types::custom_list::CustomList::try_from)
- .collect::<Result<Vec<_>, _>>()?,
- })
- }
-}
-
-impl From<mullvad_types::custom_list::CustomListLocationUpdate>
- for proto::CustomListLocationUpdate
-{
- fn from(custom_list: mullvad_types::custom_list::CustomListLocationUpdate) -> Self {
- use mullvad_types::relay_constraints::Constraint;
- match custom_list {
- mullvad_types::custom_list::CustomListLocationUpdate::Add { name, location } => {
- let location = match location {
- Constraint::Any => None,
- Constraint::Only(location) => Some(RelayLocation::from(location)),
- };
- Self {
- state: i32::from(proto::custom_list_location_update::State::Add),
- name,
- location,
- }
- }
- mullvad_types::custom_list::CustomListLocationUpdate::Remove { name, location } => {
- let location = match location {
- Constraint::Any => None,
- Constraint::Only(location) => Some(RelayLocation::from(location)),
- };
- Self {
- state: i32::from(proto::custom_list_location_update::State::Remove),
- name,
- location,
- }
- }
- }
- }
-}
-
-impl TryFrom<proto::CustomListLocationUpdate>
- for mullvad_types::custom_list::CustomListLocationUpdate
-{
- type Error = FromProtobufTypeError;
-
- fn try_from(custom_list: proto::CustomListLocationUpdate) -> Result<Self, Self::Error> {
- use mullvad_types::relay_constraints::Constraint;
- let location: Constraint<GeographicLocationConstraint> =
- Constraint::<GeographicLocationConstraint>::from(
- custom_list
- .location
- .ok_or(FromProtobufTypeError::InvalidArgument("missing location"))?,
- );
- match proto::custom_list_location_update::State::try_from(custom_list.state) {
- Ok(proto::custom_list_location_update::State::Add) => Ok(Self::Add {
- name: custom_list.name,
- location,
- }),
- Ok(proto::custom_list_location_update::State::Remove) => Ok(Self::Remove {
- name: custom_list.name,
- location,
- }),
- Err(_) => Err(FromProtobufTypeError::InvalidArgument("incorrect state")),
- }
+ .collect::<Result<Vec<CustomList>, _>>()?,
+ ))
}
}
@@ -121,18 +47,14 @@ impl TryFrom<proto::CustomList> for mullvad_types::custom_list::CustomList {
type Error = FromProtobufTypeError;
fn try_from(custom_list: proto::CustomList) -> Result<Self, Self::Error> {
- let locations: Result<Vec<GeographicLocationConstraint>, _> = custom_list
+ let locations = custom_list
.locations
.into_iter()
.map(GeographicLocationConstraint::try_from)
- .collect();
- let locations = locations.map_err(|_| {
- FromProtobufTypeError::InvalidArgument("Could not convert custom list from proto")
- })?;
+ .collect::<Result<BTreeSet<_>, Self::Error>>()?;
Ok(Self {
- id: Id::try_from(custom_list.id.as_str()).map_err(|_| {
- FromProtobufTypeError::InvalidArgument("Id could not be parsed to a uuid")
- })?,
+ id: Id::from_str(&custom_list.id)
+ .map_err(|_| FromProtobufTypeError::InvalidArgument("Invalid list ID"))?,
name: custom_list.name,
locations,
})
@@ -149,7 +71,7 @@ impl TryFrom<proto::RelayLocation> for GeographicLocationConstraint {
relay_location.hostname.as_ref(),
) {
("", ..) => Err(FromProtobufTypeError::InvalidArgument(
- "Relay location formatted incorrectly",
+ "Invalid geographic relay location",
)),
(_country, "", "") => Ok(GeographicLocationConstraint::Country(
relay_location.country,
@@ -174,27 +96,3 @@ impl TryFrom<proto::RelayLocation> for GeographicLocationConstraint {
}
}
}
-
-impl From<Vec<mullvad_types::custom_list::CustomList>> for proto::CustomLists {
- fn from(custom_lists: Vec<mullvad_types::custom_list::CustomList>) -> Self {
- let custom_lists = custom_lists
- .into_iter()
- .map(proto::CustomList::from)
- .collect();
- proto::CustomLists { custom_lists }
- }
-}
-
-impl TryFrom<proto::CustomLists> for Vec<mullvad_types::custom_list::CustomList> {
- type Error = FromProtobufTypeError;
-
- fn try_from(custom_lists: proto::CustomLists) -> Result<Self, Self::Error> {
- let mut new_custom_lists = Vec::with_capacity(custom_lists.custom_lists.len());
- for custom_list in custom_lists.custom_lists {
- new_custom_lists.push(mullvad_types::custom_list::CustomList::try_from(
- custom_list,
- )?);
- }
- Ok(new_custom_lists)
- }
-}
diff --git a/mullvad-management-interface/src/types/conversions/relay_constraints.rs b/mullvad-management-interface/src/types/conversions/relay_constraints.rs
index a3ec8af0da..b7b5f0060b 100644
--- a/mullvad-management-interface/src/types/conversions/relay_constraints.rs
+++ b/mullvad-management-interface/src/types/conversions/relay_constraints.rs
@@ -3,6 +3,7 @@ use mullvad_types::{
custom_list::Id,
relay_constraints::{Constraint, RelaySettingsUpdate},
};
+use std::str::FromStr;
use talpid_types::net::TunnelType;
impl TryFrom<&proto::WireguardConstraints>
@@ -494,7 +495,9 @@ impl From<mullvad_types::relay_constraints::LocationConstraint> for proto::Locat
)),
},
LocationConstraint::CustomList { list_id } => Self {
- r#type: Some(proto::location_constraint::Type::CustomList(list_id)),
+ r#type: Some(proto::location_constraint::Type::CustomList(
+ list_id.to_string(),
+ )),
},
}
}
@@ -521,7 +524,7 @@ impl TryFrom<proto::LocationConstraint>
}
Some(proto::location_constraint::Type::CustomList(list_id)) => {
let location = LocationConstraint::CustomList {
- list_id: Id::try_from(list_id.as_str()).map_err(|_| {
+ list_id: Id::from_str(&list_id).map_err(|_| {
FromProtobufTypeError::InvalidArgument("Id could not be parsed to a uuid")
})?,
};
diff --git a/mullvad-management-interface/src/types/conversions/settings.rs b/mullvad-management-interface/src/types/conversions/settings.rs
index 5b8bf76d86..f123b41755 100644
--- a/mullvad-management-interface/src/types/conversions/settings.rs
+++ b/mullvad-management-interface/src/types/conversions/settings.rs
@@ -39,7 +39,9 @@ impl From<&mullvad_types::settings::Settings> for proto::Settings {
&settings.obfuscation_settings,
)),
split_tunnel,
- custom_lists: Some(proto::CustomListSettings::from(&settings.custom_lists)),
+ custom_lists: Some(proto::CustomListSettings::from(
+ settings.custom_lists.clone(),
+ )),
}
}
}
diff --git a/mullvad-types/src/custom_list.rs b/mullvad-types/src/custom_list.rs
index ee4f914f75..58fd046a9e 100644
--- a/mullvad-types/src/custom_list.rs
+++ b/mullvad-types/src/custom_list.rs
@@ -1,52 +1,139 @@
-use crate::relay_constraints::{Constraint, GeographicLocationConstraint};
+use crate::relay_constraints::GeographicLocationConstraint;
#[cfg(target_os = "android")]
-use jnix::{FromJava, IntoJava};
+use jnix::{
+ jni::objects::{AutoLocal, JObject, JString},
+ FromJava, IntoJava, JnixEnv,
+};
use serde::{Deserialize, Serialize};
+use std::{
+ collections::BTreeSet,
+ ops::{Deref, DerefMut},
+ str::FromStr,
+};
-pub type Id = String;
+#[derive(Default, Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
+pub struct Id(uuid::Uuid);
+
+impl Deref for Id {
+ type Target = uuid::Uuid;
+
+ fn deref(&self) -> &Self::Target {
+ &self.0
+ }
+}
+
+impl DerefMut for Id {
+ fn deref_mut(&mut self) -> &mut Self::Target {
+ &mut self.0
+ }
+}
+
+impl FromStr for Id {
+ type Err = <uuid::Uuid as FromStr>::Err;
+
+ fn from_str(s: &str) -> Result<Self, Self::Err> {
+ uuid::Uuid::from_str(s).map(Id)
+ }
+}
+
+#[cfg(target_os = "android")]
+impl<'env, 'sub_env> FromJava<'env, JString<'sub_env>> for Id
+where
+ 'env: 'sub_env,
+{
+ const JNI_SIGNATURE: &'static str = "Ljava/lang/String;";
+
+ fn from_java(env: &JnixEnv<'env>, source: JString<'sub_env>) -> Self {
+ let s = env
+ .get_string(source)
+ .expect("Failed to convert from Java String");
+ Id::from_str(s.to_str().unwrap()).expect("invalid ID")
+ }
+}
+
+#[cfg(target_os = "android")]
+impl<'env, 'sub_env> FromJava<'env, JObject<'sub_env>> for Id
+where
+ 'env: 'sub_env,
+{
+ const JNI_SIGNATURE: &'static str = "Ljava/lang/String;";
+
+ fn from_java(env: &JnixEnv<'env>, source: JObject<'sub_env>) -> Self {
+ Id::from_java(env, JString::from(source))
+ }
+}
+
+#[cfg(target_os = "android")]
+impl<'borrow, 'env: 'borrow> IntoJava<'borrow, 'env> for Id {
+ const JNI_SIGNATURE: &'static str = "Ljava/lang/String;";
+
+ type JavaType = AutoLocal<'env, 'borrow>;
+
+ fn into_java(self, env: &'borrow JnixEnv<'env>) -> Self::JavaType {
+ let s = self.to_string();
+ let jstring = env.new_string(&s).expect("Failed to create Java String");
+
+ env.auto_local(jstring)
+ }
+}
#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq)]
-#[cfg_attr(target_os = "android", derive(FromJava, IntoJava))]
-#[cfg_attr(target_os = "android", jnix(package = "net.mullvad.mullvadvpn.model"))]
pub struct CustomListsSettings {
- pub custom_lists: Vec<CustomList>,
+ custom_lists: Vec<CustomList>,
+}
+
+impl From<Vec<CustomList>> for CustomListsSettings {
+ fn from(custom_lists: Vec<CustomList>) -> Self {
+ Self { custom_lists }
+ }
}
impl CustomListsSettings {
- pub fn get_custom_list_with_name(&self, name: &String) -> Option<&CustomList> {
- self.custom_lists
- .iter()
- .find(|custom_list| &custom_list.name == name)
+ pub fn add(&mut self, list: CustomList) {
+ self.custom_lists.push(list);
+ }
+
+ pub fn remove(&mut self, index: usize) {
+ self.custom_lists.remove(index);
}
}
-#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
-pub enum CustomListLocationUpdate {
- Add {
- name: String,
- location: Constraint<GeographicLocationConstraint>,
- },
- Remove {
- name: String,
- location: Constraint<GeographicLocationConstraint>,
- },
+impl IntoIterator for CustomListsSettings {
+ type Item = CustomList;
+ type IntoIter = <Vec<CustomList> as IntoIterator>::IntoIter;
+
+ fn into_iter(self) -> Self::IntoIter {
+ self.custom_lists.into_iter()
+ }
+}
+
+impl Deref for CustomListsSettings {
+ type Target = [CustomList];
+
+ fn deref(&self) -> &Self::Target {
+ &self.custom_lists
+ }
+}
+
+impl DerefMut for CustomListsSettings {
+ fn deref_mut(&mut self) -> &mut Self::Target {
+ &mut self.custom_lists
+ }
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
-#[cfg_attr(target_os = "android", derive(FromJava, IntoJava))]
-#[cfg_attr(target_os = "android", jnix(package = "net.mullvad.mullvadvpn.model"))]
pub struct CustomList {
pub id: Id,
pub name: String,
- pub locations: Vec<GeographicLocationConstraint>,
+ pub locations: BTreeSet<GeographicLocationConstraint>,
}
impl CustomList {
pub fn new(name: String) -> Self {
CustomList {
- id: uuid::Uuid::new_v4().to_string(),
+ id: Id(uuid::Uuid::new_v4()),
name,
- locations: Vec::new(),
+ locations: BTreeSet::new(),
}
}
}
diff --git a/mullvad-types/src/relay_constraints.rs b/mullvad-types/src/relay_constraints.rs
index dcce9fc950..014e84a5ff 100644
--- a/mullvad-types/src/relay_constraints.rs
+++ b/mullvad-types/src/relay_constraints.rs
@@ -247,7 +247,7 @@ impl<'a> fmt::Display for RelaySettingsFormatter<'a> {
#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
-#[cfg_attr(target_os = "android", derive(IntoJava, FromJava))]
+#[cfg_attr(target_os = "android", derive(FromJava, IntoJava))]
#[cfg_attr(target_os = "android", jnix(package = "net.mullvad.mullvadvpn.model"))]
pub enum LocationConstraint {
Location(GeographicLocationConstraint),
@@ -271,11 +271,17 @@ impl ResolvedLocationConstraint {
Constraint::Only(Self::Location(location))
}
Constraint::Only(LocationConstraint::CustomList { list_id }) => custom_lists
- .custom_lists
.iter()
- .find(|custom_list| custom_list.id == list_id)
- .map(|custom_list| Constraint::Only(Self::Locations(custom_list.locations.clone())))
- .unwrap_or(Constraint::Any),
+ .find(|list| list.id == list_id)
+ .map(|custom_list| {
+ Constraint::Only(Self::Locations(
+ custom_list.locations.iter().cloned().collect(),
+ ))
+ })
+ .unwrap_or_else(|| {
+ log::warn!("Resolved non-existent custom list");
+ Constraint::Only(ResolvedLocationConstraint::Locations(vec![]))
+ }),
}
}
}
@@ -347,9 +353,8 @@ impl<'a> fmt::Display for LocationConstraintFormatter<'a> {
LocationConstraint::Location(location) => write!(f, "{}", location),
LocationConstraint::CustomList { list_id } => self
.custom_lists
- .custom_lists
.iter()
- .find(|custom_list| &custom_list.id == list_id)
+ .find(|list| &list.id == list_id)
.map(|custom_list| write!(f, "{}", custom_list.name))
.unwrap_or_else(|| write!(f, "invalid custom list")),
}
@@ -441,7 +446,7 @@ impl<'a> fmt::Display for RelayConstraintsFormatter<'a> {
/// Limits the set of [`crate::relay_list::Relay`]s used by a `RelaySelector` based on
/// location.
-#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize)]
+#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize, PartialOrd, Ord)]
#[serde(rename_all = "snake_case")]
#[cfg_attr(target_os = "android", derive(FromJava, IntoJava))]
#[cfg_attr(target_os = "android", jnix(package = "net.mullvad.mullvadvpn.model"))]