summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorOskar Nyberg <oskar@mullvad.net>2021-01-07 10:36:44 +0100
committerOskar Nyberg <oskar@mullvad.net>2021-01-07 10:36:44 +0100
commit9fb0385ef16d6a76ffd6fe7be0d6267e43d0ac51 (patch)
tree48c7d3e78c9b452534af071d30203d7cf9dbf980
parent9cfad3027adea59fa26e4c610d5a0ad41e5743ed (diff)
parentf3c7e627f41fae59302de936c2ae8212300fbf1e (diff)
downloadmullvadvpn-9fb0385ef16d6a76ffd6fe7be0d6267e43d0ac51.tar.xz
mullvadvpn-9fb0385ef16d6a76ffd6fe7be0d6267e43d0ac51.zip
Merge branch 'version-check-platform-header'
-rw-r--r--CHANGELOG.md3
-rw-r--r--Cargo.lock10
-rw-r--r--Cargo.toml1
-rw-r--r--mullvad-daemon/Cargo.toml1
-rw-r--r--mullvad-daemon/src/version_check.rs10
-rw-r--r--mullvad-platform-metadata/Cargo.toml12
-rw-r--r--mullvad-platform-metadata/src/android.rs35
-rw-r--r--mullvad-platform-metadata/src/command.rs10
-rw-r--r--mullvad-platform-metadata/src/lib.rs17
-rw-r--r--mullvad-platform-metadata/src/linux.rs62
-rw-r--r--mullvad-platform-metadata/src/macos.rs18
-rw-r--r--mullvad-platform-metadata/src/windows.rs54
-rw-r--r--mullvad-problem-report/Cargo.toml5
-rw-r--r--mullvad-problem-report/src/metadata.rs155
-rw-r--r--mullvad-rpc/src/lib.rs22
-rw-r--r--mullvad-rpc/src/rest.rs7
16 files changed, 254 insertions, 168 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 2858f85f99..b72ba378c4 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -24,6 +24,9 @@ Line wrap the file at 100 chars. Th
## [Unreleased]
### Added
+- Add header containing OS version to version-check API call to enable OS specific compatibility and
+ vulnerability checks.
+
#### Android
- Allow to configure the tunnel to use custom DNS servers.
diff --git a/Cargo.lock b/Cargo.lock
index b7c07e91e2..cc29caeba4 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1243,6 +1243,7 @@ dependencies = [
"log-panics",
"mullvad-management-interface",
"mullvad-paths",
+ "mullvad-platform-metadata",
"mullvad-rpc",
"mullvad-types",
"nix 0.19.0",
@@ -1322,6 +1323,13 @@ dependencies = [
]
[[package]]
+name = "mullvad-platform-metadata"
+version = "0.1.0"
+dependencies = [
+ "rs-release",
+]
+
+[[package]]
name = "mullvad-problem-report"
version = "2020.8.0-beta2"
dependencies = [
@@ -1332,9 +1340,9 @@ dependencies = [
"err-derive",
"lazy_static",
"mullvad-paths",
+ "mullvad-platform-metadata",
"mullvad-rpc",
"regex",
- "rs-release",
"talpid-types",
"tokio",
"uuid",
diff --git a/Cargo.toml b/Cargo.toml
index f87705cf22..4d155d203a 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -7,6 +7,7 @@ members = [
"mullvad-problem-report",
"mullvad-jni",
"mullvad-paths",
+ "mullvad-platform-metadata",
"mullvad-types",
"mullvad-rpc",
# "mullvad-tests",
diff --git a/mullvad-daemon/Cargo.toml b/mullvad-daemon/Cargo.toml
index f26ed548c6..9698ab67cf 100644
--- a/mullvad-daemon/Cargo.toml
+++ b/mullvad-daemon/Cargo.toml
@@ -27,6 +27,7 @@ tokio = { version = "0.2", features = [ "fs", "rt-threaded", "stream", "sync" ]
uuid = { version = "0.8", features = ["v4"] }
mullvad-paths = { path = "../mullvad-paths" }
+mullvad-platform-metadata = { path = "../mullvad-platform-metadata" }
mullvad-types = { path = "../mullvad-types" }
mullvad-rpc = { path = "../mullvad-rpc" }
talpid-core = { path = "../talpid-core" }
diff --git a/mullvad-daemon/src/version_check.rs b/mullvad-daemon/src/version_check.rs
index d0895671ba..8f52f5694a 100644
--- a/mullvad-daemon/src/version_check.rs
+++ b/mullvad-daemon/src/version_check.rs
@@ -86,6 +86,7 @@ pub(crate) struct VersionUpdater {
cache_path: PathBuf,
update_sender: DaemonEventSender<AppVersionInfo>,
last_app_version_info: AppVersionInfo,
+ platform_version: String,
next_update_time: Instant,
show_beta_releases: bool,
rx: Option<mpsc::Receiver<bool>>,
@@ -116,6 +117,7 @@ impl VersionUpdater {
let version_proxy = AppVersionProxy::new(rpc_handle);
let cache_path = cache_dir.join(VERSION_INFO_FILENAME);
let (tx, rx) = mpsc::channel(1);
+ let platform_version = mullvad_platform_metadata::short_version();
(
Self {
@@ -123,6 +125,7 @@ impl VersionUpdater {
cache_path,
update_sender,
last_app_version_info,
+ platform_version,
next_update_time: Instant::now(),
show_beta_releases,
rx: Some(rx),
@@ -135,8 +138,13 @@ impl VersionUpdater {
&self,
) -> impl Future<Output = Result<mullvad_rpc::AppVersionResponse, Error>> + Send + 'static {
let version_proxy = self.version_proxy.clone();
+ let platform_version = self.platform_version.clone();
let download_future_factory = move || {
- let response = version_proxy.version_check(PRODUCT_VERSION.to_owned(), PLATFORM);
+ let response = version_proxy.version_check(
+ PRODUCT_VERSION.to_owned(),
+ PLATFORM,
+ platform_version.clone(),
+ );
response.map_err(Error::Download)
};
diff --git a/mullvad-platform-metadata/Cargo.toml b/mullvad-platform-metadata/Cargo.toml
new file mode 100644
index 0000000000..d1ba2f6a14
--- /dev/null
+++ b/mullvad-platform-metadata/Cargo.toml
@@ -0,0 +1,12 @@
+[package]
+name = "mullvad-platform-metadata"
+version = "0.1.0"
+authors = ["Mullvad VPN"]
+description = "Platform metadata detection functions"
+license = "GPL-3.0"
+edition = "2018"
+publish = false
+
+
+[target.'cfg(target_os = "linux")'.dependencies]
+rs-release = "0.1.7"
diff --git a/mullvad-platform-metadata/src/android.rs b/mullvad-platform-metadata/src/android.rs
new file mode 100644
index 0000000000..5573586808
--- /dev/null
+++ b/mullvad-platform-metadata/src/android.rs
@@ -0,0 +1,35 @@
+use std::collections::HashMap;
+
+mod command;
+use command::command_stdout_lossy;
+
+pub fn version() -> String {
+ let version = get_prop("ro.build.version.release").unwrap_or_else(|| "N/A".to_owned());
+ let api_level = get_prop("ro.build.version.sdk").unwrap_or_else(|| "N/A".to_owned());
+
+ let manufacturer =
+ get_prop("ro.product.manufacturer").unwrap_or_else(|| "Unknown brand".to_owned());
+ let product = get_prop("ro.product.model").unwrap_or_else(|| "Unknown model".to_owned());
+
+ format!(
+ "Android {} (API: {}) - {} {}",
+ version, api_level, manufacturer, product
+ )
+}
+
+pub fn short_version() -> String {
+ version()
+}
+
+pub fn extra_metadata() -> HashMap<String, String> {
+ let mut metadata = HashMap::new();
+ metadata.insert(
+ "abi".to_owned(),
+ get_prop("ro.product.cpu.abilist").unwrap_or_else(|| "N/A".to_owned()),
+ );
+ metadata
+}
+
+fn get_prop(property: &str) -> Option<String> {
+ command_stdout_lossy("getprop", &[property])
+}
diff --git a/mullvad-platform-metadata/src/command.rs b/mullvad-platform-metadata/src/command.rs
new file mode 100644
index 0000000000..87ed6c40a0
--- /dev/null
+++ b/mullvad-platform-metadata/src/command.rs
@@ -0,0 +1,10 @@
+use std::process::Command;
+
+/// Helper for getting stdout of some command as a String. Ignores the exit code of the command.
+pub fn command_stdout_lossy(cmd: &str, args: &[&str]) -> Option<String> {
+ Command::new(cmd)
+ .args(args)
+ .output()
+ .map(|output| String::from_utf8_lossy(&output.stdout).trim().to_string())
+ .ok()
+}
diff --git a/mullvad-platform-metadata/src/lib.rs b/mullvad-platform-metadata/src/lib.rs
new file mode 100644
index 0000000000..f4ba78eaeb
--- /dev/null
+++ b/mullvad-platform-metadata/src/lib.rs
@@ -0,0 +1,17 @@
+#[cfg(target_os = "linux")]
+#[path = "linux.rs"]
+mod imp;
+
+#[cfg(target_os = "macos")]
+#[path = "macos.rs"]
+mod imp;
+
+#[cfg(windows)]
+#[path = "windows.rs"]
+mod imp;
+
+#[cfg(target_os = "android")]
+#[path = "android.rs"]
+mod imp;
+
+pub use self::imp::{extra_metadata, short_version, version};
diff --git a/mullvad-platform-metadata/src/linux.rs b/mullvad-platform-metadata/src/linux.rs
new file mode 100644
index 0000000000..484d2eaaf8
--- /dev/null
+++ b/mullvad-platform-metadata/src/linux.rs
@@ -0,0 +1,62 @@
+mod command;
+use command::command_stdout_lossy;
+
+pub fn version() -> String {
+ // The OS version information is obtained first from the os-release file. If that
+ // information is incomplete or unavailable, an attempt is made to obtain the
+ // version information from the lsb_release command. If that fails, any partial
+ // information from os-release is used if available, or a fallback message if
+ // reading from the os-release file produced
+ // no version information.
+ let version = read_os_release_file().unwrap_or_else(|incomplete_info| {
+ parse_lsb_release().unwrap_or_else(|| {
+ incomplete_info.unwrap_or_else(|| String::from("[Failed to detect version]"))
+ })
+ });
+
+ format!("Linux {}", version)
+}
+
+pub fn short_version() -> String {
+ version()
+}
+
+fn read_os_release_file() -> Result<String, Option<String>> {
+ let mut os_release_info = rs_release::get_os_release().map_err(|_| None)?;
+ let os_name = os_release_info.remove("NAME");
+ let os_version = os_release_info.remove("VERSION");
+
+ if os_name.is_some() || os_version.is_some() {
+ let full_info_available = os_name.is_some() && os_version.is_some();
+
+ let gathered_info = format!(
+ "{} {}",
+ os_name.unwrap_or_else(|| "[unknown distribution]".to_owned()),
+ os_version.unwrap_or_else(|| "[unknown version]".to_owned())
+ );
+
+ if full_info_available {
+ Ok(gathered_info)
+ } else {
+ // Partial version information
+ Err(Some(gathered_info))
+ }
+ } else {
+ // No information was obtained
+ Err(None)
+ }
+}
+
+fn parse_lsb_release() -> Option<String> {
+ command_stdout_lossy("lsb_release", &["-ds"]).and_then(|output| {
+ if output.is_empty() {
+ None
+ } else {
+ Some(output)
+ }
+ })
+}
+
+pub fn extra_metadata() -> impl Iterator<Item = (String, String)> {
+ std::iter::empty()
+}
diff --git a/mullvad-platform-metadata/src/macos.rs b/mullvad-platform-metadata/src/macos.rs
new file mode 100644
index 0000000000..9ee496adb1
--- /dev/null
+++ b/mullvad-platform-metadata/src/macos.rs
@@ -0,0 +1,18 @@
+mod command;
+use command::command_stdout_lossy;
+
+pub fn version() -> String {
+ format!(
+ "macOS {}",
+ command_stdout_lossy("sw_vers", &["-productVersion"])
+ .unwrap_or(String::from("[Failed to detect version]"))
+ )
+}
+
+pub fn short_version() -> String {
+ version()
+}
+
+pub fn extra_metadata() -> impl Iterator<Item = (String, String)> {
+ std::iter::empty()
+}
diff --git a/mullvad-platform-metadata/src/windows.rs b/mullvad-platform-metadata/src/windows.rs
new file mode 100644
index 0000000000..41faf0099b
--- /dev/null
+++ b/mullvad-platform-metadata/src/windows.rs
@@ -0,0 +1,54 @@
+use std::collections::HashMap;
+
+mod command;
+use command::command_stdout_lossy;
+
+pub fn version() -> String {
+ let system_info = system_info();
+ let os_name = system_info.get("OS Name");
+ let os_version = system_info.get("OS Version");
+ let version = os_name.map(parse_version).unwrap_or(String::from("N/A"));
+ let full_version = os_version
+ .map(parse_full_version)
+ .unwrap_or(String::from("N/A"));
+ format!("Windows {} ({})", version, full_version)
+}
+
+pub fn short_version() -> String {
+ let system_info = system_info();
+ let os_name = system_info.get("OS Name");
+ let version = os_name.map(parse_version).unwrap_or(String::from("N/A"));
+ format!("Windows {}", version)
+}
+
+fn system_info() -> HashMap<String, String> {
+ let system_info =
+ command_stdout_lossy("systeminfo", &["/FO", "LIST"]).unwrap_or_else(String::new);
+
+ let mut info_map = HashMap::new();
+ system_info.lines().for_each(|line| {
+ let mut split = line.split(":");
+ if let Some(key) = split.next() {
+ if let Some(value) = split.next() {
+ info_map.insert(key.to_owned(), value.to_owned());
+ }
+ }
+ });
+
+ info_map
+}
+
+fn parse_version(os_name: &String) -> String {
+ os_name
+ .trim()
+ .trim_start_matches("Microsoft Windows ")
+ .to_owned()
+}
+
+fn parse_full_version(os_version: &String) -> String {
+ os_version.trim().to_owned()
+}
+
+pub fn extra_metadata() -> impl Iterator<Item = (String, String)> {
+ std::iter::empty()
+}
diff --git a/mullvad-problem-report/Cargo.toml b/mullvad-problem-report/Cargo.toml
index 79ae9b757d..18a749650f 100644
--- a/mullvad-problem-report/Cargo.toml
+++ b/mullvad-problem-report/Cargo.toml
@@ -18,6 +18,7 @@ uuid = { version = "0.8", features = ["v4"] }
tokio = { version = "0.2", features = [ "rt-core" ] }
mullvad-paths = { path = "../mullvad-paths" }
+mullvad-platform-metadata = { path = "../mullvad-platform-metadata" }
mullvad-rpc = { path = "../mullvad-rpc" }
talpid-types = { path = "../talpid-types" }
@@ -26,10 +27,6 @@ talpid-types = { path = "../talpid-types" }
duct = "0.13"
-[target.'cfg(target_os = "linux")'.dependencies]
-rs-release = "0.1.7"
-
-
[target.'cfg(windows)'.build-dependencies]
winres = "0.1"
winapi = "0.3"
diff --git a/mullvad-problem-report/src/metadata.rs b/mullvad-problem-report/src/metadata.rs
index 1e4d6c864d..0d7eecfb47 100644
--- a/mullvad-problem-report/src/metadata.rs
+++ b/mullvad-problem-report/src/metadata.rs
@@ -1,5 +1,4 @@
-use std::{collections::BTreeMap, process::Command};
-
+use std::collections::BTreeMap;
pub const PRODUCT_VERSION: &str = include_str!(concat!(env!("OUT_DIR"), "/product-version.txt"));
@@ -10,155 +9,7 @@ pub fn collect() -> BTreeMap<String, String> {
"mullvad-product-version".to_owned(),
PRODUCT_VERSION.to_owned(),
);
- metadata.insert("os".to_owned(), os::version());
- metadata.extend(os::extra_metadata());
+ metadata.insert("os".to_owned(), mullvad_platform_metadata::version());
+ metadata.extend(mullvad_platform_metadata::extra_metadata());
metadata
}
-
-#[cfg(target_os = "linux")]
-mod os {
- pub fn version() -> String {
- // The OS version information is obtained first from the os-release file. If that
- // information is incomplete or unavailable, an attempt is made to obtain the
- // version information from the lsb_release command. If that fails, any partial
- // information from os-release is used if available, or a fallback message if
- // reading from the os-release file produced
- // no version information.
- let version = read_os_release_file().unwrap_or_else(|incomplete_info| {
- parse_lsb_release().unwrap_or_else(|| {
- incomplete_info.unwrap_or_else(|| String::from("[Failed to detect version]"))
- })
- });
-
- format!("Linux {}", version)
- }
-
- fn read_os_release_file() -> Result<String, Option<String>> {
- let mut os_release_info = rs_release::get_os_release().map_err(|_| None)?;
- let os_name = os_release_info.remove("NAME");
- let os_version = os_release_info.remove("VERSION");
-
- if os_name.is_some() || os_version.is_some() {
- let full_info_available = os_name.is_some() && os_version.is_some();
-
- let gathered_info = format!(
- "{} {}",
- os_name.unwrap_or_else(|| "[unknown distribution]".to_owned()),
- os_version.unwrap_or_else(|| "[unknown version]".to_owned())
- );
-
- if full_info_available {
- Ok(gathered_info)
- } else {
- // Partial version information
- Err(Some(gathered_info))
- }
- } else {
- // No information was obtained
- Err(None)
- }
- }
-
- fn parse_lsb_release() -> Option<String> {
- super::command_stdout_lossy("lsb_release", &["-ds"]).and_then(|output| {
- if output.is_empty() {
- None
- } else {
- Some(output)
- }
- })
- }
-
- pub fn extra_metadata() -> impl Iterator<Item = (String, String)> {
- std::iter::empty()
- }
-}
-
-#[cfg(target_os = "macos")]
-mod os {
- pub fn version() -> String {
- format!(
- "macOS {}",
- super::command_stdout_lossy("sw_vers", &["-productVersion"])
- .unwrap_or(String::from("[Failed to detect version]"))
- )
- }
-
- pub fn extra_metadata() -> impl Iterator<Item = (String, String)> {
- std::iter::empty()
- }
-}
-
-#[cfg(windows)]
-mod os {
- pub fn version() -> String {
- let system_info =
- super::command_stdout_lossy("systeminfo", &["/FO", "LIST"]).unwrap_or_else(String::new);
-
- let mut version = None;
- let mut full_version = None;
-
- for info_line in system_info.lines() {
- let mut info_parts = info_line.split(":");
-
- match info_parts.next() {
- Some("OS Name") => {
- version = info_parts
- .next()
- .map(|s| s.trim().trim_start_matches("Microsoft Windows "))
- }
- Some("OS Version") => full_version = info_parts.next().map(str::trim),
- _ => {}
- }
- }
-
- let version = version.unwrap_or("N/A");
- let full_version = full_version.unwrap_or("N/A");
- format!("Windows {} ({})", version, full_version)
- }
-
- pub fn extra_metadata() -> impl Iterator<Item = (String, String)> {
- std::iter::empty()
- }
-}
-
-#[cfg(target_os = "android")]
-mod os {
- use std::collections::HashMap;
-
- pub fn version() -> String {
- let version = get_prop("ro.build.version.release").unwrap_or_else(|| "N/A".to_owned());
- let api_level = get_prop("ro.build.version.sdk").unwrap_or_else(|| "N/A".to_owned());
-
- let manufacturer =
- get_prop("ro.product.manufacturer").unwrap_or_else(|| "Unknown brand".to_owned());
- let product = get_prop("ro.product.model").unwrap_or_else(|| "Unknown model".to_owned());
-
- format!(
- "Android {} (API: {}) - {} {}",
- version, api_level, manufacturer, product
- )
- }
-
- pub fn extra_metadata() -> HashMap<String, String> {
- let mut metadata = HashMap::new();
- metadata.insert(
- "abi".to_owned(),
- get_prop("ro.product.cpu.abilist").unwrap_or_else(|| "N/A".to_owned()),
- );
- metadata
- }
-
- fn get_prop(property: &str) -> Option<String> {
- super::command_stdout_lossy("getprop", &[property])
- }
-}
-
-/// Helper for getting stdout of some command as a String. Ignores the exit code of the command.
-fn command_stdout_lossy(cmd: &str, args: &[&str]) -> Option<String> {
- Command::new(cmd)
- .args(args)
- .output()
- .map(|output| String::from_utf8_lossy(&output.stdout).trim().to_string())
- .ok()
-}
diff --git a/mullvad-rpc/src/lib.rs b/mullvad-rpc/src/lib.rs
index 4beefa7d3b..e211a89c2e 100644
--- a/mullvad-rpc/src/lib.rs
+++ b/mullvad-rpc/src/lib.rs
@@ -343,21 +343,23 @@ impl AppVersionProxy {
pub fn version_check(
&self,
- version: AppVersion,
+ app_version: AppVersion,
platform: &str,
+ platform_version: String,
) -> impl Future<Output = Result<AppVersionResponse, rest::Error>> {
let service = self.handle.service.clone();
- let request = rest::send_request(
- &self.handle.factory,
- service,
- &format!("/v1/releases/{}/{}", platform, version),
- Method::GET,
- None,
- StatusCode::OK,
- );
+ let path = format!("/v1/releases/{}/{}", platform, app_version);
+ let request = self.handle.factory.request(&path, Method::GET);
- async move { rest::deserialize_body(request.await?).await }
+ async move {
+ let mut request = request?;
+ request.add_header("M-Platform-Version", platform_version)?;
+
+ let response = service.request(request).await?;
+ let parsed_response = rest::parse_rest_response(response, StatusCode::OK).await?;
+ rest::deserialize_body(parsed_response).await
+ }
}
}
diff --git a/mullvad-rpc/src/rest.rs b/mullvad-rpc/src/rest.rs
index 12bc10263e..82a6271c74 100644
--- a/mullvad-rpc/src/rest.rs
+++ b/mullvad-rpc/src/rest.rs
@@ -319,6 +319,13 @@ impl RestRequest {
self.timeout
}
+ pub fn add_header(&mut self, key: &'static str, value: String) -> Result<()> {
+ let header_value =
+ http::HeaderValue::from_str(&value).map_err(Error::InvalidHeaderError)?;
+ self.request.headers_mut().insert(key, header_value);
+ Ok(())
+ }
+
/// Converts into a `hyper::Request<hyper::Body>`
fn into_request(self) -> Request {
let Self {