summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorBug Magnet <marco.nikic@mullvad.net>2025-03-07 16:13:46 +0100
committerBug Magnet <marco.nikic@mullvad.net>2025-03-17 16:51:50 +0100
commit6e46e26f12e0003c0e20ff8840e47689d6722806 (patch)
tree74db81c6d452d4c275076ad9390ce63dd0e4ed0a
parent73da8abf9b25e1b1327e017c83a0ab2c519c632e (diff)
downloadmullvadvpn-6e46e26f12e0003c0e20ff8840e47689d6722806.tar.xz
mullvadvpn-6e46e26f12e0003c0e20ff8840e47689d6722806.zip
Fix building for Android, rename api to access_mode in mullvad-api
-rw-r--r--Cargo.lock1
-rw-r--r--mullvad-api/Cargo.toml1
-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.rs79
-rw-r--r--mullvad-api/src/lib.rs2
-rw-r--r--mullvad-api/src/proxy.rs6
-rw-r--r--mullvad-daemon/src/access_method.rs17
-rw-r--r--mullvad-daemon/src/api.rs170
-rw-r--r--mullvad-daemon/src/lib.rs63
-rw-r--r--mullvad-jni/Cargo.toml2
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"]