diff options
| -rw-r--r-- | Cargo.lock | 13 | ||||
| -rw-r--r-- | gui/src/main/daemon-rpc.ts | 8 | ||||
| -rw-r--r-- | gui/src/main/index.ts | 4 | ||||
| -rw-r--r-- | gui/src/shared/daemon-rpc-types.ts | 9 | ||||
| -rw-r--r-- | mullvad-cli/src/cmds/status.rs | 5 | ||||
| -rw-r--r-- | mullvad-cli/src/cmds/tunnel.rs | 6 | ||||
| -rw-r--r-- | mullvad-daemon/Cargo.toml | 2 | ||||
| -rw-r--r-- | mullvad-daemon/src/lib.rs | 181 | ||||
| -rw-r--r-- | mullvad-daemon/src/management_interface.rs | 19 | ||||
| -rw-r--r-- | mullvad-daemon/src/wireguard.rs | 234 | ||||
| -rw-r--r-- | mullvad-ipc-client/src/lib.rs | 2 | ||||
| -rw-r--r-- | mullvad-jni/src/daemon_interface.rs | 8 | ||||
| -rw-r--r-- | mullvad-jni/src/jni_event_listener.rs | 5 | ||||
| -rw-r--r-- | mullvad-jni/src/lib.rs | 8 | ||||
| -rw-r--r-- | mullvad-types/src/lib.rs | 3 | ||||
| -rw-r--r-- | mullvad-types/src/wireguard.rs | 20 |
16 files changed, 474 insertions, 53 deletions
diff --git a/Cargo.lock b/Cargo.lock index f98147de1c..6ed1e941b0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1112,6 +1112,7 @@ dependencies = [ "fern 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", "ipnetwork 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)", + "jsonrpc-client-core 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", "jsonrpc-core 8.0.2 (git+https://github.com/mullvad/jsonrpc?branch=mullvad-fork)", "jsonrpc-ipc-server 8.0.1 (git+https://github.com/mullvad/jsonrpc?branch=mullvad-fork)", "jsonrpc-macros 8.0.1 (git+https://github.com/mullvad/jsonrpc?branch=mullvad-fork)", @@ -1133,6 +1134,7 @@ dependencies = [ "talpid-ipc 0.1.0", "talpid-types 0.1.0", "tokio-core 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-retry 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-timer 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "uuid 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2406,6 +2408,16 @@ dependencies = [ ] [[package]] +name = "tokio-retry" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-timer 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] name = "tokio-service" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2992,6 +3004,7 @@ dependencies = [ "checksum tokio-process 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "88e1281e412013f1ff5787def044a9577a0bed059f451e835f1643201f8b777d" "checksum tokio-proto 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8fbb47ae81353c63c487030659494b295f6cb6576242f907f203473b191b0389" "checksum tokio-reactor 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "6af16bfac7e112bea8b0442542161bfc41cbfa4466b580bdda7d18cb88b911ce" +"checksum tokio-retry 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9c03755b956458582182941061def32b8123a26c98b08fc6ddcf49ae89d18f33" "checksum tokio-service 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "24da22d077e0f15f55162bdbdc661228c1581892f52074fb242678d015b45162" "checksum tokio-signal 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "dd6dc5276ea05ce379a16de90083ec80836440d5ef8a6a39545a3207373b8296" "checksum tokio-sync 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "fda385df506bf7546e70872767f71e81640f1f251bdf2fd8eb81a0eaec5fe022" diff --git a/gui/src/main/daemon-rpc.ts b/gui/src/main/daemon-rpc.ts index f50428ea8a..215807b2a7 100644 --- a/gui/src/main/daemon-rpc.ts +++ b/gui/src/main/daemon-rpc.ts @@ -337,6 +337,14 @@ const daemonEventSchema = oneOf( object({ relay_list: relayListSchema, }), + object({ + wireguard_key: oneOf( + enumeration('too_many_keys', 'generation_failure'), + object({ + new_key: string, + }), + ), + }), ); export class ResponseParseError extends Error { diff --git a/gui/src/main/index.ts b/gui/src/main/index.ts index 15a137dd10..b086b97d08 100644 --- a/gui/src/main/index.ts +++ b/gui/src/main/index.ts @@ -486,6 +486,10 @@ class ApplicationMain { this.setSettings(daemonEvent.settings); } else if ('relayList' in daemonEvent) { this.setRelays(daemonEvent.relayList, this.settings.relaySettings); + } else if ('wireguardKey' in daemonEvent) { + /// TODO: handle wireguard key events properly. + log.info(`Received new key event`); + log.info(daemonEvent); } }, (error: Error) => { diff --git a/gui/src/shared/daemon-rpc-types.ts b/gui/src/shared/daemon-rpc-types.ts index 32cd6c4d0f..120ef3a0b0 100644 --- a/gui/src/shared/daemon-rpc-types.ts +++ b/gui/src/shared/daemon-rpc-types.ts @@ -74,7 +74,8 @@ export interface IProxyEndpoint { export type DaemonEvent = | { stateTransition: TunnelStateTransition } | { settings: ISettings } - | { relayList: IRelayList }; + | { relayList: IRelayList } + | { wireguardKey: KeygenEvent }; export type TunnelStateTransition = | { state: 'disconnected' } @@ -278,6 +279,12 @@ export interface ISettings { bridgeState: BridgeState; } +export type KeygenEvent = INewWireguardKey | 'too_many_keys' | 'generation_failure'; + +export interface INewWireguardKey { + newKey: string; +} + export type BridgeState = 'auto' | 'on' | 'off'; export interface IBridgeConstraints { diff --git a/mullvad-cli/src/cmds/status.rs b/mullvad-cli/src/cmds/status.rs index 8642c581ce..4846296fbb 100644 --- a/mullvad-cli/src/cmds/status.rs +++ b/mullvad-cli/src/cmds/status.rs @@ -57,6 +57,11 @@ impl Command for Status { println!("New relay list: {:#?}", relay_list); } } + DaemonEvent::WireguardKey(key_event) => { + if verbose { + println!("{}", key_event); + } + } } } } diff --git a/mullvad-cli/src/cmds/tunnel.rs b/mullvad-cli/src/cmds/tunnel.rs index 7eacdddb78..9fc7c12469 100644 --- a/mullvad-cli/src/cmds/tunnel.rs +++ b/mullvad-cli/src/cmds/tunnel.rs @@ -173,7 +173,11 @@ impl Tunnel { fn process_wireguard_key_generate() -> Result<()> { let mut rpc = new_rpc_client()?; - rpc.generate_wireguard_key().map_err(|e| e.into()) + let result = rpc + .generate_wireguard_key() + .map_err(|e| crate::Error::RpcClientError(e))?; + println!("{}", result); + Ok(()) } fn handle_ipv6_cmd(matches: &clap::ArgMatches<'_>) -> Result<()> { diff --git a/mullvad-daemon/Cargo.toml b/mullvad-daemon/Cargo.toml index 84abf172a8..417e2bc408 100644 --- a/mullvad-daemon/Cargo.toml +++ b/mullvad-daemon/Cargo.toml @@ -32,6 +32,8 @@ uuid = { version = "0.7", features = ["v4"] } lazy_static = "1.0" rand = "0.6" tokio-core = "0.1" +tokio-retry = "0.2" +jsonrpc-client-core = "0.5" tokio-timer = "0.1" regex = "1.0" diff --git a/mullvad-daemon/src/lib.rs b/mullvad-daemon/src/lib.rs index b3aa513f6e..7a559624ec 100644 --- a/mullvad-daemon/src/lib.rs +++ b/mullvad-daemon/src/lib.rs @@ -26,7 +26,7 @@ use crate::management_interface::{ BoxFuture, ManagementInterfaceEventBroadcaster, ManagementInterfaceServer, }; use futures::{ - future, + future::{self, Executor}, sync::{mpsc::UnboundedSender, oneshot}, Future, Sink, }; @@ -43,6 +43,7 @@ use mullvad_types::{ relay_list::{Relay, RelayList}, states::TargetState, version::{AppVersion, AppVersionInfo}, + wireguard::KeygenEvent, }; use settings::Settings; use std::{io, mem, path::PathBuf, sync::mpsc, thread, time::Duration}; @@ -52,11 +53,14 @@ use talpid_core::{ tunnel_state_machine::{self, TunnelCommand, TunnelParametersGenerator}, }; use talpid_types::{ - net::{openvpn, wireguard, TransportProtocol, TunnelParameters}, + net::{openvpn, TransportProtocol, TunnelParameters}, tunnel::{BlockReason, TunnelStateTransition}, ErrorExt, }; +#[path = "wireguard.rs"] +mod wireguard; + pub type Result<T> = std::result::Result<T, Error>; #[derive(err_derive::Error, Debug)] @@ -102,7 +106,7 @@ pub enum Error { type SyncUnboundedSender<T> = ::futures::sink::Wait<UnboundedSender<T>>; /// All events that can happen in the daemon. Sent from various threads and exposed interfaces. -enum InternalDaemonEvent { +pub(crate) enum InternalDaemonEvent { /// Tunnel has changed state. TunnelStateTransition(TunnelStateTransition), /// Request from the `MullvadTunnelParametersGenerator` to obtain a new relay. @@ -113,6 +117,13 @@ enum InternalDaemonEvent { ManagementInterfaceExited, /// Daemon shutdown triggered by a signal, ctrl-c or similar. TriggerShutdown, + /// Wireguard key generation event + WgKeyEvent( + ( + AccountToken, + ::std::result::Result<mullvad_types::wireguard::WireguardData, wireguard::Error>, + ), + ), } impl From<TunnelStateTransition> for InternalDaemonEvent { @@ -192,6 +203,9 @@ pub trait EventListener { /// Notify that the relay list changed. fn notify_relay_list(&self, relay_list: RelayList); + + /// Notify clients of a key generation event. + fn notify_key_event(&self, key_event: KeygenEvent); } pub struct Daemon<L: EventListener = ManagementInterfaceEventBroadcaster> { @@ -209,6 +223,7 @@ pub struct Daemon<L: EventListener = ManagementInterfaceEventBroadcaster> { accounts_proxy: AccountsProxy<HttpHandle>, version_proxy: AppVersionProxy<HttpHandle>, https_handle: mullvad_rpc::rest::RequestSender, + wireguard_key_manager: wireguard::KeyManager, tokio_remote: tokio_core::reactor::Remote, relay_selector: relays::RelaySelector, last_generated_relay: Option<Relay>, @@ -324,6 +339,7 @@ where (rpc, https_handle, remote) }) .map_err(Error::InitIoEventLoop)?; + let rpc_handle = rpc_handle.map_err(Error::InitRpcClient)?; let https_handle = https_handle.map_err(Error::InitHttpsClient)?; @@ -332,6 +348,7 @@ where relay_list_listener.notify_relay_list(relay_list.clone()); }; + let relay_selector = relays::RelaySelector::new( rpc_handle.clone(), on_relay_list_update, @@ -359,10 +376,17 @@ where ) .map_err(Error::TunnelError)?; + + let wireguard_key_manager = wireguard::KeyManager::new( + internal_event_tx.clone(), + rpc_handle.clone(), + tokio_remote.clone(), + ); + // Attempt to download a fresh relay list relay_selector.update(); - Ok(Daemon { + let mut daemon = Daemon { tunnel_command_tx: Sink::wait(tunnel_command_tx), tunnel_state: TunnelStateTransition::Disconnected, target_state: TargetState::Unsecured, @@ -382,7 +406,12 @@ where last_generated_relay: None, last_generated_bridge_relay: None, version, - }) + wireguard_key_manager, + }; + + daemon.ensure_wireguard_keys_for_current_account(); + + Ok(daemon) } /// Retrieve a channel for sending daemon commands. @@ -390,6 +419,7 @@ where DaemonCommandSender::new(self.tx.clone()) } + /// 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<()> { @@ -418,6 +448,7 @@ where return Err(Error::ManagementInterfaceExited); } TriggerShutdown => self.handle_trigger_shutdown_event(), + WgKeyEvent(key_event) => self.handle_wireguard_key_event(key_event), } Ok(()) } @@ -658,6 +689,70 @@ where } } + fn handle_wireguard_key_event( + &mut self, + event: ( + AccountToken, + ::std::result::Result<mullvad_types::wireguard::WireguardData, wireguard::Error>, + ), + ) { + let (account, result) = event; + // If the account has been reset whilst a key was being generated, the event should be + // dropped even if a new key was generated. + if self + .settings + .get_account_token() + .map(|current_account| current_account != account) + .unwrap_or(true) + { + log::info!("Dropping wireguard key event since account has been changed"); + return; + } + + match result { + Ok(data) => { + let public_key = data.private_key.public_key(); + let mut account_entry = self + .account_history + .get(&account) + .ok() + .and_then(|entry| entry) + .unwrap_or_else(|| account_history::AccountEntry { + account: account.clone(), + wireguard: None, + }); + account_entry.wireguard = Some(data.clone()); + match self.account_history.insert(account_entry) { + Ok(_) => self + .event_listener + .notify_key_event(KeygenEvent::NewKey(public_key)), + Err(e) => { + log::error!( + "{}", + e.display_chain_with_msg( + "Failed to add new wireguard key to account data" + ) + ); + self.event_listener + .notify_key_event(KeygenEvent::GenerationFailure) + } + } + } + Err(wireguard::Error::TooManyKeys) => { + self.event_listener + .notify_key_event(KeygenEvent::TooManyKeys); + } + Err(e) => { + log::error!( + "{}", + e.display_chain_with_msg("Failed to generate wireguard key") + ); + self.event_listener + .notify_key_event(KeygenEvent::GenerationFailure); + } + } + } + fn on_set_target_state( &mut self, tx: oneshot::Sender<::std::result::Result<(), ()>>, @@ -766,6 +861,7 @@ where Ok(account_changed) => { Self::oneshot_send(tx, (), "set_account response"); if account_changed { + self.ensure_wireguard_keys_for_current_account(); self.event_listener.notify_settings(self.settings.clone()); match account_token { Some(token) => { @@ -785,7 +881,6 @@ where Err(e) => error!("{}", e.display_chain_with_msg("Unable to save settings")), } } - fn on_get_account_history(&mut self, tx: oneshot::Sender<Vec<AccountToken>>) { Self::oneshot_send( tx, @@ -1013,11 +1108,32 @@ where } } - fn on_generate_wireguard_key( - &mut self, - tx: oneshot::Sender<::std::result::Result<(), mullvad_rpc::Error>>, - ) { - let mut result = || -> ::std::result::Result<(), String> { + fn ensure_wireguard_keys_for_current_account(&mut self) { + if let Some(account) = self.settings.get_account_token() { + if self + .account_history + .get(&account) + .map(|entry| entry.map(|e| e.wireguard.is_none()).unwrap_or(true)) + .unwrap_or(true) + { + log::info!("Autoamtically generating new wireguard key for account"); + if let Err(e) = self + .wireguard_key_manager + .generate_key_async(account.to_owned()) + { + log::error!( + "{}", + e.display_chain_with_msg("Failed to start generating wireguard key") + ); + } + } else { + log::info!("Account already has wireguard key"); + } + } + } + + fn on_generate_wireguard_key(&mut self, tx: oneshot::Sender<KeygenEvent>) { + let mut result = || -> ::std::result::Result<KeygenEvent, String> { let account_token = self .settings .get_account_token() @@ -1037,32 +1153,26 @@ where }) })?; - let private_key = wireguard::PrivateKey::new_from_random() - .map_err(|e| format!("Failed to generate new key - {}", e))?; - - let fut = self - .wg_key_proxy - .push_wg_key(account_token, private_key.public_key()); - - let mut core = tokio_core::reactor::Core::new() - .map_err(|e| format!("Failed to spawn future for pushing wg key - {}", e))?; - - let addresses = core - .run(fut) - .map_err(|e| format!("Failed to push new wireguard key: {}", e))?; - - account_entry.wireguard = Some(mullvad_types::wireguard::WireguardData { - private_key, - addresses, - }); - - self.account_history - .insert(account_entry) - .map_err(|e| format!("Failed to add new wireguard key to account data: {}", e)) + match self + .wireguard_key_manager + .generate_key_sync(account_token.clone()) + { + Ok(new_data) => { + let public_key = new_data.private_key.public_key(); + account_entry.wireguard = Some(new_data.clone()); + self.account_history.insert(account_entry).map_err(|e| { + format!("Failed to add new wireguard key to account data: {}", e) + })?; + Ok(KeygenEvent::NewKey(public_key)) + } + Err(wireguard::Error::TooManyKeys) => Ok(KeygenEvent::TooManyKeys), + Err(e) => Err(format!("Failed to generate new key - {}", e)), + } }; + match result() { - Ok(()) => { - Self::oneshot_send(tx, Ok(()), "generate_wireguard_key response"); + Ok(key_event) => { + Self::oneshot_send(tx, key_event, "generate_wireguard_key response"); } Err(e) => { log::error!("Failed to generate new wireguard key - {}", e); @@ -1085,7 +1195,6 @@ where } fn on_verify_wireguard_key(&mut self, tx: oneshot::Sender<bool>) { - use futures::future::Executor; let account = match self.settings.get_account_token() { Some(account) => account, None => { diff --git a/mullvad-daemon/src/management_interface.rs b/mullvad-daemon/src/management_interface.rs index 7c195d74e2..0ef34090d7 100644 --- a/mullvad-daemon/src/management_interface.rs +++ b/mullvad-daemon/src/management_interface.rs @@ -132,7 +132,7 @@ build_rpc_trait! { /// Generates new wireguard key for current account #[rpc(meta, name = "generate_wireguard_key")] - fn generate_wireguard_key(&self, Self::Metadata) -> BoxFuture<(), Error>; + fn generate_wireguard_key(&self, Self::Metadata) -> BoxFuture<mullvad_types::wireguard::KeygenEvent, Error>; /// Retrieve a public key for current account if the account has one. #[rpc(meta, name = "get_wireguard_key")] @@ -212,7 +212,7 @@ pub enum ManagementCommand { /// Get the daemon settings GetSettings(OneshotSender<Settings>), /// Generate new wireguard key - GenerateWireguardKey(OneshotSender<Result<(), mullvad_rpc::Error>>), + GenerateWireguardKey(OneshotSender<mullvad_types::wireguard::KeygenEvent>), /// Return a public key of the currently set wireguard private key, if there is one GetWireguardKey(OneshotSender<Option<wireguard::PublicKey>>), /// Verify if the currently set wireguard key is valid. @@ -296,6 +296,11 @@ impl EventListener for ManagementInterfaceEventBroadcaster { log::debug!("Broadcasting new relay list"); self.notify(DaemonEvent::RelayList(relay_list)); } + + fn notify_key_event(&self, key_event: mullvad_types::wireguard::KeygenEvent) { + log::debug!("Broadcasting new wireguard key event"); + self.notify(DaemonEvent::WireguardKey(key_event)); + } } impl ManagementInterfaceEventBroadcaster { @@ -611,15 +616,15 @@ impl<T: From<ManagementCommand> + 'static + Send> ManagementInterfaceApi Box::new(future) } - fn generate_wireguard_key(&self, _: Self::Metadata) -> BoxFuture<(), Error> { + fn generate_wireguard_key( + &self, + _: Self::Metadata, + ) -> BoxFuture<mullvad_types::wireguard::KeygenEvent, Error> { log::debug!("generate_wireguard_key"); let (tx, rx) = sync::oneshot::channel(); let future = self .send_command_to_daemon(ManagementCommand::GenerateWireguardKey(tx)) - .and_then(|_| { - rx.map_err(|_| Error::internal_error()) - .and_then(|res| future::result(res.map_err(|_| Error::internal_error()))) - }); + .and_then(|_| rx.map_err(|_| Error::internal_error())); Box::new(future) } diff --git a/mullvad-daemon/src/wireguard.rs b/mullvad-daemon/src/wireguard.rs new file mode 100644 index 0000000000..11134c865e --- /dev/null +++ b/mullvad-daemon/src/wireguard.rs @@ -0,0 +1,234 @@ +use crate::InternalDaemonEvent; +use futures::{future::Executor, sync::oneshot, Async, Future, Poll}; +use jsonrpc_client_core::Error as JsonRpcError; +use mullvad_types::{account::AccountToken, wireguard::WireguardData}; +use std::{sync::mpsc, time::Duration}; +pub use talpid_types::net::wireguard::*; +use talpid_types::ErrorExt; +use tokio_core::reactor::Remote; +use tokio_retry::{ + strategy::{jitter, ExponentialBackoff}, + RetryIf, +}; + +const TOO_MANY_KEYS_ERROR_CODE: i64 = -703; + + +#[derive(err_derive::Error, Debug)] +pub enum Error { + #[error(display = "Failed to generate private key")] + GenerationError(#[error(cause)] rand::Error), + #[error(display = "Failed to spawn future")] + ExectuionError, + #[error(display = "Unexpected RPC error")] + RpcError(#[error(cause)] jsonrpc_client_core::Error), + #[error(display = "Account already has maximum number of keys")] + TooManyKeys, +} + +pub type Result<T> = ::std::result::Result<T, Error>; + +pub struct KeyManager { + daemon_tx: mpsc::Sender<InternalDaemonEvent>, + http_handle: mullvad_rpc::HttpHandle, + tokio_remote: Remote, + current_job: Option<CancelHandle>, +} + +impl KeyManager { + pub(crate) fn new( + daemon_tx: mpsc::Sender<InternalDaemonEvent>, + http_handle: mullvad_rpc::HttpHandle, + tokio_remote: Remote, + ) -> Self { + Self { + daemon_tx, + http_handle, + tokio_remote, + current_job: None, + } + } + + /// Stop current key generation + pub fn reset(&mut self) { + if let Some(job) = self.current_job.take() { + job.cancel() + } + } + + /// Generate a new private key + pub fn generate_key_sync(&mut self, account: AccountToken) -> Result<WireguardData> { + self.reset(); + let private_key = PrivateKey::new_from_random().map_err(Error::GenerationError)?; + let (tx, rx) = oneshot::channel(); + let fut = self.push_future_generator(account, private_key)().then(|result| { + let _ = tx.send(result); + Ok(()) + }); + self.tokio_remote + .execute(fut) + .map_err(|_e| Error::ExectuionError)?; + + rx.wait() + .map_err(|_| Error::ExectuionError)? + .map_err(Self::map_rpc_error) + } + + + /// Generate a new private key asyncronously. The new keys will be sent to the daemon channel. + pub fn generate_key_async(&mut self, account: AccountToken) -> Result<()> { + self.reset(); + let private_key = PrivateKey::new_from_random().map_err(Error::GenerationError)?; + let future_generator = self.push_future_generator(account.clone(), private_key); + + let retry_strategy = ExponentialBackoff::from_millis(300) + .max_delay(Duration::from_secs(60 * 60)) + .map(jitter); + + let should_retry = |err: &jsonrpc_client_core::Error| -> bool { + match err.kind() { + jsonrpc_client_core::ErrorKind::JsonRpcError(err) + if err.code.code() == TOO_MANY_KEYS_ERROR_CODE => + { + false + } + _ => true, + } + }; + + let upload_future = + RetryIf::spawn(retry_strategy, future_generator, should_retry).map_err(move |err| { + match err { + // This should really be unreachable, since the retry strategy is infinite. + tokio_retry::Error::OperationError(e) => { + log::error!( + "{}", + e.display_chain_with_msg("Failed to generate wireguard key:") + ); + Self::map_rpc_error(e) + } + tokio_retry::Error::TimerError(timer_error) => { + log::error!("Tokio timer error {}", timer_error); + Error::ExectuionError + } + } + }); + + + let (fut, cancel_handle) = Cancellable::new(upload_future); + let daemon_tx = self.daemon_tx.clone(); + let fut = fut.then(move |result| { + match result { + Ok(wireguard_data) => { + let _ = daemon_tx.send(InternalDaemonEvent::WgKeyEvent(( + account, + Ok(wireguard_data), + ))); + } + Err(CancelErr::Inner(e)) => { + let _ = daemon_tx.send(InternalDaemonEvent::WgKeyEvent((account, Err(e)))); + } + Err(CancelErr::Cancelled) => { + log::error!("Key generation cancelled"); + } + }; + Ok(()) + }); + + match self + .tokio_remote + .execute(fut) + .map_err(|_| Error::ExectuionError) + { + Ok(res) => { + self.current_job = Some(cancel_handle); + Ok(res) + } + Err(e) => Err(e), + } + } + + + fn push_future_generator( + &self, + account: AccountToken, + private_key: PrivateKey, + ) -> Box<dyn FnMut() -> Box<dyn Future<Item = WireguardData, Error = JsonRpcError> + Send> + Send> + { + let mut rpc = mullvad_rpc::WireguardKeyProxy::new(self.http_handle.clone()); + let public_key = private_key.public_key(); + + let push_future = + move || -> Box<dyn Future<Item = WireguardData, Error = JsonRpcError> + Send> { + let key = private_key.clone(); + Box::new(rpc.push_wg_key(account.clone(), public_key.clone()).map( + move |addresses| WireguardData { + private_key: key, + addresses, + }, + )) + }; + Box::new(push_future) + } + + fn map_rpc_error(err: jsonrpc_client_core::Error) -> Error { + match err.kind() { + // TODO: Consider handling the invalid account case too. + jsonrpc_client_core::ErrorKind::JsonRpcError(err) if err.code.code() == -703 => { + Error::TooManyKeys + } + _ => Error::RpcError(err), + } + } +} + +pub enum CancelErr<E> { + Cancelled, + Inner(E), +} + +pub struct Cancellable<T, E, F: Future<Item = T, Error = E>> { + rx: oneshot::Receiver<()>, + f: F, +} + +pub struct CancelHandle { + tx: oneshot::Sender<()>, +} + +impl CancelHandle { + fn cancel(self) { + let _ = self.tx.send(()); + } +} + + +impl<T, E, F> Cancellable<T, E, F> +where + F: Future<Item = T, Error = E>, +{ + fn new(f: F) -> (Self, CancelHandle) { + let (tx, rx) = oneshot::channel(); + (Self { f, rx }, CancelHandle { tx }) + } +} + +impl<T, E, F> Future for Cancellable<T, E, F> +where + F: Future<Item = T, Error = E>, +{ + type Item = T; + type Error = CancelErr<E>; + + fn poll(&mut self) -> Poll<T, CancelErr<E>> { + match self.rx.poll() { + Ok(Async::Ready(_)) | Err(_) => return Err(CancelErr::Cancelled), + Ok(Async::NotReady) => (), + }; + + match self.f.poll() { + Ok(v) => Ok(v), + Err(e) => Err(CancelErr::Inner(e)), + } + } +} diff --git a/mullvad-ipc-client/src/lib.rs b/mullvad-ipc-client/src/lib.rs index 29f8985287..d42f165026 100644 --- a/mullvad-ipc-client/src/lib.rs +++ b/mullvad-ipc-client/src/lib.rs @@ -159,7 +159,7 @@ impl DaemonRpcClient { self.call("get_settings", &NO_ARGS) } - pub fn generate_wireguard_key(&mut self) -> Result<()> { + pub fn generate_wireguard_key(&mut self) -> Result<mullvad_types::wireguard::KeygenEvent> { self.call("generate_wireguard_key", &NO_ARGS) } diff --git a/mullvad-jni/src/daemon_interface.rs b/mullvad-jni/src/daemon_interface.rs index e91885fc95..a99c6e7cac 100644 --- a/mullvad-jni/src/daemon_interface.rs +++ b/mullvad-jni/src/daemon_interface.rs @@ -2,7 +2,7 @@ use futures::{sync::oneshot, Future}; use mullvad_daemon::{DaemonCommandSender, ManagementCommand}; use mullvad_types::{ account::AccountData, location::GeoIpLocation, relay_constraints::RelaySettingsUpdate, - relay_list::RelayList, settings::Settings, states::TargetState, + relay_list::RelayList, settings::Settings, states::TargetState, wireguard::KeygenEvent, }; use parking_lot::Mutex; use talpid_types::{net::wireguard, tunnel::TunnelStateTransition}; @@ -64,14 +64,12 @@ impl DaemonInterface { Ok(()) } - pub fn generate_wireguard_key(&self) -> Result<()> { + pub fn generate_wireguard_key(&self) -> Result<KeygenEvent> { let (tx, rx) = oneshot::channel(); self.send_command(ManagementCommand::GenerateWireguardKey(tx))?; - rx.wait() - .map_err(|_| Error::NoResponse)? - .map_err(Error::RpcError) + rx.wait().map_err(|_| Error::NoResponse) } pub fn get_account_data(&self, account_token: String) -> Result<AccountData> { diff --git a/mullvad-jni/src/jni_event_listener.rs b/mullvad-jni/src/jni_event_listener.rs index 9a89d85927..57d9bb6e76 100644 --- a/mullvad-jni/src/jni_event_listener.rs +++ b/mullvad-jni/src/jni_event_listener.rs @@ -5,7 +5,7 @@ use jni::{ AttachGuard, JNIEnv, }; use mullvad_daemon::EventListener; -use mullvad_types::{relay_list::RelayList, settings::Settings}; +use mullvad_types::{relay_list::RelayList, settings::Settings, wireguard::KeygenEvent}; use std::{sync::mpsc, thread}; use talpid_types::{tunnel::TunnelStateTransition, ErrorExt}; @@ -48,6 +48,9 @@ impl EventListener for JniEventListener { fn notify_relay_list(&self, relay_list: RelayList) { let _ = self.0.send(Event::RelayList(relay_list)); } + + // TODO: manage key events properly + fn notify_key_event(&self, _key_event: KeygenEvent) {} } struct JniEventHandler<'env> { diff --git a/mullvad-jni/src/lib.rs b/mullvad-jni/src/lib.rs index e7a6e862fa..947d2b5f70 100644 --- a/mullvad-jni/src/lib.rs +++ b/mullvad-jni/src/lib.rs @@ -18,6 +18,7 @@ use jni::{ }; use lazy_static::lazy_static; use mullvad_daemon::{logging, version, Daemon, DaemonCommandSender}; +use mullvad_types::wireguard::KeygenEvent; use parking_lot::RwLock; use std::{ collections::HashMap, @@ -225,7 +226,12 @@ pub extern "system" fn Java_net_mullvad_mullvadvpn_MullvadDaemon_generateWiregua _: JObject, ) -> jboolean { match DAEMON_INTERFACE.generate_wireguard_key() { - Ok(()) => JNI_TRUE, + Ok(KeygenEvent::NewKey(_)) => JNI_TRUE, + // TODO: Handle the new result better. + Ok(keygen_failure) => { + log::error!("Failed to generate wireguard key {:?}", keygen_failure); + JNI_FALSE + } Err(error) => { log::error!( "{}", diff --git a/mullvad-types/src/lib.rs b/mullvad-types/src/lib.rs index 6947c4ee9c..75088341d3 100644 --- a/mullvad-types/src/lib.rs +++ b/mullvad-types/src/lib.rs @@ -34,4 +34,7 @@ pub enum DaemonEvent { /// The daemon got an updated relay list. RelayList(relay_list::RelayList), + + /// Key event + WireguardKey(wireguard::KeygenEvent), } diff --git a/mullvad-types/src/wireguard.rs b/mullvad-types/src/wireguard.rs index 5fc0846557..635bb7d7da 100644 --- a/mullvad-types/src/wireguard.rs +++ b/mullvad-types/src/wireguard.rs @@ -1,4 +1,5 @@ use serde::{Deserialize, Serialize}; +use std::fmt; use talpid_types::net::wireguard; /// Contains account specific wireguard data @@ -15,3 +16,22 @@ pub struct AssociatedAddresses { pub ipv4_address: ipnetwork::Ipv4Network, pub ipv6_address: ipnetwork::Ipv6Network, } + +#[serde(rename_all = "snake_case")] +#[derive(Clone, Debug, Deserialize, Serialize)] +/// Event that is emitted when the daemon has finished generating a key. +pub enum KeygenEvent { + NewKey(wireguard::PublicKey), + TooManyKeys, + GenerationFailure, +} + +impl fmt::Display for KeygenEvent { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { + match self { + KeygenEvent::NewKey(public_key) => write!(f, "New wireguard key {}", public_key), + KeygenEvent::TooManyKeys => write!(f, "Account has too many keys already"), + KeygenEvent::GenerationFailure => write!(f, "Failed to generate new wireguard key"), + } + } +} |
