diff options
| author | Linus Färnstrand <linus@mullvad.net> | 2017-10-30 14:59:46 +0100 |
|---|---|---|
| committer | Linus Färnstrand <linus@mullvad.net> | 2017-10-30 14:59:46 +0100 |
| commit | 3df28edf3258b3de877a14becac237e413978e02 (patch) | |
| tree | d5dc489b8d3360bafcaa7b6eae1bd59e0be5f749 | |
| parent | a9379bf7c5628abdac284857c200e9f19346ef29 (diff) | |
| parent | 7cce6e4f1f3d3b088ae383673881067f90b09443 (diff) | |
| download | mullvadvpn-3df28edf3258b3de877a14becac237e413978e02.tar.xz mullvadvpn-3df28edf3258b3de877a14becac237e413978e02.zip | |
Merge branch 'problem-report-send'
| -rw-r--r-- | Cargo.lock | 13 | ||||
| -rw-r--r-- | Cargo.toml | 1 | ||||
| -rw-r--r-- | mullvad-cli/src/main.rs | 3 | ||||
| -rw-r--r-- | mullvad-daemon/Cargo.toml | 3 | ||||
| -rw-r--r-- | mullvad-daemon/src/bin/problem-report.rs | 116 | ||||
| -rw-r--r-- | mullvad-daemon/src/main.rs | 13 | ||||
| -rw-r--r-- | mullvad-daemon/src/management_interface.rs | 10 | ||||
| -rw-r--r-- | mullvad-daemon/src/master.rs | 16 | ||||
| -rw-r--r-- | mullvad-rpc/Cargo.toml | 13 | ||||
| -rw-r--r-- | mullvad-rpc/src/lib.rs | 49 |
10 files changed, 140 insertions, 97 deletions
diff --git a/Cargo.lock b/Cargo.lock index f080a8e078..d0c58be2c7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -673,14 +673,13 @@ dependencies = [ "error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", "fern 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)", - "jsonrpc-client-core 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "jsonrpc-client-http 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "jsonrpc-core 7.1.1 (git+https://github.com/paritytech/jsonrpc?tag=v7.1.1)", "jsonrpc-macros 7.1.1 (git+https://github.com/paritytech/jsonrpc?tag=v7.1.1)", "jsonrpc-pubsub 7.1.1 (git+https://github.com/paritytech/jsonrpc?tag=v7.1.1)", "jsonrpc-ws-server 7.1.1 (git+https://github.com/paritytech/jsonrpc?tag=v7.1.1)", "lazy_static 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "mullvad-rpc 0.1.0", "mullvad-types 0.1.0", "serde 1.0.15 (registry+https://github.com/rust-lang/crates.io-index)", "serde_derive 1.0.15 (registry+https://github.com/rust-lang/crates.io-index)", @@ -693,6 +692,16 @@ dependencies = [ ] [[package]] +name = "mullvad-rpc" +version = "0.1.0" +dependencies = [ + "chrono 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "jsonrpc-client-core 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "jsonrpc-client-http 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "mullvad-types 0.1.0", +] + +[[package]] name = "mullvad-types" version = "0.1.0" dependencies = [ diff --git a/Cargo.toml b/Cargo.toml index e5bf17fc21..0082f61236 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,6 +3,7 @@ members = [ "mullvad-daemon", "mullvad-cli", "mullvad-types", + "mullvad-rpc", "talpid-openvpn-plugin", "talpid-core", "talpid-ipc", diff --git a/mullvad-cli/src/main.rs b/mullvad-cli/src/main.rs index 274f1cc60f..e3475888fa 100644 --- a/mullvad-cli/src/main.rs +++ b/mullvad-cli/src/main.rs @@ -6,9 +6,6 @@ //! GNU General Public License as published by the Free Software Foundation, either version 3 of //! the License, or (at your option) any later version. -// `error_chain!` can recurse deeply -#![recursion_limit = "1024"] - #[macro_use] extern crate clap; extern crate env_logger; diff --git a/mullvad-daemon/Cargo.toml b/mullvad-daemon/Cargo.toml index 6dbd3430b0..7449b9120e 100644 --- a/mullvad-daemon/Cargo.toml +++ b/mullvad-daemon/Cargo.toml @@ -28,13 +28,12 @@ jsonrpc-core = { git = "https://github.com/paritytech/jsonrpc", tag = "v7.1.1" } jsonrpc-macros = { git = "https://github.com/paritytech/jsonrpc", tag = "v7.1.1" } jsonrpc-pubsub = { git = "https://github.com/paritytech/jsonrpc", tag = "v7.1.1" } jsonrpc-ws-server = { git = "https://github.com/paritytech/jsonrpc", tag = "v7.1.1" } -jsonrpc-client-core = "0.2.1" -jsonrpc-client-http = "0.2.1" uuid = { version = "0.5", features = ["v4"] } lazy_static = "0.2" toml = "0.4" mullvad-types = { path = "../mullvad-types" } +mullvad-rpc = { path = "../mullvad-rpc" } talpid-core = { path = "../talpid-core" } talpid-ipc = { path = "../talpid-ipc" } talpid-types = { path = "../talpid-types" } diff --git a/mullvad-daemon/src/bin/problem-report.rs b/mullvad-daemon/src/bin/problem-report.rs index a4b5107dec..8ecae0a0e5 100644 --- a/mullvad-daemon/src/bin/problem-report.rs +++ b/mullvad-daemon/src/bin/problem-report.rs @@ -11,6 +11,8 @@ extern crate clap; #[macro_use] extern crate error_chain; +extern crate mullvad_rpc; + use error_chain::ChainedError; use std::cmp::min; use std::fmt; @@ -20,6 +22,9 @@ use std::path::{Path, PathBuf}; /// Maximum number of bytes to read from each log file const LOG_MAX_READ_BYTES: usize = 5 * 1024 * 1024; +/// Maximum number of bytes allowed in a report. +const REPORT_MAX_SIZE: usize = 4 * LOG_MAX_READ_BYTES; + /// Field delimeter in generated problem report const LOG_DELIMITER: &'static str = "===================="; @@ -34,6 +39,9 @@ error_chain!{ description("Error reading the contents of log file") display("Error reading the contents of log file: {}", path.to_string_lossy()) } + RpcError { + description("Error during RPC call") + } } } @@ -96,40 +104,40 @@ fn run() -> Result<()> { let matches = app.get_matches(); if let Some(collect_matches) = matches.subcommand_matches("collect") { - let log_paths = values_t!(collect_matches.values_of("logs"), String) - .unwrap_or(Vec::new()) - .iter() - .map(PathBuf::from) - .collect(); - let output_path = value_t_or_exit!(collect_matches.value_of("output"), String); - collect_report(log_paths, PathBuf::from(output_path)) + let log_paths = collect_matches + .values_of_os("logs") + .map(|os_values| os_values.map(Path::new).collect()) + .unwrap_or(Vec::new()); + let output_path = Path::new(collect_matches.value_of_os("output").unwrap()); + collect_report(&log_paths, output_path) } else if let Some(send_matches) = matches.subcommand_matches("send") { - let report_path = value_t_or_exit!(send_matches.value_of("report"), String); - let user_email = value_t!(send_matches.value_of("email"), String).unwrap_or(String::new()); - let user_message = - value_t!(send_matches.value_of("message"), String).unwrap_or(String::new()); - send_problem_report(user_email, user_message, PathBuf::from(report_path)) + let report_path = Path::new(send_matches.value_of_os("report").unwrap()); + let user_email = send_matches.value_of("email").unwrap_or(""); + let user_message = send_matches.value_of("message").unwrap_or(""); + send_problem_report(user_email, user_message, report_path) } else { unreachable!("No sub command given"); } } -fn collect_report(log_paths: Vec<PathBuf>, save_path: PathBuf) -> Result<()> { +fn collect_report(log_paths: &[&Path], output_path: &Path) -> Result<()> { let mut problem_report = ProblemReport::default(); - for log_path in log_paths.into_iter() { - problem_report.add_file_log(log_path); + for log_path in log_paths { + problem_report.add_log(log_path); } - write_problem_report(&save_path, problem_report) - .chain_err(|| ErrorKind::WriteReportError(save_path.clone())) + write_problem_report(&output_path, problem_report) + .chain_err(|| ErrorKind::WriteReportError(output_path.to_path_buf())) } -fn send_problem_report( - _user_email: String, - _user_message: String, - _report_path: PathBuf, -) -> Result<()> { - // TODO: Implement submission to master - Ok(()) +fn send_problem_report(user_email: &str, user_message: &str, report_path: &Path) -> Result<()> { + let report_content = read_file_lossy(report_path, REPORT_MAX_SIZE) + .chain_err(|| ErrorKind::ReadLogError(report_path.to_path_buf()))?; + let mut rpc_client = + mullvad_rpc::ProblemReportProxy::connect().chain_err(|| ErrorKind::RpcError)?; + rpc_client + .problem_report(user_email, user_message, &report_content) + .call() + .chain_err(|| ErrorKind::RpcError) } fn write_problem_report(path: &Path, problem_report: ProblemReport) -> io::Result<()> { @@ -147,42 +155,14 @@ struct ProblemReport { } impl ProblemReport { - /// Attach file log to this report - /// Unlike `try_add_file_log` this method uses the error description - /// instead of log contents if error occurred when reading log file. - fn add_file_log(&mut self, path: PathBuf) { - if let Err(e) = self.try_add_file_log(&path) - .chain_err(|| ErrorKind::ReadLogError(path.clone())) - { - self.logs.push(( - path.to_string_lossy().into_owned(), - e.display_chain().to_string(), - )); - } - } - - /// Try reading log from file source and attach it to this report - fn try_add_file_log(&mut self, path: &Path) -> io::Result<()> { - Ok(self.logs.push(( - path.to_string_lossy().into_owned(), - Self::read_log_file(path, LOG_MAX_READ_BYTES)?, - ))) - } - - /// Private helper to safely read the given number of bytes off the tail of UTF-8 log file - /// and return it as a string - fn read_log_file(path: &Path, max_bytes: usize) -> io::Result<String> { - let mut file = File::open(path)?; - let file_size = file.metadata()?.len(); - - if file_size > max_bytes as u64 { - file.seek(SeekFrom::Start(file_size - max_bytes as u64))?; - } - - let capacity = min(file_size, max_bytes as u64) as usize; - let mut buffer = Vec::with_capacity(capacity); - file.take(max_bytes as u64).read_to_end(&mut buffer)?; - Ok(String::from_utf8_lossy(&buffer).into_owned()) + /// Attach file log to this report. This method uses the error chain instead of log + /// contents if error occurred when reading log file. + fn add_log(&mut self, path: &Path) { + let content = read_file_lossy(path, LOG_MAX_READ_BYTES) + .chain_err(|| ErrorKind::ReadLogError(path.to_path_buf())) + .unwrap_or_else(|e| e.display_chain().to_string()); + self.logs + .push((path.to_string_lossy().into_owned(), content)); } } @@ -198,3 +178,19 @@ impl fmt::Display for ProblemReport { Ok(()) } } + +/// Helper to lossily read a file to a `String`. If the file size exceeds the given `max_bytes`, +/// only the last `max_bytes` bytes of the file are read. +fn read_file_lossy(path: &Path, max_bytes: usize) -> io::Result<String> { + let mut file = File::open(path)?; + let file_size = file.metadata()?.len(); + + if file_size > max_bytes as u64 { + file.seek(SeekFrom::Start(file_size - max_bytes as u64))?; + } + + let capacity = min(file_size, max_bytes as u64) as usize; + let mut buffer = Vec::with_capacity(capacity); + file.take(max_bytes as u64).read_to_end(&mut buffer)?; + Ok(String::from_utf8_lossy(&buffer).into_owned()) +} diff --git a/mullvad-daemon/src/main.rs b/mullvad-daemon/src/main.rs index e804dcace8..ad2850b926 100644 --- a/mullvad-daemon/src/main.rs +++ b/mullvad-daemon/src/main.rs @@ -20,9 +20,6 @@ extern crate serde; #[macro_use] extern crate serde_derive; -#[macro_use] -extern crate jsonrpc_client_core; -extern crate jsonrpc_client_http; extern crate jsonrpc_core; #[macro_use] extern crate jsonrpc_macros; @@ -32,6 +29,7 @@ extern crate jsonrpc_ws_server; extern crate lazy_static; extern crate uuid; +extern crate mullvad_rpc; extern crate mullvad_types; extern crate talpid_core; extern crate talpid_ipc; @@ -39,7 +37,6 @@ extern crate talpid_types; mod cli; mod management_interface; -mod master; mod rpc_info; mod settings; mod shutdown; @@ -47,10 +44,9 @@ mod shutdown; use error_chain::ChainedError; use futures::Future; -use jsonrpc_client_http::HttpHandle; use jsonrpc_core::futures::sync::oneshot::Sender as OneshotSender; use management_interface::{BoxFuture, ManagementInterfaceServer, TunnelCommand}; -use master::AccountsProxy; +use mullvad_rpc::{AccountsProxy, HttpHandle}; use mullvad_types::account::{AccountData, AccountToken}; use mullvad_types::relay_endpoint::RelayEndpoint; use mullvad_types::states::{DaemonState, SecurityState, TargetState}; @@ -206,8 +202,7 @@ impl Daemon { tx, management_interface_broadcaster, settings: settings::Settings::load().chain_err(|| "Unable to read settings")?, - accounts_proxy: master::create_account_proxy() - .chain_err(|| "Unable to bootstrap RPC client")?, + accounts_proxy: AccountsProxy::connect().chain_err(|| "Unable to connect RPC client")?, firewall: FirewallProxy::new().chain_err(|| ErrorKind::FirewallError)?, relay_endpoint: None, tunnel_metadata: None, @@ -339,7 +334,7 @@ impl Daemon { fn on_get_account_data( &mut self, - tx: OneshotSender<BoxFuture<AccountData, jsonrpc_client_core::Error>>, + tx: OneshotSender<BoxFuture<AccountData, mullvad_rpc::Error>>, account_token: AccountToken, ) { let rpc_call = self.accounts_proxy diff --git a/mullvad-daemon/src/management_interface.rs b/mullvad-daemon/src/management_interface.rs index 829f843fca..43e55f4cb4 100644 --- a/mullvad-daemon/src/management_interface.rs +++ b/mullvad-daemon/src/management_interface.rs @@ -1,13 +1,13 @@ use error_chain; use error_chain::ChainedError; -use jsonrpc_client_core; use jsonrpc_core::{Error, ErrorCode, Metadata}; use jsonrpc_core::futures::{future, sync, Future}; use jsonrpc_core::futures::sync::oneshot::Sender as OneshotSender; use jsonrpc_macros::pubsub; use jsonrpc_pubsub::{PubSubHandler, PubSubMetadata, Session, SubscriptionId}; use jsonrpc_ws_server; +use mullvad_rpc; use mullvad_types::account::{AccountData, AccountToken}; use mullvad_types::location::{CountryCode, Location}; use mullvad_types::relay_endpoint::RelayEndpoint; @@ -126,7 +126,7 @@ pub enum TunnelCommand { GetState(OneshotSender<DaemonState>), /// Request the metadata for an account. GetAccountData( - OneshotSender<BoxFuture<AccountData, jsonrpc_client_core::Error>>, + OneshotSender<BoxFuture<AccountData, mullvad_rpc::Error>>, AccountToken, ), /// Set which account token to use for subsequent connection attempts. @@ -283,9 +283,9 @@ impl<T: From<TunnelCommand> + 'static + Send> ManagementInterface<T> { /// 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: jsonrpc_client_core::Error) -> Error { + fn map_rpc_error(error: mullvad_rpc::Error) -> Error { match error.kind() { - &jsonrpc_client_core::ErrorKind::JsonRpcError(ref rpc_error) => { + &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. Error { @@ -344,7 +344,7 @@ impl<T: From<TunnelCommand> + 'static + Send> ManagementInterfaceApi for Managem let future = self.send_command_to_daemon(TunnelCommand::GetAccountData(tx, account_token)) .and_then(|_| rx.map_err(|_| Error::internal_error())) .and_then(|rpc_future| { - rpc_future.map_err(|error: jsonrpc_client_core::Error| { + rpc_future.map_err(|error: mullvad_rpc::Error| { error!( "Unable to get account data from master: {}", error.display_chain() diff --git a/mullvad-daemon/src/master.rs b/mullvad-daemon/src/master.rs deleted file mode 100644 index 37bfbe65e5..0000000000 --- a/mullvad-daemon/src/master.rs +++ /dev/null @@ -1,16 +0,0 @@ -use chrono::DateTime; -use chrono::offset::Utc; -use jsonrpc_client_http::{Error as HttpError, HttpHandle, HttpTransport}; - -use mullvad_types::account::AccountToken; - -static MASTER_API_URI: &str = "https://api.mullvad.net/rpc/"; - -pub fn create_account_proxy() -> Result<AccountsProxy<HttpHandle>, HttpError> { - let transport = HttpTransport::new()?.handle(MASTER_API_URI)?; - Ok(AccountsProxy::new(transport)) -} - -jsonrpc_client!(pub struct AccountsProxy { - pub fn get_expiry(&mut self, account_token: AccountToken) -> RpcRequest<DateTime<Utc>>; -}); diff --git a/mullvad-rpc/Cargo.toml b/mullvad-rpc/Cargo.toml new file mode 100644 index 0000000000..41f7fccb2d --- /dev/null +++ b/mullvad-rpc/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "mullvad-rpc" +version = "0.1.0" +authors = ["Mullvad VPN <admin@mullvad.net>", "Linus Färnstrand <linus@mullvad.net>", "Erik Larkö <erik@mullvad.net>", "Andrej Mihajlov <and@mullvad.net>"] +description = "Mullvad VPN RPC clients. Providing an interface to query our infrastructure for information." +license = "GPL-3.0" + +[dependencies] +chrono = { version = "0.4", features = ["serde"] } +jsonrpc-client-core = "0.2.1" +jsonrpc-client-http = "0.2.1" + +mullvad-types = { path = "../mullvad-types" } diff --git a/mullvad-rpc/src/lib.rs b/mullvad-rpc/src/lib.rs new file mode 100644 index 0000000000..9174f1e684 --- /dev/null +++ b/mullvad-rpc/src/lib.rs @@ -0,0 +1,49 @@ +//! # 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 jsonrpc_client_core; +extern crate jsonrpc_client_http; + +extern crate mullvad_types; + +use chrono::DateTime; +use chrono::offset::Utc; +use jsonrpc_client_http::HttpTransport; + +pub use jsonrpc_client_core::{Error, ErrorKind}; +pub use jsonrpc_client_http::{Error as HttpError, HttpHandle}; + +use mullvad_types::account::AccountToken; + + +static MASTER_API_URI: &str = "https://api.mullvad.net/rpc/"; + + +jsonrpc_client!(pub struct AccountsProxy { + pub fn get_expiry(&mut self, account_token: AccountToken) -> RpcRequest<DateTime<Utc>>; +}); + +impl AccountsProxy<HttpHandle> { + pub fn connect() -> Result<Self, HttpError> { + let transport = HttpTransport::new()?.handle(MASTER_API_URI)?; + Ok(AccountsProxy::new(transport)) + } +} + +jsonrpc_client!(pub struct ProblemReportProxy { + pub fn problem_report(&mut self, email: &str, message: &str, log: &str) -> RpcRequest<()>; +}); + +impl ProblemReportProxy<HttpHandle> { + pub fn connect() -> Result<Self, HttpError> { + let transport = HttpTransport::new()?.handle(MASTER_API_URI)?; + Ok(ProblemReportProxy::new(transport)) + } +} |
