diff options
| author | Emīls <emils@mullvad.net> | 2020-03-16 13:15:59 +0000 |
|---|---|---|
| committer | Emīls Piņķis <emils@mullvad.net> | 2020-04-27 11:17:00 +0100 |
| commit | 738b59f3cea4b7f99e9dfef23c61a2aa1cda9559 (patch) | |
| tree | 35a4f1eda6c7982765e781fe3c2fb35096d4e297 | |
| parent | b8bd0ec937b92d960a539c6a1c6b51a03865afce (diff) | |
| download | mullvadvpn-738b59f3cea4b7f99e9dfef23c61a2aa1cda9559.tar.xz mullvadvpn-738b59f3cea4b7f99e9dfef23c61a2aa1cda9559.zip | |
Use HTTP RPCs in daemon
| -rw-r--r-- | mullvad-daemon/src/account_history.rs | 12 | ||||
| -rw-r--r-- | mullvad-daemon/src/event_loop.rs | 38 | ||||
| -rw-r--r-- | mullvad-daemon/src/geoip.rs | 88 | ||||
| -rw-r--r-- | mullvad-daemon/src/lib.rs | 92 | ||||
| -rw-r--r-- | mullvad-daemon/src/management_interface.rs | 60 | ||||
| -rw-r--r-- | mullvad-daemon/src/relays.rs | 14 | ||||
| -rw-r--r-- | mullvad-daemon/src/version_check.rs | 13 | ||||
| -rw-r--r-- | mullvad-daemon/src/wireguard.rs | 64 |
8 files changed, 219 insertions, 162 deletions
diff --git a/mullvad-daemon/src/account_history.rs b/mullvad-daemon/src/account_history.rs index ea8f23757a..56ebc6dc17 100644 --- a/mullvad-daemon/src/account_history.rs +++ b/mullvad-daemon/src/account_history.rs @@ -5,7 +5,7 @@ use futures::{ future::{self, Executor, Future}, sync::oneshot, }; -use mullvad_rpc::{HttpHandle, WireguardKeyProxy}; +use mullvad_rpc::{rest::MullvadRestHandle, WireguardKeyProxy}; use mullvad_types::{account::AccountToken, wireguard::WireguardData}; use std::{ collections::VecDeque, @@ -38,7 +38,7 @@ static ACCOUNT_HISTORY_LIMIT: usize = 3; pub struct AccountHistory { file: io::BufWriter<fs::File>, accounts: VecDeque<AccountEntry>, - rpc_handle: HttpHandle, + rpc_handle: MullvadRestHandle, tokio_remote: Remote, } @@ -46,7 +46,7 @@ pub struct AccountHistory { impl AccountHistory { pub fn new( cache_dir: &Path, - rpc_handle: HttpHandle, + rpc_handle: MullvadRestHandle, tokio_remote: Remote, ) -> Result<AccountHistory> { let mut options = fs::OpenOptions::new(); @@ -152,8 +152,7 @@ impl AccountHistory { wg_data: &WireguardData, ) -> impl Future<Item = (), Error = ()> { let mut rpc = WireguardKeyProxy::new(self.rpc_handle.clone()); - rpc.remove_wg_key(String::from(account), wg_data.private_key.public_key()) - .map(|removed| log::debug!("Key existed on account: {}", removed)) + rpc.remove_wireguard_key(String::from(account), &wg_data.private_key.public_key()) .map_err(|e| log::error!("Failed to remove WireGuard key: {}", e)) } @@ -216,8 +215,7 @@ impl AccountHistory { for entry in self.accounts.iter() { if let Some(wg_data) = &entry.wireguard { let fut = rpc - .remove_wg_key(entry.account.clone(), wg_data.private_key.public_key()) - .map(|_| ()) + .remove_wireguard_key(entry.account.clone(), &wg_data.private_key.public_key()) .map_err(|e| log::error!("Failed to remove WireGuard key: {}", e)); removal_futures.push(fut); } diff --git a/mullvad-daemon/src/event_loop.rs b/mullvad-daemon/src/event_loop.rs new file mode 100644 index 0000000000..238b9f4eef --- /dev/null +++ b/mullvad-daemon/src/event_loop.rs @@ -0,0 +1,38 @@ +use futures::{sync::oneshot, Future}; +use std::thread; +use tokio_core::reactor::{Core, Remote}; + +pub struct CoreHandle { + /// Remote used to spawn futures on the daemon's event loop. + pub remote: Remote, + /// A sender that will cause the event loop to stop once it's dropped. + shutdown_tx: Option<oneshot::Sender<()>>, +} + +impl Drop for CoreHandle { + fn drop(&mut self) { + if let Some(shutdown_tx) = self.shutdown_tx.take() { + if shutdown_tx.send(()).is_err() { + log::error!("Core already shut down"); + } + } + } +} + +/// Panics if a new tokio event loop can't be spawned. +pub fn spawn() -> CoreHandle { + let (tx, rx) = oneshot::channel(); + let (shutdown_tx, shutdown_rx) = oneshot::channel(); + thread::spawn(move || { + let mut core = Core::new().expect("Failed to spawn event loop"); + let remote = core.remote(); + let _ = tx.send(remote); + let _ = core.run(shutdown_rx); + }); + let remote = rx.wait().expect("Failed to spawn event loop"); + + CoreHandle { + remote, + shutdown_tx: Some(shutdown_tx), + } +} diff --git a/mullvad-daemon/src/geoip.rs b/mullvad-daemon/src/geoip.rs index 5be502f0a9..5cc3085bfe 100644 --- a/mullvad-daemon/src/geoip.rs +++ b/mullvad-daemon/src/geoip.rs @@ -1,69 +1,53 @@ use futures::{self, Future}; -use mullvad_rpc; +use mullvad_rpc::{self, rest::RequestServiceHandle}; use mullvad_types::location::{AmIMullvad, GeoIpLocation}; -use serde_json; const URI_V4: &str = "https://ipv4.am.i.mullvad.net/json"; const URI_V6: &str = "https://ipv6.am.i.mullvad.net/json"; -#[derive(err_derive::Error, Debug)] -pub enum Error { - /// Unable to send request to HTTP client. - #[error(display = "Unable to send GeoIP request to HTTP client")] - SendRequestError, - - /// The request was dropped without any response - #[error(display = "The GeoIP request was dropped without any response")] - NoResponse, - - /// Error in the HTTP client when requesting GeoIP - #[error(display = "Failed to request GeoIP")] - Transport(#[error(source)] mullvad_rpc::rest::Error), - - /// Failed to deserialize GeoIP response - #[error(display = "Failed to deserialize GeoIP response")] - Deserialize(#[error(source)] serde_json::error::Error), -} - - pub fn send_location_request( - request_sender: mullvad_rpc::rest::RequestSender, -) -> impl Future<Item = GeoIpLocation, Error = Error> { + request_sender: RequestServiceHandle, +) -> impl Future<Item = GeoIpLocation, Error = mullvad_rpc::rest::Error> { let v4_future = send_location_request_internal(URI_V4, request_sender.clone()).map(GeoIpLocation::from); let v6_future = send_location_request_internal(URI_V6, request_sender).map(GeoIpLocation::from); - v4_future.then(|v4_result| { - v6_future.then(|v6_result| match (v4_result, v6_result) { - (Ok(mut v4), Ok(v6)) => { - v4.ipv6 = v6.ipv6; - v4.mullvad_exit_ip = v4.mullvad_exit_ip && v6.mullvad_exit_ip; - Ok(v4) - } - (Ok(v4), Err(e)) => { - log::debug!("Unable to fetch IPv6 GeoIP location: {}", e); - Ok(v4) - } - (Err(e), Ok(v6)) => { - log::debug!("Unable to fetch IPv4 GeoIP location: {}", e); - Ok(v6) - } - (Err(e_v4), Err(_)) => Err(e_v4), - }) - }) + v4_future.then( + |v4_result: Result<GeoIpLocation, mullvad_rpc::rest::Error>| { + v6_future.then( + |v6_result: Result<GeoIpLocation, mullvad_rpc::rest::Error>| match ( + v4_result, v6_result, + ) { + (Ok(mut v4), Ok(v6)) => { + v4.ipv6 = v6.ipv6; + v4.mullvad_exit_ip = v4.mullvad_exit_ip && v6.mullvad_exit_ip; + Ok(v4) + } + (Ok(v4), Err(e)) => { + log::debug!("Unable to fetch IPv6 GeoIP location: {}", e); + Ok(v4) + } + (Err(e), Ok(v6)) => { + log::debug!("Unable to fetch IPv4 GeoIP location: {}", e); + Ok(v6) + } + (Err(e_v4), Err(_)) => Err(e_v4), + }, + ) + }, + ) } fn send_location_request_internal( uri: &'static str, - request_sender: mullvad_rpc::rest::RequestSender, -) -> impl Future<Item = AmIMullvad, Error = Error> { - let (response_tx, response_rx) = futures::sync::oneshot::channel(); - let request = mullvad_rpc::rest::create_get_request(uri.parse().unwrap()); - - futures::Sink::send(request_sender, (request, response_tx)) - .map_err(|_| Error::SendRequestError) - .and_then(|_| response_rx.map_err(|_| Error::NoResponse)) - .and_then(|response_result| response_result.map_err(Error::Transport)) - .and_then(|response| serde_json::from_slice(&response).map_err(Error::Deserialize)) + service: RequestServiceHandle, +) -> impl Future<Item = AmIMullvad, Error = mullvad_rpc::rest::Error> { + let future_service = service.clone(); + let future = async move { + let request = mullvad_rpc::rest::RestRequest::get(uri)?; + let response = future_service.request(request).await?; + mullvad_rpc::rest::deserialize_body(response).await + }; + service.compat_spawn(future) } diff --git a/mullvad-daemon/src/lib.rs b/mullvad-daemon/src/lib.rs index 6dd6812dd2..4f8d828d37 100644 --- a/mullvad-daemon/src/lib.rs +++ b/mullvad-daemon/src/lib.rs @@ -27,7 +27,7 @@ use futures::{ Future, Stream, }; use log::{debug, error, info, warn}; -use mullvad_rpc::{AccountsProxy, HttpHandle, WireguardKeyProxy}; +use mullvad_rpc::AccountsProxy; use mullvad_types::{ account::{AccountData, AccountToken, VoucherSubmission}, endpoint::MullvadEndpoint, @@ -71,6 +71,7 @@ use talpid_types::{ mod wireguard; const TARGET_START_STATE_FILE: &str = "target-start-state.json"; +mod event_loop; /// FIXME(linus): This is here just because the futures crate has deprecated it and jsonrpc_core /// did not introduce their own yet (https://github.com/paritytech/jsonrpc/pull/196). @@ -89,10 +90,7 @@ pub enum Error { InitIoEventLoop(#[error(source)] io::Error), #[error(display = "Unable to create RPC client")] - InitRpcClient(#[error(source)] mullvad_rpc::HttpError), - - #[error(display = "Unable to create am.i.mullvad client")] - InitHttpsClient(#[error(source)] mullvad_rpc::rest::Error), + InitRpcFactory(#[error(source)] mullvad_rpc::Error), #[error(display = "Unable to load account history with wireguard key cache")] LoadAccountHistory(#[error(source)] account_history::Error), @@ -147,17 +145,17 @@ pub enum DaemonCommand { GetState(oneshot::Sender<TunnelState>), /// Get the current geographical location. GetCurrentLocation(oneshot::Sender<Option<GeoIpLocation>>), - CreateNewAccount(oneshot::Sender<std::result::Result<String, mullvad_rpc::Error>>), + CreateNewAccount(oneshot::Sender<std::result::Result<String, mullvad_rpc::rest::Error>>), /// Request the metadata for an account. GetAccountData( - oneshot::Sender<BoxFuture<AccountData, mullvad_rpc::Error>>, + oneshot::Sender<BoxFuture<AccountData, mullvad_rpc::rest::Error>>, AccountToken, ), /// Request www auth token for an account - GetWwwAuthToken(oneshot::Sender<BoxFuture<String, mullvad_rpc::Error>>), + GetWwwAuthToken(oneshot::Sender<BoxFuture<String, mullvad_rpc::rest::Error>>), /// Submit voucher to add time to the current account. Returns time added in seconds SubmitVoucher( - oneshot::Sender<BoxFuture<VoucherSubmission, mullvad_rpc::Error>>, + oneshot::Sender<BoxFuture<VoucherSubmission, mullvad_rpc::rest::Error>>, String, ), /// Request account history @@ -244,7 +242,7 @@ pub(crate) enum InternalDaemonEvent { /// New Account created NewAccountEvent( AccountToken, - oneshot::Sender<Result<String, mullvad_rpc::Error>>, + oneshot::Sender<Result<String, mullvad_rpc::rest::Error>>, ), /// The background job fetching new `AppVersionInfo`s got a new info object. NewAppVersionInfo(AppVersionInfo), @@ -439,11 +437,10 @@ pub struct Daemon<L: EventListener> { event_listener: L, settings: SettingsPersister, account_history: account_history::AccountHistory, - wg_key_proxy: WireguardKeyProxy<HttpHandle>, - accounts_proxy: AccountsProxy<HttpHandle>, - https_handle: mullvad_rpc::rest::RequestSender, + accounts_proxy: AccountsProxy, + rpc_runtime: mullvad_rpc::MullvadRpcRuntime, wireguard_key_manager: wireguard::KeyManager, - tokio_remote: tokio_core::reactor::Remote, + core_handle: event_loop::CoreHandle, relay_selector: relays::RelaySelector, last_generated_relay: Option<Relay>, last_generated_bridge_relay: Option<Relay>, @@ -471,20 +468,11 @@ where let (tunnel_state_machine_shutdown_tx, tunnel_state_machine_shutdown_signal) = oneshot::channel(); - 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) - }) - .map_err(Error::InitIoEventLoop)?; + let mut rpc_runtime = mullvad_rpc::MullvadRpcRuntime::with_cache_dir(&cache_dir, &ca_path) + .map_err(Error::InitRpcFactory)?; + let rpc_handle = rpc_runtime.mullvad_rest_handle(); - let rpc_handle = rpc_handle.map_err(Error::InitRpcClient)?; - let https_handle = https_handle.map_err(Error::InitHttpsClient)?; + let core_handle = event_loop::spawn(); let relay_list_listener = event_listener.clone(); let on_relay_list_update = move |relay_list: &RelayList| { @@ -506,7 +494,7 @@ where internal_event_tx.to_specialized_sender(), app_version_info.clone(), ); - tokio_remote.spawn(|_| version_check_future); + core_handle.remote.spawn(|_| version_check_future); let mut settings = SettingsPersister::load(&settings_dir); @@ -517,7 +505,7 @@ where let account_history = account_history::AccountHistory::new( &cache_dir, rpc_handle.clone(), - tokio_remote.clone(), + core_handle.remote.clone(), ) .map_err(Error::LoadAccountHistory)?; @@ -561,7 +549,7 @@ where let wireguard_key_manager = wireguard::KeyManager::new( internal_event_tx.clone(), rpc_handle.clone(), - tokio_remote.clone(), + core_handle.remote.clone(), ); // Attempt to download a fresh relay list @@ -591,11 +579,10 @@ where event_listener, settings, account_history, - wg_key_proxy: WireguardKeyProxy::new(rpc_handle.clone()), + rpc_runtime, accounts_proxy: AccountsProxy::new(rpc_handle), - https_handle, wireguard_key_manager, - tokio_remote, + core_handle, relay_selector, last_generated_relay: None, last_generated_bridge_relay: None, @@ -1063,7 +1050,7 @@ where fn handle_new_account_event( &mut self, new_token: AccountToken, - tx: oneshot::Sender<Result<String, mullvad_rpc::Error>>, + tx: oneshot::Sender<Result<String, mullvad_rpc::rest::Error>>, ) { match self.set_account(Some(new_token.clone())) { Ok(_) => { @@ -1106,7 +1093,7 @@ where Self::oneshot_send(tx, self.tunnel_state.clone(), "current state"); } - fn on_get_current_location(&self, tx: oneshot::Sender<Option<GeoIpLocation>>) { + fn on_get_current_location(&mut self, tx: oneshot::Sender<Option<GeoIpLocation>>) { use self::TunnelState::*; let get_location: Box<dyn Future<Item = Option<GeoIpLocation>, Error = ()> + Send> = match &self.tunnel_state { @@ -1131,13 +1118,13 @@ where } }; - self.tokio_remote.spawn(move |_| { + self.core_handle.remote.spawn(move |_| { get_location.map(|location| Self::oneshot_send(tx, location, "current location")) }); } - fn get_geo_location(&self) -> impl Future<Item = GeoIpLocation, Error = ()> { - let https_handle = self.https_handle.clone(); + fn get_geo_location(&mut self) -> impl Future<Item = GeoIpLocation, Error = ()> { + let https_handle = self.rpc_runtime.rest_handle(); geoip::send_location_request(https_handle).map_err(|e| { warn!("Unable to fetch GeoIP location: {}", e.display_chain()); @@ -1166,7 +1153,10 @@ where }) } - fn on_create_new_account(&mut self, tx: oneshot::Sender<Result<String, mullvad_rpc::Error>>) { + fn on_create_new_account( + &mut self, + tx: oneshot::Sender<Result<String, mullvad_rpc::rest::Error>>, + ) { let daemon_tx = self.tx.clone(); let future = self .accounts_proxy @@ -1184,14 +1174,14 @@ where Ok(()) }); - if self.tokio_remote.execute(future).is_err() { + if self.core_handle.remote.execute(future).is_err() { log::error!("Failed to spawn future for creating a new account"); } } fn on_get_account_data( &mut self, - tx: oneshot::Sender<BoxFuture<AccountData, mullvad_rpc::Error>>, + tx: oneshot::Sender<BoxFuture<AccountData, mullvad_rpc::rest::Error>>, account_token: AccountToken, ) { let rpc_call = self @@ -1203,7 +1193,7 @@ where fn on_get_www_auth_token( &mut self, - tx: oneshot::Sender<BoxFuture<String, mullvad_rpc::Error>>, + tx: oneshot::Sender<BoxFuture<String, mullvad_rpc::rest::Error>>, ) { if let Some(account_token) = self.settings.get_account_token() { let rpc_call = self.accounts_proxy.get_www_auth_token(account_token); @@ -1213,7 +1203,7 @@ where fn on_submit_voucher( &mut self, - tx: oneshot::Sender<BoxFuture<VoucherSubmission, mullvad_rpc::Error>>, + tx: oneshot::Sender<BoxFuture<VoucherSubmission, mullvad_rpc::rest::Error>>, voucher: String, ) { if let Some(account_token) = self.settings.get_account_token() { @@ -1647,7 +1637,10 @@ where Ok(keygen_event) } Err(wireguard::Error::TooManyKeys) => Ok(KeygenEvent::TooManyKeys), - Err(e) => Err(format!("Failed to generate new key - {}", e)), + Err(e) => Err(format!( + "Failed to generate new key - {}", + e.display_chain_with_msg("Failed to generate new wireguard key:") + )), } }; @@ -1698,13 +1691,14 @@ where }; let fut = self - .wg_key_proxy - .check_wg_key(account, public_key) - .map(|is_valid| { + .wireguard_key_manager + .verify_wireguard_key(account, public_key) + .and_then(|is_valid| { Self::oneshot_send(tx, is_valid, "verify_wireguard_key response"); + Ok(()) }) - .map_err(|e| log::error!("Failed to verify wireguard key - {}", e)); - if let Err(e) = self.tokio_remote.execute(fut) { + .map_err(|e: wireguard::Error| log::error!("Failed to verify wireguard key - {}", e)); + if let Err(e) = self.core_handle.remote.execute(fut) { log::error!("Failed to spawn a future to verify wireguard key: {:?}", e); } } diff --git a/mullvad-daemon/src/management_interface.rs b/mullvad-daemon/src/management_interface.rs index 00254472be..84c5db8570 100644 --- a/mullvad-daemon/src/management_interface.rs +++ b/mullvad-daemon/src/management_interface.rs @@ -7,7 +7,7 @@ use jsonrpc_ipc_server; use jsonrpc_macros::{build_rpc_trait, metadata, pubsub}; use jsonrpc_pubsub::{PubSubHandler, PubSubMetadata, Session, SubscriptionId}; use mullvad_paths; -use mullvad_rpc; +use mullvad_rpc::{rest::Error as RestError, StatusCode}; use mullvad_types::{ account::{AccountData, AccountToken, VoucherSubmission}, location::GeoIpLocation, @@ -26,6 +26,11 @@ use talpid_ipc; use talpid_types::ErrorExt; use uuid; +pub const INVALID_VOUCHER_CODE: i64 = -400; +pub const VOUCHER_USED_ALREADY_CODE: i64 = -401; +pub const INVALID_ACCOUNT_CODE: i64 = -200; + + build_rpc_trait! { pub trait ManagementInterfaceApi { type Metadata; @@ -306,18 +311,16 @@ impl ManagementInterface { future::result(self.tx.send(command)).map_err(|_| Error::internal_error()) } - /// Converts the given error to an error that can be given to the caller of the API. - /// Will let any actual RPC error through as is, any other error is changed to an internal - /// error. - fn map_rpc_error(error: &mullvad_rpc::Error) -> Error { - match error.kind() { - mullvad_rpc::ErrorKind::JsonRpcError(ref rpc_error) => { - // We have to manually copy the error since we have different - // versions of the jsonrpc_core library at the moment. + /// Converts a REST API error for an account into a JSONRPC error for the JSONRPC client. + fn map_rest_account_error(error: RestError) -> Error { + match error { + RestError::ApiError(status, message) + if status == StatusCode::UNAUTHORIZED || status == StatusCode::FORBIDDEN => + { Error { - code: ErrorCode::from(rpc_error.code.code()), - message: rpc_error.message.clone(), - data: rpc_error.data.clone(), + code: ErrorCode::from(INVALID_ACCOUNT_CODE), + message, + data: None, } } _ => Error::internal_error(), @@ -335,7 +338,7 @@ impl ManagementInterfaceApi for ManagementInterface { .and_then(|_| rx.map_err(|_| Error::internal_error())) .and_then(|result| match result { Ok(account_token) => Ok(account_token), - Err(e) => Err(Self::map_rpc_error(&e)), + Err(_) => Err(Error::internal_error()), }); Box::new(future) @@ -352,12 +355,12 @@ impl ManagementInterfaceApi for ManagementInterface { .send_command_to_daemon(DaemonCommand::GetAccountData(tx, account_token)) .and_then(|_| rx.map_err(|_| Error::internal_error())) .and_then(|rpc_future| { - rpc_future.map_err(|error: mullvad_rpc::Error| { + rpc_future.map_err(|error: RestError| { log::error!( "Unable to get account data from API: {}", error.display_chain() ); - Self::map_rpc_error(&error) + Self::map_rest_account_error(error) }) }); Box::new(future) @@ -370,12 +373,12 @@ impl ManagementInterfaceApi for ManagementInterface { .send_command_to_daemon(DaemonCommand::GetWwwAuthToken(tx)) .and_then(|_| rx.map_err(|_| Error::internal_error())) .and_then(|rpc_future| { - rpc_future.map_err(|error: mullvad_rpc::Error| { + rpc_future.map_err(|error: mullvad_rpc::rest::Error| { log::error!( "Unable to get account data from API: {}", error.display_chain() ); - Self::map_rpc_error(&error) + Self::map_rest_account_error(error) }) }); Box::new(future) @@ -391,7 +394,28 @@ impl ManagementInterfaceApi for ManagementInterface { let future = self .send_command_to_daemon(DaemonCommand::SubmitVoucher(tx, voucher)) .and_then(|_| rx.map_err(|_| Error::internal_error())) - .and_then(|f| f.map_err(|e| Self::map_rpc_error(&e))); + .and_then(|f| { + f.map_err(|e| match e { + RestError::ApiError(StatusCode::BAD_REQUEST, message) => { + match &message.as_str() { + &mullvad_rpc::INVALID_VOUCHER => Error { + code: ErrorCode::from(INVALID_VOUCHER_CODE), + message, + data: None, + }, + + &mullvad_rpc::VOUCHER_USED => Error { + code: ErrorCode::from(VOUCHER_USED_ALREADY_CODE), + message, + data: None, + }, + + _ => Error::internal_error(), + } + } + _ => Error::internal_error(), + }) + }); Box::new(future) } diff --git a/mullvad-daemon/src/relays.rs b/mullvad-daemon/src/relays.rs index 25a3b8eb40..94e6c66cf9 100644 --- a/mullvad-daemon/src/relays.rs +++ b/mullvad-daemon/src/relays.rs @@ -3,7 +3,7 @@ use chrono::{DateTime, Local}; use futures::Future; -use mullvad_rpc::{HttpHandle, RelayListProxy}; +use mullvad_rpc::{rest::MullvadRestHandle, RelayListProxy}; use mullvad_types::{ endpoint::MullvadEndpoint, location::Location, @@ -52,7 +52,7 @@ pub enum Error { WriteRelayCache(#[error(source)] io::Error), #[error(display = "Failed to download the list of relays")] - Download(#[error(source)] mullvad_rpc::Error), + Download(#[error(source)] mullvad_rpc::rest::Error), #[error(display = "Timed out when trying to download the list of relays")] DownloadTimeout, @@ -155,7 +155,7 @@ impl RelaySelector { /// Returns a new `RelaySelector` backed by relays cached on disk. Use the `update` method /// to refresh the relay list from the internet. pub fn new( - rpc_handle: HttpHandle, + rpc_handle: MullvadRestHandle, on_update: impl Fn(&RelayList) + Send + 'static, resource_dir: &Path, cache_dir: &Path, @@ -783,7 +783,7 @@ impl RelaySelector { type RelayListUpdaterHandle = mpsc::Sender<()>; struct RelayListUpdater { - rpc_client: RelayListProxy<HttpHandle>, + rpc_client: RelayListProxy, cache_path: PathBuf, parsed_relays: Arc<Mutex<ParsedRelays>>, on_update: Box<dyn Fn(&RelayList)>, @@ -792,7 +792,7 @@ struct RelayListUpdater { impl RelayListUpdater { pub fn spawn( - rpc_handle: HttpHandle, + rpc_handle: MullvadRestHandle, cache_path: PathBuf, parsed_relays: Arc<Mutex<ParsedRelays>>, on_update: Box<dyn Fn(&RelayList) + Send + 'static>, @@ -807,7 +807,7 @@ impl RelayListUpdater { } fn new( - rpc_handle: HttpHandle, + rpc_handle: MullvadRestHandle, cache_path: PathBuf, parsed_relays: Arc<Mutex<ParsedRelays>>, on_update: Box<dyn Fn(&RelayList)>, @@ -879,7 +879,7 @@ impl RelayListUpdater { } fn download_relay_list(&mut self) -> Result<RelayList, Error> { - let download_future = self.rpc_client.relay_list_v3().map_err(Error::Download); + let download_future = self.rpc_client.relay_list().map_err(Error::Download); let relay_list = Timer::default() .timeout(download_future, DOWNLOAD_TIMEOUT) .wait()?; diff --git a/mullvad-daemon/src/version_check.rs b/mullvad-daemon/src/version_check.rs index 754509b407..7a4446708d 100644 --- a/mullvad-daemon/src/version_check.rs +++ b/mullvad-daemon/src/version_check.rs @@ -1,6 +1,6 @@ use crate::{version::PRODUCT_VERSION, DaemonEventSender}; use futures::{Async, Future, Poll}; -use mullvad_rpc::{AppVersionProxy, HttpHandle}; +use mullvad_rpc::{rest::MullvadRestHandle, AppVersionProxy}; use mullvad_types::version::AppVersionInfo; use serde::{Deserialize, Serialize}; use std::{ @@ -68,7 +68,7 @@ pub enum Error { DownloadTimeout, #[error(display = "Failed to check the latest app version")] - Download(#[error(source)] mullvad_rpc::Error), + Download(#[error(source)] mullvad_rpc::rest::Error), #[error(display = "Clearing version check cache due to a version mismatch")] CacheVersionMismatch, @@ -82,7 +82,7 @@ impl<T> From<TimeoutError<T>> for Error { pub(crate) struct VersionUpdater { - version_proxy: AppVersionProxy<HttpHandle>, + version_proxy: AppVersionProxy, cache_path: PathBuf, update_sender: DaemonEventSender<AppVersionInfo>, last_app_version_info: AppVersionInfo, @@ -97,7 +97,7 @@ enum VersionUpdaterState { impl VersionUpdater { pub fn new( - rpc_handle: HttpHandle, + rpc_handle: MullvadRestHandle, cache_dir: PathBuf, update_sender: DaemonEventSender<AppVersionInfo>, last_app_version_info: AppVersionInfo, @@ -123,7 +123,7 @@ impl VersionUpdater { ) -> Box<dyn Future<Item = AppVersionInfo, Error = Error> + Send + 'static> { let download_future = self .version_proxy - .app_version_check(&PRODUCT_VERSION.to_owned(), PLATFORM) + .version_check(PRODUCT_VERSION.to_owned(), PLATFORM) .map_err(Error::Download); let future = Timer::default().timeout(download_future, DOWNLOAD_TIMEOUT); Box::new(future) @@ -226,8 +226,9 @@ pub fn load_cache(cache_dir: &Path) -> AppVersionInfo { ); // If we don't have a cache, start out with sane defaults. AppVersionInfo { - current_is_supported: true, + supported: true, latest_stable: PRODUCT_VERSION.to_owned(), + latest_beta: PRODUCT_VERSION.to_owned(), latest: PRODUCT_VERSION.to_owned(), } } diff --git a/mullvad-daemon/src/wireguard.rs b/mullvad-daemon/src/wireguard.rs index 9b234dfaab..5cc4b9be7d 100644 --- a/mullvad-daemon/src/wireguard.rs +++ b/mullvad-daemon/src/wireguard.rs @@ -1,7 +1,7 @@ use crate::{account_history::AccountHistory, DaemonEventSender, InternalDaemonEvent}; use chrono::offset::Utc; use futures::{future::Executor, stream::Stream, sync::oneshot, Async, Future, Poll}; -use jsonrpc_client_core::Error as JsonRpcError; +use mullvad_rpc::rest::{Error as RestError, MullvadRestHandle}; use mullvad_types::account::AccountToken; pub use mullvad_types::wireguard::*; use std::time::Duration; @@ -17,8 +17,6 @@ use tokio_retry::{ }; use tokio_timer; -const TOO_MANY_KEYS_ERROR_CODE: i64 = -703; - /// Default automatic key rotation const DEFAULT_AUTOMATIC_KEY_ROTATION: Duration = Duration::from_secs(7 * 24 * 60 * 60); /// How long to wait before reattempting to rotate keys on failure @@ -31,8 +29,8 @@ const KEY_CHECK_INTERVAL: Duration = Duration::from_secs(60); pub enum Error { #[error(display = "Failed to spawn future")] ExectuionError, - #[error(display = "Unexpected RPC error")] - RpcError(#[error(source)] jsonrpc_client_core::Error), + #[error(display = "Unexpected HTTP request error")] + RestError(#[error(source)] mullvad_rpc::rest::Error), #[error(display = "Account already has maximum number of keys")] TooManyKeys, #[error(display = "Failed to create rotation timer")] @@ -43,7 +41,7 @@ pub type Result<T> = std::result::Result<T, Error>; pub struct KeyManager { daemon_tx: DaemonEventSender, - http_handle: mullvad_rpc::HttpHandle, + http_handle: MullvadRestHandle, tokio_remote: Remote, current_job: Option<CancelHandle>, @@ -54,7 +52,7 @@ pub struct KeyManager { impl KeyManager { pub(crate) fn new( daemon_tx: DaemonEventSender, - http_handle: mullvad_rpc::HttpHandle, + http_handle: MullvadRestHandle, tokio_remote: Remote, ) -> Self { Self { @@ -149,6 +147,25 @@ impl KeyManager { )) } + /// Verifies whether a key is valid or not. + pub fn verify_wireguard_key( + &self, + account: AccountToken, + key: talpid_types::net::wireguard::PublicKey, + ) -> impl Future<Item = bool, Error = Error> { + let mut rpc = mullvad_rpc::WireguardKeyProxy::new(self.http_handle.clone()); + rpc.get_wireguard_key(account, &key) + .then(|response| match response { + Ok(_) => Ok(true), + Err(mullvad_rpc::rest::Error::ApiError(status, _code)) + if status == mullvad_rpc::StatusCode::NOT_FOUND => + { + Ok(false) + } + Err(err) => Err(Self::map_rpc_error(err)), + }) + } + /// Generate a new private key asynchronously. The new keys will be sent to the daemon channel. pub fn generate_key_async(&mut self, account: AccountToken) -> Result<()> { @@ -160,11 +177,9 @@ impl KeyManager { .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 => - { + let should_retry = |err: &RestError| -> bool { + match err { + RestError::ApiError(_status, code) if code == mullvad_rpc::KEY_LIMIT_REACHED => { false } _ => true, @@ -225,13 +240,13 @@ impl KeyManager { &self, account: AccountToken, private_key: PrivateKey, - ) -> Box<dyn FnMut() -> Box<dyn Future<Item = WireguardData, Error = JsonRpcError> + Send> + Send> + ) -> Box<dyn FnMut() -> Box<dyn Future<Item = WireguardData, Error = RestError> + 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> { + move || -> Box<dyn Future<Item = WireguardData, Error = RestError> + Send> { let key = private_key.clone(); Box::new(rpc.push_wg_key(account.clone(), public_key.clone()).map( move |addresses| WireguardData { @@ -245,7 +260,7 @@ impl KeyManager { } fn replace_key_rpc( - http_handle: mullvad_rpc::HttpHandle, + http_handle: MullvadRestHandle, account: AccountToken, old_key: PublicKey, new_key: PrivateKey, @@ -261,13 +276,16 @@ impl KeyManager { }) } - fn map_rpc_error(err: jsonrpc_client_core::Error) -> Error { - match err.kind() { + fn map_rpc_error(err: mullvad_rpc::rest::Error) -> Error { + match &err { // TODO: Consider handling the invalid account case too. - jsonrpc_client_core::ErrorKind::JsonRpcError(err) if err.code.code() == -703 => { + mullvad_rpc::rest::Error::ApiError(status, message) + if *status == mullvad_rpc::StatusCode::BAD_REQUEST + && message == mullvad_rpc::KEY_LIMIT_REACHED => + { Error::TooManyKeys } - _ => Error::RpcError(err), + _ => Error::RestError(err), } } @@ -290,7 +308,7 @@ impl KeyManager { fn next_automatic_rotation( daemon_tx: DaemonEventSender, - http_handle: mullvad_rpc::HttpHandle, + http_handle: MullvadRestHandle, public_key: PublicKey, rotation_interval_secs: u64, account_token: AccountToken, @@ -330,7 +348,7 @@ impl KeyManager { fn create_automatic_rotation( daemon_tx: DaemonEventSender, - http_handle: mullvad_rpc::HttpHandle, + http_handle: MullvadRestHandle, public_key: PublicKey, rotation_interval_secs: u64, account_token: AccountToken, @@ -355,8 +373,8 @@ impl KeyManager { } Err(e) => { log::error!( - "Key rotation failed: {}. Retrying in {} seconds", - e, + "{}. Retrying in {} seconds", + e.display_chain_with_msg("Key rotation failed:"), AUTOMATIC_ROTATION_RETRY_DELAY.as_secs(), ); Ok(old_public_key) |
