diff options
| author | Linus Färnstrand <linus@mullvad.net> | 2017-07-18 17:04:28 +0200 |
|---|---|---|
| committer | Linus Färnstrand <linus@mullvad.net> | 2017-07-18 17:04:28 +0200 |
| commit | 3ed79a04900596968495303e068b9e80ef73bbbe (patch) | |
| tree | 02fa2ab55f07faa2fa1482387a0da7e5a7506361 | |
| parent | 90588bced678c1ecbf7db0190c7c039d79e94d3e (diff) | |
| parent | 535f9097ba24fd2060bf724dc7854b729d5c4d6d (diff) | |
| download | mullvadvpn-3ed79a04900596968495303e068b9e80ef73bbbe.tar.xz mullvadvpn-3ed79a04900596968495303e068b9e80ef73bbbe.zip | |
Merge branch 'persistent-settings'
| -rw-r--r-- | Cargo.lock | 49 | ||||
| -rw-r--r-- | mullvad-daemon/Cargo.toml | 2 | ||||
| -rw-r--r-- | mullvad-daemon/src/main.rs | 27 | ||||
| -rw-r--r-- | mullvad-daemon/src/management_interface.rs | 18 | ||||
| -rw-r--r-- | mullvad-daemon/src/settings.rs | 112 |
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(), + } + } +} |
