diff options
| author | Bug Magnet <marco.nikic@mullvad.net> | 2025-03-07 16:13:46 +0100 |
|---|---|---|
| committer | Bug Magnet <marco.nikic@mullvad.net> | 2025-03-17 16:51:50 +0100 |
| commit | 6e46e26f12e0003c0e20ff8840e47689d6722806 (patch) | |
| tree | 74db81c6d452d4c275076ad9390ce63dd0e4ed0a | |
| parent | 73da8abf9b25e1b1327e017c83a0ab2c519c632e (diff) | |
| download | mullvadvpn-6e46e26f12e0003c0e20ff8840e47689d6722806.tar.xz mullvadvpn-6e46e26f12e0003c0e20ff8840e47689d6722806.zip | |
Fix building for Android, rename api to access_mode in mullvad-api
| -rw-r--r-- | Cargo.lock | 1 | ||||
| -rw-r--r-- | mullvad-api/Cargo.toml | 1 | ||||
| -rw-r--r-- | mullvad-api/src/access_mode.rs (renamed from mullvad-api/src/api.rs) | 242 | ||||
| -rw-r--r-- | mullvad-api/src/bin/relay_list.rs | 79 | ||||
| -rw-r--r-- | mullvad-api/src/lib.rs | 2 | ||||
| -rw-r--r-- | mullvad-api/src/proxy.rs | 6 | ||||
| -rw-r--r-- | mullvad-daemon/src/access_method.rs | 17 | ||||
| -rw-r--r-- | mullvad-daemon/src/api.rs | 170 | ||||
| -rw-r--r-- | mullvad-daemon/src/lib.rs | 63 | ||||
| -rw-r--r-- | mullvad-jni/Cargo.toml | 2 |
10 files changed, 286 insertions, 297 deletions
diff --git a/Cargo.lock b/Cargo.lock index d1a29c00c8..f6b3c2c335 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2614,7 +2614,6 @@ dependencies = [ "mockito", "mullvad-encrypted-dns-proxy", "mullvad-fs", - "mullvad-relay-selector", "mullvad-types", "rustls-pemfile 2.1.3", "serde", diff --git a/mullvad-api/Cargo.toml b/mullvad-api/Cargo.toml index 566a78f1c1..46eb8fbaff 100644 --- a/mullvad-api/Cargo.toml +++ b/mullvad-api/Cargo.toml @@ -52,7 +52,6 @@ mullvad-fs = { path = "../mullvad-fs" } mullvad-types = { path = "../mullvad-types" } talpid-types = { path = "../talpid-types" } talpid-time = { path = "../talpid-time" } -mullvad-relay-selector = { path = "../mullvad-relay-selector" } shadowsocks = { workspace = true, features = ["stream-cipher"] } diff --git a/mullvad-api/src/api.rs b/mullvad-api/src/access_mode.rs index c1123eba4b..2a9b4f1f16 100644 --- a/mullvad-api/src/api.rs +++ b/mullvad-api/src/access_mode.rs @@ -4,25 +4,16 @@ //! [`ApiConnectionMode`], which in turn is used by `mullvad-api` for //! establishing connections when performing API requests. -use crate::{ - proxy::{AllowedClientsProvider, ApiConnectionMode, ConnectionModeProvider, ProxyConfig}, - AddressCache, -}; +use crate::proxy::{ApiConnectionMode, ConnectionModeProvider}; +#[cfg(feature = "api-override")] +use crate::ApiEndpoint; +use async_trait::async_trait; use futures::{ channel::{mpsc, oneshot}, StreamExt, }; -#[cfg(feature = "api-override")] -use mullvad_api::ApiEndpoint; -use mullvad_encrypted_dns_proxy::state::EncryptedDnsProxyState; -use mullvad_relay_selector::RelaySelector; -use mullvad_types::access_method::{ - AccessMethod, AccessMethodSetting, BuiltInAccessMethod, Id, Settings, -}; -use std::{net::SocketAddr, path::PathBuf}; -use talpid_types::net::{ - proxy::CustomProxy, AllowedEndpoint, Endpoint, TransportProtocol, -}; +use mullvad_types::access_method::{AccessMethod, AccessMethodSetting, Id, Settings}; +use talpid_types::net::AllowedEndpoint; pub enum Message { Get(ResponseTx<ResolvedConnectionMode>), @@ -35,10 +26,6 @@ pub enum Message { ), } -/// Calling [`AccessMethodEvent::send`] will cause a -/// [`crate::InternalDaemonEvent::AccessMethodEvent`] being sent to the daemon, -/// which in turn will handle updating the firewall and notifying clients as -/// applicable. pub enum AccessMethodEvent { /// A [`AccessMethodEvent::New`] event is emitted when the active access /// method changes. @@ -48,12 +35,13 @@ pub enum AccessMethodEvent { New { /// The new active [`AccessMethodSetting`]. setting: AccessMethodSetting, + connection_mode: ApiConnectionMode, /// The endpoint which represents how to connect to the Mullvad API and /// which clients are allowed to initiate such a connection. #[cfg(not(target_os = "android"))] endpoint: AllowedEndpoint, }, - /// Emitted when the the firewall should be updated. + /// Emitted when the API endpoint is updated. /// /// This is useful for example when testing if some [`AccessMethodSetting`] /// can be used to reach the Mullvad API. In this scenario, the currently @@ -69,14 +57,11 @@ pub enum AccessMethodEvent { impl AccessMethodEvent { pub async fn send( self, - daemon_event_sender: mpsc::UnboundedSender<(AccessMethodEvent, oneshot::Sender<()>)>, + event_sender: mpsc::UnboundedSender<(AccessMethodEvent, oneshot::Sender<()>)>, ) -> Result<()> { - // It is up to the daemon to actually allow traffic to/from `api_endpoint` - // by updating the firewall. This [`oneshot::Sender`] allows the daemon to - // communicate when that action is done. let (update_finished_tx, update_finished_rx) = oneshot::channel(); - let _ = daemon_event_sender.unbounded_send((self, update_finished_tx)); - // Wait for the daemon to finish processing `event`. + let _ = event_sender.unbounded_send((self, update_finished_tx)); + // Wait for the listener to finish processing `event`. update_finished_rx.await.map_err(Error::NotRunning) } } @@ -99,8 +84,7 @@ pub struct ResolvedConnectionMode { pub setting: AccessMethodSetting, } -/// Describes all the ways the daemon service which handles access methods can -/// fail. +/// Describes all the ways handling access methods can fail. #[derive(thiserror::Error, Debug)] pub enum Error { #[error("No access methods were provided.")] @@ -130,9 +114,6 @@ impl std::fmt::Display for Message { impl Error { /// Check if this error implies that the currenly running /// [`AccessModeSelector`] can not continue to operate properly. - /// - /// To recover from this kind of error, the daemon will probably have to - /// intervene. fn is_critical_error(&self) -> bool { matches!( self, @@ -247,35 +228,26 @@ impl ConnectionModeProvider for AccessModeConnectionModeProvider { /// [`ApiConnectionMode::Direct`]) via a bridge ([`ApiConnectionMode::Proxied`]) /// or via any supported custom proxy protocol /// ([`talpid_types::net::proxy::CustomProxy`]). -pub struct AccessModeSelector { +pub struct AccessModeSelector<B: AccessMethodResolver> { #[cfg(feature = "api-override")] api_endpoint: ApiEndpoint, cmd_rx: mpsc::UnboundedReceiver<Message>, - cache_dir: PathBuf, - /// Used for selecting a Bridge when the `Mullvad Bridges` access method is used. - relay_selector: RelaySelector, - /// Used for selecting a config for the 'Encrypted DNS proxy' access method. - encrypted_dns_proxy_cache: EncryptedDnsProxyState, + bridge_dns_proxy_provider: B, access_method_settings: Settings, - address_cache: AddressCache, access_method_event_sender: mpsc::UnboundedSender<(AccessMethodEvent, oneshot::Sender<()>)>, connection_mode_provider_sender: mpsc::UnboundedSender<ApiConnectionMode>, current: ResolvedConnectionMode, /// `index` is used to keep track of the [`AccessMethodSetting`] to use. index: usize, - provider: Box<dyn AllowedClientsProvider>, } -impl AccessModeSelector { +impl<B: AccessMethodResolver + 'static> AccessModeSelector<B> { pub async fn spawn( - cache_dir: PathBuf, - relay_selector: RelaySelector, + mut bridge_dns_proxy_provider: B, #[cfg_attr(not(feature = "api-override"), allow(unused_mut))] mut access_method_settings: Settings, #[cfg(feature = "api-override")] api_endpoint: ApiEndpoint, access_method_event_sender: mpsc::UnboundedSender<(AccessMethodEvent, oneshot::Sender<()>)>, - address_cache: AddressCache, - provider: Box<dyn AllowedClientsProvider>, ) -> Result<(AccessModeSelectorHandle, AccessModeConnectionModeProvider)> { let (cmd_tx, cmd_rx) = mpsc::unbounded(); @@ -287,38 +259,25 @@ impl AccessModeSelector { } } - // Initialize the Encrypted DNS cache - let mut encrypted_dns_proxy_cache = EncryptedDnsProxyState::default(); - // Always start looking from the position of `Direct`. let (index, next) = Self::find_next_active(0, &access_method_settings); - let initial_connection_mode = Self::resolve_inner_with_default( - &next, - &relay_selector, - &mut encrypted_dns_proxy_cache, - &address_cache, - &*provider, - ) - .await; + let initial_connection_mode = + Self::resolve_with_default(&next, &mut bridge_dns_proxy_provider).await; let (change_tx, change_rx) = mpsc::unbounded(); let api_connection_mode = initial_connection_mode.connection_mode.clone(); let selector = AccessModeSelector { + #[cfg(feature = "api-override")] + api_endpoint, cmd_rx, - cache_dir, - relay_selector, - encrypted_dns_proxy_cache, + bridge_dns_proxy_provider, access_method_settings, - address_cache, access_method_event_sender, connection_mode_provider_sender: change_tx, current: initial_connection_mode, index, - provider, - #[cfg(feature = "api-override")] - api_endpoint, }; tokio::spawn(selector.into_future()); @@ -420,7 +379,8 @@ impl AccessModeSelector { } async fn set_current(&mut self, access_method: AccessMethodSetting) { - let resolved = self.resolve_with_default(access_method).await; + let resolved = + Self::resolve_with_default(&access_method, &mut self.bridge_dns_proxy_provider).await; // Note: If the daemon is busy waiting for a call to this function // to complete while we wait for the daemon to fully handle this @@ -432,26 +392,19 @@ impl AccessModeSelector { let setting = resolved.setting.clone(); #[cfg(not(target_os = "android"))] let endpoint = resolved.endpoint.clone(); - let daemon_sender = self.access_method_event_sender.clone(); + let sender = self.access_method_event_sender.clone(); + let connection_mode = resolved.connection_mode.clone(); tokio::spawn(async move { let _ = AccessMethodEvent::New { setting, + connection_mode, #[cfg(not(target_os = "android"))] endpoint, } - .send(daemon_sender) + .send(sender) .await; }); - // Save the new connection mode to cache! - let cache_dir = self.cache_dir.clone(); - let connection_mode = resolved.connection_mode.clone(); - tokio::spawn(async move { - if connection_mode.save(&cache_dir).await.is_err() { - log::warn!("Failed to save {connection_mode:#?} to cache") - } - }); - // Notify REST client let _ = self .connection_mode_provider_sender @@ -534,134 +487,49 @@ impl AccessModeSelector { async fn resolve( &mut self, - access_method: AccessMethodSetting, + method_setting: AccessMethodSetting, ) -> Option<ResolvedConnectionMode> { - Self::resolve_inner( - &access_method, - &self.relay_selector, - &mut self.encrypted_dns_proxy_cache, - &self.address_cache, - &*self.provider, - ) - .await - } - - async fn resolve_inner( - access_method: &AccessMethodSetting, - relay_selector: &RelaySelector, - encrypted_dns_proxy_cache: &mut EncryptedDnsProxyState, - address_cache: &AddressCache, - provider: &dyn AllowedClientsProvider, - ) -> Option<ResolvedConnectionMode> { - let connection_mode = - Self::resolve_connection_mode(access_method, relay_selector, encrypted_dns_proxy_cache) - .await?; - let endpoint = resolve_allowed_endpoint( - &connection_mode, - address_cache.get_address().await, - provider, - ); + let (endpoint, connection_mode) = self + .bridge_dns_proxy_provider + .resolve_access_method_setting(&method_setting.access_method) + .await?; Some(ResolvedConnectionMode { connection_mode, endpoint, - setting: access_method.clone(), + setting: method_setting, }) } /// Resolve an access method into a set of connection details - fall back to /// [`ApiConnectionMode::Direct`] in case `access_method` does not yield anything. async fn resolve_with_default( - &mut self, - access_method: AccessMethodSetting, - ) -> ResolvedConnectionMode { - Self::resolve_inner_with_default( - &access_method, - &self.relay_selector, - &mut self.encrypted_dns_proxy_cache, - &self.address_cache, - &*self.provider, - ) - .await - } - - async fn resolve_inner_with_default( - access_method: &AccessMethodSetting, - relay_selector: &RelaySelector, - encrypted_dns_proxy_cache: &mut EncryptedDnsProxyState, - address_cache: &AddressCache, - provider: &dyn AllowedClientsProvider, + method_setting: &AccessMethodSetting, + bridge_dns_proxy_provider: &mut B, ) -> ResolvedConnectionMode { - match Self::resolve_inner( - access_method, - relay_selector, - encrypted_dns_proxy_cache, - address_cache, - provider, - ) - .await + let (endpoint, connection_mode) = match bridge_dns_proxy_provider + .resolve_access_method_setting(&method_setting.access_method) + .await { Some(resolved) => resolved, - None => { - log::trace!("Defaulting to direct API connection"); - ResolvedConnectionMode { - connection_mode: ApiConnectionMode::Direct, - endpoint: resolve_allowed_endpoint( - &ApiConnectionMode::Direct, - address_cache.get_address().await, - provider, - ), - setting: access_method.clone(), - } - } - } - } - - async fn resolve_connection_mode( - access_method: &AccessMethodSetting, - relay_selector: &RelaySelector, - encrypted_dns_proxy_cache: &mut EncryptedDnsProxyState, - ) -> Option<ApiConnectionMode> { - let connection_mode = { - match &access_method.access_method { - AccessMethod::BuiltIn(BuiltInAccessMethod::Direct) => ApiConnectionMode::Direct, - AccessMethod::BuiltIn(BuiltInAccessMethod::Bridge) => { - let Some(bridge) = relay_selector.get_bridge_forced() else { - log::warn!("Could not select a Mullvad bridge"); - log::debug!("The relay list might be empty"); - return None; - }; - let proxy = CustomProxy::Shadowsocks(bridge); - ApiConnectionMode::Proxied(ProxyConfig::from(proxy)) - } - AccessMethod::BuiltIn(BuiltInAccessMethod::EncryptedDnsProxy) => { - if let Err(error) = encrypted_dns_proxy_cache.fetch_configs("frakta.eu").await { - log::warn!("Failed to fetch new Encrypted DNS Proxy configurations"); - log::debug!("{error:#?}"); - } - let Some(edp) = encrypted_dns_proxy_cache.next_configuration() else { - log::warn!("Could not select next Encrypted DNS proxy config"); - return None; - }; - ApiConnectionMode::Proxied(ProxyConfig::from(edp)) - } - AccessMethod::Custom(config) => { - ApiConnectionMode::Proxied(ProxyConfig::from(config.clone())) - } - } + None => ( + bridge_dns_proxy_provider.default_connection_mode().await, + ApiConnectionMode::Direct, + ), }; - Some(connection_mode) + ResolvedConnectionMode { + connection_mode, + endpoint, + setting: method_setting.clone(), + } } } -pub fn resolve_allowed_endpoint( - connection_mode: &ApiConnectionMode, - fallback: SocketAddr, - provider: &dyn AllowedClientsProvider, -) -> AllowedEndpoint { - let endpoint = match connection_mode.get_endpoint() { - Some(endpoint) => endpoint, - None => Endpoint::from_socket_address(fallback, TransportProtocol::Tcp), - }; - let clients = provider.allowed_clients(connection_mode); - AllowedEndpoint { endpoint, clients } +#[async_trait] +pub trait AccessMethodResolver: Send + Sync { + async fn resolve_access_method_setting( + &mut self, + access_method: &AccessMethod, + ) -> Option<(AllowedEndpoint, ApiConnectionMode)>; + + async fn default_connection_mode(&self) -> AllowedEndpoint; } diff --git a/mullvad-api/src/bin/relay_list.rs b/mullvad-api/src/bin/relay_list.rs index 3ea771cc81..fbf7e8af42 100644 --- a/mullvad-api/src/bin/relay_list.rs +++ b/mullvad-api/src/bin/relay_list.rs @@ -2,41 +2,54 @@ //! Used by the installer artifact packer to bundle the latest available //! relay list at the time of creating the installer. -use mullvad_api::{ - proxy::ApiConnectionMode, rest::Error as RestError, ApiEndpoint, RelayListProxy, -}; -use std::process; -use talpid_types::ErrorExt; +#[cfg(not(target_os = "android"))] +mod imp { + use mullvad_api::{ + proxy::ApiConnectionMode, rest::Error as RestError, ApiEndpoint, RelayListProxy, + }; + use std::process; + use talpid_types::ErrorExt; + + pub async fn main() { + let runtime = mullvad_api::Runtime::new( + tokio::runtime::Handle::current(), + &ApiEndpoint::from_env_vars(), + ); + + let relay_list_request = RelayListProxy::new( + runtime.mullvad_rest_handle(ApiConnectionMode::Direct.into_provider()), + ) + .relay_list(None) + .await; + + let relay_list = match relay_list_request { + Ok(relay_list) => relay_list, + Err(RestError::TimeoutError) => { + eprintln!("Request timed out"); + process::exit(2); + } + Err(e @ RestError::DeserializeError(_)) => { + eprintln!( + "{}", + e.display_chain_with_msg("Failed to deserialize relay list") + ); + process::exit(3); + } + Err(e) => { + eprintln!("{}", e.display_chain_with_msg("Failed to fetch relay list")); + process::exit(1); + } + }; + println!("{}", serde_json::to_string_pretty(&relay_list).unwrap()); + } +} #[tokio::main] async fn main() { - let runtime = mullvad_api::Runtime::new( - tokio::runtime::Handle::current(), - &ApiEndpoint::from_env_vars(), - ); - - let relay_list_request = - RelayListProxy::new(runtime.mullvad_rest_handle(ApiConnectionMode::Direct.into_provider())) - .relay_list(None) - .await; + imp::main().await +} - let relay_list = match relay_list_request { - Ok(relay_list) => relay_list, - Err(RestError::TimeoutError) => { - eprintln!("Request timed out"); - process::exit(2); - } - Err(e @ RestError::DeserializeError(_)) => { - eprintln!( - "{}", - e.display_chain_with_msg("Failed to deserialize relay list") - ); - process::exit(3); - } - Err(e) => { - eprintln!("{}", e.display_chain_with_msg("Failed to fetch relay list")); - process::exit(1); - } - }; - println!("{}", serde_json::to_string_pretty(&relay_list).unwrap()); +#[cfg(target_os = "android")] +mod imp { + pub async fn main() {} } diff --git a/mullvad-api/src/lib.rs b/mullvad-api/src/lib.rs index 6954c185c4..d8980a17b5 100644 --- a/mullvad-api/src/lib.rs +++ b/mullvad-api/src/lib.rs @@ -25,7 +25,7 @@ use availability::ApiAvailability; pub mod rest; mod abortable_stream; -pub mod api; +pub mod access_mode; mod https_client_with_sni; pub mod proxy; mod tls_stream; diff --git a/mullvad-api/src/proxy.rs b/mullvad-api/src/proxy.rs index 9b67270f01..106449bb30 100644 --- a/mullvad-api/src/proxy.rs +++ b/mullvad-api/src/proxy.rs @@ -8,7 +8,7 @@ use std::{ task::{self, Poll}, }; use talpid_types::{ - net::{proxy, AllowedClients, Endpoint, TransportProtocol}, + net::{proxy, Endpoint, TransportProtocol}, ErrorExt, }; use tokio::{ @@ -53,10 +53,6 @@ impl ConnectionModeProvider for StaticConnectionModeProvider { } } -pub trait AllowedClientsProvider: Send + Sync { - fn allowed_clients(&self, connection_mode: &ApiConnectionMode) -> AllowedClients; -} - #[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] pub enum ApiConnectionMode { /// Connect directly to the target. diff --git a/mullvad-daemon/src/access_method.rs b/mullvad-daemon/src/access_method.rs index cad5d9bdd8..cfc9241452 100644 --- a/mullvad-daemon/src/access_method.rs +++ b/mullvad-daemon/src/access_method.rs @@ -1,6 +1,5 @@ use crate::{settings, Daemon}; -use mullvad_api::{api, rest}; -use mullvad_api::{proxy::ApiConnectionMode, ApiProxy}; +use mullvad_api::{access_mode, proxy::ApiConnectionMode, rest, ApiProxy}; use mullvad_types::{ access_method::{self, AccessMethod, AccessMethodSetting}, settings::Settings, @@ -17,7 +16,7 @@ pub enum Error { /// Some error occured in the daemon's state of handling /// [`AccessMethodSetting`]s & [`ApiConnectionMode`]s #[error("Error occured when handling connection settings & details")] - ApiService(#[from] api::Error), + ApiService(#[from] access_mode::Error), /// A REST request failed #[error("Reset request failed")] Rest(#[from] rest::Error), @@ -154,9 +153,9 @@ impl Daemon { #[cfg(not(target_os = "android"))] pub(crate) async fn test_access_method( proxy: talpid_types::net::AllowedEndpoint, - access_method_selector: mullvad_api::api::AccessModeSelectorHandle, + access_method_selector: access_mode::AccessModeSelectorHandle, daemon_event_sender: crate::DaemonEventSender<( - mullvad_api::api::AccessMethodEvent, + access_mode::AccessMethodEvent, futures::channel::oneshot::Sender<()>, )>, api_proxy: ApiProxy, @@ -166,13 +165,13 @@ impl Daemon { .await .map(|connection_mode| connection_mode.endpoint)?; - mullvad_api::api::AccessMethodEvent::Allow { endpoint: proxy } + access_mode::AccessMethodEvent::Allow { endpoint: proxy } .send(daemon_event_sender.to_unbounded_sender()) .await?; let result = Self::perform_api_request(api_proxy).await; - mullvad_api::api::AccessMethodEvent::Allow { endpoint: reset } + access_mode::AccessMethodEvent::Allow { endpoint: reset } .send(daemon_event_sender.to_unbounded_sender()) .await?; @@ -182,9 +181,9 @@ impl Daemon { #[cfg(target_os = "android")] pub(crate) async fn test_access_method( _: talpid_types::net::AllowedEndpoint, - _: api::AccessModeSelectorHandle, + _: access_mode::AccessModeSelectorHandle, _: crate::DaemonEventSender<( - api::AccessMethodEvent, + access_mode::AccessMethodEvent, futures::channel::oneshot::Sender<()>, )>, api_proxy: ApiProxy, diff --git a/mullvad-daemon/src/api.rs b/mullvad-daemon/src/api.rs index cc3eb16d59..39648963d8 100644 --- a/mullvad-daemon/src/api.rs +++ b/mullvad-daemon/src/api.rs @@ -1,13 +1,136 @@ +use std::net::SocketAddr; + #[cfg(target_os = "android")] use crate::DaemonCommand; -use futures::channel::mpsc; -use futures::StreamExt; -use mullvad_api::availability::ApiAvailability; -use mullvad_api::proxy::AllowedClientsProvider; -use mullvad_api::proxy::ApiConnectionMode; -use mullvad_api::proxy::ProxyConfig; -use talpid_types::net::AllowedClients; -use talpid_types::net::Connectivity; +#[cfg(target_os = "android")] +use crate::DaemonEventSender; +use futures::{channel::mpsc, StreamExt}; +use mullvad_api::AddressCache; +use mullvad_api::{ + access_mode::AccessMethodResolver, + availability::ApiAvailability, + proxy::{ApiConnectionMode, ProxyConfig}, +}; +use mullvad_encrypted_dns_proxy::state::EncryptedDnsProxyState; +use mullvad_management_interface::async_trait; +use mullvad_relay_selector::RelaySelector; +use mullvad_types::access_method::{AccessMethod, BuiltInAccessMethod}; +#[cfg(target_os = "android")] +use talpid_core::mpsc::Sender; +use talpid_types::net::AllowedEndpoint; +use talpid_types::net::Endpoint; +use talpid_types::net::TransportProtocol; +use talpid_types::net::{proxy::CustomProxy, AllowedClients, Connectivity}; + +pub struct DaemonAccessMethodResolver { + relay_selector: RelaySelector, + encrypted_dns_proxy_cache: EncryptedDnsProxyState, + address_cache: AddressCache, +} + +impl DaemonAccessMethodResolver { + pub fn new( + relay_selector: RelaySelector, + encrypted_dns_proxy_cache: EncryptedDnsProxyState, + address_cache: AddressCache, + ) -> Self { + Self { + relay_selector, + encrypted_dns_proxy_cache, + address_cache, + } + } +} + +#[async_trait] +impl AccessMethodResolver for DaemonAccessMethodResolver { + async fn resolve_access_method_setting( + &mut self, + access_method: &AccessMethod, + ) -> Option<(AllowedEndpoint, ApiConnectionMode)> { + let connection_mode = { + match access_method { + AccessMethod::BuiltIn(BuiltInAccessMethod::Direct) => ApiConnectionMode::Direct, + AccessMethod::BuiltIn(BuiltInAccessMethod::Bridge) => { + let Some(bridge) = self.relay_selector.get_bridge_forced() else { + log::warn!("Could not select a Mullvad bridge"); + log::debug!("The relay list might be empty"); + return None; + }; + let proxy = CustomProxy::Shadowsocks(bridge); + ApiConnectionMode::Proxied(ProxyConfig::from(proxy)) + } + AccessMethod::BuiltIn(BuiltInAccessMethod::EncryptedDnsProxy) => { + if let Err(error) = self + .encrypted_dns_proxy_cache + .fetch_configs("frakta.eu") + .await + { + log::warn!("Failed to fetch new Encrypted DNS Proxy configurations"); + log::debug!("{error:#?}"); + } + let Some(edp) = self.encrypted_dns_proxy_cache.next_configuration() else { + log::warn!("Could not select next Encrypted DNS proxy config"); + return None; + }; + ApiConnectionMode::Proxied(ProxyConfig::from(edp)) + } + AccessMethod::Custom(config) => { + ApiConnectionMode::Proxied(ProxyConfig::from(config.clone())) + } + } + }; + let endpoint = + resolve_allowed_endpoint(&connection_mode, self.address_cache.get_address().await); + Some((endpoint, connection_mode)) + } + + async fn default_connection_mode(&self) -> AllowedEndpoint { + log::trace!("Defaulting to direct API connection"); + resolve_allowed_endpoint( + &ApiConnectionMode::Direct, + self.address_cache.get_address().await, + ) + } +} + +pub fn resolve_allowed_endpoint( + connection_mode: &ApiConnectionMode, + fallback: SocketAddr, +) -> AllowedEndpoint { + let endpoint = match connection_mode.get_endpoint() { + Some(endpoint) => endpoint, + None => Endpoint::from_socket_address(fallback, TransportProtocol::Tcp), + }; + let clients = allowed_clients(connection_mode); + AllowedEndpoint { endpoint, clients } +} + +#[cfg(unix)] +pub fn allowed_clients(connection_mode: &ApiConnectionMode) -> AllowedClients { + match connection_mode { + ApiConnectionMode::Proxied(ProxyConfig::Socks5Local(_)) => AllowedClients::All, + ApiConnectionMode::Direct | ApiConnectionMode::Proxied(_) => AllowedClients::Root, + } +} + +#[cfg(windows)] +pub fn allowed_clients(connection_mode: &ApiConnectionMode) -> AllowedClients { + match connection_mode { + ApiConnectionMode::Proxied(ProxyConfig::Socks5Local(_)) => AllowedClients::all(), + ApiConnectionMode::Direct | ApiConnectionMode::Proxied(_) => { + let daemon_exe = std::env::current_exe().expect("failed to obtain executable path"); + vec![ + daemon_exe + .parent() + .expect("missing executable parent directory") + .join("mullvad-problem-report.exe"), + daemon_exe, + ] + .into() + } + } +} #[cfg(target_os = "android")] pub(crate) fn create_bypass_tx( @@ -29,37 +152,6 @@ pub(crate) fn create_bypass_tx( Some(bypass_tx) } -#[derive(Clone, Copy)] -pub struct AllowedClientsSelector {} - -impl AllowedClientsProvider for AllowedClientsSelector { - #[cfg(unix)] - fn allowed_clients(&self, connection_mode: &ApiConnectionMode) -> AllowedClients { - match connection_mode { - ApiConnectionMode::Proxied(ProxyConfig::Socks5Local(_)) => AllowedClients::All, - ApiConnectionMode::Direct | ApiConnectionMode::Proxied(_) => AllowedClients::Root, - } - } - - #[cfg(windows)] - fn allowed_clients(&self, connection_mode: &ApiConnectionMode) -> AllowedClients { - match connection_mode { - ApiConnectionMode::Proxied(ProxyConfig::Socks5Local(_)) => AllowedClients::all(), - ApiConnectionMode::Direct | ApiConnectionMode::Proxied(_) => { - let daemon_exe = std::env::current_exe().expect("failed to obtain executable path"); - vec![ - daemon_exe - .parent() - .expect("missing executable parent directory") - .join("mullvad-problem-report.exe"), - daemon_exe, - ] - .into() - } - } - } -} - /// Forwards the received values from `offline_state_rx` to the [`ApiAvailability`]. pub(crate) fn forward_offline_state( api_availability: ApiAvailability, diff --git a/mullvad-daemon/src/lib.rs b/mullvad-daemon/src/lib.rs index 45819476fa..aae456a065 100644 --- a/mullvad-daemon/src/lib.rs +++ b/mullvad-daemon/src/lib.rs @@ -31,7 +31,7 @@ pub mod version; mod version_check; use crate::target_state::PersistentTargetState; -use api::AllowedClientsSelector; +use api::DaemonAccessMethodResolver; use device::{AccountEvent, PrivateAccountAndDevice, PrivateDeviceEvent}; use futures::{ channel::{mpsc, oneshot}, @@ -41,8 +41,8 @@ use futures::{ use geoip::GeoIpHandler; use leak_checker::{LeakChecker, LeakInfo}; use management_interface::ManagementInterfaceServer; -use mullvad_api::ApiEndpoint; -use mullvad_api::{api::AccessMethodEvent, proxy::AllowedClientsProvider}; +use mullvad_api::{access_mode::AccessMethodEvent, proxy::ApiConnectionMode, ApiEndpoint}; +use mullvad_encrypted_dns_proxy::state::EncryptedDnsProxyState; use mullvad_relay_selector::{RelaySelector, SelectorConfig}; #[cfg(target_os = "android")] use mullvad_types::account::{PlayPurchase, PlayPurchasePaymentToken}; @@ -197,7 +197,7 @@ pub enum Error { AccessMethodError(#[source] access_method::Error), #[error("API connection mode error")] - ApiConnectionModeError(#[source] mullvad_api::api::Error), + ApiConnectionModeError(#[source] mullvad_api::access_mode::Error), #[error("No custom bridge has been specified")] NoCustomProxySaved, @@ -581,7 +581,7 @@ where { let (tx, mut rx) = mpsc::unbounded::<T>(); let sender = self.sender.clone(); - tokio::runtime::Handle::current().spawn(async move { + tokio::spawn(async move { while let Some(msg) = rx.next().await { if let Some(tx) = sender.upgrade() { let e: E = E::from(msg); @@ -611,7 +611,7 @@ pub struct Daemon { account_history: account_history::AccountHistory, device_checker: device::TunnelStateChangeHandler, account_manager: device::AccountManagerHandle, - access_mode_handler: mullvad_api::api::AccessModeSelectorHandle, + access_mode_handler: mullvad_api::access_mode::AccessModeSelectorHandle, api_runtime: mullvad_api::Runtime, api_handle: mullvad_api::rest::MullvadRestHandle, version_updater_handle: version_check::VersionUpdaterHandle, @@ -624,6 +624,7 @@ pub struct Daemon { volume_update_tx: mpsc::UnboundedSender<()>, location_handler: GeoIpHandler, leak_checker: LeakChecker, + cache_dir: PathBuf, } pub struct DaemonConfig { pub log_dir: Option<PathBuf>, @@ -707,19 +708,20 @@ impl Daemon { .set_config(SelectorConfig::from_settings(settings)); }); - let allowed_clients_selector = AllowedClientsSelector {}; - let selector_box: Box<dyn AllowedClientsProvider> = Box::new(allowed_clients_selector); + let encrypted_dns_proxy_cache = EncryptedDnsProxyState::default(); + let bridge_dns_proxy_provider = DaemonAccessMethodResolver::new( + relay_selector.clone(), + encrypted_dns_proxy_cache, + api_runtime.address_cache().clone(), + ); let (access_mode_handler, access_mode_provider) = - mullvad_api::api::AccessModeSelector::spawn( - config.cache_dir.clone(), - relay_selector.clone(), + mullvad_api::access_mode::AccessModeSelector::spawn( + bridge_dns_proxy_provider, settings.api_access_methods.clone(), #[cfg(feature = "api-override")] config.endpoint.clone(), internal_event_tx.to_unbounded_sender(), - api_runtime.address_cache().clone(), - selector_box, ) .await .map_err(Error::ApiConnectionModeError)?; @@ -945,6 +947,7 @@ impl Daemon { volume_update_tx, location_handler, leak_checker, + cache_dir: config.cache_dir, }; api_availability.unsuspend(); @@ -1518,6 +1521,16 @@ impl Daemon { } } + fn save_connection_mode_to_cache(&self, connection_mode: ApiConnectionMode) { + // Save the new connection mode to cache! + let cache_dir = self.cache_dir.clone(); + tokio::spawn(async move { + if connection_mode.save(&cache_dir).await.is_err() { + log::warn!("Failed to save {connection_mode:#?} to cache") + } + }); + } + fn handle_access_method_event( &mut self, event: AccessMethodEvent, @@ -1525,7 +1538,12 @@ impl Daemon { ) { #[cfg(target_os = "android")] match event { - AccessMethodEvent::New { setting, .. } => { + AccessMethodEvent::New { + setting, + connection_mode, + .. + } => { + self.save_connection_mode_to_cache(connection_mode.clone()); // On android mullvad-api invokes protect on a socket to send requests // outside the tunnel let notifier = self.management_interface.notifier().clone(); @@ -1549,7 +1567,12 @@ impl Daemon { let _ = endpoint_active_tx.send(()); }); } - AccessMethodEvent::New { setting, endpoint } => { + AccessMethodEvent::New { + setting, + connection_mode, + endpoint, + } => { + self.save_connection_mode_to_cache(connection_mode.clone()); // Update the firewall to exempt a new API endpoint. let (completion_tx, completion_rx) = oneshot::channel(); self.send_tunnel_command(TunnelCommand::AllowEndpoint(endpoint, completion_tx)); @@ -2848,7 +2871,6 @@ impl Daemon { tx: ResponseTx<bool, Error>, proxy: talpid_types::net::proxy::CustomProxy, ) { - use crate::AllowedClientsSelector; use mullvad_api::proxy::{ApiConnectionMode, ProxyConfig}; use talpid_types::net::AllowedEndpoint; @@ -2856,7 +2878,7 @@ impl Daemon { let api_proxy = self.create_limited_api_proxy(connection_mode.clone()); let proxy_endpoint = AllowedEndpoint { endpoint: proxy.get_remote_endpoint().endpoint, - clients: AllowedClientsSelector {}.allowed_clients(&connection_mode), + clients: api::allowed_clients(&connection_mode), }; let daemon_event_sender = self.tx.to_specialized_sender(); @@ -2898,9 +2920,10 @@ impl Daemon { { Ok(Some(test_subject)) => test_subject, Ok(None) => { - let error = Error::ApiConnectionModeError(mullvad_api::api::Error::Resolve { - access_method: access_method.access_method, - }); + let error = + Error::ApiConnectionModeError(mullvad_api::access_mode::Error::Resolve { + access_method: access_method.access_method, + }); reply(Err(error)); return; } diff --git a/mullvad-jni/Cargo.toml b/mullvad-jni/Cargo.toml index 6af0c9c12b..65e47b26d9 100644 --- a/mullvad-jni/Cargo.toml +++ b/mullvad-jni/Cargo.toml @@ -12,7 +12,7 @@ workspace = true [features] # Allow the API server to use to be configured -api-override = ["mullvad-api/api-override"] +api-override = ["mullvad-api/api-override", "mullvad-daemon/api-override"] [lib] crate-type = ["cdylib"] |
