diff options
| author | David Lönnhager <david.l@mullvad.net> | 2023-11-14 12:01:31 +0100 |
|---|---|---|
| committer | David Lönnhager <david.l@mullvad.net> | 2023-11-17 11:09:25 +0100 |
| commit | 1dd07884754b1f07ce9ffd3f83987993458e072a (patch) | |
| tree | 01c7e75959eca62a7da3e39b3e1cb8c2f3fe4361 | |
| parent | f6ab6637456de4f30cd59b33de0e3e455ea3fb5a (diff) | |
| download | mullvadvpn-1dd07884754b1f07ce9ffd3f83987993458e072a.tar.xz mullvadvpn-1dd07884754b1f07ce9ffd3f83987993458e072a.zip | |
Add support for applying JSON patches to management interface
| -rw-r--r-- | mullvad-daemon/src/lib.rs | 15 | ||||
| -rw-r--r-- | mullvad-daemon/src/management_interface.rs | 128 | ||||
| -rw-r--r-- | mullvad-daemon/src/settings/mod.rs | 17 | ||||
| -rw-r--r-- | mullvad-daemon/src/settings/patch.rs | 18 | ||||
| -rw-r--r-- | mullvad-management-interface/proto/management_interface.proto | 3 | ||||
| -rw-r--r-- | mullvad-management-interface/src/client.rs | 5 |
6 files changed, 102 insertions, 84 deletions
diff --git a/mullvad-daemon/src/lib.rs b/mullvad-daemon/src/lib.rs index d4964a8b80..890cdfb13e 100644 --- a/mullvad-daemon/src/lib.rs +++ b/mullvad-daemon/src/lib.rs @@ -349,6 +349,8 @@ pub enum DaemonCommand { /// Verify that a google play payment was successful through the API. #[cfg(target_os = "android")] VerifyPlayPurchase(ResponseTx<(), Error>, PlayPurchase), + /// Patch the settings using a blob of JSON settings + ApplyJsonSettings(ResponseTx<(), settings::patch::Error>, String), } /// All events that can happen in the daemon. Sent from various threads and exposed interfaces. @@ -1171,6 +1173,7 @@ where VerifyPlayPurchase(tx, play_purchase) => { self.on_verify_play_purchase(tx, play_purchase) } + ApplyJsonSettings(tx, blob) => self.on_apply_json_settings(tx, blob).await, } } @@ -2439,6 +2442,18 @@ where }); } + async fn on_apply_json_settings( + &mut self, + tx: ResponseTx<(), settings::patch::Error>, + blob: String, + ) { + let result = settings::patch::merge_validate_patch(&mut self.settings, &blob).await; + if result.is_ok() { + self.reconnect_tunnel(); + } + Self::oneshot_send(tx, result, "apply_json_settings response"); + } + /// Set the target state of the client. If it changed trigger the operations needed to /// progress towards that state. /// Returns a bool representing whether or not a state change was initiated. diff --git a/mullvad-daemon/src/management_interface.rs b/mullvad-daemon/src/management_interface.rs index e67a02117c..f042a923e5 100644 --- a/mullvad-daemon/src/management_interface.rs +++ b/mullvad-daemon/src/management_interface.rs @@ -1,4 +1,4 @@ -use crate::{account_history, device, settings, DaemonCommand, DaemonCommandSender, EventListener}; +use crate::{account_history, device, DaemonCommand, DaemonCommandSender, EventListener}; use futures::{ channel::{mpsc, oneshot}, StreamExt, @@ -177,10 +177,8 @@ impl ManagementService for ManagementServiceImpl { let message = DaemonCommand::SetRelaySettings(tx, constraints_update); self.send_command_to_daemon(message)?; - self.wait_for_result(rx) - .await? - .map(Response::new) - .map_err(map_settings_error) + self.wait_for_result(rx).await??; + Ok(Response::new(())) } async fn get_relay_locations(&self, _: Request<()>) -> ServiceResult<types::RelayList> { @@ -215,10 +213,8 @@ impl ManagementService for ManagementServiceImpl { let (tx, rx) = oneshot::channel(); self.send_command_to_daemon(DaemonCommand::SetBridgeSettings(tx, settings))?; - let settings_result = self.wait_for_result(rx).await?; - settings_result - .map(Response::new) - .map_err(map_settings_error) + self.wait_for_result(rx).await??; + Ok(Response::new(())) } async fn set_obfuscation_settings( @@ -230,10 +226,8 @@ impl ManagementService for ManagementServiceImpl { log::debug!("set_obfuscation_settings({:?})", settings); let (tx, rx) = oneshot::channel(); self.send_command_to_daemon(DaemonCommand::SetObfuscationSettings(tx, settings))?; - let settings_result = self.wait_for_result(rx).await?; - settings_result - .map(Response::new) - .map_err(map_settings_error) + self.wait_for_result(rx).await??; + Ok(Response::new(())) } async fn set_bridge_state(&self, request: Request<types::BridgeState>) -> ServiceResult<()> { @@ -243,10 +237,8 @@ impl ManagementService for ManagementServiceImpl { log::debug!("set_bridge_state({:?})", bridge_state); let (tx, rx) = oneshot::channel(); self.send_command_to_daemon(DaemonCommand::SetBridgeState(tx, bridge_state))?; - let settings_result = self.wait_for_result(rx).await?; - settings_result - .map(Response::new) - .map_err(map_settings_error) + self.wait_for_result(rx).await??; + Ok(Response::new(())) } // Settings @@ -266,10 +258,8 @@ impl ManagementService for ManagementServiceImpl { log::debug!("set_allow_lan({})", allow_lan); let (tx, rx) = oneshot::channel(); self.send_command_to_daemon(DaemonCommand::SetAllowLan(tx, allow_lan))?; - self.wait_for_result(rx) - .await? - .map(Response::new) - .map_err(map_settings_error) + self.wait_for_result(rx).await??; + Ok(Response::new(())) } async fn set_show_beta_releases(&self, request: Request<bool>) -> ServiceResult<()> { @@ -277,10 +267,8 @@ impl ManagementService for ManagementServiceImpl { log::debug!("set_show_beta_releases({})", enabled); let (tx, rx) = oneshot::channel(); self.send_command_to_daemon(DaemonCommand::SetShowBetaReleases(tx, enabled))?; - self.wait_for_result(rx) - .await? - .map(Response::new) - .map_err(map_settings_error) + self.wait_for_result(rx).await??; + Ok(Response::new(())) } async fn set_block_when_disconnected(&self, request: Request<bool>) -> ServiceResult<()> { @@ -291,10 +279,8 @@ impl ManagementService for ManagementServiceImpl { tx, block_when_disconnected, ))?; - self.wait_for_result(rx) - .await? - .map(Response::new) - .map_err(map_settings_error) + self.wait_for_result(rx).await??; + Ok(Response::new(())) } async fn set_auto_connect(&self, request: Request<bool>) -> ServiceResult<()> { @@ -302,10 +288,8 @@ impl ManagementService for ManagementServiceImpl { log::debug!("set_auto_connect({})", auto_connect); let (tx, rx) = oneshot::channel(); self.send_command_to_daemon(DaemonCommand::SetAutoConnect(tx, auto_connect))?; - self.wait_for_result(rx) - .await? - .map(Response::new) - .map_err(map_settings_error) + self.wait_for_result(rx).await??; + Ok(Response::new(())) } async fn set_openvpn_mssfix(&self, request: Request<u32>) -> ServiceResult<()> { @@ -318,10 +302,8 @@ impl ManagementService for ManagementServiceImpl { log::debug!("set_openvpn_mssfix({:?})", mssfix); let (tx, rx) = oneshot::channel(); self.send_command_to_daemon(DaemonCommand::SetOpenVpnMssfix(tx, mssfix))?; - self.wait_for_result(rx) - .await? - .map(Response::new) - .map_err(map_settings_error) + self.wait_for_result(rx).await??; + Ok(Response::new(())) } async fn set_wireguard_mtu(&self, request: Request<u32>) -> ServiceResult<()> { @@ -330,10 +312,8 @@ impl ManagementService for ManagementServiceImpl { log::debug!("set_wireguard_mtu({:?})", mtu); let (tx, rx) = oneshot::channel(); self.send_command_to_daemon(DaemonCommand::SetWireguardMtu(tx, mtu))?; - self.wait_for_result(rx) - .await? - .map(Response::new) - .map_err(map_settings_error) + self.wait_for_result(rx).await??; + Ok(Response::new(())) } async fn set_enable_ipv6(&self, request: Request<bool>) -> ServiceResult<()> { @@ -341,10 +321,8 @@ impl ManagementService for ManagementServiceImpl { log::debug!("set_enable_ipv6({})", enable_ipv6); let (tx, rx) = oneshot::channel(); self.send_command_to_daemon(DaemonCommand::SetEnableIpv6(tx, enable_ipv6))?; - self.wait_for_result(rx) - .await? - .map(Response::new) - .map_err(map_settings_error) + self.wait_for_result(rx).await??; + Ok(Response::new(())) } async fn set_quantum_resistant_tunnel( @@ -357,10 +335,8 @@ impl ManagementService for ManagementServiceImpl { log::debug!("set_quantum_resistant_tunnel({state:?})"); let (tx, rx) = oneshot::channel(); self.send_command_to_daemon(DaemonCommand::SetQuantumResistantTunnel(tx, state))?; - self.wait_for_result(rx) - .await? - .map(Response::new) - .map_err(map_settings_error) + self.wait_for_result(rx).await??; + Ok(Response::new(())) } #[cfg(not(target_os = "android"))] @@ -370,10 +346,8 @@ impl ManagementService for ManagementServiceImpl { let (tx, rx) = oneshot::channel(); self.send_command_to_daemon(DaemonCommand::SetDnsOptions(tx, options))?; - self.wait_for_result(rx) - .await? - .map(Response::new) - .map_err(map_settings_error) + self.wait_for_result(rx).await??; + Ok(Response::new(())) } #[cfg(target_os = "android")] @@ -390,20 +364,16 @@ impl ManagementService for ManagementServiceImpl { log::debug!("set_relay_override"); let (tx, rx) = oneshot::channel(); self.send_command_to_daemon(DaemonCommand::SetRelayOverride(tx, relay_override))?; - self.wait_for_result(rx) - .await? - .map(Response::new) - .map_err(map_settings_error) + self.wait_for_result(rx).await??; + Ok(Response::new(())) } async fn clear_all_relay_overrides(&self, _: Request<()>) -> ServiceResult<()> { log::debug!("clear_all_relay_overrides"); let (tx, rx) = oneshot::channel(); self.send_command_to_daemon(DaemonCommand::ClearAllRelayOverrides(tx))?; - self.wait_for_result(rx) - .await? - .map(Response::new) - .map_err(map_settings_error) + self.wait_for_result(rx).await??; + Ok(Response::new(())) } // Account management @@ -571,20 +541,16 @@ impl ManagementService for ManagementServiceImpl { tx, Some(interval), ))?; - self.wait_for_result(rx) - .await? - .map(Response::new) - .map_err(map_settings_error) + self.wait_for_result(rx).await??; + Ok(Response::new(())) } async fn reset_wireguard_rotation_interval(&self, _: Request<()>) -> ServiceResult<()> { log::debug!("reset_wireguard_rotation_interval"); let (tx, rx) = oneshot::channel(); self.send_command_to_daemon(DaemonCommand::SetWireguardRotationInterval(tx, None))?; - self.wait_for_result(rx) - .await? - .map(Response::new) - .map_err(map_settings_error) + self.wait_for_result(rx).await??; + Ok(Response::new(())) } async fn rotate_wireguard_key(&self, _: Request<()>) -> ServiceResult<()> { @@ -929,6 +895,14 @@ impl ManagementService for ManagementServiceImpl { async fn check_volumes(&self, _: Request<()>) -> ServiceResult<()> { Ok(Response::new(())) } + + async fn apply_json_settings(&self, blob: Request<String>) -> ServiceResult<()> { + log::debug!("apply_json_settings"); + let (tx, rx) = oneshot::channel(); + self.send_command_to_daemon(DaemonCommand::ApplyJsonSettings(tx, blob.into_inner()))?; + self.wait_for_result(rx).await??; + Ok(Response::new(())) + } } impl ManagementServiceImpl { @@ -1061,7 +1035,7 @@ fn map_daemon_error(error: crate::Error) -> Status { match error { DaemonError::RestError(error) => map_rest_error(&error), - DaemonError::SettingsError(error) => map_settings_error(error), + DaemonError::SettingsError(error) => Status::from(error), DaemonError::AlreadyLoggedIn => Status::already_exists(error.to_string()), DaemonError::LoginError(error) => map_device_error(&error), DaemonError::LogoutError(error) => map_device_error(&error), @@ -1121,20 +1095,6 @@ fn map_rest_error(error: &RestError) -> Status { } } -/// Converts an instance of [`mullvad_daemon::settings::Error`] into a tonic status. -fn map_settings_error(error: settings::Error) -> Status { - match error { - settings::Error::DeleteError(..) - | settings::Error::WriteError(..) - | settings::Error::ReadError(..) => { - Status::new(Code::FailedPrecondition, error.to_string()) - } - settings::Error::SerializeError(..) | settings::Error::ParseError(..) => { - Status::new(Code::Internal, error.to_string()) - } - } -} - /// Converts an instance of [`mullvad_daemon::device::Error`] into a tonic status. fn map_device_error(error: &device::Error) -> Status { match error { diff --git a/mullvad-daemon/src/settings/mod.rs b/mullvad-daemon/src/settings/mod.rs index 671ed87346..99f637c023 100644 --- a/mullvad-daemon/src/settings/mod.rs +++ b/mullvad-daemon/src/settings/mod.rs @@ -40,6 +40,23 @@ pub enum Error { WriteError(String, #[error(source)] io::Error), } +/// Converts an [Error] to a management interface status +#[cfg(not(target_os = "android"))] +impl From<Error> for mullvad_management_interface::Status { + fn from(error: Error) -> mullvad_management_interface::Status { + use mullvad_management_interface::{Code, Status}; + + match error { + Error::DeleteError(..) | Error::WriteError(..) | Error::ReadError(..) => { + Status::new(Code::FailedPrecondition, error.to_string()) + } + Error::SerializeError(..) | Error::ParseError(..) => { + Status::new(Code::Internal, error.to_string()) + } + } + } +} + pub struct SettingsPersister { settings: Settings, path: PathBuf, diff --git a/mullvad-daemon/src/settings/patch.rs b/mullvad-daemon/src/settings/patch.rs index 63152b740d..f006b21056 100644 --- a/mullvad-daemon/src/settings/patch.rs +++ b/mullvad-daemon/src/settings/patch.rs @@ -41,6 +41,24 @@ pub enum Error { Settings(#[error(source)] super::Error), } +/// Converts an [Error] to a management interface status +#[cfg(not(target_os = "android"))] +impl From<Error> for mullvad_management_interface::Status { + fn from(error: Error) -> mullvad_management_interface::Status { + use mullvad_management_interface::Status; + + match error { + Error::InvalidOrMissingValue(_) + | Error::UnknownOrProhibitedKey(_) + | Error::ParsePatch(_) + | Error::DeserializePatched(_) + | Error::RecursionLimit => Status::invalid_argument(error.to_string()), + Error::Settings(error) => Status::from(error), + Error::SerializeSettings(error) => Status::internal(error.to_string()), + } + } +} + enum MergeStrategy { /// Replace or append keys to objects, and replace everything else Replace, diff --git a/mullvad-management-interface/proto/management_interface.proto b/mullvad-management-interface/proto/management_interface.proto index 64d87d37ef..a27698f317 100644 --- a/mullvad-management-interface/proto/management_interface.proto +++ b/mullvad-management-interface/proto/management_interface.proto @@ -99,6 +99,9 @@ service ManagementService { // Notify the split tunnel monitor that a volume was mounted or dismounted // (Windows). rpc CheckVolumes(google.protobuf.Empty) returns (google.protobuf.Empty) {} + + // Apply a JSON blob to the settings + rpc ApplyJsonSettings(google.protobuf.StringValue) returns (google.protobuf.Empty) {} } message UUID { string value = 1; } diff --git a/mullvad-management-interface/src/client.rs b/mullvad-management-interface/src/client.rs index 140eddc08a..c1b27fea65 100644 --- a/mullvad-management-interface/src/client.rs +++ b/mullvad-management-interface/src/client.rs @@ -677,6 +677,11 @@ impl MullvadProxyClient { } // check_volumes + + pub async fn apply_json_settings(&mut self, blob: String) -> Result<()> { + self.0.apply_json_settings(blob).await.map_err(Error::Rpc)?; + Ok(()) + } } fn map_device_error(status: Status) -> Error { |
