summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--mullvad-daemon/src/device/api.rs12
-rw-r--r--mullvad-daemon/src/device/mod.rs74
-rw-r--r--mullvad-daemon/src/device/service.rs4
3 files changed, 86 insertions, 4 deletions
diff --git a/mullvad-daemon/src/device/api.rs b/mullvad-daemon/src/device/api.rs
index 6e00b669ca..6cd46ccc48 100644
--- a/mullvad-daemon/src/device/api.rs
+++ b/mullvad-daemon/src/device/api.rs
@@ -1,5 +1,6 @@
use std::pin::Pin;
+use chrono::{DateTime, Utc};
use futures::{future::FusedFuture, Future};
use mullvad_types::{device::Device, wireguard::WireguardData};
@@ -34,6 +35,10 @@ 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 is_validating(&self) -> bool {
matches!(
&self.current_call,
@@ -41,6 +46,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 +97,7 @@ enum Call {
TimerKeyRotation(ApiCall<WireguardData>),
OneshotKeyRotation(ApiCall<WireguardData>),
Validation(ApiCall<Device>),
+ ExpiryCheck(ApiCall<DateTime<Utc>>),
}
impl futures::Future for Call {
@@ -110,6 +120,7 @@ impl futures::Future for Call {
Pin::new(call).poll(cx).map(ApiResult::Rotation)
}
Validation(call) => Pin::new(call).poll(cx).map(ApiResult::Validation),
+ ExpiryCheck(call) => Pin::new(call).poll(cx).map(ApiResult::ExpiryCheck),
}
}
}
@@ -118,4 +129,5 @@ pub(crate) enum ApiResult {
Login(Result<PrivateAccountAndDevice, Error>, ResponseTx<()>),
Rotation(Result<WireguardData, Error>),
Validation(Result<Device, Error>),
+ ExpiryCheck(Result<DateTime<Utc>, Error>),
}
diff --git a/mullvad-daemon/src/device/mod.rs b/mullvad-daemon/src/device/mod.rs
index 31ea46ea36..1eb5614f82 100644
--- a/mullvad-daemon/src/device/mod.rs
+++ b/mullvad-daemon/src/device/mod.rs
@@ -303,6 +303,7 @@ enum AccountManagerCommand {
RotateKey(ResponseTx<()>),
SetRotationInterval(RotationInterval, ResponseTx<()>),
ValidateDevice(ResponseTx<()>),
+ CheckExpiry(ResponseTx<DateTime<Utc>>),
Shutdown(oneshot::Sender<()>),
}
@@ -351,6 +352,10 @@ impl AccountManagerHandle {
.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
@@ -373,12 +378,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>>,
last_validation: Option<SystemTime>,
validation_requests: Vec<ResponseTx<()>>,
+ expiry_requests: Vec<ResponseTx<DateTime<Utc>>>,
rotation_requests: Vec<ResponseTx<()>>,
data_requests: Vec<ResponseTx<PrivateDeviceState>>,
}
@@ -403,12 +410,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![],
};
@@ -489,6 +498,9 @@ impl AccountManager {
Some(AccountManagerCommand::ValidateDevice(tx)) => {
self.handle_validation_request(tx, &mut current_api_call);
},
+ Some(AccountManagerCommand::CheckExpiry(tx)) => {
+ self.handle_expiry_request(tx, &mut current_api_call);
+ },
None => {
break;
@@ -539,6 +551,31 @@ impl AccountManager {
}
}
+ 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,
@@ -549,6 +586,7 @@ 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,
+ ExpiryCheck(data_response) => self.consume_expiry_result(data_response).await,
}
}
@@ -563,6 +601,27 @@ impl AccountManager {
Self::drain_requests(&mut self.data_requests, || Ok(data.clone()));
}
+ async fn consume_expiry_result(&mut self, response: Result<DateTime<Utc>, Error>) {
+ match response {
+ Ok(expiry) => {
+ Self::drain_requests(&mut self.expiry_requests, || Ok(expiry));
+ }
+ Err(Error::InvalidAccount) => {
+ self.invalidate_current_data(|| Error::InvalidAccount).await;
+ }
+ Err(Error::InvalidDevice) => {
+ self.invalidate_current_data(|| 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>,
@@ -646,11 +705,10 @@ 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);
}
}
}
@@ -661,12 +719,12 @@ impl AccountManager {
self.invalidate_current_data(|| 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()))
@@ -713,6 +771,7 @@ 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());
@@ -840,6 +899,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;
diff --git a/mullvad-daemon/src/device/service.rs b/mullvad-daemon/src/device/service.rs
index 90a44d5fe1..61cbe0e4bd 100644
--- a/mullvad-daemon/src/device/service.rs
+++ b/mullvad-daemon/src/device/service.rs
@@ -310,6 +310,10 @@ 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,
account_token: AccountToken,