diff options
| author | David Lönnhager <david.l@mullvad.net> | 2025-03-24 15:47:04 +0100 |
|---|---|---|
| committer | David Lönnhager <david.l@mullvad.net> | 2025-03-25 10:37:04 +0100 |
| commit | 5b1f6858aceef7f80c12e620a586b0e6298c8bed (patch) | |
| tree | c2cae41a4ae0a7d5da47c6b97754db6d83540a48 /mullvad-api/src | |
| parent | 2094a39d7ec07d5c154f46cff82a3335d23af751 (diff) | |
| download | mullvadvpn-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.rs | 48 | ||||
| -rw-r--r-- | mullvad-api/src/rest.rs | 26 |
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(¶ms, 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)] |
