summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorLinus Färnstrand <linus@mullvad.net>2017-07-18 17:04:28 +0200
committerLinus Färnstrand <linus@mullvad.net>2017-07-18 17:04:28 +0200
commit3ed79a04900596968495303e068b9e80ef73bbbe (patch)
tree02fa2ab55f07faa2fa1482387a0da7e5a7506361
parent90588bced678c1ecbf7db0190c7c039d79e94d3e (diff)
parent535f9097ba24fd2060bf724dc7854b729d5c4d6d (diff)
downloadmullvadvpn-3ed79a04900596968495303e068b9e80ef73bbbe.tar.xz
mullvadvpn-3ed79a04900596968495303e068b9e80ef73bbbe.zip
Merge branch 'persistent-settings'
-rw-r--r--Cargo.lock49
-rw-r--r--mullvad-daemon/Cargo.toml2
-rw-r--r--mullvad-daemon/src/main.rs27
-rw-r--r--mullvad-daemon/src/management_interface.rs18
-rw-r--r--mullvad-daemon/src/settings.rs112
5 files changed, 189 insertions, 19 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 55c97f35ed..3903f4a902 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -23,6 +23,17 @@ version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
+name = "app_dirs"
+version = "1.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "ole32-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "shell32-sys 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
+ "xdg 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
name = "assert_matches"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -393,6 +404,7 @@ dependencies = [
name = "mullvad-daemon"
version = "0.1.0"
dependencies = [
+ "app_dirs 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"assert_matches 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"chrono 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"clap 2.25.0 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -411,6 +423,7 @@ dependencies = [
"simple-signal 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"talpid-core 0.1.0",
"talpid-ipc 0.1.0",
+ "toml 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
"uuid 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
@@ -478,6 +491,15 @@ version = "0.1.39"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
+name = "ole32-sys"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
name = "openvpn-ffi"
version = "0.1.0"
dependencies = [
@@ -633,6 +655,15 @@ dependencies = [
]
[[package]]
+name = "shell32-sys"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
name = "simple-signal"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -788,6 +819,14 @@ dependencies = [
]
[[package]]
+name = "toml"
+version = "0.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "serde 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
name = "unicode-bidi"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -900,9 +939,15 @@ dependencies = [
"winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
+[[package]]
+name = "xdg"
+version = "2.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
[metadata]
"checksum aho-corasick 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)" = "500909c4f87a9e52355b26626d890833e9e1d53ac566db76c36faa984b889699"
"checksum ansi_term 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "23ac7c30002a5accbf7e8987d0632fa6de155b7c3d39d0067317a391e00a2ef6"
+"checksum app_dirs 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b7d1c0d48a81bbb13043847f957971f4d87c81542d80ece5e84ba3cba4058fd4"
"checksum assert_matches 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9e772942dccdf11b368c31e044e4fca9189f80a773d2f0808379de65894cbf57"
"checksum atty 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d912da0db7fa85514874458ca3651fe2cddace8d0b0505571dbdcd41ab490159"
"checksum backtrace 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "72f9b4182546f4b04ebc4ab7f84948953a118bd6021a1b6a6c909e3e94f6be76"
@@ -952,6 +997,7 @@ dependencies = [
"checksum num-integer 0.1.34 (registry+https://github.com/rust-lang/crates.io-index)" = "ef1a4bf6f9174aa5783a9b4cc892cacd11aebad6c69ad027a0b65c6ca5f8aa37"
"checksum num-iter 0.1.33 (registry+https://github.com/rust-lang/crates.io-index)" = "f7d1891bd7b936f12349b7d1403761c8a0b85a18b148e9da4429d5d102c1a41e"
"checksum num-traits 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)" = "1708c0628602a98b52fad936cf3edb9a107af06e52e49fdf0707e884456a6af6"
+"checksum ole32-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5d2c49021782e5233cd243168edfa8037574afed4eba4bbaf538b3d8d1789d8c"
"checksum os_pipe 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "998bfbb3042e715190fe2a41abfa047d7e8cb81374d2977d7f100eacd8619cb1"
"checksum owning_ref 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "cdf84f41639e037b484f93433aa3897863b561ed65c6e59c7073d7c561710f37"
"checksum parking_lot 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "37f364e2ce5efa24c7d0b6646d5bb61145551a0112f107ffd7499f1a3e322fbd"
@@ -971,6 +1017,7 @@ dependencies = [
"checksum serde_json 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "48b04779552e92037212c3615370f6bd57a40ebba7f20e554ff9f55e41a69a7b"
"checksum sha1 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cc30b1e1e8c40c121ca33b86c23308a090d19974ef001b4bf6e61fd1a0fb095c"
"checksum shared_child 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "bcd5e483b3475af9bc2a35311c2f3bbf0bd98fde91410ab15a0d4ba3c3127b4e"
+"checksum shell32-sys 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "72f20b8f3c060374edb8046591ba28f62448c369ccbdc7b02075103fb3a9e38d"
"checksum simple-signal 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c1eb01a0c2d12db9e52684e73038eac812494e5937571ae2631f5cf53dc56687"
"checksum slab 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "17b4fcaed89ab08ef143da37bc52adbcc04d4a69014f4c1208d6b51f0c47bc23"
"checksum smallvec 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "26aa2afb825226fa29f0315de04d5a4af5fd44adadf837296accc01a49929724"
@@ -985,6 +1032,7 @@ dependencies = [
"checksum time 0.1.38 (registry+https://github.com/rust-lang/crates.io-index)" = "d5d788d3aa77bc0ef3e9621256885555368b47bd495c13dd2e7413c89f845520"
"checksum tokio-core 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "6a20ba4738d283cac7495ca36e045c80c2a8df3e05dd0909b17a06646af5a7ed"
"checksum tokio-io 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c2c3ce9739f7387a0fa65b5421e81feae92e04d603f008898f4257790ce8c2db"
+"checksum toml 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b0601da6c97135c8d330c7a13a013ca6cd4143221b01de2f8d4edc50a9e551c7"
"checksum unicode-bidi 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a6a2c4e3710edd365cd7e78383153ed739fa31af19f9172f72d3575060f5a43a"
"checksum unicode-normalization 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "51ccda9ef9efa3f7ef5d91e8f9b83bbe6955f9bf86aec89d5cce2c874625920f"
"checksum unicode-segmentation 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "18127285758f0e2c6cf325bb3f3d138a12fee27de4f23e146cd6a179f26c2cf3"
@@ -1001,3 +1049,4 @@ dependencies = [
"checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc"
"checksum ws 0.7.1 (git+https://github.com/tomusdrw/ws-rs)" = "<none>"
"checksum ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e"
+"checksum xdg 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a66b7c2281ebde13cf4391d70d4c7e5946c3c25e72a7b859ca8f677dcd0b0c61"
diff --git a/mullvad-daemon/Cargo.toml b/mullvad-daemon/Cargo.toml
index e70e03280c..34341f1801 100644
--- a/mullvad-daemon/Cargo.toml
+++ b/mullvad-daemon/Cargo.toml
@@ -8,6 +8,7 @@ description = "Mullvad VPN backend daemon. Runs and controls the VPN tunnels."
name = "mullvadd"
[dependencies]
+app_dirs = "1.1"
chrono = "0.4"
clap = "2.25"
error-chain = "0.10"
@@ -21,6 +22,7 @@ jsonrpc-pubsub = { git = "https://github.com/paritytech/jsonrpc" }
jsonrpc-ws-server = { git = "https://github.com/paritytech/jsonrpc" }
uuid = { version = "0.5", features = ["v4"] }
lazy_static = "0.2"
+toml = "0.4"
mullvad-types = { path = "../mullvad-types" }
talpid-core = { path = "../talpid-core" }
diff --git a/mullvad-daemon/src/main.rs b/mullvad-daemon/src/main.rs
index b26ca315a8..3710adc087 100644
--- a/mullvad-daemon/src/main.rs
+++ b/mullvad-daemon/src/main.rs
@@ -27,6 +27,7 @@ extern crate talpid_ipc;
mod cli;
mod management_interface;
mod rpc_info;
+mod settings;
mod shutdown;
use error_chain::ChainedError;
@@ -73,7 +74,7 @@ lazy_static! {
];
}
-static CRATE_NAME: &str = "mullvadd";
+const CRATE_NAME: &str = "mullvadd";
/// All events that can happen in the daemon. Sent from various threads and exposed interfaces.
@@ -140,12 +141,11 @@ struct Daemon {
rx: mpsc::Receiver<DaemonEvent>,
tx: mpsc::Sender<DaemonEvent>,
management_interface_broadcaster: management_interface::EventBroadcaster,
+ settings: settings::Settings,
// Just for testing. A cyclic iterator iterating over the hardcoded remotes,
// picking a new one for each retry.
remote_iter: std::iter::Cycle<std::iter::Cloned<std::slice::Iter<'static, Endpoint>>>,
- // The current account token for now. Should be moved into the settings later.
- account_token: Option<String>,
}
impl Daemon {
@@ -167,8 +167,8 @@ impl Daemon {
rx,
tx,
management_interface_broadcaster,
+ settings: settings::Settings::load().chain_err(|| "Unable to read settings")?,
remote_iter: REMOTES.iter().cloned().cycle(),
- account_token: None,
},
)
}
@@ -271,9 +271,17 @@ impl Daemon {
warn!("Unable to send current state to management interface client",);
}
}
- SetAccount(account_token) => self.account_token = account_token,
+ SetAccount(tx, account_token) => {
+ let save_result = self.settings.set_account_token(account_token);
+ match save_result.chain_err(|| "Unable to save settings") {
+ Ok(()) => if let Err(_) = tx.send(()) {
+ warn!("Unable to send response to management interface client");
+ },
+ Err(e) => error!("{}", e.display()),
+ }
+ }
GetAccount(tx) => {
- if let Err(_) = tx.send(self.account_token.clone()) {
+ if let Err(_) = tx.send(self.settings.get_account_token()) {
warn!("Unable to send current account to management interface client");
}
}
@@ -372,10 +380,9 @@ impl Daemon {
ErrorKind::InvalidState
);
let remote = self.remote_iter.next().unwrap();
- let account_token = self.account_token
- .as_ref()
- .ok_or(ErrorKind::InvalidSettings("No account token"))?
- .clone();
+ let account_token = self.settings
+ .get_account_token()
+ .ok_or(ErrorKind::InvalidSettings("No account token"))?;
let tunnel_monitor = self.spawn_tunnel_monitor(remote, &account_token)?;
self.tunnel_close_handle = Some(tunnel_monitor.close_handle());
self.spawn_tunnel_monitor_wait_thread(tunnel_monitor);
diff --git a/mullvad-daemon/src/management_interface.rs b/mullvad-daemon/src/management_interface.rs
index ffba3dca0f..f78f991c0e 100644
--- a/mullvad-daemon/src/management_interface.rs
+++ b/mullvad-daemon/src/management_interface.rs
@@ -49,8 +49,8 @@ build_rpc_trait! {
fn get_countries(&self) -> Result<HashMap<CountryCode, String>, Error>;
/// Set which account to connect with.
- #[rpc(name = "set_account")]
- fn set_account(&self, Option<AccountToken>) -> Result<(), Error>;
+ #[rpc(async, name = "set_account")]
+ fn set_account(&self, Option<AccountToken>) -> BoxFuture<(), Error>;
/// Get which account is configured.
#[rpc(async, name = "get_account")]
@@ -118,7 +118,7 @@ pub enum TunnelCommand {
/// Request the current state.
GetState(sync::oneshot::Sender<DaemonState>),
/// Set which account token to use for subsequent connection attempts.
- SetAccount(Option<AccountToken>),
+ SetAccount(sync::oneshot::Sender<()>, Option<AccountToken>),
/// Request the current account token being used.
GetAccount(sync::oneshot::Sender<Option<AccountToken>>),
}
@@ -260,13 +260,13 @@ impl<T: From<TunnelCommand> + 'static + Send> ManagementInterfaceApi for Managem
Ok(HashMap::new())
}
- fn set_account(&self, account_token: Option<AccountToken>) -> Result<(), Error> {
+ fn set_account(&self, account_token: Option<AccountToken>) -> BoxFuture<(), Error> {
trace!("set_account");
- self.tx
- .lock()
- .unwrap()
- .send(TunnelCommand::SetAccount(account_token))
- .map_err(|_| Error::internal_error())
+ let (tx, rx) = sync::oneshot::channel();
+ match self.tx.lock().unwrap().send(TunnelCommand::SetAccount(tx, account_token)) {
+ Ok(()) => rx.map_err(|_| Error::internal_error()).boxed(),
+ Err(_) => future::err(Error::internal_error()).boxed(),
+ }
}
fn get_account(&self) -> BoxFuture<Option<AccountToken>, Error> {
diff --git a/mullvad-daemon/src/settings.rs b/mullvad-daemon/src/settings.rs
new file mode 100644
index 0000000000..229ccd9709
--- /dev/null
+++ b/mullvad-daemon/src/settings.rs
@@ -0,0 +1,112 @@
+extern crate app_dirs;
+extern crate toml;
+
+use self::app_dirs::{AppDataType, AppInfo};
+
+use std::fs::File;
+use std::io::{self, Read, Write};
+use std::path::PathBuf;
+
+error_chain! {
+ errors {
+ DirectoryError {
+ description("Unable to create settings directory for program")
+ }
+ ReadError(path: PathBuf) {
+ description("Unable to read settings file")
+ display("Unable to read settings from {}", path.to_string_lossy())
+ }
+ WriteError(path: PathBuf) {
+ description("Unable to write settings file")
+ display("Unable to write settings to {}", path.to_string_lossy())
+ }
+ ParseError {
+ description("Malformed settings")
+ }
+ }
+}
+
+static APP_INFO: AppInfo = AppInfo {
+ name: ::CRATE_NAME,
+ author: "Mullvad",
+};
+
+static SETTINGS_FILE: &str = "settings.toml";
+
+#[derive(Debug, Clone, Deserialize, Serialize)]
+pub struct Settings {
+ account_token: Option<String>,
+}
+
+const DEFAULT_SETTINGS: Settings = Settings { account_token: None };
+
+impl Settings {
+ /// Loads user settings from file. If no file is present it returns the defaults.
+ pub fn load() -> Result<Settings> {
+ let settings_path = Self::get_settings_path()?;
+ match File::open(&settings_path) {
+ Ok(mut file) => {
+ info!("Loading settings from {}", settings_path.to_string_lossy());
+ Self::read_settings(&mut file, settings_path)
+ }
+ Err(ref e) if e.kind() == io::ErrorKind::NotFound => {
+ info!(
+ "No settings file at {}, using defaults",
+ settings_path.to_string_lossy()
+ );
+ Ok(DEFAULT_SETTINGS)
+ }
+ Err(e) => Err(e).chain_err(|| ErrorKind::ReadError(settings_path)),
+ }
+ }
+
+ /// Serializes the settings and saves them to the file it was loaded from.
+ fn save(&self) -> Result<()> {
+ let settings_path = Self::get_settings_path()?;
+ let data = toml::to_string(self).chain_err(|| ErrorKind::ParseError)?;
+
+ debug!("Writing settings to {}", settings_path.to_string_lossy());
+ let mut file = File::create(&settings_path)
+ .chain_err(|| ErrorKind::WriteError(settings_path.clone()))?;
+ file.write_all(data.as_bytes()).chain_err(|| ErrorKind::WriteError(settings_path))?;
+ Ok(())
+ }
+
+ fn get_settings_path() -> Result<PathBuf> {
+ let dir = app_dirs::app_root(AppDataType::UserConfig, &APP_INFO)
+ .chain_err(|| ErrorKind::DirectoryError)?;
+ Ok(dir.join(SETTINGS_FILE))
+ }
+
+ fn read_settings(file: &mut File, path: PathBuf) -> Result<Settings> {
+ let mut data = Vec::new();
+ file.read_to_end(&mut data).chain_err(|| ErrorKind::ReadError(path))?;
+ toml::from_slice(&data).chain_err(|| ErrorKind::ParseError)
+ }
+
+ pub fn get_account_token(&self) -> Option<String> {
+ self.account_token.clone()
+ }
+
+ /// Changes account number to the one given. Also saves the new settings to disk.
+ pub fn set_account_token(&mut self, account_token: Option<String>) -> Result<()> {
+ if account_token != self.account_token {
+ info!(
+ "Changing account token from {} to {}",
+ Self::format_account_token(&self.account_token),
+ Self::format_account_token(&account_token),
+ );
+ self.account_token = account_token;
+ self.save()
+ } else {
+ Ok(())
+ }
+ }
+
+ fn format_account_token(account_token: &Option<String>) -> String {
+ match *account_token {
+ Some(ref account_token) => format!("\"{}\"", account_token),
+ None => "[none]".to_owned(),
+ }
+ }
+}