summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorMarkus Pettersson <markus.pettersson@mullvad.net>2023-12-22 12:55:36 +0100
committerMarkus Pettersson <markus.pettersson@mullvad.net>2024-01-08 09:03:52 +0100
commit10c990ee1d296970ecd60fbde0ce147ca922ec99 (patch)
tree1b32cb7780fb1ec32fa6468241838b2b43d5702f
parent1bcf0ef542af225747fcaa3a6fcebd0819d82f0c (diff)
downloadmullvadvpn-10c990ee1d296970ecd60fbde0ce147ca922ec99.tar.xz
mullvadvpn-10c990ee1d296970ecd60fbde0ce147ca922ec99.zip
Add new internal daemon event `AccessMethodEvent`
Add a new `InternalDaemonEvent` for announcing when the current API access method changes.
-rw-r--r--mullvad-cli/src/cmds/status.rs5
-rw-r--r--mullvad-daemon/src/api.rs441
-rw-r--r--mullvad-daemon/src/lib.rs26
-rw-r--r--mullvad-daemon/src/management_interface.rs12
-rw-r--r--mullvad-jni/src/jni_event_listener.rs6
-rw-r--r--mullvad-management-interface/proto/management_interface.proto1
-rw-r--r--mullvad-management-interface/src/client.rs6
-rw-r--r--mullvad-management-interface/src/types/conversions/access_method.rs10
8 files changed, 344 insertions, 163 deletions
diff --git a/mullvad-cli/src/cmds/status.rs b/mullvad-cli/src/cmds/status.rs
index 5bed82b4c0..15b0d10dfe 100644
--- a/mullvad-cli/src/cmds/status.rs
+++ b/mullvad-cli/src/cmds/status.rs
@@ -75,6 +75,11 @@ impl Status {
println!("Remove device event: {device:#?}");
}
}
+ DaemonEvent::NewAccessMethod(access_method) => {
+ if args.debug {
+ println!("New access method: {access_method:#?}");
+ }
+ }
}
}
Ok(())
diff --git a/mullvad-daemon/src/api.rs b/mullvad-daemon/src/api.rs
index efb8f3088d..6efec8112b 100644
--- a/mullvad-daemon/src/api.rs
+++ b/mullvad-daemon/src/api.rs
@@ -4,33 +4,123 @@
//! [`ApiConnectionMode`], which in turn is used by `mullvad-api` for
//! establishing connections when performing API requests.
#[cfg(target_os = "android")]
-use crate::{DaemonCommand, DaemonEventSender};
+use crate::DaemonCommand;
+use crate::DaemonEventSender;
use futures::{
- channel::{mpsc, oneshot},
- stream::unfold,
+ channel::{
+ mpsc,
+ oneshot::{self, Canceled},
+ },
Stream, StreamExt,
};
use mullvad_api::{
availability::ApiAvailabilityHandle,
proxy::{ApiConnectionMode, ProxyConfig},
- ApiEndpointUpdateCallback,
+ AddressCache,
};
use mullvad_relay_selector::RelaySelector;
use mullvad_types::access_method::{AccessMethod, AccessMethodSetting, BuiltInAccessMethod};
-use std::{
- path::PathBuf,
- sync::{Arc, Mutex, Weak},
-};
-#[cfg(target_os = "android")]
+use std::{net::SocketAddr, path::PathBuf};
use talpid_core::mpsc::Sender;
-use talpid_core::tunnel_state_machine::TunnelCommand;
-use talpid_types::net::{AllowedEndpoint, Endpoint};
+use talpid_types::net::{
+ proxy::{self, CustomProxy},
+ AllowedClients, AllowedEndpoint, Endpoint, TransportProtocol,
+};
pub enum Message {
- Get(ResponseTx<AccessMethodSetting>),
+ Get(ResponseTx<ResolvedConnectionMode>),
Set(ResponseTx<()>, AccessMethodSetting),
Next(ResponseTx<ApiConnectionMode>),
Update(ResponseTx<()>, Vec<AccessMethodSetting>),
+ Resolve(ResponseTx<ResolvedConnectionMode>, AccessMethodSetting),
+}
+
+/// A [`NewAccessMethodEvent`] is emitted when the active access method changes,
+/// which happens in any of the following two scenarios:
+///
+/// * When a [`mullvad_api::rest::RequestService`] requests a new
+/// [`ApiConnectionMode`] from the running [`AccessModeSelector`]. This will
+/// lead to a [`crate::InternalDaemonEvent::AccessMethodEvent`] being sent to
+/// the daemon, which in turn will notify all clients about the new access
+/// method.
+///
+/// * When testing if some [`AccessMethodSetting`] can be used to reach the
+/// Mullvad API. In this scenario, the currently active access method will
+/// temporarily change (approximately for the duration of 1 API call). Since
+/// this is just an internal test which should be opaque to any client, it
+/// should not produce any unwanted noise and as such it is *not* broadcasted
+/// after the daemon is done processing this [`NewAccessMethodEvent`].
+pub struct NewAccessMethodEvent {
+ /// The new active [`AccessMethodSetting`].
+ pub setting: AccessMethodSetting,
+ /// The endpoint which represents how to connect to the Mullvad API and
+ /// which clients are allowed to initiate such a connection.
+ pub endpoint: AllowedEndpoint,
+ /// If the daemon should notify clients about the new access method.
+ ///
+ /// Defaults to `true`.
+ pub announce: bool,
+}
+
+impl NewAccessMethodEvent {
+ /// Create a new [`NewAccessMethodEvent`] for the daemon to process. A
+ /// [`oneshot::Receiver`] can be used to await the daemon while it finishes
+ /// handling the new event.
+ pub fn new(setting: AccessMethodSetting, endpoint: AllowedEndpoint) -> NewAccessMethodEvent {
+ NewAccessMethodEvent {
+ setting,
+ endpoint,
+ announce: true,
+ }
+ }
+
+ /// Whether the daemon should notify clients about the new access method or
+ /// not.
+ ///
+ /// * If `announce` is set to `true` the daemon will broadcast this event to
+ /// clients.
+ /// * If `announce` is set to `false` the daemon will *not* broadcast this
+ /// event.
+ pub fn announce(mut self, announce: bool) -> Self {
+ self.announce = announce;
+ self
+ }
+
+ /// Send an internal daemon event which will punch a hole in the firewall
+ /// for the connection mode we are testing.
+ ///
+ /// Returns the channel on which the daemon will send a message over when it
+ /// is done applying the firewall changes.
+ pub(crate) async fn send(
+ self,
+ daemon_event_sender: DaemonEventSender<(NewAccessMethodEvent, oneshot::Sender<()>)>,
+ ) -> std::result::Result<(), Canceled> {
+ // 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.send((self, update_finished_tx));
+ // Wait for the daemon to finish processing `event`.
+ update_finished_rx.await
+ }
+}
+
+/// This struct represent a concrete API endpoint (in the form of an
+/// [`ApiConnectionMode`] and [`AllowedEndpoint`]) which has been derived from
+/// some [`AccessMethodSetting`] (most likely the currently active access
+/// method). These logically related values are sometimes useful to group
+/// together into one value, which is encoded by [`ResolvedConnectionMode`].
+#[derive(Clone)]
+pub struct ResolvedConnectionMode {
+ /// The connection strategy to be used by the `mullvad-api` crate when
+ /// initializing API requests.
+ pub connection_mode: ApiConnectionMode,
+ /// The actual endpoint of the Mullvad API and which clients should be
+ /// allowed to initialize a connection to this endpoint.
+ pub endpoint: AllowedEndpoint,
+ /// This is the [`AccessMethodSetting`] which resolved into
+ /// `connection_mode` and `endpoint`.
+ pub setting: AccessMethodSetting,
}
#[derive(err_derive::Error, Debug)]
@@ -52,6 +142,7 @@ impl std::fmt::Display for Message {
Message::Set(..) => f.write_str("Set"),
Message::Next(_) => f.write_str("Next"),
Message::Update(..) => f.write_str("Update"),
+ Message::Resolve(..) => f.write_str("Resolve"),
}
}
}
@@ -83,13 +174,11 @@ pub struct AccessModeSelectorHandle {
impl AccessModeSelectorHandle {
async fn send_command<T>(&self, make_cmd: impl FnOnce(ResponseTx<T>) -> Message) -> Result<T> {
let (tx, rx) = oneshot::channel();
- self.cmd_tx
- .unbounded_send(make_cmd(tx))
- .map_err(Error::SendFailed)?;
+ self.cmd_tx.unbounded_send(make_cmd(tx))?;
rx.await.map_err(Error::NotRunning)?
}
- pub async fn get_access_method(&self) -> Result<AccessMethodSetting> {
+ pub async fn get_current(&self) -> Result<ResolvedConnectionMode> {
self.send_command(Message::Get).await.map_err(|err| {
log::debug!("Failed to get current access method!");
err
@@ -114,6 +203,15 @@ impl AccessModeSelectorHandle {
})
}
+ pub async fn resolve(&self, setting: AccessMethodSetting) -> Result<ResolvedConnectionMode> {
+ self.send_command(|tx| Message::Resolve(tx, setting))
+ .await
+ .map_err(|err| {
+ log::error!("Failed to update new access methods!");
+ err
+ })
+ }
+
pub async fn next(&self) -> Result<ApiConnectionMode> {
self.send_command(Message::Next).await.map_err(|err| {
log::debug!("Failed while getting the next access method");
@@ -124,10 +222,10 @@ impl AccessModeSelectorHandle {
/// Convert this handle to a [`Stream`] of [`ApiConnectionMode`] from the
/// associated [`AccessModeSelector`].
///
- /// Practically converts the handle to a listener for when the
- /// currently valid connection modes changes.
+ /// Calling `next` on this stream will poll for the next access method,
+ /// which will be lazily produced (on-demand rather than speculatively).
pub fn into_stream(self) -> impl Stream<Item = ApiConnectionMode> {
- unfold(self, |handle| async move {
+ futures::stream::unfold(self, |handle| async move {
match handle.next().await {
Ok(connection_mode) => Some((connection_mode, handle)),
// End this stream in case of failure in `next`. `next` should
@@ -155,17 +253,22 @@ pub struct AccessModeSelector {
/// Used for selecting a Bridge when the `Mullvad Bridges` access method is used.
relay_selector: RelaySelector,
connection_modes: ConnectionModesIterator,
+ address_cache: AddressCache,
+ access_method_event_sender: DaemonEventSender<(NewAccessMethodEvent, oneshot::Sender<()>)>,
+ current: ResolvedConnectionMode,
}
impl AccessModeSelector {
- pub fn spawn(
+ pub(crate) async fn spawn(
cache_dir: PathBuf,
relay_selector: RelaySelector,
connection_modes: Vec<AccessMethodSetting>,
- ) -> AccessModeSelectorHandle {
+ access_method_event_sender: DaemonEventSender<(NewAccessMethodEvent, oneshot::Sender<()>)>,
+ address_cache: AddressCache,
+ ) -> Result<AccessModeSelectorHandle> {
let (cmd_tx, cmd_rx) = mpsc::unbounded();
- let connection_modes = match ConnectionModesIterator::new(connection_modes) {
+ let mut connection_modes = match ConnectionModesIterator::new(connection_modes) {
Ok(provider) => provider,
Err(Error::NoAccessMethods) | Err(_) => {
// No settings seem to have been found. Default to using the the
@@ -176,14 +279,25 @@ impl AccessModeSelector {
)
}
};
+
+ let initial_connection_mode = {
+ let next = connection_modes.next().ok_or(Error::NoAccessMethods)?;
+ Self::resolve_inner(next, &relay_selector, &address_cache).await
+ };
+
let selector = AccessModeSelector {
cmd_rx,
cache_dir,
relay_selector,
connection_modes,
+ address_cache,
+ access_method_event_sender,
+ current: initial_connection_mode,
};
+
tokio::spawn(selector.into_future());
- AccessModeSelectorHandle { cmd_tx }
+
+ Ok(AccessModeSelectorHandle { cmd_tx })
}
async fn into_future(mut self) {
@@ -192,8 +306,9 @@ impl AccessModeSelector {
let execution = match cmd {
Message::Get(tx) => self.on_get_access_method(tx),
Message::Set(tx, value) => self.on_set_access_method(tx, value),
- Message::Next(tx) => self.on_next_connection_mode(tx),
+ Message::Next(tx) => self.on_next_connection_mode(tx).await,
Message::Update(tx, values) => self.on_update_access_methods(tx, values),
+ Message::Resolve(tx, setting) => self.on_resolve_access_method(tx, setting).await,
};
match execution {
Ok(_) => (),
@@ -213,13 +328,8 @@ impl AccessModeSelector {
Ok(())
}
- fn on_get_access_method(&mut self, tx: ResponseTx<AccessMethodSetting>) -> Result<()> {
- let value = self.get_access_method();
- self.reply(tx, value)
- }
-
- fn get_access_method(&mut self) -> AccessMethodSetting {
- self.connection_modes.peek()
+ fn on_get_access_method(&mut self, tx: ResponseTx<ResolvedConnectionMode>) -> Result<()> {
+ self.reply(tx, self.current.clone())
}
fn on_set_access_method(
@@ -231,40 +341,55 @@ impl AccessModeSelector {
self.reply(tx, ())
}
+ /// Set the next access method to be returned by the [`Stream`] produced by
+ /// calling `into_stream`.
fn set_access_method(&mut self, value: AccessMethodSetting) {
self.connection_modes.set_access_method(value);
}
- fn on_next_connection_mode(&mut self, tx: ResponseTx<ApiConnectionMode>) -> Result<()> {
- let next = self.next_connection_mode();
- // Save the new connection mode to cache!
- {
- let cache_dir = self.cache_dir.clone();
- let next = next.clone();
- tokio::spawn(async move {
- if next.save(&cache_dir).await.is_err() {
- log::warn!(
- "Failed to save {connection_mode} to cache",
- connection_mode = next
- )
- }
- });
- }
+ async fn on_next_connection_mode(&mut self, tx: ResponseTx<ApiConnectionMode>) -> Result<()> {
+ let next = self.next_connection_mode().await?;
self.reply(tx, next)
}
- fn next_connection_mode(&mut self) -> ApiConnectionMode {
- let access_method = self
- .connection_modes
- .next()
- .map(|access_method_setting| access_method_setting.access_method)
- .unwrap_or(AccessMethod::from(BuiltInAccessMethod::Direct));
+ async fn next_connection_mode(&mut self) -> Result<ApiConnectionMode> {
+ let access_method = self.connection_modes.next().ok_or(Error::NoAccessMethods)?;
+ log::info!(
+ "A new API access method has been selected: {name}",
+ name = access_method.name
+ );
+ let resolved = self.resolve(access_method).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
+ // `NewAccessMethodEvent`, then we find ourselves in a deadlock.
+ // This can happen during daemon startup when spawning a new
+ // `MullvadRestHandle`, which will call and await `next` on a Stream
+ // created from this `AccessModeSelector` instance. As such, the
+ // completion channel is discarded in this instance.
+ let setting = resolved.setting.clone();
+ let endpoint = resolved.endpoint.clone();
+ let daemon_sender = self.access_method_event_sender.clone();
+ tokio::spawn(async move {
+ let _ = NewAccessMethodEvent::new(setting, endpoint)
+ .send(daemon_sender)
+ .await;
+ });
- let connection_mode = self.from(access_method);
- log::info!("New API connection mode selected: {connection_mode}");
- connection_mode
- }
+ // Save the new connection mode to cache!
+ let cache_dir = self.cache_dir.clone();
+ let new_connection_mode = resolved.connection_mode.clone();
+ tokio::spawn(async move {
+ if new_connection_mode.save(&cache_dir).await.is_err() {
+ log::warn!(
+ "Failed to save {connection_mode} to cache",
+ connection_mode = new_connection_mode
+ )
+ }
+ });
+ self.current = resolved;
+ Ok(self.current.connection_mode.clone())
+ }
fn on_update_access_methods(
&mut self,
tx: ResponseTx<()>,
@@ -278,51 +403,78 @@ impl AccessModeSelector {
self.connection_modes.update_access_methods(values)
}
- /// Ad-hoc version of [`std::convert::From::from`], but since some
- /// [`ApiConnectionMode`]s require extra logic/data from
- /// [`ApiConnectionModeProvider`] the standard [`std::convert::From`] trait
- /// can not be implemented.
- fn from(&mut self, access_method: AccessMethod) -> ApiConnectionMode {
- use talpid_types::net::proxy;
- match access_method {
- AccessMethod::BuiltIn(access_method) => match access_method {
- BuiltInAccessMethod::Direct => ApiConnectionMode::Direct,
- BuiltInAccessMethod::Bridge => self
- .relay_selector
- .get_bridge_forced()
- .and_then(|settings| match settings {
- proxy::CustomProxy::Shadowsocks(ss_settings) => {
- let ss_settings: proxy::Shadowsocks = proxy::Shadowsocks::new(
- ss_settings.endpoint,
- ss_settings.cipher,
- ss_settings.password,
- );
- Some(ApiConnectionMode::Proxied(ProxyConfig::Shadowsocks(
- ss_settings,
- )))
- }
- _ => {
- log::error!("Received unexpected proxy settings type");
- None
- }
- })
- .unwrap_or(ApiConnectionMode::Direct),
- },
- AccessMethod::Custom(access_method) => match access_method {
- proxy::CustomProxy::Shadowsocks(shadowsocks_config) => {
- ApiConnectionMode::Proxied(ProxyConfig::Shadowsocks(shadowsocks_config))
- }
- proxy::CustomProxy::Socks5Local(socks_config) => {
- ApiConnectionMode::Proxied(ProxyConfig::Socks5Local(socks_config))
- }
- proxy::CustomProxy::Socks5Remote(socks_config) => {
- ApiConnectionMode::Proxied(ProxyConfig::Socks5Remote(socks_config))
- }
- },
+ pub async fn on_resolve_access_method(
+ &mut self,
+ tx: ResponseTx<ResolvedConnectionMode>,
+ setting: AccessMethodSetting,
+ ) -> Result<()> {
+ let reply = self.resolve(setting).await;
+ self.reply(tx, reply)
+ }
+
+ async fn resolve(&mut self, access_method: AccessMethodSetting) -> ResolvedConnectionMode {
+ Self::resolve_inner(access_method, &self.relay_selector, &self.address_cache).await
+ }
+
+ async fn resolve_inner(
+ access_method: AccessMethodSetting,
+ relay_selector: &RelaySelector,
+ address_cache: &AddressCache,
+ ) -> ResolvedConnectionMode {
+ let connection_mode =
+ resolve_connection_mode(access_method.access_method.clone(), relay_selector);
+ let endpoint =
+ resolve_allowed_endpoint(&connection_mode, address_cache.get_address().await);
+ ResolvedConnectionMode {
+ connection_mode,
+ endpoint,
+ setting: access_method,
}
}
}
+/// Ad-hoc version of [`std::convert::From::from`], but since some
+/// [`ApiConnectionMode`]s require extra logic/data from
+/// [`ApiConnectionModeProvider`] the standard [`std::convert::From`] trait
+/// can not be implemented.
+fn resolve_connection_mode(
+ access_method: AccessMethod,
+ relay_selector: &RelaySelector,
+) -> ApiConnectionMode {
+ match access_method {
+ AccessMethod::BuiltIn(access_method) => match access_method {
+ BuiltInAccessMethod::Direct => ApiConnectionMode::Direct,
+ BuiltInAccessMethod::Bridge => relay_selector
+ .get_bridge_forced()
+ .and_then(|settings| match settings {
+ CustomProxy::Shadowsocks(settings) => Some(ApiConnectionMode::Proxied(
+ ProxyConfig::Shadowsocks(proxy::Shadowsocks::new(
+ settings.endpoint,
+ settings.cipher,
+ settings.password,
+ )),
+ )),
+ _ => {
+ log::error!("Received unexpected proxy settings type");
+ None
+ }
+ })
+ .unwrap_or(ApiConnectionMode::Direct),
+ },
+ AccessMethod::Custom(access_method) => match access_method {
+ CustomProxy::Shadowsocks(shadowsocks) => {
+ ApiConnectionMode::Proxied(ProxyConfig::Shadowsocks(shadowsocks))
+ }
+ CustomProxy::Socks5Local(socks) => {
+ ApiConnectionMode::Proxied(ProxyConfig::Socks5Local(socks))
+ }
+ CustomProxy::Socks5Remote(socks) => {
+ ApiConnectionMode::Proxied(ProxyConfig::Socks5Remote(socks))
+ }
+ },
+ }
+}
+
/// An iterator which will always produce an [`AccessMethod`].
///
/// Safety: It is always safe to [`unwrap`] after calling [`next`] on a
@@ -353,6 +505,7 @@ impl ConnectionModesIterator {
pub fn set_access_method(&mut self, next: AccessMethodSetting) {
self.next = Some(next);
}
+
/// Update the collection of [`AccessMethod`] which this iterator will
/// return.
pub fn update_access_methods(
@@ -376,11 +529,6 @@ impl ConnectionModesIterator {
Ok(Box::new(access_methods.into_iter().cycle()))
}
}
-
- /// Look at the currently active [`AccessMethod`]
- pub fn peek(&self) -> AccessMethodSetting {
- self.current.clone()
- }
}
impl Iterator for ConnectionModesIterator {
@@ -397,73 +545,44 @@ impl Iterator for ConnectionModesIterator {
}
}
-/// Notifies the tunnel state machine that the API (real or proxied) endpoint has
-/// changed. [ApiEndpointUpdaterHandle::callback()] creates a callback that may
-/// be passed to the `mullvad-api` runtime.
-pub(super) struct ApiEndpointUpdaterHandle {
- tunnel_cmd_tx: Arc<Mutex<Option<Weak<mpsc::UnboundedSender<TunnelCommand>>>>>,
+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 }
}
-impl ApiEndpointUpdaterHandle {
- pub fn new() -> Self {
- Self {
- tunnel_cmd_tx: Arc::new(Mutex::new(None)),
- }
- }
-
- pub fn set_tunnel_command_tx(&self, tunnel_cmd_tx: Weak<mpsc::UnboundedSender<TunnelCommand>>) {
- *self.tunnel_cmd_tx.lock().unwrap() = Some(tunnel_cmd_tx);
+#[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,
}
+}
- pub fn callback(&self) -> impl ApiEndpointUpdateCallback {
- let tunnel_tx = self.tunnel_cmd_tx.clone();
- move |allowed_endpoint: AllowedEndpoint| {
- let inner_tx = tunnel_tx.clone();
- async move {
- let tunnel_tx = if let Some(tunnel_tx) = { inner_tx.lock().unwrap().as_ref() }
- .and_then(|tx: &Weak<mpsc::UnboundedSender<TunnelCommand>>| tx.upgrade())
- {
- tunnel_tx
- } else {
- log::error!("Rejecting allowed endpoint: Tunnel state machine is not running");
- return false;
- };
- let (result_tx, result_rx) = oneshot::channel();
- let _ = tunnel_tx.unbounded_send(TunnelCommand::AllowEndpoint(
- allowed_endpoint.clone(),
- result_tx,
- ));
- // Wait for the firewall policy to be updated.
- let _ = result_rx.await;
- log::debug!(
- "API endpoint: {endpoint}",
- endpoint = allowed_endpoint.endpoint
- );
- true
- }
+#[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()
}
}
}
-pub(super) fn get_allowed_endpoint(endpoint: Endpoint) -> AllowedEndpoint {
- #[cfg(unix)]
- let clients = talpid_types::net::AllowedClients::Root;
- #[cfg(windows)]
- let clients = {
- 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()
- };
-
- AllowedEndpoint { endpoint, clients }
-}
-
pub(crate) fn forward_offline_state(
api_availability: ApiAvailabilityHandle,
mut offline_state_rx: mpsc::UnboundedReceiver<bool>,
diff --git a/mullvad-daemon/src/lib.rs b/mullvad-daemon/src/lib.rs
index f365496baf..f505ac94e4 100644
--- a/mullvad-daemon/src/lib.rs
+++ b/mullvad-daemon/src/lib.rs
@@ -27,6 +27,7 @@ pub mod version;
mod version_check;
use crate::target_state::PersistentTargetState;
+use api::NewAccessMethodEvent;
use device::{AccountEvent, PrivateAccountAndDevice, PrivateDeviceEvent};
use futures::{
channel::{mpsc, oneshot},
@@ -369,6 +370,11 @@ pub(crate) enum InternalDaemonEvent {
NewAppVersionInfo(AppVersionInfo),
/// Sent when a device is updated in any way (key rotation, login, logout, etc.).
DeviceEvent(AccountEvent),
+ /// Sent when access methods are changed in any way (new active access method).
+ AccessMethodEvent {
+ event: NewAccessMethodEvent,
+ endpoint_active_tx: oneshot::Sender<()>,
+ },
/// Handles updates from versions without devices.
DeviceMigrationEvent(Result<PrivateAccountAndDevice, device::Error>),
/// A geographical location has has been received from am.i.mullvad.net
@@ -408,6 +414,15 @@ impl From<AccountEvent> for InternalDaemonEvent {
}
}
+impl From<(NewAccessMethodEvent, oneshot::Sender<()>)> for InternalDaemonEvent {
+ fn from(event: (NewAccessMethodEvent, oneshot::Sender<()>)) -> Self {
+ InternalDaemonEvent::AccessMethodEvent {
+ event: event.0,
+ endpoint_active_tx: event.1,
+ }
+ }
+}
+
#[derive(Clone, Debug, Eq, PartialEq)]
enum DaemonExecutionState {
Running,
@@ -590,6 +605,9 @@ pub trait EventListener {
/// Notify that a device was revoked using `RemoveDevice`.
fn notify_remove_device_event(&self, event: RemoveDeviceEvent);
+
+ /// Notify that the api access method changed.
+ fn notify_new_access_method_event(&self, new_access_method: AccessMethodSetting);
}
pub struct Daemon<L: EventListener> {
@@ -692,6 +710,8 @@ where
cache_dir.clone(),
relay_selector.clone(),
connection_modes,
+ internal_event_tx.to_specialized_sender(),
+ )
);
let api_handle = api_runtime
@@ -962,6 +982,10 @@ where
self.handle_new_app_version_info(app_version_info);
}
DeviceEvent(event) => self.handle_device_event(event).await,
+ AccessMethodEvent {
+ event,
+ endpoint_active_tx,
+ } => self.handle_access_method_event(event, endpoint_active_tx),
DeviceMigrationEvent(event) => self.handle_device_migration_event(event),
LocationEvent(location_data) => self.handle_location_event(location_data),
#[cfg(windows)]
@@ -1317,6 +1341,8 @@ where
}
}
+ fn handle_access_method_event(&mut self, event: NewAccessMethodEvent) {}
+
fn handle_device_migration_event(
&mut self,
result: Result<PrivateAccountAndDevice, device::Error>,
diff --git a/mullvad-daemon/src/management_interface.rs b/mullvad-daemon/src/management_interface.rs
index 6985af685c..8b8d840cfb 100644
--- a/mullvad-daemon/src/management_interface.rs
+++ b/mullvad-daemon/src/management_interface.rs
@@ -1009,6 +1009,18 @@ impl EventListener for ManagementInterfaceEventBroadcaster {
)),
})
}
+
+ fn notify_new_access_method_event(
+ &self,
+ new_access_method: mullvad_types::access_method::AccessMethodSetting,
+ ) {
+ log::debug!("Broadcasting access method event");
+ self.notify(types::DaemonEvent {
+ event: Some(daemon_event::Event::NewAccessMethod(
+ types::AccessMethodSetting::from(new_access_method),
+ )),
+ })
+ }
}
impl ManagementInterfaceEventBroadcaster {
diff --git a/mullvad-jni/src/jni_event_listener.rs b/mullvad-jni/src/jni_event_listener.rs
index 2aeb8320e3..4567622975 100644
--- a/mullvad-jni/src/jni_event_listener.rs
+++ b/mullvad-jni/src/jni_event_listener.rs
@@ -7,6 +7,7 @@ use jnix::{
};
use mullvad_daemon::EventListener;
use mullvad_types::{
+ access_method::AccessMethodSetting,
device::{DeviceEvent, RemoveDeviceEvent},
relay_list::RelayList,
settings::Settings,
@@ -71,6 +72,11 @@ impl EventListener for JniEventListener {
fn notify_remove_device_event(&self, event: RemoveDeviceEvent) {
let _ = self.0.send(Event::RemoveDevice(event));
}
+
+ // TODO: Implement this function when API access methods is implemented in
+ // the Android app.
+ #[allow(dead_code, unused_variables)]
+ fn notify_new_access_method_event(&self, access_method: AccessMethodSetting) {}
}
struct JniEventHandler<'env> {
diff --git a/mullvad-management-interface/proto/management_interface.proto b/mullvad-management-interface/proto/management_interface.proto
index 268e70327d..6978e5a666 100644
--- a/mullvad-management-interface/proto/management_interface.proto
+++ b/mullvad-management-interface/proto/management_interface.proto
@@ -594,6 +594,7 @@ message DaemonEvent {
AppVersionInfo version_info = 4;
DeviceEvent device = 5;
RemoveDeviceEvent remove_device = 6;
+ AccessMethodSetting new_access_method = 7;
}
}
diff --git a/mullvad-management-interface/src/client.rs b/mullvad-management-interface/src/client.rs
index 85048e29e7..2c0ca2fecf 100644
--- a/mullvad-management-interface/src/client.rs
+++ b/mullvad-management-interface/src/client.rs
@@ -37,6 +37,7 @@ pub enum DaemonEvent {
AppVersionInfo(AppVersionInfo),
Device(DeviceEvent),
RemoveDevice(RemoveDeviceEvent),
+ NewAccessMethod(AccessMethodSetting),
}
impl TryFrom<types::daemon_event::Event> for DaemonEvent {
@@ -62,6 +63,11 @@ impl TryFrom<types::daemon_event::Event> for DaemonEvent {
types::daemon_event::Event::RemoveDevice(event) => RemoveDeviceEvent::try_from(event)
.map(DaemonEvent::RemoveDevice)
.map_err(Error::InvalidResponse),
+ types::daemon_event::Event::NewAccessMethod(event) => {
+ AccessMethodSetting::try_from(event)
+ .map(DaemonEvent::NewAccessMethod)
+ .map_err(Error::InvalidResponse)
+ }
}
}
}
diff --git a/mullvad-management-interface/src/types/conversions/access_method.rs b/mullvad-management-interface/src/types/conversions/access_method.rs
index 368528f5bc..a5aa84d374 100644
--- a/mullvad-management-interface/src/types/conversions/access_method.rs
+++ b/mullvad-management-interface/src/types/conversions/access_method.rs
@@ -252,14 +252,20 @@ mod data {
}
}
- impl From<Id> for proto::Uuid {
- fn from(value: Id) -> Self {
+ impl From<&Id> for proto::Uuid {
+ fn from(value: &Id) -> Self {
proto::Uuid {
value: value.to_string(),
}
}
}
+ impl From<Id> for proto::Uuid {
+ fn from(value: Id) -> Self {
+ proto::Uuid::from(&value)
+ }
+ }
+
impl TryFrom<proto::Uuid> for Id {
type Error = FromProtobufTypeError;