diff options
| -rw-r--r-- | CHANGELOG.md | 1 | ||||
| -rw-r--r-- | mullvad-daemon/src/device/api.rs | 40 | ||||
| -rw-r--r-- | mullvad-daemon/src/device/mod.rs | 208 | ||||
| -rw-r--r-- | mullvad-daemon/src/device/service.rs | 12 | ||||
| -rw-r--r-- | mullvad-daemon/src/lib.rs | 70 | ||||
| -rw-r--r-- | mullvad-daemon/src/management_interface.rs | 24 |
6 files changed, 283 insertions, 72 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index 853af3f5aa..5d18ade54e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -67,6 +67,7 @@ Line wrap the file at 100 chars. Th - Enable interface settings when app is logged out - Fix 'mullvad status -v' to include the port of the endpoint when connecting over TCP. - Check whether the device is valid when reconnecting from the error state. +- Stop reconnecting when the account has run out of time. #### Windows - Only use the most recent list of apps to split when resuming from hibernation/sleep if applying diff --git a/mullvad-daemon/src/device/api.rs b/mullvad-daemon/src/device/api.rs index 6e00b669ca..93dae78261 100644 --- a/mullvad-daemon/src/device/api.rs +++ b/mullvad-daemon/src/device/api.rs @@ -1,7 +1,8 @@ use std::pin::Pin; +use chrono::{DateTime, Utc}; use futures::{future::FusedFuture, Future}; -use mullvad_types::{device::Device, wireguard::WireguardData}; +use mullvad_types::{account::VoucherSubmission, device::Device, wireguard::WireguardData}; use super::{Error, PrivateAccountAndDevice, ResponseTx}; @@ -34,6 +35,18 @@ impl CurrentApiCall { self.current_call = Some(Call::Validation(validation)); } + pub fn set_expiry_check(&mut self, expiry_call: ApiCall<DateTime<Utc>>) { + self.current_call = Some(Call::ExpiryCheck(expiry_call)); + } + + pub fn set_voucher_submission( + &mut self, + voucher_call: ApiCall<VoucherSubmission>, + tx: ResponseTx<VoucherSubmission>, + ) { + self.current_call = Some(Call::VoucherSubmission(voucher_call, Some(tx))); + } + pub fn is_validating(&self) -> bool { matches!( &self.current_call, @@ -41,6 +54,10 @@ impl CurrentApiCall { ) } + pub fn is_checking_expiry(&self) -> bool { + matches!(&self.current_call, Some(Call::ExpiryCheck(_))) + } + pub fn is_running_timed_totation(&self) -> bool { matches!(&self.current_call, Some(Call::TimerKeyRotation(_))) } @@ -88,6 +105,11 @@ enum Call { TimerKeyRotation(ApiCall<WireguardData>), OneshotKeyRotation(ApiCall<WireguardData>), Validation(ApiCall<Device>), + VoucherSubmission( + ApiCall<VoucherSubmission>, + Option<ResponseTx<VoucherSubmission>>, + ), + ExpiryCheck(ApiCall<DateTime<Utc>>), } impl futures::Future for Call { @@ -110,6 +132,17 @@ impl futures::Future for Call { Pin::new(call).poll(cx).map(ApiResult::Rotation) } Validation(call) => Pin::new(call).poll(cx).map(ApiResult::Validation), + VoucherSubmission(call, tx) => { + if let std::task::Poll::Ready(response) = Pin::new(call).poll(cx) { + std::task::Poll::Ready(ApiResult::VoucherSubmission( + response, + tx.take().unwrap(), + )) + } else { + std::task::Poll::Pending + } + } + ExpiryCheck(call) => Pin::new(call).poll(cx).map(ApiResult::ExpiryCheck), } } } @@ -118,4 +151,9 @@ pub(crate) enum ApiResult { Login(Result<PrivateAccountAndDevice, Error>, ResponseTx<()>), Rotation(Result<WireguardData, Error>), Validation(Result<Device, Error>), + VoucherSubmission( + Result<VoucherSubmission, Error>, + ResponseTx<VoucherSubmission>, + ), + ExpiryCheck(Result<DateTime<Utc>, Error>), } diff --git a/mullvad-daemon/src/device/mod.rs b/mullvad-daemon/src/device/mod.rs index 886680fd39..b2b9976501 100644 --- a/mullvad-daemon/src/device/mod.rs +++ b/mullvad-daemon/src/device/mod.rs @@ -6,7 +6,7 @@ use futures::{ use mullvad_api::rest; use mullvad_types::{ - account::AccountToken, + account::{AccountToken, VoucherSubmission}, device::{ AccountAndDevice, Device, DeviceEvent, DeviceEventCause, DeviceId, DeviceName, DevicePort, DeviceState, @@ -56,6 +56,10 @@ pub enum Error { InvalidDevice, #[error(display = "Invalid account")] InvalidAccount, + #[error(display = "Invalid voucher code")] + InvalidVoucher, + #[error(display = "The voucher has already been used")] + UsedVoucher, #[error(display = "Failed to read or write device cache")] DeviceIoError(#[error(source)] io::Error), #[error(display = "Failed parse device cache")] @@ -221,6 +225,14 @@ impl From<PrivateDevice> for Device { } #[derive(Clone)] +pub(crate) enum AccountEvent { + /// Emitted when the device state changes. + Device(PrivateDeviceEvent), + /// Emitted when the account expiry is fetched. + Expiry(DateTime<Utc>), +} + +#[derive(Clone)] pub(crate) enum PrivateDeviceEvent { /// Logged in on a new device. Login(PrivateAccountAndDevice), @@ -243,10 +255,8 @@ impl From<PrivateDeviceEvent> for DeviceEvent { PrivateDeviceEvent::Updated(_) => DeviceEventCause::Updated, PrivateDeviceEvent::RotatedKey(_) => DeviceEventCause::RotatedKey, }; - DeviceEvent { - cause, - new_state: DeviceState::from(event.state()), - } + let new_state = DeviceState::from(event.state()); + DeviceEvent { cause, new_state } } } @@ -299,6 +309,8 @@ enum AccountManagerCommand { RotateKey(ResponseTx<()>), SetRotationInterval(RotationInterval, ResponseTx<()>), ValidateDevice(ResponseTx<()>), + SubmitVoucher(String, ResponseTx<VoucherSubmission>), + CheckExpiry(ResponseTx<DateTime<Utc>>), Shutdown(oneshot::Sender<()>), } @@ -347,6 +359,15 @@ impl AccountManagerHandle { .await } + pub async fn submit_voucher(&self, voucher: String) -> Result<VoucherSubmission, Error> { + self.send_command(move |tx| AccountManagerCommand::SubmitVoucher(voucher, tx)) + .await + } + + pub async fn check_expiry(&self) -> Result<DateTime<Utc>, Error> { + self.send_command(AccountManagerCommand::CheckExpiry).await + } + pub async fn shutdown(self) { let (tx, rx) = oneshot::channel(); let _ = self @@ -369,12 +390,14 @@ impl AccountManagerHandle { pub(crate) struct AccountManager { cacher: DeviceCacher, + account_service: AccountService, device_service: DeviceService, data: PrivateDeviceState, rotation_interval: RotationInterval, - listeners: Vec<Box<dyn Sender<PrivateDeviceEvent> + Send>>, + listeners: Vec<Box<dyn Sender<AccountEvent> + Send>>, last_validation: Option<SystemTime>, validation_requests: Vec<ResponseTx<()>>, + expiry_requests: Vec<ResponseTx<DateTime<Utc>>>, rotation_requests: Vec<ResponseTx<()>>, data_requests: Vec<ResponseTx<PrivateDeviceState>>, } @@ -386,7 +409,7 @@ impl AccountManager { rest_handle: rest::MullvadRestHandle, settings_dir: &Path, initial_rotation_interval: RotationInterval, - listener_tx: impl Sender<PrivateDeviceEvent> + Send + 'static, + listener_tx: impl Sender<AccountEvent> + Send + 'static, ) -> Result<(AccountManagerHandle, PrivateDeviceState), Error> { let (cacher, data) = DeviceCacher::new(settings_dir).await?; let token = data.device().map(|state| state.account_token.clone()); @@ -399,12 +422,14 @@ impl AccountManager { let device_service = DeviceService::new(rest_handle, api_availability); let manager = AccountManager { cacher, + account_service: account_service.clone(), device_service: device_service.clone(), data: data.clone(), rotation_interval: initial_rotation_interval, listeners: vec![Box::new(listener_tx)], last_validation: None, validation_requests: vec![], + expiry_requests: vec![], rotation_requests: vec![], data_requests: vec![], }; @@ -485,6 +510,12 @@ impl AccountManager { Some(AccountManagerCommand::ValidateDevice(tx)) => { self.handle_validation_request(tx, &mut current_api_call); }, + Some(AccountManagerCommand::SubmitVoucher(voucher, tx)) => { + self.handle_voucher_submission(tx, voucher, &mut current_api_call); + }, + Some(AccountManagerCommand::CheckExpiry(tx)) => { + self.handle_expiry_request(tx, &mut current_api_call); + }, None => { break; @@ -535,6 +566,59 @@ impl AccountManager { } } + fn handle_voucher_submission( + &mut self, + tx: ResponseTx<VoucherSubmission>, + voucher: String, + current_api_call: &mut api::CurrentApiCall, + ) { + if current_api_call.is_logging_in() { + let _ = tx.send(Err(Error::AccountChange)); + return; + } + + let create_submission = move || { + let old_config = self.data.device().ok_or(Error::NoDevice)?; + let account_token = old_config.account_token.clone(); + let account_service = self.account_service.clone(); + Ok(async move { account_service.submit_voucher(account_token, voucher).await }) + }; + + match create_submission() { + Ok(call) => { + current_api_call.set_voucher_submission(Box::pin(call), tx); + } + Err(err) => { + let _ = tx.send(Err(err)); + } + } + } + + fn handle_expiry_request( + &mut self, + tx: ResponseTx<DateTime<Utc>>, + current_api_call: &mut api::CurrentApiCall, + ) { + if current_api_call.is_logging_in() { + let _ = tx.send(Err(Error::AccountChange)); + return; + } + if current_api_call.is_checking_expiry() { + self.expiry_requests.push(tx); + return; + } + + match self.expiry_call() { + Ok(call) => { + current_api_call.set_expiry_check(Box::pin(call)); + self.expiry_requests.push(tx); + } + Err(err) => { + let _ = tx.send(Err(err)); + } + } + } + async fn consume_api_result( &mut self, result: api::ApiResult, @@ -545,6 +629,10 @@ impl AccountManager { Login(data, tx) => self.consume_login(data, tx).await, Rotation(rotation_response) => self.consume_rotation_result(rotation_response).await, Validation(data_response) => self.consume_validation(data_response, api_call).await, + VoucherSubmission(data_response, tx) => { + self.consume_voucher_result(data_response, tx).await + } + ExpiryCheck(data_response) => self.consume_expiry_result(data_response).await, } } @@ -559,6 +647,61 @@ impl AccountManager { Self::drain_requests(&mut self.data_requests, || Ok(data.clone())); } + async fn consume_voucher_result( + &mut self, + response: Result<VoucherSubmission, Error>, + tx: ResponseTx<VoucherSubmission>, + ) { + match &response { + Ok(submission) => { + // Send expiry update event + let event = AccountEvent::Expiry(submission.new_expiry); + self.listeners + .retain(|listener| listener.send(event.clone()).is_ok()); + } + Err(Error::InvalidAccount) => { + self.revoke_device(|| Error::InvalidAccount).await; + } + Err(Error::InvalidDevice) => { + self.revoke_device(|| Error::InvalidDevice).await; + } + Err(err) => log::error!("Failed to submit voucher: {}", err), + } + let _ = tx.send(response); + } + + async fn consume_expiry_result(&mut self, response: Result<DateTime<Utc>, Error>) { + match response { + Ok(expiry) => { + if expiry > chrono::Utc::now() { + log::debug!("Account has time left"); + } else { + log::debug!("Account has no time left"); + } + + // Send expiry update event + let event = AccountEvent::Expiry(expiry); + self.listeners + .retain(|listener| listener.send(event.clone()).is_ok()); + + Self::drain_requests(&mut self.expiry_requests, || Ok(expiry)); + } + Err(Error::InvalidAccount) => { + self.revoke_device(|| Error::InvalidAccount).await; + } + Err(Error::InvalidDevice) => { + self.revoke_device(|| Error::InvalidDevice).await; + } + Err(err) => { + log::error!("Failed to check account expiry: {}", err); + let cloneable_err = Arc::new(err); + Self::drain_requests(&mut self.expiry_requests, || { + Err(Error::ResponseFailure(cloneable_err.clone())) + }); + } + } + } + async fn consume_validation( &mut self, response: Result<Device, Error>, @@ -602,10 +745,10 @@ impl AccountManager { } } Err(Error::InvalidAccount) => { - self.invalidate_current_data(|| Error::InvalidAccount).await; + self.revoke_device(|| Error::InvalidAccount).await; } Err(Error::InvalidDevice) => { - self.invalidate_current_data(|| Error::InvalidDevice).await; + self.revoke_device(|| Error::InvalidDevice).await; } Err(err) => { log::error!("Failed to validate device: {}", err); @@ -642,27 +785,26 @@ impl AccountManager { match self.set(PrivateDeviceEvent::RotatedKey(config)).await { Ok(_) => { Self::drain_requests(&mut self.rotation_requests, || Ok(())); - Self::drain_requests(&mut self.validation_requests, || Ok(())); } Err(err) => { - self.drain_requests_with_err(err); + self.drain_device_requests_with_err(err); } } } Err(Error::InvalidAccount) => { - self.invalidate_current_data(|| Error::InvalidAccount).await; + self.revoke_device(|| Error::InvalidAccount).await; } Err(Error::InvalidDevice) => { - self.invalidate_current_data(|| Error::InvalidDevice).await; + self.revoke_device(|| Error::InvalidDevice).await; } Err(err) => { - self.drain_requests_with_err(err); + self.drain_device_requests_with_err(err); } } } - fn drain_requests_with_err(&mut self, err: Error) { + fn drain_device_requests_with_err(&mut self, err: Error) { let cloneable_err = Arc::new(err); Self::drain_requests(&mut self.rotation_requests, || { Err(Error::ResponseFailure(cloneable_err.clone())) @@ -696,7 +838,7 @@ impl AccountManager { }) } - async fn invalidate_current_data(&mut self, err_constructor: impl Fn() -> Error) { + async fn revoke_device(&mut self, err_constructor: impl Fn() -> Error) { log::debug!("Invalidating the current device"); if let Err(err) = self.cacher.write(&PrivateDeviceState::Revoked).await { @@ -709,9 +851,13 @@ impl AccountManager { Self::drain_requests(&mut self.validation_requests, || Err(err_constructor())); Self::drain_requests(&mut self.rotation_requests, || Err(err_constructor())); + Self::drain_requests(&mut self.expiry_requests, || Err(err_constructor())); - self.listeners - .retain(|listener| listener.send(PrivateDeviceEvent::Revoked).is_ok()); + self.listeners.retain(|listener| { + listener + .send(AccountEvent::Device(PrivateDeviceEvent::Revoked)) + .is_ok() + }); } async fn logout(&mut self, tx: ResponseTx<()>) { @@ -727,8 +873,11 @@ impl AccountManager { let old_config = self.data.logout(); - self.listeners - .retain(|listener| listener.send(PrivateDeviceEvent::Logout).is_ok()); + self.listeners.retain(|listener| { + listener + .send(AccountEvent::Device(PrivateDeviceEvent::Logout)) + .is_ok() + }); if let Some(old_config) = old_config { let logout_call = tokio::spawn(Box::pin(self.logout_api_call(old_config))); @@ -776,6 +925,7 @@ impl AccountManager { self.data = device_state; + let event = AccountEvent::Device(event); self.listeners .retain(|listener| listener.send(event.clone()).is_ok()); @@ -836,6 +986,13 @@ impl AccountManager { Ok(self.fetch_device_config(old_config)) } + fn expiry_call(&self) -> Result<impl Future<Output = Result<DateTime<Utc>, Error>>, Error> { + let old_config = self.data.device().ok_or(Error::NoDevice)?; + let account_token = old_config.account_token.clone(); + let account_service = self.account_service.clone(); + Ok(async move { account_service.check_expiry_2(account_token).await }) + } + fn needs_validation(&mut self) -> bool { if !self.data.logged_in() { return true; @@ -988,10 +1145,12 @@ impl TunnelStateChangeHandler { if !check_validity.swap(false, Ordering::SeqCst) { return; } - if let Err(error) = handle.validate_device().await { + if let Err(error) = Self::check_validity(handle).await { log::error!( "{}", - error.display_chain_with_msg("Failed to check device validity") + error.display_chain_with_msg( + "Failed to check device or account validity" + ) ); if error.is_network_error() || error.is_aborted() { check_validity.store(true, Ordering::SeqCst); @@ -1009,4 +1168,9 @@ impl TunnelStateChangeHandler { _ => (), } } + + pub async fn check_validity(handle: AccountManagerHandle) -> Result<(), Error> { + handle.validate_device().await?; + handle.check_expiry().await.map(|_expiry| ()) + } } diff --git a/mullvad-daemon/src/device/service.rs b/mullvad-daemon/src/device/service.rs index 90a44d5fe1..1f574f272f 100644 --- a/mullvad-daemon/src/device/service.rs +++ b/mullvad-daemon/src/device/service.rs @@ -310,11 +310,15 @@ impl AccountService { result } + pub async fn check_expiry_2(&self, token: AccountToken) -> Result<DateTime<Utc>, Error> { + self.check_expiry(token).await.map_err(map_rest_error) + } + pub async fn submit_voucher( - &mut self, + &self, account_token: AccountToken, voucher: String, - ) -> Result<VoucherSubmission, rest::Error> { + ) -> Result<VoucherSubmission, Error> { let mut proxy = self.proxy.clone(); let api_handle = self.api_availability.clone(); let result = retry_future_n( @@ -328,7 +332,7 @@ impl AccountService { self.initial_check_abort_handle.abort(); self.api_availability.resume_background(); } - result + result.map_err(map_rest_error) } } @@ -422,6 +426,8 @@ fn map_rest_error(error: rest::Error) -> Error { mullvad_api::DEVICE_NOT_FOUND => Error::InvalidDevice, mullvad_api::INVALID_ACCOUNT => Error::InvalidAccount, mullvad_api::MAX_DEVICES_REACHED => Error::MaxDevicesReached, + mullvad_api::INVALID_VOUCHER => Error::InvalidVoucher, + mullvad_api::VOUCHER_USED => Error::UsedVoucher, _ => Error::OtherRestError(error), }, error => Error::OtherRestError(error), diff --git a/mullvad-daemon/src/lib.rs b/mullvad-daemon/src/lib.rs index 238cfb86b0..49f27751cc 100644 --- a/mullvad-daemon/src/lib.rs +++ b/mullvad-daemon/src/lib.rs @@ -28,7 +28,7 @@ pub mod version; mod version_check; use crate::target_state::PersistentTargetState; -use device::{PrivateAccountAndDevice, PrivateDeviceEvent}; +use device::{AccountEvent, PrivateAccountAndDevice, PrivateDeviceEvent}; use futures::{ channel::{mpsc, oneshot}, future::{abortable, AbortHandle, Future, LocalBoxFuture}, @@ -126,6 +126,9 @@ pub enum Error { #[error(display = "Failed to update device")] UpdateDeviceError(#[error(source)] device::Error), + #[error(display = "Failed to submit voucher")] + VoucherSubmission(#[error(source)] device::Error), + #[cfg(target_os = "linux")] #[error(display = "Unable to initialize split tunneling")] InitSplitTunneling(#[error(source)] split_tunnel::Error), @@ -301,7 +304,7 @@ pub(crate) enum InternalDaemonEvent { /// The background job fetching new `AppVersionInfo`s got a new info object. NewAppVersionInfo(AppVersionInfo), /// Sent when a device is updated in any way (key rotation, login, logout, etc.). - DeviceEvent(PrivateDeviceEvent), + DeviceEvent(AccountEvent), /// Handles updates from versions without devices. DeviceMigrationEvent(Result<PrivateAccountAndDevice, device::Error>), /// The split tunnel paths or state were updated. @@ -333,8 +336,8 @@ impl From<AppVersionInfo> for InternalDaemonEvent { } } -impl From<PrivateDeviceEvent> for InternalDaemonEvent { - fn from(event: PrivateDeviceEvent) -> Self { +impl From<AccountEvent> for InternalDaemonEvent { + fn from(event: AccountEvent) -> Self { InternalDaemonEvent::DeviceEvent(event) } } @@ -892,6 +895,8 @@ where } if let ErrorStateCause::AuthFailed(_) = error_state.cause() { + // If time is added outside of the app, no notifications + // are received. So we must continually try to reconnect. self.schedule_reconnect(Duration::from_secs(60)) } } @@ -1039,9 +1044,9 @@ where self.event_listener.notify_app_version(app_version_info); } - async fn handle_device_event(&mut self, event: PrivateDeviceEvent) { + async fn handle_device_event(&mut self, event: AccountEvent) { match &event { - PrivateDeviceEvent::Login(device) => { + AccountEvent::Device(PrivateDeviceEvent::Login(device)) => { if let Err(error) = self.account_history.set(device.account_token.clone()).await { log::error!( "{}", @@ -1053,26 +1058,43 @@ where self.reconnect_tunnel(); } } - PrivateDeviceEvent::Logout => { + AccountEvent::Device(PrivateDeviceEvent::Logout) => { log::info!("Disconnecting because account token was cleared"); self.set_target_state(TargetState::Unsecured).await; } - PrivateDeviceEvent::Revoked => { + AccountEvent::Device(PrivateDeviceEvent::Revoked) => { // If we're currently in a secured state, reconnect to make sure we immediately // enter the error state. if *self.target_state == TargetState::Secured { self.connect_tunnel(); } } - PrivateDeviceEvent::RotatedKey(_) => { + AccountEvent::Device(PrivateDeviceEvent::RotatedKey(_)) => { if self.get_target_tunnel_type() == Some(TunnelType::Wireguard) { self.schedule_reconnect(WG_RECONNECT_DELAY); } } + AccountEvent::Expiry(expiry) if *self.target_state == TargetState::Secured => { + if expiry >= &chrono::Utc::now() { + if let TunnelState::Error(ref state) = self.tunnel_state { + if matches!(state.cause(), ErrorStateCause::AuthFailed(_)) { + log::debug!("Reconnecting since the account has time on it"); + self.connect_tunnel(); + } + } + } else if self.get_target_tunnel_type() == Some(TunnelType::Wireguard) { + log::debug!("Entering blocking state since the account is out of time"); + self.send_tunnel_command(TunnelCommand::Block(ErrorStateCause::AuthFailed( + None, + ))) + } + } _ => (), } - self.event_listener - .notify_device_event(DeviceEvent::from(event)); + if let AccountEvent::Device(event) = event { + self.event_listener + .notify_device_event(DeviceEvent::from(event)); + } } async fn handle_device_migration_event( @@ -1297,21 +1319,17 @@ where tx: ResponseTx<VoucherSubmission, Error>, voucher: String, ) { - if let Ok(Some(device)) = self.account_manager.data().await.map(|s| s.into_device()) { - let mut account = self.account_manager.account_service.clone(); - tokio::spawn(async move { - Self::oneshot_send( - tx, - account - .submit_voucher(device.account_token, voucher) - .await - .map_err(Error::RestError), - "submit_voucher response", - ); - }); - } else { - Self::oneshot_send(tx, Err(Error::NoAccountToken), "submit_voucher response"); - } + let manager = self.account_manager.clone(); + tokio::spawn(async move { + Self::oneshot_send( + tx, + manager + .submit_voucher(voucher) + .await + .map_err(Error::VoucherSubmission), + "submit_voucher response", + ); + }); } fn on_get_relay_locations(&mut self, tx: oneshot::Sender<RelayList>) { diff --git a/mullvad-daemon/src/management_interface.rs b/mullvad-daemon/src/management_interface.rs index 68ceed4c16..bac3ed0f93 100644 --- a/mullvad-daemon/src/management_interface.rs +++ b/mullvad-daemon/src/management_interface.rs @@ -493,10 +493,7 @@ impl ManagementService for ManagementServiceImpl { }), }) }) - .map_err(|error| match error { - crate::Error::RestError(error) => map_rest_voucher_error(error), - error => map_daemon_error(error), - }) + .map_err(map_daemon_error) } // Device management @@ -954,6 +951,7 @@ fn map_daemon_error(error: crate::Error) -> Status { DaemonError::ListDevicesError(error) => map_device_error(&error), DaemonError::RemoveDeviceError(error) => map_device_error(&error), DaemonError::UpdateDeviceError(error) => map_device_error(&error), + DaemonError::VoucherSubmission(error) => map_device_error(&error), #[cfg(windows)] DaemonError::SplitTunnelError(error) => map_split_tunnel_error(error), DaemonError::AccountHistory(error) => map_account_history_error(error), @@ -981,22 +979,6 @@ fn map_split_tunnel_error(error: talpid_core::split_tunnel::Error) -> Status { } } -/// Converts a REST API voucher error into a tonic status. -fn map_rest_voucher_error(error: RestError) -> Status { - match error { - RestError::ApiError(StatusCode::BAD_REQUEST, message) => match &message.as_str() { - &mullvad_api::INVALID_VOUCHER => Status::new(Code::NotFound, INVALID_VOUCHER_MESSAGE), - - &mullvad_api::VOUCHER_USED => { - Status::new(Code::ResourceExhausted, USED_VOUCHER_MESSAGE) - } - - error => Status::unknown(format!("Voucher error: {}", error)), - }, - error => map_rest_error(&error), - } -} - /// Converts a REST API error into a tonic status. fn map_rest_error(error: &RestError) -> Status { match error { @@ -1034,6 +1016,8 @@ fn map_device_error(error: &device::Error) -> Status { device::Error::InvalidDevice | device::Error::NoDevice => { Status::new(Code::NotFound, error.to_string()) } + device::Error::InvalidVoucher => Status::new(Code::NotFound, INVALID_VOUCHER_MESSAGE), + device::Error::UsedVoucher => Status::new(Code::ResourceExhausted, USED_VOUCHER_MESSAGE), device::Error::DeviceIoError(ref _error) => { Status::new(Code::Unavailable, error.to_string()) } |
