1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
|
use std::future::Future;
use std::sync::Arc;
use http::StatusCode;
use mullvad_update::version::{VersionInfo, VersionParameters};
type AppVersion = String;
use super::APP_URL_PREFIX;
use super::rest;
#[derive(Clone)]
pub struct AppVersionProxy {
handle: super::rest::MullvadRestHandle,
}
#[derive(serde::Deserialize, Debug)]
pub struct AppVersionResponse {
pub supported: bool,
pub latest: AppVersion,
pub latest_stable: Option<AppVersion>,
pub latest_beta: Option<AppVersion>,
}
/// Reply from `/app/releases/<platform>.json` endpoint
pub struct AppVersionResponse2 {
/// Information about available versions for the current target
pub version_info: VersionInfo,
/// Index of the metadata version used to sign the response.
/// Used to prevent replay/downgrade attacks.
pub metadata_version: usize,
}
impl AppVersionProxy {
/// Maximum size of `version_check_2` response
const SIZE_LIMIT: usize = 1024 * 1024;
pub fn new(handle: rest::MullvadRestHandle) -> Self {
Self { handle }
}
pub fn version_check(
&self,
app_version: AppVersion,
platform: &str,
platform_version: String,
) -> impl Future<Output = Result<AppVersionResponse, rest::Error>> + use<> {
let service = self.handle.service.clone();
let path = format!("{APP_URL_PREFIX}/releases/{platform}/{app_version}");
let request = self.handle.factory.get(&path);
async move {
let request = request?
.expected_status(&[StatusCode::OK])
.header("M-Platform-Version", &platform_version)?;
let response = service.request(request).await?;
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<AppVersionResponse2, rest::Error>> + use<> {
let service = self.handle.service.clone();
let path = format!("app/releases/{platform}.json");
let request = self.handle.factory.get(&path);
async move {
let request = request?.expected_status(&[StatusCode::OK]);
let response = service.request(request).await?;
let bytes = response.body_with_max_size(Self::SIZE_LIMIT).await?;
let response = mullvad_update::format::SignedResponse::deserialize_and_verify(
&bytes,
lowest_metadata_version,
)
.map_err(|err| rest::Error::FetchVersions(Arc::new(err)))?;
let params = VersionParameters {
architecture,
rollout,
// NOTE: On Linux, version metadata contains no installers
allow_empty: cfg!(target_os = "linux"),
lowest_metadata_version,
};
let metadata_version = response.signed.metadata_version;
Ok(AppVersionResponse2 {
version_info: VersionInfo::try_from_response(¶ms, response.signed)
.map_err(Arc::new)
.map_err(rest::Error::FetchVersions)?,
metadata_version,
})
}
}
}
|