mod account; pub mod config; mod dns; mod helpers; mod install; mod settings; mod test_metadata; mod tunnel; mod tunnel_state; mod ui; use crate::mullvad_daemon::RpcClientProvider; use anyhow::Context; use helpers::reset_relay_settings; pub use test_metadata::TestMetadata; use test_rpc::ServiceClient; use futures::future::BoxFuture; use mullvad_management_interface::{ types::{self, Settings}, ManagementServiceClient, }; use once_cell::sync::OnceCell; use std::time::Duration; const PING_TIMEOUT: Duration = Duration::from_secs(3); const WAIT_FOR_TUNNEL_STATE_TIMEOUT: Duration = Duration::from_secs(40); #[derive(Clone)] pub struct TestContext { pub rpc_provider: RpcClientProvider, } pub type TestWrapperFunction = Box< dyn Fn( TestContext, ServiceClient, Box, ) -> BoxFuture<'static, Result<(), Error>>, >; #[derive(err_derive::Error, Debug)] pub enum Error { #[error(display = "RPC call failed")] Rpc(#[source] test_rpc::Error), #[error(display = "Timeout waiting for ping")] PingTimeout, #[error(display = "geoip lookup failed")] GeoipError(test_rpc::Error), #[error(display = "Found running daemon unexpectedly")] DaemonRunning, #[error(display = "Daemon unexpectedly not running")] DaemonNotRunning, #[error(display = "The daemon returned an error: {}", _0)] DaemonError(String), #[error(display = "Failed to parse gRPC response")] InvalidGrpcResponse(#[error(source)] types::FromProtobufTypeError), #[cfg(target_os = "macos")] #[error(display = "An error occurred: {}", _0)] Other(String), } static DEFAULT_SETTINGS: OnceCell = OnceCell::new(); /// Initializes `DEFAULT_SETTINGS`. This has only has an effect the first time it's called. pub async fn init_default_settings(mullvad_client: &mut ManagementServiceClient) { if DEFAULT_SETTINGS.get().is_none() { let settings: Settings = mullvad_client .get_settings(()) .await .expect("Failed to obtain settings") .into_inner(); DEFAULT_SETTINGS.set(settings).unwrap(); } } /// Restore settings to `DEFAULT_SETTINGS`. /// /// # Panics /// /// `DEFAULT_SETTINGS` must be initialized using `init_default_settings` before any settings are /// modified, or this function panics. pub async fn cleanup_after_test( mullvad_client: &mut ManagementServiceClient, ) -> anyhow::Result<()> { log::debug!("Cleaning up daemon in test cleanup"); let default_settings = DEFAULT_SETTINGS .get() .expect("default settings were not initialized"); reset_relay_settings(mullvad_client).await?; mullvad_client .set_auto_connect(default_settings.auto_connect) .await .context("Could not set auto connect in cleanup")?; mullvad_client .set_allow_lan(default_settings.allow_lan) .await .context("Could not set allow lan in cleanup")?; mullvad_client .set_show_beta_releases(default_settings.show_beta_releases) .await .context("Could not set show beta releases in cleanup")?; mullvad_client .set_bridge_state(default_settings.bridge_state.clone().unwrap()) .await .context("Could not set bridge state in cleanup")?; mullvad_client .set_bridge_settings(default_settings.bridge_settings.clone().unwrap()) .await .context("Could not set bridge settings in cleanup")?; mullvad_client .set_obfuscation_settings(default_settings.obfuscation_settings.clone().unwrap()) .await .context("Could set obfuscation settings in cleanup")?; mullvad_client .set_block_when_disconnected(default_settings.block_when_disconnected) .await .context("Could not set block when disconnected setting in cleanup")?; mullvad_client .clear_split_tunnel_apps(()) .await .context("Could not clear split tunnel apps in cleanup")?; mullvad_client .clear_split_tunnel_processes(()) .await .context("Could not clear split tunnel processes in cleanup")?; mullvad_client .set_dns_options( default_settings .tunnel_options .as_ref() .unwrap() .dns_options .as_ref() .unwrap() .clone(), ) .await .context("Could not clear dns options in cleanup")?; mullvad_client .set_quantum_resistant_tunnel( default_settings .tunnel_options .as_ref() .unwrap() .wireguard .as_ref() .unwrap() .quantum_resistant .as_ref() .unwrap() .clone(), ) .await .context("Could not clear PQ options in cleanup")?; Ok(()) }