diff options
| author | Markus Pettersson <markus.pettersson@mullvad.net> | 2024-01-09 10:41:08 +0100 |
|---|---|---|
| committer | Markus Pettersson <markus.pettersson@mullvad.net> | 2024-01-11 09:18:26 +0100 |
| commit | 8b0fd0d971e67df8415da1bdac558e366bfd7290 (patch) | |
| tree | e6b51f09eb39ca4a343c9d21880accb2903122f3 /mullvad-daemon/src | |
| parent | 75eb89c820f12d488a76934f59ba29fe999cf59c (diff) | |
| download | mullvadvpn-8b0fd0d971e67df8415da1bdac558e366bfd7290.tar.xz mullvadvpn-8b0fd0d971e67df8415da1bdac558e366bfd7290.zip | |
Synchronize `mullvad-api` and `mullvad-daemon` when the `api-override`
feature is enabled
Move the logic for using overridden API endpoints for API calls from
`mullvad-api::rest` to `mullvad_daemon::api`. This is in line with how
the interaction between the two crates work for a normal release build,
i.e. when the `api-override` feature is disabled.
This commit also removes references to `force_direct_connection` in the
Android code. The flag does not exist in the `mullvad-*` rust crates
anymore, so it would be erroneous to try to serialize/deserialize the
value from the Android client.
Diffstat (limited to 'mullvad-daemon/src')
| -rw-r--r-- | mullvad-daemon/src/api.rs | 118 | ||||
| -rw-r--r-- | mullvad-daemon/src/lib.rs | 76 |
2 files changed, 106 insertions, 88 deletions
diff --git a/mullvad-daemon/src/api.rs b/mullvad-daemon/src/api.rs index 3f6f1747a6..caf39ec874 100644 --- a/mullvad-daemon/src/api.rs +++ b/mullvad-daemon/src/api.rs @@ -32,65 +32,39 @@ pub enum Message { 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. +/// 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. /// - /// 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. + /// This happens when a [`mullvad_api::rest::RequestService`] requests a new + /// [`ApiConnectionMode`] from the running [`AccessModeSelector`]. + New { + /// The new active [`AccessMethodSetting`]. + setting: AccessMethodSetting, + /// The endpoint which represents how to connect to the Mullvad API and + /// which clients are allowed to initiate such a connection. + endpoint: AllowedEndpoint, + }, + /// Emitted when the the firewall should be updated. /// - /// * 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 - } + /// This is useful for example 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 [`AccessMethodEvent::Allow`]. + Allow { endpoint: AllowedEndpoint }, +} - /// 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. +impl AccessMethodEvent { pub(crate) async fn send( self, - daemon_event_sender: DaemonEventSender<(NewAccessMethodEvent, oneshot::Sender<()>)>, + daemon_event_sender: DaemonEventSender<(AccessMethodEvent, 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 @@ -251,7 +225,7 @@ pub struct AccessModeSelector { relay_selector: RelaySelector, connection_modes: ConnectionModesIterator, address_cache: AddressCache, - access_method_event_sender: DaemonEventSender<(NewAccessMethodEvent, oneshot::Sender<()>)>, + access_method_event_sender: DaemonEventSender<(AccessMethodEvent, oneshot::Sender<()>)>, current: ResolvedConnectionMode, } @@ -260,7 +234,7 @@ impl AccessModeSelector { cache_dir: PathBuf, relay_selector: RelaySelector, connection_modes: Vec<AccessMethodSetting>, - access_method_event_sender: DaemonEventSender<(NewAccessMethodEvent, oneshot::Sender<()>)>, + access_method_event_sender: DaemonEventSender<(AccessMethodEvent, oneshot::Sender<()>)>, address_cache: AddressCache, ) -> Result<AccessModeSelectorHandle> { let (cmd_tx, cmd_rx) = mpsc::unbounded(); @@ -348,6 +322,36 @@ impl AccessModeSelector { } async fn next_connection_mode(&mut self) -> Result<ApiConnectionMode> { + #[cfg(feature = "api-override")] + { + use mullvad_api::API; + // If the API address has been explicitly overridden, it should + // always be used. This implies that a direct API connection mode is + // used. + if API.address.is_some() { + log::debug!("API proxies are disabled"); + let endpoint = resolve_allowed_endpoint( + &ApiConnectionMode::Direct, + // Note that the address cache *should* be initialized with + // the overridden API endpoint, so we can simply fetch the + // endpoint address from it. + self.address_cache.get_address().await, + ); + let daemon_sender = self.access_method_event_sender.clone(); + tokio::spawn(async move { + let _ = AccessMethodEvent::Allow { endpoint } + .send(daemon_sender) + .await; + }); + return Ok(ApiConnectionMode::Direct); + } + + log::debug!( + "The `api-override` feature is enabled, but the API address \ + was not overridden. Selecting API access methods as normal" + ); + } + let access_method = self.connection_modes.next().ok_or(Error::NoAccessMethods)?; log::info!( "A new API access method has been selected: {name}", @@ -365,7 +369,7 @@ impl AccessModeSelector { let endpoint = resolved.endpoint.clone(); let daemon_sender = self.access_method_event_sender.clone(); tokio::spawn(async move { - let _ = NewAccessMethodEvent::new(setting, endpoint) + let _ = AccessMethodEvent::New { setting, endpoint } .send(daemon_sender) .await; }); diff --git a/mullvad-daemon/src/lib.rs b/mullvad-daemon/src/lib.rs index 06e289d22d..bb257ebc60 100644 --- a/mullvad-daemon/src/lib.rs +++ b/mullvad-daemon/src/lib.rs @@ -27,7 +27,7 @@ pub mod version; mod version_check; use crate::target_state::PersistentTargetState; -use api::NewAccessMethodEvent; +use api::AccessMethodEvent; use device::{AccountEvent, PrivateAccountAndDevice, PrivateDeviceEvent}; use futures::{ channel::{mpsc, oneshot}, @@ -374,7 +374,7 @@ pub(crate) enum InternalDaemonEvent { DeviceEvent(AccountEvent), /// Sent when access methods are changed in any way (new active access method). AccessMethodEvent { - event: NewAccessMethodEvent, + event: AccessMethodEvent, endpoint_active_tx: oneshot::Sender<()>, }, /// Handles updates from versions without devices. @@ -416,8 +416,8 @@ impl From<AccountEvent> for InternalDaemonEvent { } } -impl From<(NewAccessMethodEvent, oneshot::Sender<()>)> for InternalDaemonEvent { - fn from(event: (NewAccessMethodEvent, oneshot::Sender<()>)) -> Self { +impl From<(AccessMethodEvent, oneshot::Sender<()>)> for InternalDaemonEvent { + fn from(event: (AccessMethodEvent, oneshot::Sender<()>)) -> Self { InternalDaemonEvent::AccessMethodEvent { event: event.0, endpoint_active_tx: event.1, @@ -1341,28 +1341,39 @@ where fn handle_access_method_event( &mut self, - event: NewAccessMethodEvent, + event: AccessMethodEvent, endpoint_active_tx: oneshot::Sender<()>, ) { - // Update the firewall to exempt a new API endpoint. - let (completion_tx, completion_rx) = oneshot::channel(); - self.send_tunnel_command(TunnelCommand::AllowEndpoint(event.endpoint, completion_tx)); - // If the `NewAccessMethodEvent` should be announced to any client - // listening for updates of the currently active access method, we need - // to clone the handle to the broadcaster of such events. The - // announcement should be made after the firewall policy has been - // updated, since the new access method will be useless before then. - let event_listener = self.event_listener.clone(); - tokio::spawn(async move { - // Wait for the firewall policy to be updated. - let _ = completion_rx.await; - // Let the emitter of this event know that the firewall has been updated. - let _ = endpoint_active_tx.send(()); - // Notify clients about the change if necessary. - if event.announce { - event_listener.notify_new_access_method_event(event.setting); + match event { + AccessMethodEvent::Allow { endpoint } => { + let (completion_tx, completion_rx) = oneshot::channel(); + self.send_tunnel_command(TunnelCommand::AllowEndpoint(endpoint, completion_tx)); + tokio::spawn(async move { + // Wait for the firewall policy to be updated. + let _ = completion_rx.await; + // Let the emitter of this event know that the firewall has been updated. + let _ = endpoint_active_tx.send(()); + }); } - }); + AccessMethodEvent::New { setting, endpoint } => { + // 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)); + // Announce to all clients listening for updates of the + // currently active access method. The announcement should be + // made after the firewall policy has been updated, since the + // new access method will be useless before then. + let event_listener = self.event_listener.clone(); + tokio::spawn(async move { + // Wait for the firewall policy to be updated. + let _ = completion_rx.await; + // Let the emitter of this event know that the firewall has been updated. + let _ = endpoint_active_tx.send(()); + // Notify clients about the change if necessary. + event_listener.notify_new_access_method_event(setting); + }); + } + } } fn handle_device_migration_event( @@ -2496,10 +2507,11 @@ where let result = async move { // Send an internal daemon event which will punch a hole in the firewall // for the connection mode we are testing. - let _ = api::NewAccessMethodEvent::new(test_subject.setting, test_subject.endpoint) - .announce(false) - .send(daemon_event_sender.clone()) - .await; + let _ = api::AccessMethodEvent::Allow { + endpoint: test_subject.endpoint, + } + .send(daemon_event_sender.clone()) + .await; // Send a HEAD request to some Mullvad API endpoint. We issue a HEAD // request because we are *only* concerned with if we get a reply from @@ -2515,10 +2527,12 @@ where .get_current() .await .map_err(Error::ApiConnectionModeError)?; - let _ = api::NewAccessMethodEvent::new(active.setting, active.endpoint) - .announce(false) - .send(daemon_event_sender.clone()) - .await; + + let _ = api::AccessMethodEvent::Allow { + endpoint: active.endpoint, + } + .send(daemon_event_sender.clone()) + .await; result } |
