summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorLinus Färnstrand <linus@mullvad.net>2018-09-24 16:26:39 +0200
committerLinus Färnstrand <linus@mullvad.net>2018-09-24 16:26:39 +0200
commite0619be7d66bd3db2136d640e15131685d6084b9 (patch)
tree89f264e82883d73fdd99c327e8c9e4833758a226
parentcd6675854025b3866a931ba4d42205ffbf7613a0 (diff)
parent967bf662fe56017dcb7cb9211c0e7f8440458cb2 (diff)
downloadmullvadvpn-e0619be7d66bd3db2136d640e15131685d6084b9.tar.xz
mullvadvpn-e0619be7d66bd3db2136d640e15131685d6084b9.zip
Merge branch 'split-daemon-into-library'
-rw-r--r--Cargo.lock24
-rwxr-xr-xbuild.sh2
-rw-r--r--mullvad-daemon/Cargo.toml10
-rw-r--r--mullvad-daemon/build.rs24
-rw-r--r--mullvad-daemon/resources.rc8
-rw-r--r--mullvad-daemon/src/lib.rs678
-rw-r--r--mullvad-daemon/src/logging.rs2
-rw-r--r--mullvad-daemon/src/main.rs661
-rw-r--r--mullvad-daemon/src/relays.rs3
-rw-r--r--mullvad-daemon/src/system_service.rs19
-rw-r--r--mullvad-daemon/version.rc25
11 files changed, 756 insertions, 700 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 44a7c4bf64..5418b80e24 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -946,7 +946,7 @@ dependencies = [
"uuid 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
"windows-service 0.1.0 (git+https://github.com/mullvad/windows-service-rs.git?rev=55c5dfb372e6b3f5607a3159c5388d27b6b84ff6)",
- "windres 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winres 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@@ -1217,7 +1217,7 @@ source = "git+https://github.com/nikvolf/parity-tokio-ipc#306ea3e6ff8b8c1bb03081
dependencies = [
"bytes 0.4.9 (registry+https://github.com/rust-lang/crates.io-index)",
"futures 0.1.23 (registry+https://github.com/rust-lang/crates.io-index)",
- "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
+ "log 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
"mio-named-pipes 0.1.6 (git+https://github.com/alexcrichton/mio-named-pipes)",
"miow 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -1234,7 +1234,7 @@ source = "git+https://github.com/NikVolf/parity-tokio-ipc?rev=master#306ea3e6ff8
dependencies = [
"bytes 0.4.9 (registry+https://github.com/rust-lang/crates.io-index)",
"futures 0.1.23 (registry+https://github.com/rust-lang/crates.io-index)",
- "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
+ "log 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
"mio-named-pipes 0.1.6 (git+https://github.com/alexcrichton/mio-named-pipes)",
"miow 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -1978,6 +1978,14 @@ dependencies = [
]
[[package]]
+name = "toml"
+version = "0.4.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "serde 1.0.71 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
name = "try-lock"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2157,6 +2165,14 @@ dependencies = [
]
[[package]]
+name = "winres"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "toml 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
name = "ws2_32-sys"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2357,6 +2373,7 @@ dependencies = [
"checksum tokio-timer 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "1c76b4e97a4f61030edff8bd272364e4f731b9f54c7307eb4eb733c3926eb96a"
"checksum tokio-udp 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "43eb534af6e8f37d43ab1b612660df14755c42bd003c5f8d2475ee78cc4600c0"
"checksum tokio-uds 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "424c1ed15a0132251813ccea50640b224c809d6ceafb88154c1a8775873a0e89"
+"checksum toml 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "a0263c6c02c4db6c8f7681f9fd35e90de799ebd4cfdeab77a38f4ff6b3d8c0d9"
"checksum try-lock 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee2aa4715743892880f70885373966c83d73ef1b0838a664ef0c76fffd35e7c2"
"checksum ucd-util 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "fd2be2d6639d0f8fe6cdda291ad456e23629558d466e2789d2c3e9892bda285d"
"checksum unicase 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "284b6d3db520d67fbe88fd778c21510d1b0ba4a551e5d0fbb023d33405f6de8a"
@@ -2384,4 +2401,5 @@ dependencies = [
"checksum windows-service 0.1.0 (git+https://github.com/mullvad/windows-service-rs.git?rev=55c5dfb372e6b3f5607a3159c5388d27b6b84ff6)" = "<none>"
"checksum windres 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dd6e13ff705d7fab032bdf857354035c56c8c889e364029e4300779eb6e1d729"
"checksum winreg 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a27a759395c1195c4cc5cda607ef6f8f6498f64e78f7900f5de0a127a424704a"
+"checksum winres 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "f07dabda4e79413ecac65bc9a2234ad3d85dc49f9d289f868cd9d8611d88f28d"
"checksum ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e"
diff --git a/build.sh b/build.sh
index 0e18fc5be8..fe1d4687df 100755
--- a/build.sh
+++ b/build.sh
@@ -117,7 +117,7 @@ if [[ "$(uname -s)" == "MINGW"* ]]; then
fi
echo "Building Rust code in release mode using $RUSTC_VERSION..."
-cargo +stable build --release
+MULLVAD_ADD_MANIFEST="1" cargo +stable build --release
################################################################################
# Other work to prepare the release.
diff --git a/mullvad-daemon/Cargo.toml b/mullvad-daemon/Cargo.toml
index 89d7a7aa2e..2892869152 100644
--- a/mullvad-daemon/Cargo.toml
+++ b/mullvad-daemon/Cargo.toml
@@ -44,4 +44,12 @@ windows-service = { git = "https://github.com/mullvad/windows-service-rs.git", r
winapi = "0.3"
[target.'cfg(windows)'.build-dependencies]
-windres = "0.2"
+winres = "0.1"
+winapi = "0.3"
+
+[package.metadata.winres]
+ProductName = "Mullvad VPN"
+CompanyName = "Amagicom AB"
+LegalCopyright = "(c) 2018 Amagicom AB"
+InternalName = "mullvad-daemon"
+OriginalFilename = "mullvad-daemon.exe"
diff --git a/mullvad-daemon/build.rs b/mullvad-daemon/build.rs
index 45ce36bdba..f35e2fa4a1 100644
--- a/mullvad-daemon/build.rs
+++ b/mullvad-daemon/build.rs
@@ -4,18 +4,36 @@ use std::path::PathBuf;
use std::process::Command;
#[cfg(windows)]
-extern crate windres;
+extern crate winapi;
+#[cfg(windows)]
+extern crate winres;
fn main() {
let out_dir = PathBuf::from(env::var_os("OUT_DIR").unwrap());
let product_version = env!("CARGO_PKG_VERSION").replacen(".0", "", 1);
- fs::write(out_dir.join("product-version.txt"), product_version).unwrap();
+ fs::write(out_dir.join("product-version.txt"), &product_version).unwrap();
fs::write(out_dir.join("git-commit-date.txt"), commit_date()).unwrap();
#[cfg(windows)]
{
- windres::Build::new().compile("resources.rc").unwrap();
+ let mut res = winres::WindowsResource::new();
+ res.set("ProductVersion", &product_version);
+ res.set_icon("../dist-assets/icon.ico");
+ res.set_language(winapi::um::winnt::MAKELANGID(
+ winapi::um::winnt::LANG_ENGLISH,
+ winapi::um::winnt::SUBLANG_ENGLISH_US,
+ ));
+ println!("cargo:rerun-if-env-changed=MULLVAD_ADD_MANIFEST");
+ if env::var("MULLVAD_ADD_MANIFEST")
+ .map(|s| s != "0")
+ .unwrap_or(false)
+ {
+ res.set_manifest_file("mullvad-daemon.manifest");
+ } else {
+ println!("cargo:warning=Skipping mullvad-daemon manifest");
+ }
+ res.compile().expect("Unable to generate windows resources");
}
}
diff --git a/mullvad-daemon/resources.rc b/mullvad-daemon/resources.rc
deleted file mode 100644
index d55f0136e3..0000000000
--- a/mullvad-daemon/resources.rc
+++ /dev/null
@@ -1,8 +0,0 @@
-IDI_MAIN ICON "../dist-assets/icon.ico"
-
-#include "version.rc"
-
-#define RT_MANIFEST 24
-#define CREATEPROCESS_MANIFEST_RESOURCE_ID 1
-
-CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST "mullvad-daemon.manifest"
diff --git a/mullvad-daemon/src/lib.rs b/mullvad-daemon/src/lib.rs
new file mode 100644
index 0000000000..9856ee5856
--- /dev/null
+++ b/mullvad-daemon/src/lib.rs
@@ -0,0 +1,678 @@
+//! # License
+//!
+//! Copyright (C) 2017 Amagicom AB
+//!
+//! This program is free software: you can redistribute it and/or modify it under the terms of the
+//! GNU General Public License as published by the Free Software Foundation, either version 3 of
+//! the License, or (at your option) any later version.
+
+extern crate chrono;
+#[macro_use]
+extern crate error_chain;
+extern crate futures;
+#[cfg(unix)]
+extern crate libc;
+#[macro_use]
+extern crate log;
+
+#[macro_use]
+extern crate serde;
+extern crate serde_json;
+
+extern crate jsonrpc_core;
+#[macro_use]
+extern crate jsonrpc_macros;
+extern crate jsonrpc_ipc_server;
+extern crate jsonrpc_pubsub;
+extern crate rand;
+extern crate tokio_core;
+extern crate tokio_timer;
+extern crate uuid;
+
+extern crate mullvad_ipc_client;
+extern crate mullvad_paths;
+extern crate mullvad_rpc;
+extern crate mullvad_types;
+extern crate talpid_core;
+extern crate talpid_ipc;
+extern crate talpid_types;
+
+mod account_history;
+mod geoip;
+mod management_interface;
+mod relays;
+mod rpc_uniqueness_check;
+
+use error_chain::ChainedError;
+use futures::sync::mpsc::UnboundedSender;
+use futures::{Future, Sink};
+use jsonrpc_core::futures::sync::oneshot::{self, Sender as OneshotSender};
+
+use management_interface::{BoxFuture, ManagementCommand, ManagementInterfaceServer};
+use mullvad_rpc::{AccountsProxy, AppVersionProxy, HttpHandle};
+
+use mullvad_types::{
+ account::{AccountData, AccountToken},
+ location::GeoIpLocation,
+ relay_constraints::{RelaySettings, RelaySettingsUpdate},
+ relay_list::{Relay, RelayList},
+ settings::Settings,
+ states::TargetState,
+ version::{AppVersion, AppVersionInfo},
+};
+
+use std::{mem, net::IpAddr, path::PathBuf, sync::mpsc, thread, time::Duration};
+
+use talpid_core::{
+ mpsc::IntoSender,
+ tunnel_state_machine::{self, TunnelCommand, TunnelParameters},
+};
+use talpid_types::{
+ net::TunnelEndpoint,
+ tunnel::{BlockReason, TunnelStateTransition},
+};
+
+
+error_chain!{
+ errors {
+ NoCacheDir {
+ description("Unable to create cache directory")
+ }
+ DaemonIsAlreadyRunning {
+ description("Another instance of the daemon is already running")
+ }
+ ManagementInterfaceError(msg: &'static str) {
+ description("Error in the management interface")
+ display("Management interface error: {}", msg)
+ }
+ }
+ links {
+ TunnelError(tunnel_state_machine::Error, tunnel_state_machine::ErrorKind);
+ }
+}
+
+type SyncUnboundedSender<T> = ::futures::sink::Wait<UnboundedSender<T>>;
+
+/// All events that can happen in the daemon. Sent from various threads and exposed interfaces.
+pub enum DaemonEvent {
+ /// Tunnel has changed state.
+ TunnelStateTransition(TunnelStateTransition),
+ /// An event coming from the JSONRPC-2.0 management interface.
+ ManagementInterfaceEvent(ManagementCommand),
+ /// Triggered if the server hosting the JSONRPC-2.0 management interface dies unexpectedly.
+ ManagementInterfaceExited,
+ /// Daemon shutdown triggered by a signal, ctrl-c or similar.
+ TriggerShutdown,
+}
+
+impl From<TunnelStateTransition> for DaemonEvent {
+ fn from(tunnel_state_transition: TunnelStateTransition) -> Self {
+ DaemonEvent::TunnelStateTransition(tunnel_state_transition)
+ }
+}
+
+impl From<ManagementCommand> for DaemonEvent {
+ fn from(command: ManagementCommand) -> Self {
+ DaemonEvent::ManagementInterfaceEvent(command)
+ }
+}
+
+#[derive(Clone, Debug, Eq, PartialEq)]
+enum DaemonExecutionState {
+ Running,
+ Exiting,
+ Finished,
+}
+
+impl DaemonExecutionState {
+ pub fn shutdown(&mut self, tunnel_state: &TunnelStateTransition) {
+ use self::DaemonExecutionState::*;
+
+ match self {
+ Running => {
+ match tunnel_state {
+ TunnelStateTransition::Disconnected => mem::replace(self, Finished),
+ _ => mem::replace(self, Exiting),
+ };
+ }
+ Exiting | Finished => {}
+ };
+ }
+
+ pub fn disconnected(&mut self) {
+ use self::DaemonExecutionState::*;
+
+ match self {
+ Exiting => {
+ mem::replace(self, Finished);
+ }
+ Running | Finished => {}
+ };
+ }
+
+ pub fn is_running(&mut self) -> bool {
+ use self::DaemonExecutionState::*;
+
+ match self {
+ Running => true,
+ Exiting | Finished => false,
+ }
+ }
+}
+
+
+pub struct Daemon {
+ tunnel_command_tx: SyncUnboundedSender<TunnelCommand>,
+ tunnel_state: TunnelStateTransition,
+ target_state: TargetState,
+ state: DaemonExecutionState,
+ rx: mpsc::Receiver<DaemonEvent>,
+ tx: mpsc::Sender<DaemonEvent>,
+ management_interface_broadcaster: management_interface::EventBroadcaster,
+ #[cfg(unix)]
+ management_interface_socket_path: String,
+ settings: Settings,
+ accounts_proxy: AccountsProxy<HttpHandle>,
+ version_proxy: AppVersionProxy<HttpHandle>,
+ https_handle: mullvad_rpc::rest::RequestSender,
+ tokio_remote: tokio_core::reactor::Remote,
+ relay_selector: relays::RelaySelector,
+ current_relay: Option<Relay>,
+ log_dir: Option<PathBuf>,
+ resource_dir: PathBuf,
+ version: String,
+}
+
+impl Daemon {
+ pub fn new(
+ log_dir: Option<PathBuf>,
+ resource_dir: PathBuf,
+ cache_dir: PathBuf,
+ version: String,
+ ) -> Result<Self> {
+ ensure!(
+ !rpc_uniqueness_check::is_another_instance_running(),
+ ErrorKind::DaemonIsAlreadyRunning
+ );
+ let ca_path = resource_dir.join(mullvad_paths::resources::API_CA_FILENAME);
+
+ let mut rpc_manager = mullvad_rpc::MullvadRpcFactory::with_cache_dir(&cache_dir, &ca_path);
+
+ let (rpc_handle, https_handle, tokio_remote) =
+ mullvad_rpc::event_loop::create(move |core| {
+ let handle = core.handle();
+ let rpc = rpc_manager.new_connection_on_event_loop(&handle);
+ let https_handle = mullvad_rpc::rest::create_https_client(&ca_path, &handle);
+ let remote = core.remote();
+ (rpc, https_handle, remote)
+ }).chain_err(|| "Unable to initialize network event loop")?;
+ let rpc_handle = rpc_handle.chain_err(|| "Unable to create RPC client")?;
+ let https_handle = https_handle.chain_err(|| "Unable to create am.i.mullvad client")?;
+
+ let relay_selector =
+ relays::RelaySelector::new(rpc_handle.clone(), &resource_dir, &cache_dir);
+
+ let (tx, rx) = mpsc::channel();
+ let tunnel_command_tx =
+ tunnel_state_machine::spawn(cache_dir.clone(), IntoSender::from(tx.clone()))?;
+
+ let target_state = TargetState::Unsecured;
+ let management_interface_result =
+ Self::start_management_interface(tx.clone(), cache_dir.clone())?;
+
+ // Attempt to download a fresh relay list
+ relay_selector.update();
+
+ Ok(Daemon {
+ tunnel_command_tx: Sink::wait(tunnel_command_tx),
+ tunnel_state: TunnelStateTransition::Disconnected,
+ target_state,
+ state: DaemonExecutionState::Running,
+ rx,
+ tx,
+ management_interface_broadcaster: management_interface_result.0,
+ #[cfg(unix)]
+ management_interface_socket_path: management_interface_result.1,
+ settings: Settings::load().chain_err(|| "Unable to read settings")?,
+ accounts_proxy: AccountsProxy::new(rpc_handle.clone()),
+ version_proxy: AppVersionProxy::new(rpc_handle),
+ https_handle,
+ tokio_remote,
+ relay_selector,
+ current_relay: None,
+ log_dir,
+ resource_dir,
+ version,
+ })
+ }
+
+ // Starts the management interface and spawns a thread that will process it.
+ // Returns a handle that allows notifying all subscribers on events.
+ fn start_management_interface(
+ event_tx: mpsc::Sender<DaemonEvent>,
+ cache_dir: PathBuf,
+ ) -> Result<(management_interface::EventBroadcaster, String)> {
+ let multiplex_event_tx = IntoSender::from(event_tx.clone());
+ let server = Self::start_management_interface_server(multiplex_event_tx, cache_dir)?;
+ let event_broadcaster = server.event_broadcaster();
+ let socket_path = server.socket_path().to_owned();
+ Self::spawn_management_interface_wait_thread(server, event_tx);
+ Ok((event_broadcaster, socket_path))
+ }
+
+ fn start_management_interface_server(
+ event_tx: IntoSender<ManagementCommand, DaemonEvent>,
+ cache_dir: PathBuf,
+ ) -> Result<ManagementInterfaceServer> {
+ let server = ManagementInterfaceServer::start(event_tx, cache_dir)
+ .chain_err(|| ErrorKind::ManagementInterfaceError("Failed to start server"))?;
+ info!(
+ "Mullvad management interface listening on {}",
+ server.socket_path()
+ );
+
+ Ok(server)
+ }
+
+ fn spawn_management_interface_wait_thread(
+ server: ManagementInterfaceServer,
+ exit_tx: mpsc::Sender<DaemonEvent>,
+ ) {
+ thread::spawn(move || {
+ server.wait();
+ error!("Mullvad management interface shut down");
+ let _ = exit_tx.send(DaemonEvent::ManagementInterfaceExited);
+ });
+ }
+
+ /// Consume the `Daemon` and run the main event loop. Blocks until an error happens or a
+ /// shutdown event is received.
+ pub fn run(mut self) -> Result<()> {
+ if self.settings.get_auto_connect() {
+ info!("Automatically connecting since auto-connect is turned on");
+ if self.set_target_state(TargetState::Secured).is_err() {
+ warn!("Aborting auto-connect since no account token is set");
+ }
+ }
+ while let Ok(event) = self.rx.recv() {
+ self.handle_event(event)?;
+ if self.state == DaemonExecutionState::Finished {
+ break;
+ }
+ }
+ Ok(())
+ }
+
+ fn handle_event(&mut self, event: DaemonEvent) -> Result<()> {
+ use DaemonEvent::*;
+ match event {
+ TunnelStateTransition(transition) => {
+ Ok(self.handle_tunnel_state_transition(transition))
+ }
+ ManagementInterfaceEvent(event) => Ok(self.handle_management_interface_event(event)),
+ ManagementInterfaceExited => self.handle_management_interface_exited(),
+ TriggerShutdown => Ok(self.handle_trigger_shutdown_event()),
+ }
+ }
+
+ fn handle_tunnel_state_transition(&mut self, tunnel_state: TunnelStateTransition) {
+ use self::TunnelStateTransition::*;
+
+ debug!("New tunnel state: {:?}", tunnel_state);
+ match tunnel_state {
+ Disconnected => {
+ self.state.disconnected();
+ self.current_relay = None;
+ }
+ Blocked(ref reason) => {
+ info!("Blocking all network connections, reason: {}", reason);
+
+ match reason {
+ BlockReason::AuthFailed(_) => self.schedule_reconnect(Duration::from_secs(60)),
+ _ => {}
+ }
+ }
+ _ => {}
+ }
+
+ self.tunnel_state = tunnel_state.clone();
+ self.management_interface_broadcaster
+ .notify_new_state(tunnel_state);
+ }
+
+ fn schedule_reconnect(&mut self, delay: Duration) {
+ let command_tx = self.tx.clone();
+
+ thread::spawn(move || {
+ let (result_tx, _result_rx) = oneshot::channel();
+
+ thread::sleep(delay);
+ debug!("Attempting to reconnect");
+ let _ = command_tx.send(DaemonEvent::ManagementInterfaceEvent(
+ ManagementCommand::SetTargetState(result_tx, TargetState::Secured),
+ ));
+ });
+ }
+
+ fn handle_management_interface_event(&mut self, event: ManagementCommand) {
+ use ManagementCommand::*;
+ match event {
+ SetTargetState(tx, state) => self.on_set_target_state(tx, state),
+ GetState(tx) => self.on_get_state(tx),
+ GetCurrentLocation(tx) => self.on_get_current_location(tx),
+ GetAccountData(tx, account_token) => self.on_get_account_data(tx, account_token),
+ GetRelayLocations(tx) => self.on_get_relay_locations(tx),
+ SetAccount(tx, account_token) => self.on_set_account(tx, account_token),
+ UpdateRelaySettings(tx, update) => self.on_update_relay_settings(tx, update),
+ SetAllowLan(tx, allow_lan) => self.on_set_allow_lan(tx, allow_lan),
+ SetAutoConnect(tx, auto_connect) => self.on_set_auto_connect(tx, auto_connect),
+ SetOpenVpnMssfix(tx, mssfix_arg) => self.on_set_openvpn_mssfix(tx, mssfix_arg),
+ SetEnableIpv6(tx, enable_ipv6) => self.on_set_enable_ipv6(tx, enable_ipv6),
+ GetSettings(tx) => self.on_get_settings(tx),
+ GetVersionInfo(tx) => self.on_get_version_info(tx),
+ GetCurrentVersion(tx) => self.on_get_current_version(tx),
+ Shutdown => self.handle_trigger_shutdown_event(),
+ }
+ }
+
+ fn on_set_target_state(
+ &mut self,
+ tx: OneshotSender<::std::result::Result<(), ()>>,
+ new_target_state: TargetState,
+ ) {
+ if self.state.is_running() {
+ Self::oneshot_send(tx, self.set_target_state(new_target_state), "targe state");
+ } else {
+ warn!("Ignoring target state change request due to shutdown");
+ Self::oneshot_send(tx, Ok(()), "targe state");
+ }
+ }
+
+ fn on_get_state(&self, tx: OneshotSender<TunnelStateTransition>) {
+ Self::oneshot_send(tx, self.tunnel_state.clone(), "current state");
+ }
+
+ fn on_get_current_location(&self, tx: OneshotSender<GeoIpLocation>) {
+ if let Some(ref relay) = self.current_relay {
+ let location = relay.location.as_ref().cloned().unwrap();
+ let geo_ip_location = GeoIpLocation {
+ ip: IpAddr::V4(relay.ipv4_addr_exit),
+ country: location.country,
+ city: Some(location.city),
+ latitude: location.latitude,
+ longitude: location.longitude,
+ mullvad_exit_ip: true,
+ };
+ Self::oneshot_send(tx, geo_ip_location, "current location");
+ } else {
+ let https_handle = self.https_handle.clone();
+ self.tokio_remote.spawn(move |_| {
+ geoip::send_location_request(https_handle)
+ .map(move |location| Self::oneshot_send(tx, location, "current location"))
+ .map_err(|e| {
+ warn!("Unable to fetch GeoIP location: {}", e.display_chain());
+ })
+ });
+ }
+ }
+
+ fn on_get_account_data(
+ &mut self,
+ tx: OneshotSender<BoxFuture<AccountData, mullvad_rpc::Error>>,
+ account_token: AccountToken,
+ ) {
+ let rpc_call = self
+ .accounts_proxy
+ .get_expiry(account_token)
+ .map(|expiry| AccountData { expiry });
+ Self::oneshot_send(tx, Box::new(rpc_call), "account data")
+ }
+
+ fn on_get_relay_locations(&mut self, tx: OneshotSender<RelayList>) {
+ Self::oneshot_send(tx, self.relay_selector.get_locations(), "relay locations");
+ }
+
+
+ fn on_set_account(&mut self, tx: OneshotSender<()>, account_token: Option<String>) {
+ let account_token_cleared = account_token.is_none();
+ let save_result = self.settings.set_account_token(account_token);
+
+ match save_result.chain_err(|| "Unable to save settings") {
+ Ok(account_changed) => {
+ Self::oneshot_send(tx, (), "set_account response");
+ if account_changed {
+ self.management_interface_broadcaster
+ .notify_settings(&self.settings);
+ if account_token_cleared {
+ info!("Disconnecting because account token was cleared");
+ let _ = self.set_target_state(TargetState::Unsecured);
+ } else {
+ info!("Initiating tunnel restart because the account token changed");
+ self.reconnect_tunnel();
+ }
+ }
+ }
+ Err(e) => error!("{}", e.display_chain()),
+ }
+ }
+
+ fn on_get_version_info(
+ &mut self,
+ tx: OneshotSender<BoxFuture<AppVersionInfo, mullvad_rpc::Error>>,
+ ) {
+ let fut = self
+ .version_proxy
+ .latest_app_version()
+ .join(self.version_proxy.is_app_version_supported(&self.version))
+ .map(|(latest_versions, is_supported)| AppVersionInfo {
+ current_is_supported: is_supported,
+ latest: latest_versions,
+ });
+ Self::oneshot_send(tx, Box::new(fut), "get_version_info response");
+ }
+
+ fn on_get_current_version(&mut self, tx: OneshotSender<AppVersion>) {
+ Self::oneshot_send(tx, self.version.clone(), "get_current_version response");
+ }
+
+ fn on_update_relay_settings(&mut self, tx: OneshotSender<()>, update: RelaySettingsUpdate) {
+ let save_result = self.settings.update_relay_settings(update);
+ match save_result.chain_err(|| "Unable to save settings") {
+ Ok(settings_changed) => {
+ Self::oneshot_send(tx, (), "update_relay_settings response");
+ if settings_changed {
+ self.management_interface_broadcaster
+ .notify_settings(&self.settings);
+ info!("Initiating tunnel restart because the relay settings changed");
+ self.reconnect_tunnel();
+ }
+ }
+ Err(e) => error!("{}", e.display_chain()),
+ }
+ }
+
+ fn on_set_allow_lan(&mut self, tx: OneshotSender<()>, allow_lan: bool) {
+ let save_result = self.settings.set_allow_lan(allow_lan);
+ match save_result.chain_err(|| "Unable to save settings") {
+ Ok(settings_changed) => {
+ Self::oneshot_send(tx, (), "set_allow_lan response");
+ if settings_changed {
+ self.management_interface_broadcaster
+ .notify_settings(&self.settings);
+ self.send_tunnel_command(TunnelCommand::AllowLan(allow_lan));
+ }
+ }
+ Err(e) => error!("{}", e.display_chain()),
+ }
+ }
+
+ fn on_set_auto_connect(&mut self, tx: OneshotSender<()>, auto_connect: bool) {
+ let save_result = self.settings.set_auto_connect(auto_connect);
+ match save_result.chain_err(|| "Unable to save settings") {
+ Ok(settings_changed) => {
+ Self::oneshot_send(tx, (), "set auto-connect response");
+ if settings_changed {
+ self.management_interface_broadcaster
+ .notify_settings(&self.settings);
+ }
+ }
+ Err(e) => error!("{}", e.display_chain()),
+ }
+ }
+
+ fn on_set_openvpn_mssfix(&mut self, tx: OneshotSender<()>, mssfix_arg: Option<u16>) {
+ let save_result = self.settings.set_openvpn_mssfix(mssfix_arg);
+ match save_result.chain_err(|| "Unable to save settings") {
+ Ok(settings_changed) => {
+ Self::oneshot_send(tx, (), "set_openvpn_mssfix response");
+ if settings_changed {
+ self.management_interface_broadcaster
+ .notify_settings(&self.settings);
+ }
+ }
+ Err(e) => error!("{}", e.display_chain()),
+ }
+ }
+
+ fn on_set_enable_ipv6(&mut self, tx: OneshotSender<()>, enable_ipv6: bool) {
+ let save_result = self.settings.set_enable_ipv6(enable_ipv6);
+ match save_result.chain_err(|| "Unable to save settings") {
+ Ok(settings_changed) => {
+ Self::oneshot_send(tx, (), "set_enable_ipv6 response");
+ if settings_changed {
+ self.management_interface_broadcaster
+ .notify_settings(&self.settings);
+ info!("Initiating tunnel restart because the enable IPv6 setting changed");
+ self.reconnect_tunnel();
+ }
+ }
+ Err(e) => error!("{}", e.display_chain()),
+ }
+ }
+
+ fn on_get_settings(&self, tx: OneshotSender<Settings>) {
+ Self::oneshot_send(tx, self.settings.clone(), "get_settings response");
+ }
+
+ fn oneshot_send<T>(tx: OneshotSender<T>, t: T, msg: &'static str) {
+ if let Err(_) = tx.send(t) {
+ warn!("Unable to send {} to management interface client", msg);
+ }
+ }
+
+ fn handle_management_interface_exited(&self) -> Result<()> {
+ Err(ErrorKind::ManagementInterfaceError("Server exited unexpectedly").into())
+ }
+
+ fn handle_trigger_shutdown_event(&mut self) {
+ self.state.shutdown(&self.tunnel_state);
+ self.disconnect_tunnel();
+ }
+
+ /// Set the target state of the client. If it changed trigger the operations needed to
+ /// progress towards that state.
+ /// Returns an error if trying to set secured state, but no account token is present.
+ fn set_target_state(&mut self, new_state: TargetState) -> ::std::result::Result<(), ()> {
+ if new_state != self.target_state || self.tunnel_state.is_blocked() {
+ debug!("Target state {:?} => {:?}", self.target_state, new_state);
+ self.target_state = new_state;
+ match self.target_state {
+ TargetState::Secured => match self.settings.get_account_token() {
+ Some(account_token) => self.connect_tunnel(account_token),
+ None => {
+ self.set_target_state(TargetState::Unsecured)?;
+ return Err(());
+ }
+ },
+ TargetState::Unsecured => self.disconnect_tunnel(),
+ }
+ }
+ Ok(())
+ }
+
+ fn connect_tunnel(&mut self, account_token: AccountToken) {
+ let command = match self.settings.get_relay_settings() {
+ RelaySettings::CustomTunnelEndpoint(custom_relay) => custom_relay
+ .to_tunnel_endpoint()
+ .chain_err(|| "Custom tunnel endpoint could not be resolved"),
+ RelaySettings::Normal(constraints) => self
+ .relay_selector
+ .get_tunnel_endpoint(&constraints)
+ .chain_err(|| "No valid relay servers match the current settings")
+ .map(|(relay, endpoint)| {
+ self.current_relay = Some(relay);
+ endpoint
+ }),
+ }.map(|endpoint| self.build_tunnel_parameters(account_token, endpoint))
+ .map(|parameters| TunnelCommand::Connect(parameters))
+ .unwrap_or_else(|error| {
+ error!("{}", error.display_chain());
+ TunnelCommand::Block(BlockReason::NoMatchingRelay, self.settings.get_allow_lan())
+ });
+ self.send_tunnel_command(command);
+ }
+
+ fn disconnect_tunnel(&mut self) {
+ self.send_tunnel_command(TunnelCommand::Disconnect);
+ }
+
+ fn reconnect_tunnel(&mut self) {
+ if self.target_state == TargetState::Secured {
+ if let Some(account_token) = self.settings.get_account_token() {
+ self.connect_tunnel(account_token);
+ }
+ }
+ }
+
+ fn build_tunnel_parameters(
+ &self,
+ account_token: AccountToken,
+ endpoint: TunnelEndpoint,
+ ) -> TunnelParameters {
+ TunnelParameters {
+ endpoint,
+ options: self.settings.get_tunnel_options().clone(),
+ log_dir: self.log_dir.clone(),
+ resource_dir: self.resource_dir.clone(),
+ username: account_token,
+ allow_lan: self.settings.get_allow_lan(),
+ }
+ }
+
+ fn send_tunnel_command(&mut self, command: TunnelCommand) {
+ self.tunnel_command_tx
+ .send(command)
+ .expect("Tunnel state machine has stopped");
+ }
+
+ pub fn shutdown_handle(&self) -> DaemonShutdownHandle {
+ DaemonShutdownHandle {
+ tx: self.tx.clone(),
+ }
+ }
+}
+
+pub struct DaemonShutdownHandle {
+ tx: mpsc::Sender<DaemonEvent>,
+}
+
+impl DaemonShutdownHandle {
+ pub fn shutdown(&self) {
+ let _ = self.tx.send(DaemonEvent::TriggerShutdown);
+ }
+}
+
+impl Drop for Daemon {
+ fn drop(&mut self) {
+ #[cfg(unix)]
+ {
+ use std::fs;
+ if let Err(e) = fs::remove_file(&self.management_interface_socket_path) {
+ error!(
+ "Failed to remove RPC socket {}: {}",
+ self.management_interface_socket_path, e
+ );
+ }
+ }
+ }
+}
diff --git a/mullvad-daemon/src/logging.rs b/mullvad-daemon/src/logging.rs
index 24b60bb5e9..3f8f4f59dc 100644
--- a/mullvad-daemon/src/logging.rs
+++ b/mullvad-daemon/src/logging.rs
@@ -58,7 +58,7 @@ const LINE_SEPARATOR: &str = "\n";
#[cfg(windows)]
const LINE_SEPARATOR: &str = "\r\n";
-pub const DATE_TIME_FORMAT_STR: &str = "[%Y-%m-%d %H:%M:%S%.3f]";
+const DATE_TIME_FORMAT_STR: &str = "[%Y-%m-%d %H:%M:%S%.3f]";
pub fn init_logger(
log_level: log::LevelFilter,
diff --git a/mullvad-daemon/src/main.rs b/mullvad-daemon/src/main.rs
index 661da885dc..e8bd24ccc1 100644
--- a/mullvad-daemon/src/main.rs
+++ b/mullvad-daemon/src/main.rs
@@ -11,77 +11,31 @@ extern crate chrono;
extern crate clap;
#[macro_use]
extern crate error_chain;
-extern crate futures;
#[cfg(unix)]
extern crate libc;
#[macro_use]
extern crate log;
extern crate log_panics;
-
-#[macro_use]
-extern crate serde;
-extern crate serde_json;
-
-extern crate jsonrpc_core;
-#[macro_use]
-extern crate jsonrpc_macros;
-extern crate jsonrpc_ipc_server;
-extern crate jsonrpc_pubsub;
-extern crate rand;
-extern crate tokio_core;
-extern crate tokio_timer;
-extern crate uuid;
-
-extern crate mullvad_ipc_client;
+extern crate mullvad_daemon;
extern crate mullvad_paths;
-extern crate mullvad_rpc;
-extern crate mullvad_types;
extern crate talpid_core;
-extern crate talpid_ipc;
-extern crate talpid_types;
#[cfg(windows)]
#[macro_use]
extern crate windows_service;
-mod account_history;
+use error_chain::ChainedError;
+use mullvad_daemon::Daemon;
+use std::{thread, time::Duration};
+
mod cli;
-mod geoip;
mod logging;
-mod management_interface;
-mod relays;
-mod rpc_uniqueness_check;
mod shutdown;
+#[cfg(windows)]
mod system_service;
mod version;
-use error_chain::ChainedError;
-use futures::sync::mpsc::UnboundedSender;
-use futures::{Future, Sink};
-use jsonrpc_core::futures::sync::oneshot::{self, Sender as OneshotSender};
-
-use management_interface::{BoxFuture, ManagementCommand, ManagementInterfaceServer};
-use mullvad_rpc::{AccountsProxy, AppVersionProxy, HttpHandle};
-
-use mullvad_types::account::{AccountData, AccountToken};
-use mullvad_types::location::GeoIpLocation;
-use mullvad_types::relay_constraints::{RelaySettings, RelaySettingsUpdate};
-use mullvad_types::relay_list::{Relay, RelayList};
-use mullvad_types::settings::Settings;
-use mullvad_types::states::TargetState;
-use mullvad_types::version::{AppVersion, AppVersionInfo};
-
-use std::net::IpAddr;
-use std::path::PathBuf;
-use std::sync::mpsc;
-use std::time::Duration;
-use std::{mem, thread};
-
-use talpid_core::mpsc::IntoSender;
-use talpid_core::tunnel_state_machine::{self, TunnelCommand, TunnelParameters};
-use talpid_types::net::TunnelEndpoint;
-use talpid_types::tunnel::{BlockReason, TunnelStateTransition};
-
+const DAEMON_LOG_FILENAME: &str = "daemon.log";
error_chain!{
errors {
@@ -89,604 +43,12 @@ error_chain!{
description("Error setting up log")
display("Error setting up log: {}", msg)
}
- NoCacheDir {
- description("Unable to create cache directory")
- }
- DaemonIsAlreadyRunning {
- description("Another instance of the daemon is already running")
- }
- ManagementInterfaceError(msg: &'static str) {
- description("Error in the management interface")
- display("Management interface error: {}", msg)
- }
}
links {
- TunnelError(tunnel_state_machine::Error, tunnel_state_machine::ErrorKind);
- }
-}
-
-const DAEMON_LOG_FILENAME: &str = "daemon.log";
-
-type SyncUnboundedSender<T> = ::futures::sink::Wait<UnboundedSender<T>>;
-
-/// All events that can happen in the daemon. Sent from various threads and exposed interfaces.
-pub enum DaemonEvent {
- /// Tunnel has changed state.
- TunnelStateTransition(TunnelStateTransition),
- /// An event coming from the JSONRPC-2.0 management interface.
- ManagementInterfaceEvent(ManagementCommand),
- /// Triggered if the server hosting the JSONRPC-2.0 management interface dies unexpectedly.
- ManagementInterfaceExited,
- /// Daemon shutdown triggered by a signal, ctrl-c or similar.
- TriggerShutdown,
-}
-
-impl From<TunnelStateTransition> for DaemonEvent {
- fn from(tunnel_state_transition: TunnelStateTransition) -> Self {
- DaemonEvent::TunnelStateTransition(tunnel_state_transition)
- }
-}
-
-impl From<ManagementCommand> for DaemonEvent {
- fn from(command: ManagementCommand) -> Self {
- DaemonEvent::ManagementInterfaceEvent(command)
- }
-}
-
-#[derive(Clone, Debug, Eq, PartialEq)]
-enum DaemonExecutionState {
- Running,
- Exiting,
- Finished,
-}
-
-impl DaemonExecutionState {
- pub fn shutdown(&mut self, tunnel_state: &TunnelStateTransition) {
- use self::DaemonExecutionState::*;
-
- match self {
- Running => {
- match tunnel_state {
- TunnelStateTransition::Disconnected => mem::replace(self, Finished),
- _ => mem::replace(self, Exiting),
- };
- }
- Exiting | Finished => {}
- };
- }
-
- pub fn disconnected(&mut self) {
- use self::DaemonExecutionState::*;
-
- match self {
- Exiting => {
- mem::replace(self, Finished);
- }
- Running | Finished => {}
- };
- }
-
- pub fn is_running(&mut self) -> bool {
- use self::DaemonExecutionState::*;
-
- match self {
- Running => true,
- Exiting | Finished => false,
- }
+ DaemonError(mullvad_daemon::Error, mullvad_daemon::ErrorKind);
}
}
-
-struct Daemon {
- tunnel_command_tx: SyncUnboundedSender<TunnelCommand>,
- tunnel_state: TunnelStateTransition,
- target_state: TargetState,
- state: DaemonExecutionState,
- rx: mpsc::Receiver<DaemonEvent>,
- tx: mpsc::Sender<DaemonEvent>,
- management_interface_broadcaster: management_interface::EventBroadcaster,
- settings: Settings,
- accounts_proxy: AccountsProxy<HttpHandle>,
- version_proxy: AppVersionProxy<HttpHandle>,
- https_handle: mullvad_rpc::rest::RequestSender,
- tokio_remote: tokio_core::reactor::Remote,
- relay_selector: relays::RelaySelector,
- current_relay: Option<Relay>,
- log_dir: Option<PathBuf>,
- resource_dir: PathBuf,
-}
-
-impl Daemon {
- pub fn new(
- log_dir: Option<PathBuf>,
- resource_dir: PathBuf,
- cache_dir: PathBuf,
- ) -> Result<Self> {
- ensure!(
- !rpc_uniqueness_check::is_another_instance_running(),
- ErrorKind::DaemonIsAlreadyRunning
- );
- let ca_path = resource_dir.join(mullvad_paths::resources::API_CA_FILENAME);
-
- let mut rpc_manager = mullvad_rpc::MullvadRpcFactory::with_cache_dir(&cache_dir, &ca_path);
-
- let (rpc_handle, https_handle, tokio_remote) =
- mullvad_rpc::event_loop::create(move |core| {
- let handle = core.handle();
- let rpc = rpc_manager.new_connection_on_event_loop(&handle);
- let https_handle = mullvad_rpc::rest::create_https_client(&ca_path, &handle);
- let remote = core.remote();
- (rpc, https_handle, remote)
- }).chain_err(|| "Unable to initialize network event loop")?;
- let rpc_handle = rpc_handle.chain_err(|| "Unable to create RPC client")?;
- let https_handle = https_handle.chain_err(|| "Unable to create am.i.mullvad client")?;
-
- let relay_selector =
- relays::RelaySelector::new(rpc_handle.clone(), &resource_dir, &cache_dir);
-
- let (tx, rx) = mpsc::channel();
- let tunnel_command_tx =
- tunnel_state_machine::spawn(cache_dir.clone(), IntoSender::from(tx.clone()))?;
-
- let target_state = TargetState::Unsecured;
- let management_interface_broadcaster =
- Self::start_management_interface(tx.clone(), cache_dir.clone())?;
-
- // Attempt to download a fresh relay list
- relay_selector.update();
-
- Ok(Daemon {
- tunnel_command_tx: Sink::wait(tunnel_command_tx),
- tunnel_state: TunnelStateTransition::Disconnected,
- target_state,
- state: DaemonExecutionState::Running,
- rx,
- tx,
- management_interface_broadcaster,
- settings: Settings::load().chain_err(|| "Unable to read settings")?,
- accounts_proxy: AccountsProxy::new(rpc_handle.clone()),
- version_proxy: AppVersionProxy::new(rpc_handle),
- https_handle,
- tokio_remote,
- relay_selector,
- current_relay: None,
- log_dir,
- resource_dir,
- })
- }
-
- // Starts the management interface and spawns a thread that will process it.
- // Returns a handle that allows notifying all subscribers on events.
- fn start_management_interface(
- event_tx: mpsc::Sender<DaemonEvent>,
- cache_dir: PathBuf,
- ) -> Result<management_interface::EventBroadcaster> {
- let multiplex_event_tx = IntoSender::from(event_tx.clone());
- let server = Self::start_management_interface_server(multiplex_event_tx, cache_dir)?;
- let event_broadcaster = server.event_broadcaster();
- Self::spawn_management_interface_wait_thread(server, event_tx);
- Ok(event_broadcaster)
- }
-
- fn start_management_interface_server(
- event_tx: IntoSender<ManagementCommand, DaemonEvent>,
- cache_dir: PathBuf,
- ) -> Result<ManagementInterfaceServer> {
- let server = ManagementInterfaceServer::start(event_tx, cache_dir)
- .chain_err(|| ErrorKind::ManagementInterfaceError("Failed to start server"))?;
- info!(
- "Mullvad management interface listening on {}",
- server.socket_path()
- );
-
- Ok(server)
- }
-
- fn spawn_management_interface_wait_thread(
- server: ManagementInterfaceServer,
- exit_tx: mpsc::Sender<DaemonEvent>,
- ) {
- thread::spawn(move || {
- server.wait();
- error!("Mullvad management interface shut down");
- let _ = exit_tx.send(DaemonEvent::ManagementInterfaceExited);
- });
- }
-
- /// Consume the `Daemon` and run the main event loop. Blocks until an error happens or a
- /// shutdown event is received.
- pub fn run(mut self) -> Result<()> {
- if self.settings.get_auto_connect() {
- info!("Automatically connecting since auto-connect is turned on");
- if self.set_target_state(TargetState::Secured).is_err() {
- warn!("Aborting auto-connect since no account token is set");
- }
- }
- while let Ok(event) = self.rx.recv() {
- self.handle_event(event)?;
- if self.state == DaemonExecutionState::Finished {
- break;
- }
- }
- Ok(())
- }
-
- fn handle_event(&mut self, event: DaemonEvent) -> Result<()> {
- use DaemonEvent::*;
- match event {
- TunnelStateTransition(transition) => {
- Ok(self.handle_tunnel_state_transition(transition))
- }
- ManagementInterfaceEvent(event) => Ok(self.handle_management_interface_event(event)),
- ManagementInterfaceExited => self.handle_management_interface_exited(),
- TriggerShutdown => Ok(self.handle_trigger_shutdown_event()),
- }
- }
-
- fn handle_tunnel_state_transition(&mut self, tunnel_state: TunnelStateTransition) {
- use self::TunnelStateTransition::*;
-
- debug!("New tunnel state: {:?}", tunnel_state);
- match tunnel_state {
- Disconnected => {
- self.state.disconnected();
- self.current_relay = None;
- }
- Blocked(ref reason) => {
- info!("Blocking all network connections, reason: {}", reason);
-
- match reason {
- BlockReason::AuthFailed(_) => self.schedule_reconnect(Duration::from_secs(60)),
- _ => {}
- }
- }
- _ => {}
- }
-
- self.tunnel_state = tunnel_state.clone();
- self.management_interface_broadcaster
- .notify_new_state(tunnel_state);
- }
-
- fn schedule_reconnect(&mut self, delay: Duration) {
- let command_tx = self.tx.clone();
-
- thread::spawn(move || {
- let (result_tx, _result_rx) = oneshot::channel();
-
- thread::sleep(delay);
- debug!("Attempting to reconnect");
- let _ = command_tx.send(DaemonEvent::ManagementInterfaceEvent(
- ManagementCommand::SetTargetState(result_tx, TargetState::Secured),
- ));
- });
- }
-
- fn handle_management_interface_event(&mut self, event: ManagementCommand) {
- use ManagementCommand::*;
- match event {
- SetTargetState(tx, state) => self.on_set_target_state(tx, state),
- GetState(tx) => self.on_get_state(tx),
- GetCurrentLocation(tx) => self.on_get_current_location(tx),
- GetAccountData(tx, account_token) => self.on_get_account_data(tx, account_token),
- GetRelayLocations(tx) => self.on_get_relay_locations(tx),
- SetAccount(tx, account_token) => self.on_set_account(tx, account_token),
- UpdateRelaySettings(tx, update) => self.on_update_relay_settings(tx, update),
- SetAllowLan(tx, allow_lan) => self.on_set_allow_lan(tx, allow_lan),
- SetAutoConnect(tx, auto_connect) => self.on_set_auto_connect(tx, auto_connect),
- SetOpenVpnMssfix(tx, mssfix_arg) => self.on_set_openvpn_mssfix(tx, mssfix_arg),
- SetEnableIpv6(tx, enable_ipv6) => self.on_set_enable_ipv6(tx, enable_ipv6),
- GetSettings(tx) => self.on_get_settings(tx),
- GetVersionInfo(tx) => self.on_get_version_info(tx),
- GetCurrentVersion(tx) => self.on_get_current_version(tx),
- Shutdown => self.handle_trigger_shutdown_event(),
- }
- }
-
- fn on_set_target_state(
- &mut self,
- tx: OneshotSender<::std::result::Result<(), ()>>,
- new_target_state: TargetState,
- ) {
- if self.state.is_running() {
- Self::oneshot_send(tx, self.set_target_state(new_target_state), "targe state");
- } else {
- warn!("Ignoring target state change request due to shutdown");
- Self::oneshot_send(tx, Ok(()), "targe state");
- }
- }
-
- fn on_get_state(&self, tx: OneshotSender<TunnelStateTransition>) {
- Self::oneshot_send(tx, self.tunnel_state.clone(), "current state");
- }
-
- fn on_get_current_location(&self, tx: OneshotSender<GeoIpLocation>) {
- if let Some(ref relay) = self.current_relay {
- let location = relay.location.as_ref().cloned().unwrap();
- let geo_ip_location = GeoIpLocation {
- ip: IpAddr::V4(relay.ipv4_addr_exit),
- country: location.country,
- city: Some(location.city),
- latitude: location.latitude,
- longitude: location.longitude,
- mullvad_exit_ip: true,
- };
- Self::oneshot_send(tx, geo_ip_location, "current location");
- } else {
- let https_handle = self.https_handle.clone();
- self.tokio_remote.spawn(move |_| {
- geoip::send_location_request(https_handle)
- .map(move |location| Self::oneshot_send(tx, location, "current location"))
- .map_err(|e| {
- warn!("Unable to fetch GeoIP location: {}", e.display_chain());
- })
- });
- }
- }
-
- fn on_get_account_data(
- &mut self,
- tx: OneshotSender<BoxFuture<AccountData, mullvad_rpc::Error>>,
- account_token: AccountToken,
- ) {
- let rpc_call = self
- .accounts_proxy
- .get_expiry(account_token)
- .map(|expiry| AccountData { expiry });
- Self::oneshot_send(tx, Box::new(rpc_call), "account data")
- }
-
- fn on_get_relay_locations(&mut self, tx: OneshotSender<RelayList>) {
- Self::oneshot_send(tx, self.relay_selector.get_locations(), "relay locations");
- }
-
-
- fn on_set_account(&mut self, tx: OneshotSender<()>, account_token: Option<String>) {
- let account_token_cleared = account_token.is_none();
- let save_result = self.settings.set_account_token(account_token);
-
- match save_result.chain_err(|| "Unable to save settings") {
- Ok(account_changed) => {
- Self::oneshot_send(tx, (), "set_account response");
- if account_changed {
- self.management_interface_broadcaster
- .notify_settings(&self.settings);
- if account_token_cleared {
- info!("Disconnecting because account token was cleared");
- let _ = self.set_target_state(TargetState::Unsecured);
- } else {
- info!("Initiating tunnel restart because the account token changed");
- self.reconnect_tunnel();
- }
- }
- }
- Err(e) => error!("{}", e.display_chain()),
- }
- }
-
- fn on_get_version_info(
- &mut self,
- tx: OneshotSender<BoxFuture<AppVersionInfo, mullvad_rpc::Error>>,
- ) {
- let current_version = version::CURRENT.to_owned();
- let fut = self
- .version_proxy
- .latest_app_version()
- .join(
- self.version_proxy
- .is_app_version_supported(&current_version),
- ).map(|(latest_versions, is_supported)| AppVersionInfo {
- current_is_supported: is_supported,
- latest: latest_versions,
- });
- Self::oneshot_send(tx, Box::new(fut), "get_version_info response");
- }
-
- fn on_get_current_version(&mut self, tx: OneshotSender<AppVersion>) {
- let current_version = version::CURRENT.to_owned();
- Self::oneshot_send(tx, current_version, "get_current_version response");
- }
-
- fn on_update_relay_settings(&mut self, tx: OneshotSender<()>, update: RelaySettingsUpdate) {
- let save_result = self.settings.update_relay_settings(update);
- match save_result.chain_err(|| "Unable to save settings") {
- Ok(settings_changed) => {
- Self::oneshot_send(tx, (), "update_relay_settings response");
- if settings_changed {
- self.management_interface_broadcaster
- .notify_settings(&self.settings);
- info!("Initiating tunnel restart because the relay settings changed");
- self.reconnect_tunnel();
- }
- }
- Err(e) => error!("{}", e.display_chain()),
- }
- }
-
- fn on_set_allow_lan(&mut self, tx: OneshotSender<()>, allow_lan: bool) {
- let save_result = self.settings.set_allow_lan(allow_lan);
- match save_result.chain_err(|| "Unable to save settings") {
- Ok(settings_changed) => {
- Self::oneshot_send(tx, (), "set_allow_lan response");
- if settings_changed {
- self.management_interface_broadcaster
- .notify_settings(&self.settings);
- self.send_tunnel_command(TunnelCommand::AllowLan(allow_lan));
- }
- }
- Err(e) => error!("{}", e.display_chain()),
- }
- }
-
- fn on_set_auto_connect(&mut self, tx: OneshotSender<()>, auto_connect: bool) {
- let save_result = self.settings.set_auto_connect(auto_connect);
- match save_result.chain_err(|| "Unable to save settings") {
- Ok(settings_changed) => {
- Self::oneshot_send(tx, (), "set auto-connect response");
- if settings_changed {
- self.management_interface_broadcaster
- .notify_settings(&self.settings);
- }
- }
- Err(e) => error!("{}", e.display_chain()),
- }
- }
-
- fn on_set_openvpn_mssfix(&mut self, tx: OneshotSender<()>, mssfix_arg: Option<u16>) {
- let save_result = self.settings.set_openvpn_mssfix(mssfix_arg);
- match save_result.chain_err(|| "Unable to save settings") {
- Ok(settings_changed) => {
- Self::oneshot_send(tx, (), "set_openvpn_mssfix response");
- if settings_changed {
- self.management_interface_broadcaster
- .notify_settings(&self.settings);
- }
- }
- Err(e) => error!("{}", e.display_chain()),
- }
- }
-
- fn on_set_enable_ipv6(&mut self, tx: OneshotSender<()>, enable_ipv6: bool) {
- let save_result = self.settings.set_enable_ipv6(enable_ipv6);
- match save_result.chain_err(|| "Unable to save settings") {
- Ok(settings_changed) => {
- Self::oneshot_send(tx, (), "set_enable_ipv6 response");
- if settings_changed {
- self.management_interface_broadcaster
- .notify_settings(&self.settings);
- info!("Initiating tunnel restart because the enable IPv6 setting changed");
- self.reconnect_tunnel();
- }
- }
- Err(e) => error!("{}", e.display_chain()),
- }
- }
-
- fn on_get_settings(&self, tx: OneshotSender<Settings>) {
- Self::oneshot_send(tx, self.settings.clone(), "get_settings response");
- }
-
- fn oneshot_send<T>(tx: OneshotSender<T>, t: T, msg: &'static str) {
- if let Err(_) = tx.send(t) {
- warn!("Unable to send {} to management interface client", msg);
- }
- }
-
- fn handle_management_interface_exited(&self) -> Result<()> {
- Err(ErrorKind::ManagementInterfaceError("Server exited unexpectedly").into())
- }
-
- fn handle_trigger_shutdown_event(&mut self) {
- self.state.shutdown(&self.tunnel_state);
- self.disconnect_tunnel();
- }
-
- /// Set the target state of the client. If it changed trigger the operations needed to
- /// progress towards that state.
- /// Returns an error if trying to set secured state, but no account token is present.
- fn set_target_state(&mut self, new_state: TargetState) -> ::std::result::Result<(), ()> {
- if new_state != self.target_state || self.tunnel_state.is_blocked() {
- debug!("Target state {:?} => {:?}", self.target_state, new_state);
- self.target_state = new_state;
- match self.target_state {
- TargetState::Secured => match self.settings.get_account_token() {
- Some(account_token) => self.connect_tunnel(account_token),
- None => {
- self.set_target_state(TargetState::Unsecured)?;
- return Err(());
- }
- },
- TargetState::Unsecured => self.disconnect_tunnel(),
- }
- }
- Ok(())
- }
-
- fn connect_tunnel(&mut self, account_token: AccountToken) {
- let command = match self.settings.get_relay_settings() {
- RelaySettings::CustomTunnelEndpoint(custom_relay) => custom_relay
- .to_tunnel_endpoint()
- .chain_err(|| "Custom tunnel endpoint could not be resolved"),
- RelaySettings::Normal(constraints) => self
- .relay_selector
- .get_tunnel_endpoint(&constraints)
- .chain_err(|| "No valid relay servers match the current settings")
- .map(|(relay, endpoint)| {
- self.current_relay = Some(relay);
- endpoint
- }),
- }.map(|endpoint| self.build_tunnel_parameters(account_token, endpoint))
- .map(|parameters| TunnelCommand::Connect(parameters))
- .unwrap_or_else(|error| {
- error!("{}", error.display_chain());
- TunnelCommand::Block(BlockReason::NoMatchingRelay, self.settings.get_allow_lan())
- });
- self.send_tunnel_command(command);
- }
-
- fn disconnect_tunnel(&mut self) {
- self.send_tunnel_command(TunnelCommand::Disconnect);
- }
-
- fn reconnect_tunnel(&mut self) {
- if self.target_state == TargetState::Secured {
- if let Some(account_token) = self.settings.get_account_token() {
- self.connect_tunnel(account_token);
- }
- }
- }
-
- fn build_tunnel_parameters(
- &self,
- account_token: AccountToken,
- endpoint: TunnelEndpoint,
- ) -> TunnelParameters {
- TunnelParameters {
- endpoint,
- options: self.settings.get_tunnel_options().clone(),
- log_dir: self.log_dir.clone(),
- resource_dir: self.resource_dir.clone(),
- username: account_token,
- allow_lan: self.settings.get_allow_lan(),
- }
- }
-
- fn send_tunnel_command(&mut self, command: TunnelCommand) {
- self.tunnel_command_tx
- .send(command)
- .expect("Tunnel state machine has stopped");
- }
-
- pub fn shutdown_handle(&self) -> DaemonShutdownHandle {
- DaemonShutdownHandle {
- tx: self.tx.clone(),
- }
- }
-}
-
-struct DaemonShutdownHandle {
- tx: mpsc::Sender<DaemonEvent>,
-}
-
-impl DaemonShutdownHandle {
- pub fn shutdown(&self) {
- let _ = self.tx.send(DaemonEvent::TriggerShutdown);
- }
-}
-
-impl Drop for Daemon {
- fn drop(self: &mut Daemon) {
- #[cfg(unix)]
- {
- use std::fs;
- if let Err(e) = fs::remove_file(mullvad_paths::get_rpc_socket_path()) {
- error!("Failed to remove RPC socket: {}", e);
- }
- }
- }
-}
-
-
fn main() {
let exit_code = match run() {
Ok(_) => 0,
@@ -779,7 +141,12 @@ fn create_daemon(config: cli::Config) -> Result<Daemon> {
let resource_dir = mullvad_paths::get_resource_dir();
let cache_dir = mullvad_paths::cache_dir().chain_err(|| "Unable to get cache dir")?;
- Daemon::new(log_dir, resource_dir, cache_dir).chain_err(|| "Unable to initialize daemon")
+ Daemon::new(
+ log_dir,
+ resource_dir,
+ cache_dir,
+ version::CURRENT.to_owned(),
+ ).chain_err(|| "Unable to initialize daemon")
}
fn log_version() {
diff --git a/mullvad-daemon/src/relays.rs b/mullvad-daemon/src/relays.rs
index 0f35a25625..b55cfad22b 100644
--- a/mullvad-daemon/src/relays.rs
+++ b/mullvad-daemon/src/relays.rs
@@ -23,6 +23,7 @@ use std::{io, thread};
use rand::{self, Rng, ThreadRng};
use tokio_timer::{TimeoutError, Timer};
+const DATE_TIME_FORMAT_STR: &str = "[%Y-%m-%d %H:%M:%S%.3f]";
const RELAYS_FILENAME: &str = "relays.json";
const DOWNLOAD_TIMEOUT: Duration = Duration::from_secs(15);
const UPDATE_INTERVAL: Duration = Duration::from_secs(60 * 60);
@@ -142,7 +143,7 @@ impl RelaySelector {
"Initialized with {} cached relays from {}",
unsynchronized_parsed_relays.relays().len(),
DateTime::<Local>::from(unsynchronized_parsed_relays.last_updated())
- .format(::logging::DATE_TIME_FORMAT_STR)
+ .format(DATE_TIME_FORMAT_STR)
);
let parsed_relays = Arc::new(Mutex::new(unsynchronized_parsed_relays));
let updater = RelayListUpdater::spawn(rpc_handle, cache_path, parsed_relays.clone());
diff --git a/mullvad-daemon/src/system_service.rs b/mullvad-daemon/src/system_service.rs
index 5f3b1299b4..ee08cae841 100644
--- a/mullvad-daemon/src/system_service.rs
+++ b/mullvad-daemon/src/system_service.rs
@@ -1,5 +1,3 @@
-#![cfg(windows)]
-
use std::ffi::OsString;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::{mpsc, Arc};
@@ -18,7 +16,8 @@ use windows_service::service_control_handler::{
use windows_service::service_dispatcher;
use windows_service::service_manager::{ServiceManager, ServiceManagerAccess};
-use super::{DaemonShutdownHandle, Result, ResultExt};
+use super::{Result, ResultExt};
+use mullvad_daemon::DaemonShutdownHandle;
static SERVICE_NAME: &'static str = "MullvadVPN";
static SERVICE_DISPLAY_NAME: &'static str = "Mullvad VPN Service";
@@ -34,15 +33,15 @@ pub fn run() -> Result<()> {
define_windows_service!(service_main, handle_service_main);
-pub fn handle_service_main(arguments: Vec<OsString>) {
+pub fn handle_service_main(_arguments: Vec<OsString>) {
info!("Service started.");
- match run_service(arguments) {
- Ok(_) => info!("Service stopped."),
- Err(ref e) => error!("{}", e.display_chain()),
+ match run_service() {
+ Ok(()) => info!("Service stopped."),
+ Err(error) => error!("{}", error.display_chain()),
};
}
-fn run_service(_arguments: Vec<OsString>) -> Result<()> {
+fn run_service() -> Result<()> {
let (event_tx, event_rx) = mpsc::channel();
// Register service event handler
@@ -76,11 +75,11 @@ fn run_service(_arguments: Vec<OsString>) -> Result<()> {
persistent_service_status.set_running().unwrap();
- daemon.run()
+ Ok(daemon.run()?)
});
let exit_code = match result {
- Ok(_) => ServiceExitCode::default(),
+ Ok(()) => ServiceExitCode::default(),
Err(_) => ServiceExitCode::ServiceSpecific(1),
};
diff --git a/mullvad-daemon/version.rc b/mullvad-daemon/version.rc
deleted file mode 100644
index 1c7360bbc0..0000000000
--- a/mullvad-daemon/version.rc
+++ /dev/null
@@ -1,25 +0,0 @@
-#include "../dist-assets/windows/version.h"
-
-1 VERSIONINFO
-FILEVERSION MAJOR_VERSION,MINOR_VERSION,PATCH_VERSION,0
-PRODUCTVERSION MAJOR_VERSION,MINOR_VERSION,PATCH_VERSION,0
-BEGIN
-BLOCK "StringFileInfo"
-BEGIN
- BLOCK "040904E4"
- BEGIN
- VALUE "CompanyName", "Amagicom AB"
- VALUE "FileDescription", "Daemon that manages the VPN tunnel and system security"
- VALUE "FileVersion", PRODUCT_VERSION
- VALUE "InternalName", "mullvad-daemon"
- VALUE "LegalCopyright", "(c) 2018 Amagicom AB"
- VALUE "OriginalFilename", "mullvad-daemon.exe"
- VALUE "ProductName", "Mullvad VPN"
- VALUE "ProductVersion", PRODUCT_VERSION
- END
-END
-BLOCK "VarFileInfo"
-BEGIN
- VALUE "Translation", 0x409, 1252
-END
-END