summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--mullvad-types/src/settings/migrations/mod.rs68
-rw-r--r--mullvad-types/src/settings/migrations/v1.rs170
-rw-r--r--mullvad-types/src/settings/mod.rs11
3 files changed, 83 insertions, 166 deletions
diff --git a/mullvad-types/src/settings/migrations/mod.rs b/mullvad-types/src/settings/migrations/mod.rs
index 078a6498f9..a732848a7b 100644
--- a/mullvad-types/src/settings/migrations/mod.rs
+++ b/mullvad-types/src/settings/migrations/mod.rs
@@ -1,15 +1,16 @@
-use super::{Error, Result, Settings};
+use super::{Error, Result};
use serde::{Deserialize, Deserializer, Serialize, Serializer};
-use std::io::Read;
mod v1;
-#[derive(Debug, PartialEq, Clone, Copy)]
+#[derive(Debug, PartialEq, PartialOrd, Clone, Copy)]
#[repr(u32)]
pub enum SettingsVersion {
V2 = 2,
}
+pub const CURRENT_SETTINGS_VERSION: SettingsVersion = SettingsVersion::V2;
+
impl<'de> Deserialize<'de> for SettingsVersion {
fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
where
@@ -35,66 +36,33 @@ impl Serialize for SettingsVersion {
}
-#[derive(Debug)]
-enum VersionedSettings {
- V1(v1::Settings),
- V2(crate::settings::Settings),
-}
-
-impl VersionedSettings {
- /// Unrwaps the latest version of settings or panics.
- fn unwrap(self) -> Settings {
- match self {
- VersionedSettings::V2(settings) => settings,
- lower => {
- panic!("Unexpected settings version - {:?}", lower);
- }
- }
- }
-}
-
-
trait SettingsMigration {
- fn read(&self, reader: &mut dyn Read) -> Result<VersionedSettings>;
- fn migrate(&self, settings: VersionedSettings) -> VersionedSettings;
-}
-
-fn migrations() -> Vec<Box<dyn SettingsMigration>> {
- vec![Box::new(v1::Migration)]
+ fn version_matches(&self, settings: &mut serde_json::Value) -> bool;
+ fn migrate(&self, settings: &mut serde_json::Value) -> Result<()>;
}
pub fn try_migrate_settings(mut settings_file: &[u8]) -> Result<crate::settings::Settings> {
- let mut migrations_to_apply = vec![];
- let mut valid_settings = None;
+ let mut settings: serde_json::Value =
+ serde_json::from_reader(&mut settings_file).map_err(Error::ParseError)?;
- let migrations = migrations();
- for migration in migrations.iter() {
- match migration.read(&mut settings_file) {
- Ok(settings) => {
- valid_settings = Some(migration.migrate(settings));
- break;
- }
- Err(_e) => {
- migrations_to_apply.push(migration);
- }
- };
+ if !settings.is_object() {
+ return Err(Error::NoMatchingVersion);
}
- if let Some(settings) = valid_settings {
- let upgraded_settings = migrations_to_apply
- .iter()
- .rev()
- .fold(settings, |old_settings, migration| {
- migration.migrate(old_settings)
- });
- return Ok(upgraded_settings.unwrap());
+ for migration in &[Box::new(v1::Migration)] {
+ if !migration.version_matches(&mut settings) {
+ continue;
+ }
+ migration.migrate(&mut settings)?;
}
- return Err(Error::NoMatchingVersion);
+
+ serde_json::from_value(settings).map_err(Error::ParseError)
}
#[cfg(test)]
mod test {
use super::SettingsVersion;
+ use serde_json;
#[test]
#[should_panic]
diff --git a/mullvad-types/src/settings/migrations/v1.rs b/mullvad-types/src/settings/migrations/v1.rs
index 3b4f1a2075..033c25a580 100644
--- a/mullvad-types/src/settings/migrations/v1.rs
+++ b/mullvad-types/src/settings/migrations/v1.rs
@@ -1,78 +1,33 @@
-use super::{Error, Result, VersionedSettings};
+use super::{Error, Result};
use crate::{
custom_tunnel::CustomTunnelEndpoint,
relay_constraints::{
- BridgeConstraints, BridgeSettings, BridgeState, Constraint, LocationConstraint,
- OpenVpnConstraints, RelaySettings as NewRelaySettings, WireguardConstraints,
+ Constraint, LocationConstraint, OpenVpnConstraints, RelaySettings as NewRelaySettings,
+ WireguardConstraints,
},
- settings::TunnelOptions,
};
use serde::{Deserialize, Serialize};
-use std::io::Read;
use talpid_types::net::TunnelType;
-/// Mullvad daemon settings.
-#[derive(Debug, Clone, Deserialize, Serialize)]
-#[serde(default)]
-pub struct Settings {
- account_token: Option<String>,
- relay_settings: RelaySettings,
- bridge_settings: BridgeSettings,
- bridge_state: BridgeState,
- /// If the daemon should allow communication with private (LAN) networks.
- allow_lan: bool,
- /// Extra level of kill switch. When this setting is on, the disconnected state will block
- /// the firewall to not allow any traffic in or out.
- block_when_disconnected: bool,
- /// If the daemon should connect the VPN tunnel directly on start or not.
- auto_connect: bool,
- /// Options that should be applied to tunnels of a specific type regardless of where the relays
- /// might be located.
- tunnel_options: TunnelOptions,
-}
-
-impl Default for Settings {
- fn default() -> Self {
- Settings {
- account_token: None,
- relay_settings: RelaySettings::Normal(RelayConstraints {
- location: Constraint::Only(LocationConstraint::Country("se".to_owned())),
- tunnel: Constraint::Any,
- }),
- bridge_settings: BridgeSettings::Normal(BridgeConstraints::default()),
- bridge_state: BridgeState::Auto,
- allow_lan: false,
- block_when_disconnected: false,
- auto_connect: false,
- tunnel_options: TunnelOptions::default(),
- }
- }
-}
-
pub(super) struct Migration;
+
impl super::SettingsMigration for Migration {
- fn read(&self, mut reader: &mut dyn Read) -> Result<VersionedSettings> {
- serde_json::from_reader(&mut reader)
- .map(VersionedSettings::V1)
- .map_err(Error::ParseError)
+ fn version_matches(&self, settings: &mut serde_json::Value) -> bool {
+ settings.get("settings_version").is_none()
}
- fn migrate(&self, old: VersionedSettings) -> VersionedSettings {
- match old {
- VersionedSettings::V1(old) => VersionedSettings::V2(crate::settings::Settings {
- account_token: old.account_token,
- relay_settings: migrate_relay_settings(old.relay_settings),
- bridge_settings: old.bridge_settings,
- bridge_state: old.bridge_state,
- allow_lan: old.allow_lan,
- block_when_disconnected: old.block_when_disconnected,
- auto_connect: old.auto_connect,
- tunnel_options: old.tunnel_options,
- show_beta_releases: false,
- settings_version: super::SettingsVersion::V2,
- }),
- VersionedSettings::V2(new) => VersionedSettings::V2(new),
- }
+
+ fn migrate(&self, settings: &mut serde_json::Value) -> Result<()> {
+ let old_relay_settings: RelaySettings =
+ serde_json::from_value(settings["relay_settings"].clone())
+ .map_err(Error::ParseError)?;
+ let new_relay_settings = migrate_relay_settings(old_relay_settings);
+
+ settings["relay_settings"] = serde_json::json!(new_relay_settings);
+ settings["show_beta_releases"] = serde_json::json!(false);
+ settings["settings_version"] = serde_json::json!(super::SettingsVersion::V2);
+
+ Ok(())
}
}
@@ -124,9 +79,10 @@ pub enum TunnelConstraints {
#[cfg(test)]
mod test {
- use super::super::SettingsMigration;
+ use super::super::try_migrate_settings;
use serde_json;
- const OLD_SETTINGS: &str = r#"
+
+ pub const NEW_SETTINGS: &str = r#"
{
"account_token": "1234",
"relay_settings": {
@@ -136,16 +92,16 @@ mod test {
"country": "se"
}
},
- "tunnel": {
- "only": {
- "openvpn": {
- "port": {
- "only": 53
- },
- "protocol": {
- "only": "udp"
- }
- }
+ "tunnel_protocol": "any",
+ "wireguard_constraints": {
+ "port": "any"
+ },
+ "openvpn_constraints": {
+ "port": {
+ "only": 53
+ },
+ "protocol": {
+ "only": "udp"
}
}
}
@@ -169,11 +125,12 @@ mod test {
"generic": {
"enable_ipv6": false
}
- }
+ },
+ "settings_version": 2
}
"#;
- const NEW_SETTINGS: &str = r#"
+ const V1_SETTINGS: &str = r#"
{
"account_token": "1234",
"relay_settings": {
@@ -183,16 +140,16 @@ mod test {
"country": "se"
}
},
- "tunnel_protocol": "any",
- "wireguard_constraints": {
- "port": "any"
- },
- "openvpn_constraints": {
- "port": {
- "only": 53
- },
- "protocol": {
- "only": "udp"
+ "tunnel": {
+ "only": {
+ "openvpn": {
+ "port": {
+ "only": 53
+ },
+ "protocol": {
+ "only": "udp"
+ }
+ }
}
}
}
@@ -216,12 +173,11 @@ mod test {
"generic": {
"enable_ipv6": false
}
- },
- "settings_version": 2
+ }
}
"#;
- const SETTINGS_2019V3: &str = r#"
+ const V1_SETTINGS_2019V3: &str = r#"
{
"account_token": "1234",
"relay_settings": {
@@ -265,34 +221,20 @@ mod test {
"#;
#[test]
- fn test_migration() {
- let m = super::Migration;
- let old_settings = m
- .read(&mut OLD_SETTINGS.as_bytes())
- .expect("Failed to deserialize old format");
- let new_settings = serde_json::from_str(&NEW_SETTINGS).unwrap();
+ fn test_v1_migration() {
+ let migrated_settings =
+ try_migrate_settings(V1_SETTINGS.as_bytes()).expect("Migration failed");
+ let new_settings = serde_json::from_str(NEW_SETTINGS).unwrap();
- assert_eq!(&m.migrate(old_settings).unwrap(), &new_settings);
+ assert_eq!(&migrated_settings, &new_settings);
}
#[test]
- #[should_panic]
- fn test_deserialization_failure() {
- let m = super::Migration;
- m.read(&mut NEW_SETTINGS.as_bytes())
- .expect("Failed to deserialize old format");
- }
-
- #[test]
- fn test_2019v3_migration() {
- let m = super::Migration;
- let old_settings = m
- .read(&mut SETTINGS_2019V3.as_bytes())
- .expect("Failed to deserialize old format");
-
- let new_settings = serde_json::from_str(&NEW_SETTINGS).unwrap();
-
+ fn test_v1_2019v3_migration() {
+ let migrated_settings =
+ try_migrate_settings(V1_SETTINGS_2019V3.as_bytes()).expect("Migration failed");
+ let new_settings = serde_json::from_str(NEW_SETTINGS).unwrap();
- assert_eq!(&m.migrate(old_settings).unwrap(), &new_settings);
+ assert_eq!(&migrated_settings, &new_settings);
}
}
diff --git a/mullvad-types/src/settings/mod.rs b/mullvad-types/src/settings/mod.rs
index c589d1660a..fa5d2dfb8c 100644
--- a/mullvad-types/src/settings/mod.rs
+++ b/mullvad-types/src/settings/mod.rs
@@ -23,6 +23,9 @@ pub enum Error {
#[error(display = "Malformed settings")]
ParseError(#[error(source)] serde_json::Error),
+ #[error(display = "Settings version mismatch")]
+ VersionMismatch,
+
#[error(display = "Unable to read any version of the settings")]
NoMatchingVersion,
}
@@ -74,14 +77,18 @@ impl Default for Settings {
auto_connect: false,
tunnel_options: TunnelOptions::default(),
show_beta_releases: false,
- settings_version: migrations::SettingsVersion::V2,
+ settings_version: migrations::CURRENT_SETTINGS_VERSION,
}
}
}
impl Settings {
pub fn load_from_bytes(bytes: &[u8]) -> Result<Self> {
- serde_json::from_slice(bytes).map_err(Error::ParseError)
+ let settings: Self = serde_json::from_slice(bytes).map_err(Error::ParseError)?;
+ if settings.settings_version < migrations::CURRENT_SETTINGS_VERSION {
+ return Err(Error::VersionMismatch);
+ }
+ Ok(settings)
}
pub fn migrate_from_bytes(bytes: &[u8]) -> Result<Self> {