summaryrefslogtreecommitdiffhomepage
path: root/mullvad-api/src
diff options
context:
space:
mode:
authorDavid Lönnhager <david.l@mullvad.net>2025-03-24 15:47:04 +0100
committerDavid Lönnhager <david.l@mullvad.net>2025-03-25 10:37:04 +0100
commit5b1f6858aceef7f80c12e620a586b0e6298c8bed (patch)
treec2cae41a4ae0a7d5da47c6b97754db6d83540a48 /mullvad-api/src
parent2094a39d7ec07d5c154f46cff82a3335d23af751 (diff)
downloadmullvadvpn-5b1f6858aceef7f80c12e620a586b0e6298c8bed.tar.xz
mullvadvpn-5b1f6858aceef7f80c12e620a586b0e6298c8bed.zip
Add function for fetching version info from new endpoint to mullvad-api
Co-authored-by: Sebastian Holmin <sebastian.holmin@mullvad.net>
Diffstat (limited to 'mullvad-api/src')
-rw-r--r--mullvad-api/src/lib.rs48
-rw-r--r--mullvad-api/src/rest.rs26
2 files changed, 72 insertions, 2 deletions
diff --git a/mullvad-api/src/lib.rs b/mullvad-api/src/lib.rs
index d8980a17b5..86f83aa842 100644
--- a/mullvad-api/src/lib.rs
+++ b/mullvad-api/src/lib.rs
@@ -9,6 +9,7 @@ use mullvad_types::{
account::{AccountData, AccountNumber, VoucherSubmission},
version::AppVersion,
};
+use mullvad_update::version::{VersionInfo, VersionParameters};
use proxy::{ApiConnectionMode, ConnectionModeProvider};
use std::{
collections::BTreeMap,
@@ -19,6 +20,7 @@ use std::{
sync::Arc,
};
use talpid_types::ErrorExt;
+use vec1::vec1;
pub mod availability;
use availability::ApiAvailability;
@@ -712,6 +714,8 @@ pub struct AppVersionResponse {
}
impl AppVersionProxy {
+ const VERSION_PROVIDER_PUBKEY: &str = include_str!("../../mullvad-update/stagemole-pubkey");
+
pub fn new(handle: rest::MullvadRestHandle) -> Self {
Self { handle }
}
@@ -735,6 +739,50 @@ impl AppVersionProxy {
response.deserialize().await
}
}
+
+ /// Get versions from `/app/releases/<platform>.json`
+ pub fn version_check_2(
+ &self,
+ platform: &str,
+ architecture: mullvad_update::format::Architecture,
+ rollout: f32,
+ lowest_metadata_version: usize,
+ ) -> impl Future<Output = Result<VersionInfo, rest::Error>> + use<> {
+ // Maximum size of version response
+ const SIZE_LIMIT: usize = 1024 * 1024;
+
+ let service = self.handle.service.clone();
+ let path = format!("app/releases/{platform}.json");
+ let request = self.handle.factory.get(&path);
+
+ let verifying_key =
+ mullvad_update::format::key::VerifyingKey::from_hex(Self::VERSION_PROVIDER_PUBKEY)
+ .expect("valid key");
+ let verifying_keys = vec1![verifying_key];
+
+ async move {
+ let request = request?.expected_status(&[StatusCode::OK]);
+ let response = service.request(request).await?;
+ let bytes = response.body_with_max_size(SIZE_LIMIT).await?;
+
+ let response = mullvad_update::format::SignedResponse::deserialize_and_verify(
+ &verifying_keys,
+ &bytes,
+ lowest_metadata_version,
+ )
+ .map_err(|err| rest::Error::FetchVersions(Arc::new(err)))?;
+
+ let params = VersionParameters {
+ architecture,
+ rollout,
+ lowest_metadata_version,
+ };
+
+ VersionInfo::try_from_response(&params, response.signed)
+ .map_err(Arc::new)
+ .map_err(rest::Error::FetchVersions)
+ }
+ }
}
#[derive(Clone)]
diff --git a/mullvad-api/src/rest.rs b/mullvad-api/src/rest.rs
index bab6d8112a..94c43392ad 100644
--- a/mullvad-api/src/rest.rs
+++ b/mullvad-api/src/rest.rs
@@ -13,7 +13,7 @@ use futures::{
};
use http_body_util::{combinators::BoxBody, BodyExt, Empty, Full};
use hyper::{
- body::{Body, Bytes, Incoming},
+ body::{Body, Buf, Bytes, Incoming},
header::{self, HeaderValue},
Method, Uri,
};
@@ -73,6 +73,14 @@ pub enum Error {
#[error("Set account number on factory with no access token store")]
NoAccessTokenStore,
+
+ /// Failed to obtain versions
+ #[error("Failed to obtain versions")]
+ FetchVersions(#[from] Arc<anyhow::Error>),
+
+ /// Body exceeded size limit
+ #[error("Body exceeded size limit")]
+ BodyTooLarge,
}
impl From<Infallible> for Error {
@@ -493,7 +501,7 @@ pub struct Response<B> {
response: hyper::Response<B>,
}
-impl<B: Body> Response<B>
+impl<B: Body + Unpin> Response<B>
where
Error: From<<B as Body>::Error>,
{
@@ -516,6 +524,20 @@ where
pub async fn body(self) -> Result<Vec<u8>> {
Ok(BodyExt::collect(self.response).await?.to_bytes().to_vec())
}
+
+ pub async fn body_with_max_size(self, size_limit: usize) -> Result<Vec<u8>> {
+ let mut data: Vec<u8> = vec![];
+ let mut stream = self.response.into_data_stream();
+
+ while let Some(chunk) = stream.next().await {
+ data.extend(chunk?.chunk());
+ if data.len() > size_limit {
+ return Err(Error::BodyTooLarge);
+ }
+ }
+
+ Ok(data)
+ }
}
#[derive(serde::Deserialize)]