diff options
| author | Linus Färnstrand <linus@mullvad.net> | 2019-04-05 20:50:25 +0200 |
|---|---|---|
| committer | Linus Färnstrand <linus@mullvad.net> | 2019-04-05 20:50:25 +0200 |
| commit | e2bc2596cf130a26ab143a173b79039f01ddb1d1 (patch) | |
| tree | 18cca9b3b5ebb07d8b638387740eeae1c091ab9e | |
| parent | 95eaba22e7cc2f4e7fcf2717258486244e320aa3 (diff) | |
| parent | e56799a487983124ce3cfcd87a275a67c91c942b (diff) | |
| download | mullvadvpn-e2bc2596cf130a26ab143a173b79039f01ddb1d1.tar.xz mullvadvpn-e2bc2596cf130a26ab143a173b79039f01ddb1d1.zip | |
Merge branch 'eliminate-error-chain-tunnel'
| -rw-r--r-- | Cargo.lock | 4 | ||||
| -rw-r--r-- | talpid-core/Cargo.toml | 1 | ||||
| -rw-r--r-- | talpid-core/src/dns/linux/static_resolv_conf.rs | 73 | ||||
| -rw-r--r-- | talpid-core/src/firewall/linux.rs | 2 | ||||
| -rw-r--r-- | talpid-core/src/lib.rs | 3 | ||||
| -rw-r--r-- | talpid-core/src/routing/android.rs | 11 | ||||
| -rw-r--r-- | talpid-core/src/routing/linux.rs | 6 | ||||
| -rw-r--r-- | talpid-core/src/routing/macos.rs | 6 | ||||
| -rw-r--r-- | talpid-core/src/routing/mod.rs | 12 | ||||
| -rw-r--r-- | talpid-core/src/tunnel/mod.rs | 66 | ||||
| -rw-r--r-- | talpid-core/src/tunnel/openvpn.rs | 156 | ||||
| -rw-r--r-- | talpid-core/src/tunnel/wireguard/mod.rs | 74 | ||||
| -rw-r--r-- | talpid-core/src/tunnel/wireguard/wireguard_go.rs | 18 | ||||
| -rw-r--r-- | talpid-core/src/tunnel_state_machine/connecting_state.rs | 18 | ||||
| -rw-r--r-- | talpid-openvpn-plugin/Cargo.toml | 3 | ||||
| -rw-r--r-- | talpid-openvpn-plugin/src/lib.rs | 68 | ||||
| -rw-r--r-- | talpid-openvpn-plugin/src/processing.rs | 55 |
17 files changed, 295 insertions, 281 deletions
diff --git a/Cargo.lock b/Cargo.lock index a447c280d1..e42c14056b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1984,7 +1984,6 @@ dependencies = [ "derive_more 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)", "duct 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", "err-derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "error-chain 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", "hex 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2047,13 +2046,14 @@ name = "talpid-openvpn-plugin" version = "0.1.0" dependencies = [ "env_logger 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", - "error-chain 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", + "err-derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", "jsonrpc-client-core 0.5.0 (git+https://github.com/mullvad/jsonrpc-client-rs?rev=68aac55b)", "jsonrpc-client-ipc 0.5.0 (git+https://github.com/mullvad/jsonrpc-client-rs?rev=68aac55b)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "openvpn-plugin 0.3.0 (git+https://github.com/mullvad/openvpn-plugin-rs?branch=auth-failed-event)", "talpid-ipc 0.1.0", + "talpid-types 0.1.0", "tokio 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-reactor 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/talpid-core/Cargo.toml b/talpid-core/Cargo.toml index b27c509fcb..5c0e57e557 100644 --- a/talpid-core/Cargo.toml +++ b/talpid-core/Cargo.toml @@ -10,7 +10,6 @@ edition = "2018" atty = "0.2" derive_more = "0.14" duct = "0.12" -error-chain = "0.12" err-derive = "0.1.5" futures = "0.1" jsonrpc-core = { git = "https://github.com/mullvad/jsonrpc", branch = "mullvad-fork" } diff --git a/talpid-core/src/dns/linux/static_resolv_conf.rs b/talpid-core/src/dns/linux/static_resolv_conf.rs index 71b6973edf..73c9aa4221 100644 --- a/talpid-core/src/dns/linux/static_resolv_conf.rs +++ b/talpid-core/src/dns/linux/static_resolv_conf.rs @@ -1,5 +1,4 @@ use super::RESOLV_CONF_PATH; -use error_chain::ChainedError; use notify::{RecommendedWatcher, RecursiveMode, Watcher}; use parking_lot::Mutex; use resolv_conf::{Config, ScopedIp}; @@ -9,27 +8,28 @@ use std::{ sync::{mpsc, Arc}, thread, }; +use talpid_types::ErrorExt; const RESOLV_CONF_BACKUP_PATH: &str = "/etc/resolv.conf.mullvadbackup"; -error_chain! { - errors { - WatchResolvConf { - description("Failed to watch /etc/resolv.conf for changes") - } +pub type Result<T> = std::result::Result<T, Error>; - WriteResolvConf { - description("Failed to write to /etc/resolv.conf") - } +#[derive(err_derive::Error, Debug)] +pub enum Error { + #[error(display = "Failed to watch /etc/resolv.conf for changes")] + WatchResolvConf(#[error(cause)] notify::Error), - BackupResolvConf { - description("Failed to create backup of /etc/resolv.conf") - } + #[error(display = "Failed to write to {}", _0)] + WriteResolvConf(&'static str, #[error(cause)] io::Error), - RestoreResolvConf { - description("Failed to restore /etc/resolv.conf from backup") - } - } + #[error(display = "Failed to read from {}", _0)] + ReadResolvConf(&'static str, #[error(cause)] io::Error), + + #[error(display = "resolv.conf at {} could not be parsed", _0)] + ParseError(&'static str, #[error(cause)] resolv_conf::ParseError), + + #[error(display = "Failed to remove stale resolv.conf backup at {}", _0)] + RemoveBackup(&'static str, #[error(cause)] io::Error), } pub struct StaticResolvConf { @@ -39,7 +39,7 @@ pub struct StaticResolvConf { impl StaticResolvConf { pub fn new() -> Result<Self> { - restore_from_backup().chain_err(|| ErrorKind::RestoreResolvConf)?; + restore_from_backup()?; let state = Arc::new(Mutex::new(None)); let watcher = DnsWatcher::start(state.clone())?; @@ -54,8 +54,8 @@ impl StaticResolvConf { let mut state = self.state.lock(); let new_state = match state.take() { None => { - let backup = read_config().chain_err(|| ErrorKind::BackupResolvConf)?; - write_backup(&backup).chain_err(|| ErrorKind::BackupResolvConf)?; + let backup = read_config()?; + write_backup(&backup)?; State { backup, @@ -111,11 +111,11 @@ struct DnsWatcher { impl DnsWatcher { fn start(state: Arc<Mutex<Option<State>>>) -> Result<Self> { let (event_tx, event_rx) = mpsc::channel(); - let mut watcher = notify::raw_watcher(event_tx).chain_err(|| ErrorKind::WatchResolvConf)?; + let mut watcher = notify::raw_watcher(event_tx).map_err(Error::WatchResolvConf)?; watcher .watch(RESOLV_CONF_PATH, RecursiveMode::NonRecursive) - .chain_err(|| ErrorKind::WatchResolvConf)?; + .map_err(Error::WatchResolvConf)?; thread::spawn(move || Self::event_loop(event_rx, &state)); @@ -127,9 +127,12 @@ impl DnsWatcher { let mut locked_state = state.lock(); if let Err(error) = Self::update(locked_state.as_mut()) { - let chained_error = error - .chain_err(|| "Failed to update DNS state after DNS settings have changed."); - log::error!("{}", chained_error.display_chain()); + log::error!( + "{}", + error.display_chain_with_msg( + "Failed to update DNS state after DNS settings changed" + ) + ); } } } @@ -153,7 +156,7 @@ impl DnsWatcher { new_config.nameservers.append(&mut state.backup.nameservers); state.backup = new_config; - write_backup(&state.backup).chain_err(|| "Failed to update /etc/resolv.conf backup") + write_backup(&state.backup) } } else { Ok(()) @@ -162,22 +165,21 @@ impl DnsWatcher { } fn read_config() -> Result<Config> { - let contents = - fs::read_to_string(RESOLV_CONF_PATH).chain_err(|| "Failed to read /etc/resolv.conf")?; - let config = - Config::parse(&contents).chain_err(|| "Failed to parse contents of /etc/resolv.conf")?; + let contents = fs::read_to_string(RESOLV_CONF_PATH) + .map_err(|e| Error::ReadResolvConf(RESOLV_CONF_PATH, e))?; + let config = Config::parse(&contents).map_err(|e| Error::ParseError(RESOLV_CONF_PATH, e))?; Ok(config) } fn write_config(config: &Config) -> Result<()> { fs::write(RESOLV_CONF_PATH, config.to_string().as_bytes()) - .chain_err(|| ErrorKind::WriteResolvConf) + .map_err(|e| Error::WriteResolvConf(RESOLV_CONF_PATH, e)) } fn write_backup(backup: &Config) -> Result<()> { fs::write(RESOLV_CONF_BACKUP_PATH, backup.to_string().as_bytes()) - .chain_err(|| "Failed to write to /etc/resolv.conf backup file") + .map_err(|e| Error::WriteResolvConf(RESOLV_CONF_BACKUP_PATH, e)) } fn restore_from_backup() -> Result<()> { @@ -185,20 +187,17 @@ fn restore_from_backup() -> Result<()> { Ok(backup) => { log::info!("Restoring DNS state from backup"); let config = Config::parse(&backup) - .chain_err(|| "Backup of /etc/resolv.conf could not be parsed")?; + .map_err(|e| Error::ParseError(RESOLV_CONF_BACKUP_PATH, e))?; write_config(&config)?; fs::remove_file(RESOLV_CONF_BACKUP_PATH) - .chain_err(|| "Failed to remove stale backup of /etc/resolv.conf") + .map_err(|e| Error::RemoveBackup(RESOLV_CONF_BACKUP_PATH, e)) } Err(ref error) if error.kind() == io::ErrorKind::NotFound => { log::debug!("No DNS state backup to restore"); Ok(()) } - Err(error) => Err(Error::with_chain( - error, - "Failed to read /etc/resolv.conf backup", - )), + Err(error) => Err(Error::ReadResolvConf(RESOLV_CONF_BACKUP_PATH, error)), } } diff --git a/talpid-core/src/firewall/linux.rs b/talpid-core/src/firewall/linux.rs index 5c8d9e777e..540bdff1ee 100644 --- a/talpid-core/src/firewall/linux.rs +++ b/talpid-core/src/firewall/linux.rs @@ -164,7 +164,7 @@ impl Firewall { "Expected '{}' netfilter table to be set, but it is not", expected_table.to_string_lossy() ); - bail!(Error::NetfilterTableNotSetError) + return Err(Error::NetfilterTableNotSetError); } } Ok(()) diff --git a/talpid-core/src/lib.rs b/talpid-core/src/lib.rs index 1499557439..3b2bac9529 100644 --- a/talpid-core/src/lib.rs +++ b/talpid-core/src/lib.rs @@ -11,9 +11,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. -#[macro_use] -extern crate error_chain; - /// Misc FFI utilities. #[cfg(windows)] #[macro_use] diff --git a/talpid-core/src/routing/android.rs b/talpid-core/src/routing/android.rs index fa5c5c8f8e..5c562537f1 100644 --- a/talpid-core/src/routing/android.rs +++ b/talpid-core/src/routing/android.rs @@ -7,7 +7,10 @@ use std::{ net::{IpAddr, Ipv4Addr}, }; -error_chain! {} +/// Stub error type for routing errors on Android. +#[derive(Debug, err_derive::Error)] +#[error(display = "Unknown Android routing error")] +pub struct Error; pub struct RouteManager; @@ -18,15 +21,15 @@ impl super::RoutingT for RouteManager { Ok(RouteManager) } - fn add_routes(&mut self, _required_routes: RequiredRoutes) -> Result<()> { + fn add_routes(&mut self, _required_routes: RequiredRoutes) -> Result<(), Self::Error> { Ok(()) } - fn delete_routes(&mut self) -> Result<()> { + fn delete_routes(&mut self) -> Result<(), Self::Error> { Ok(()) } - fn get_default_route_node(&mut self) -> Result<IpAddr> { + fn get_default_route_node(&mut self) -> Result<IpAddr, Self::Error> { Ok(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0))) } } diff --git a/talpid-core/src/routing/linux.rs b/talpid-core/src/routing/linux.rs index 73de509a0e..b229b192c2 100644 --- a/talpid-core/src/routing/linux.rs +++ b/talpid-core/src/routing/linux.rs @@ -9,20 +9,26 @@ use std::{ pub type Result<T> = std::result::Result<T, Error>; +/// Errors that can happen in the Linux routing integration #[derive(err_derive::Error, Debug)] pub enum Error { + /// Failed to add route. #[error(display = "Failed to add route")] FailedToAddRoute(#[error(cause)] io::Error), + /// Failed to remove route. #[error(display = "Failed to remove route")] FailedToRemoveRoute(#[error(cause)] io::Error), + /// Error while running "ip route". #[error(display = "Error while running \"ip route\"")] FailedToRunIp(#[error(cause)] io::Error), + /// No default route in "ip route" output. #[error(display = "No default route in \"ip route\" output")] NoDefaultRoute, + /// Failed to parse default route as IP. #[error(display = "Failed to parse default route as IP: {}", _0)] ParseDefaultRoute(String, #[error(cause)] AddrParseError), } diff --git a/talpid-core/src/routing/macos.rs b/talpid-core/src/routing/macos.rs index d0e9497c1c..722efbb320 100644 --- a/talpid-core/src/routing/macos.rs +++ b/talpid-core/src/routing/macos.rs @@ -10,20 +10,26 @@ use std::{ pub type Result<T> = std::result::Result<T, Error>; +/// Errors that can happen in the macOS routing integration. #[derive(err_derive::Error, Debug)] pub enum Error { + /// Failed to add route. #[error(display = "Failed to add route")] FailedToAddRoute(#[error(cause)] io::Error), + /// Failed to remove route. #[error(display = "Failed to remove route")] FailedToRemoveRoute(#[error(cause)] io::Error), + /// Error while running "ip route". #[error(display = "Error while running \"ip route\"")] FailedToRunIp(#[error(cause)] io::Error), + /// No default route in "ip route" output. #[error(display = "No default route in \"ip route\" output")] NoDefaultRoute, + /// Failed to parse default route as IP. #[error(display = "Failed to parse default route as IP: {}", _0)] ParseDefaultRoute(String, #[error(cause)] AddrParseError), } diff --git a/talpid-core/src/routing/mod.rs b/talpid-core/src/routing/mod.rs index 612c0b2396..b2e299553f 100644 --- a/talpid-core/src/routing/mod.rs +++ b/talpid-core/src/routing/mod.rs @@ -13,6 +13,8 @@ mod imp; #[path = "android.rs"] mod imp; +pub use self::imp::Error; + mod subprocess; /// A single route @@ -53,24 +55,24 @@ pub struct RouteManager { impl RouteManager { /// Creates a new RouteManager. - pub fn new() -> Result<Self, imp::Error> { + pub fn new() -> Result<Self, Error> { Ok(RouteManager { inner: imp::RouteManager::new()?, }) } /// Set routes in the routing table. - pub fn add_routes(&mut self, required_routes: RequiredRoutes) -> Result<(), imp::Error> { + pub fn add_routes(&mut self, required_routes: RequiredRoutes) -> Result<(), Error> { self.inner.add_routes(required_routes) } /// Remove previously set routes from the routing table. - pub fn delete_routes(&mut self) -> Result<(), imp::Error> { + pub fn delete_routes(&mut self) -> Result<(), Error> { self.inner.delete_routes() } /// Retrieves the gateway for the default route. - pub fn get_default_route_node(&mut self) -> Result<std::net::IpAddr, imp::Error> { + pub fn get_default_route_node(&mut self) -> Result<std::net::IpAddr, Error> { // use routing::RoutingT; self.inner.get_default_route_node() } @@ -87,7 +89,7 @@ impl Drop for RouteManager { /// This trait unifies platform specific implementations of route managers pub trait RoutingT: Sized { /// Error type of the implementation - type Error: ::std::error::Error; + type Error: std::error::Error; /// Creates a new router fn new() -> Result<Self, Self::Error>; diff --git a/talpid-core/src/tunnel/mod.rs b/talpid-core/src/tunnel/mod.rs index 04332908c4..987f80c872 100644 --- a/talpid-core/src/tunnel/mod.rs +++ b/talpid-core/src/tunnel/mod.rs @@ -19,36 +19,37 @@ pub mod wireguard; const OPENVPN_LOG_FILENAME: &str = "openvpn.log"; const WIREGUARD_LOG_FILENAME: &str = "wireguard.log"; +/// Results from operations in the tunnel module. +pub type Result<T> = std::result::Result<T, Error>; -error_chain! { - errors { - /// Tunnel can't have IPv6 enabled because the system has disabled IPv6 support. - EnableIpv6Error { - description("Can't enable IPv6 on tunnel interface because IPv6 is disabled") - } - /// Running on an operating system which is not supported yet. - UnsupportedPlatform { - description("Tunnel type not supported on this operating system") - } - /// Failed to rotate tunnel log file - RotateLogError { - description("Failed to rotate tunnel log file") - } - /// Failure to build Wireguard configuration. - WireguardConfigError { - description("Failed to configure Wireguard with the given parameters") - } - } +/// Errors that can occur in the [`TunnelMonitor`]. +#[derive(err_derive::Error, Debug, derive_more::From)] +pub enum Error { + /// Tunnel can't have IPv6 enabled because the system has disabled IPv6 support. + #[error(display = "Can't enable IPv6 on tunnel interface because IPv6 is disabled")] + EnableIpv6Error, - links { - OpenVpnTunnelMonitoringError(openvpn::Error, openvpn::ErrorKind) - /// There was an error listening for events from the OpenVPN tunnel - ; - WirguardTunnelMonitoringError(wireguard::Error, wireguard::ErrorKind) - /// There was an error listening for events from the OpenVPN tunnel - #[cfg(any(target_os = "linux", target_os = "macos"))] - ; - } + /// Running on an operating system which is not supported yet. + #[error(display = "Tunnel type not supported on this operating system")] + UnsupportedPlatform, + + /// Failed to rotate tunnel log file + #[error(display = "Failed to rotate tunnel log file")] + RotateLogError(#[error(cause)] crate::logging::RotateLogError), + + /// Failure to build Wireguard configuration. + #[cfg(any(target_os = "linux", target_os = "macos"))] + #[error(display = "Failed to configure Wireguard with the given parameters")] + WireguardConfigError(#[error(cause)] self::wireguard::config::Error), + + /// There was an error listening for events from the OpenVPN tunnel + #[error(display = "Failed while listening for events from the OpenVPN tunnel")] + OpenVpnTunnelMonitoringError(#[error(cause)] openvpn::Error), + + /// There was an error listening for events from the Wireguard tunnel + #[cfg(any(target_os = "linux", target_os = "macos"))] + #[error(display = "Failed while listening for events from the Wireguard tunnel")] + WirguardTunnelMonitoringError(#[error(cause)] wireguard::Error), } @@ -151,7 +152,7 @@ impl TunnelMonitor { Self::start_wireguard_tunnel(&config, log_file, on_event) } #[cfg(any(windows, target_os = "android"))] - TunnelParameters::Wireguard(_) => bail!(ErrorKind::UnsupportedPlatform), + TunnelParameters::Wireguard(_) => Err(Error::UnsupportedPlatform), } } @@ -164,8 +165,7 @@ impl TunnelMonitor { where L: Fn(TunnelEvent) + Send + Sync + Clone + 'static, { - let config = wireguard::config::Config::from_parameters(¶ms) - .chain_err(|| ErrorKind::WireguardConfigError)?; + let config = wireguard::config::Config::from_parameters(¶ms)?; let monitor = wireguard::WireguardMonitor::start( &config, log.as_ref().map(|p| p.as_path()), @@ -195,7 +195,7 @@ impl TunnelMonitor { fn ensure_ipv6_can_be_used_if_enabled(tunnel_options: &GenericTunnelOptions) -> Result<()> { if tunnel_options.enable_ipv6 && !is_ipv6_enabled_in_os() { - bail!(ErrorKind::EnableIpv6Error); + Err(Error::EnableIpv6Error) } else { Ok(()) } @@ -211,7 +211,7 @@ impl TunnelMonitor { TunnelParameters::Wireguard(_) => WIREGUARD_LOG_FILENAME, }; let tunnel_log = log_dir.join(filename); - logging::rotate_log(&tunnel_log).chain_err(|| ErrorKind::RotateLogError)?; + logging::rotate_log(&tunnel_log)?; Ok(Some(tunnel_log)) } else { Ok(None) diff --git a/talpid-core/src/tunnel/openvpn.rs b/talpid-core/src/tunnel/openvpn.rs index 8e0de74430..d8c97d468a 100644 --- a/talpid-core/src/tunnel/openvpn.rs +++ b/talpid-core/src/tunnel/openvpn.rs @@ -28,52 +28,70 @@ use talpid_types::net::openvpn; #[cfg(target_os = "linux")] use which; -error_chain! { - errors { - /// Unable to start, wait for or kill the OpenVPN process. - ChildProcessError(msg: &'static str) { - description("Unable to start, wait for or kill the OpenVPN process") - display("OpenVPN process error: {}", msg) - } - /// Unable to start or manage the IPC server listening for events from OpenVPN. - EventDispatcherError { - description("Unable to start or manage the event dispatcher IPC server") - } - #[cfg(windows)] - /// No TAP adapter was detected - MissingTapAdapter { - description("No TAP adapter was detected") - } - #[cfg(windows)] - /// TAP adapter seems to be disabled - DisabledTapAdapter { - description("The TAP adapter appears to be disabled") - } - /// The IP routing program was not found. - #[cfg(target_os = "linux")] - IpRouteNotFound { - description("The IP routing program `ip` was not found.") - } - /// The OpenVPN binary was not found. - OpenVpnNotFound(path: PathBuf) { - description("No OpenVPN binary found") - display("No OpenVPN binary found at {}", path.display()) - } - /// The OpenVPN plugin was not found. - PluginNotFound(path: PathBuf) { - description("No OpenVPN plugin found") - display("No OpenVPN plugin found at {}", path.display()) - } - /// There was an error when writing authentication credentials to temporary file. - CredentialsWriteError { - description("Error while writing credentials to temporary file") - } - /// Failures related to the proxy service. - ProxyError(msg: String) { - description("Unable to start, wait for or kill the proxy service") - display("Proxy error: {}", msg) - } - } + +/// Results from fallible operations on the OpenVPN tunnel. +pub type Result<T> = std::result::Result<T, Error>; + +/// Errors that can happen when using the OpenVPN tunnel. +#[derive(err_derive::Error, Debug)] +pub enum Error { + /// Unable to start, wait for or kill the OpenVPN process. + #[error(display = "Error in OpenVPN process management: {}", _0)] + ChildProcessError(&'static str, #[error(cause)] io::Error), + + /// Unable to start or manage the IPC server listening for events from OpenVPN. + #[error(display = "Unable to start or manage the event dispatcher IPC server")] + EventDispatcherError(#[error(cause)] talpid_ipc::Error), + + /// The OpenVPN event dispatcher exited unexpectedly + #[error(display = "The OpenVPN event dispatcher exited unexpectedly")] + EventDispatcherExited, + + /// No TAP adapter was detected + #[cfg(windows)] + #[error(display = "No TAP adapter was detected")] + MissingTapAdapter, + + /// TAP adapter seems to be disabled + #[cfg(windows)] + #[error(display = "The TAP adapter appears to be disabled")] + DisabledTapAdapter, + + /// OpenVPN process died unexpectedly + #[error(display = "OpenVPN process died unexpectedly")] + ChildProcessDied, + + /// The IP routing program was not found. + #[cfg(target_os = "linux")] + #[error(display = "The IP routing program `ip` was not found")] + IpRouteNotFound(#[error(cause)] failure::Compat<which::Error>), + + /// The OpenVPN binary was not found. + #[error(display = "No OpenVPN binary found at {}", _0)] + OpenVpnNotFound(String), + + /// The OpenVPN plugin was not found. + #[error(display = "No OpenVPN plugin found at {}", _0)] + PluginNotFound(String), + + /// Error while writing credentials to temporary file. + #[error(display = "Error while writing credentials to temporary file")] + CredentialsWriteError(#[error(cause)] io::Error), + + /// Failures related to the proxy service. + #[error(display = "Unable to start the proxy service")] + StartProxyError(#[error(cause)] io::Error), + + /// Error while monitoring proxy service + #[error(display = "Error while monitoring proxy service")] + MonitorProxyError(#[error(cause)] io::Error), + + /// The proxy exited unexpectedly + #[error( + display = "The proxy exited unexpectedly providing these details: {}", + _0 + )] + ProxyExited(String), } @@ -124,10 +142,10 @@ impl OpenVpnMonitor<OpenVpnCommand> { { let user_pass_file = Self::create_credentials_file(¶ms.config.username, ¶ms.config.password) - .chain_err(|| ErrorKind::CredentialsWriteError)?; + .map_err(Error::CredentialsWriteError)?; let proxy_auth_file = Self::create_proxy_auth_file(¶ms.options.proxy) - .chain_err(|| ErrorKind::CredentialsWriteError)?; + .map_err(Error::CredentialsWriteError)?; let user_pass_file_path = user_pass_file.to_path_buf(); @@ -205,13 +223,13 @@ impl<C: OpenVpnBuilder + 'static> OpenVpnMonitor<C> { L: Fn(openvpn_plugin::EventType, HashMap<String, String>) + Send + Sync + 'static, { let event_dispatcher = - event_server::start(on_event).chain_err(|| ErrorKind::EventDispatcherError)?; + event_server::start(on_event).map_err(Error::EventDispatcherError)?; let child = cmd .plugin(plugin_path, vec![event_dispatcher.path().to_owned()]) .log(log_path.as_ref().map(|p| p.as_path())) .start() - .chain_err(|| ErrorKind::ChildProcessError("Failed to start"))?; + .map_err(|e| Error::ChildProcessError("Failed to start", e))?; Ok(OpenVpnMonitor { child: Arc::new(child), @@ -267,21 +285,13 @@ impl<C: OpenVpnBuilder + 'static> OpenVpnMonitor<C> { // The proxy should never exit before openvpn. match proxy_result { Ok(proxy::WaitResult::ProperShutdown) => { - return Err(ErrorKind::ProxyError("The proxy exited unexpectedly without providing additional details".into()).into()); + return Err(Error::ProxyExited("No details".to_owned())); } Ok(proxy::WaitResult::UnexpectedExit(details)) => { - return Err(ErrorKind::ProxyError(format!( - "The proxy exited unexpectedly providing these details: {}", - details - )) - .into()); + return Err(Error::ProxyExited(details)); } Err(err) => { - return Err(err).chain_err(|| { - ErrorKind::ProxyError( - "Failed to wait for/monitor proxy service".into(), - ) - }); + return Err(err).map_err(Error::MonitorProxyError); } } } @@ -309,11 +319,11 @@ impl<C: OpenVpnBuilder + 'static> OpenVpnMonitor<C> { } WaitResult::Child(Err(e), _) => { log::error!("OpenVPN process wait error: {}", e); - Err(e).chain_err(|| ErrorKind::ChildProcessError("Error when waiting")) + Err(Error::ChildProcessError("Error when waiting", e)) } WaitResult::EventDispatcher => { log::error!("OpenVPN Event server exited unexpectedly"); - Err(ErrorKind::EventDispatcherError.into()) + Err(Error::EventDispatcherExited) } } } @@ -356,16 +366,16 @@ impl<C: OpenVpnBuilder + 'static> OpenVpnMonitor<C> { if let Some(log_path) = self.log_path.take() { if let Ok(log) = fs::read_to_string(log_path) { if log.contains("There are no TAP-Windows adapters on this system") { - return ErrorKind::MissingTapAdapter.into(); + return Error::MissingTapAdapter; } if log.contains("CreateFile failed on TAP device") { - return ErrorKind::DisabledTapAdapter.into(); + return Error::DisabledTapAdapter; } } } } - ErrorKind::ChildProcessError("Died unexpectedly").into() + Error::ChildProcessDied } fn create_proxy_auth_file( @@ -388,8 +398,8 @@ impl<C: OpenVpnBuilder + 'static> OpenVpnMonitor<C> { proxy_resources: &ProxyResourceData, ) -> Result<Option<Box<dyn ProxyMonitor>>> { if let Some(ref settings) = proxy_settings { - let proxy_monitor = proxy::start_proxy(settings, proxy_resources) - .chain_err(|| ErrorKind::ProxyError("Failed to start proxy service".into()))?; + let proxy_monitor = + proxy::start_proxy(settings, proxy_resources).map_err(Error::StartProxyError)?; return Ok(Some(proxy_monitor)); } Ok(None) @@ -423,7 +433,7 @@ impl<C: OpenVpnBuilder + 'static> OpenVpnMonitor<C> { log::trace!("Using OpenVPN plugin at {}", path.display()); Ok(path) } else { - bail!(ErrorKind::PluginNotFound(path)); + Err(Error::PluginNotFound(path.display().to_string())) } } @@ -443,7 +453,7 @@ impl<C: OpenVpnBuilder + 'static> OpenVpnMonitor<C> { cmd.iproute_bin( which::which("ip") .compat() - .chain_err(|| ErrorKind::IpRouteNotFound)?, + .map_err(Error::IpRouteNotFound)?, ); cmd.remote(params.config.get_tunnel_endpoint().endpoint) .user_pass(user_pass_file) @@ -467,7 +477,7 @@ impl<C: OpenVpnBuilder + 'static> OpenVpnMonitor<C> { log::trace!("Using OpenVPN at {}", path.display()); Ok(path) } else { - bail!(ErrorKind::OpenVpnNotFound(path)); + Err(Error::OpenVpnNotFound(path.display().to_string())) } } @@ -746,8 +756,8 @@ mod tests { let error = OpenVpnMonitor::new_internal(builder, |_, _| {}, "", None, TempFile::new(), None, None) .unwrap_err(); - match error.kind() { - ErrorKind::ChildProcessError(_) => (), + match error { + Error::ChildProcessError(..) => (), _ => panic!("Wrong error"), } } diff --git a/talpid-core/src/tunnel/wireguard/mod.rs b/talpid-core/src/tunnel/wireguard/mod.rs index d1c0c451d9..79034f22cd 100644 --- a/talpid-core/src/tunnel/wireguard/mod.rs +++ b/talpid-core/src/tunnel/wireguard/mod.rs @@ -1,8 +1,9 @@ #![allow(missing_docs)] + use self::config::Config; use super::{TunnelEvent, TunnelMetadata}; use crate::routing; -use std::{path::Path, sync::mpsc}; +use std::{io, path::Path, sync::mpsc}; pub mod config; mod ping_monitor; @@ -13,37 +14,38 @@ pub use self::wireguard_go::WgGoTunnel; // amount of seconds to run `ping` until it returns. const PING_TIMEOUT: u16 = 7; -error_chain! { - errors { - /// Failed to setup a tunnel device - SetupTunnelDeviceError { - description("Failed to create tunnel device") - } - /// Failed to setup wireguard tunnel - StartWireguardError(status: i32) { - display("Failed to start wireguard tunnel - {}", status) - } - /// Failed to tear down wireguard tunnel - StopWireguardError(status: i32) { - display("Failed to stop wireguard tunnel - {}", status) - } - /// Failed to set up routing - SetupRoutingError { - display("Failed to setup routing") - } - /// Failed to move or craete a log file - PrepareLogFileError { - display("Failed to setup a logging file") - } - /// Tunnel interface name contained null bytes - InterfaceNameError { - display("Tunnel interface name contains null bytes") - } - /// Pinging timed out - PingTimeoutError { - display("Ping timed out") - } - } +pub type Result<T> = std::result::Result<T, Error>; + +/// Errors that can happen in the Wireguard tunnel monitor. +#[derive(err_derive::Error, Debug)] +pub enum Error { + /// Failed to setup a tunnel device. + #[error(display = "Failed to create tunnel device")] + SetupTunnelDeviceError(#[error(cause)] crate::network_interface::Error), + + /// Failed to setup wireguard tunnel. + #[error(display = "Failed to start wireguard tunnel - {}", status)] + StartWireguardError { status: i32 }, + + /// Failed to tear down wireguard tunnel. + #[error(display = "Failed to stop wireguard tunnel - {}", status)] + StopWireguardError { status: i32 }, + + /// Failed to set up routing. + #[error(display = "Failed to setup routing")] + SetupRoutingError(#[error(cause)] crate::routing::Error), + + /// Failed to move or craete a log file. + #[error(display = "Failed to setup a logging file")] + PrepareLogFileError(#[error(cause)] io::Error), + + /// Invalid tunnel interface name. + #[error(display = "Invalid tunnel interface name")] + InterfaceNameError(#[error(cause)] std::ffi::NulError), + + /// Pinging timed out. + #[error(display = "Ping timed out")] + PingTimeoutError, } /// Spawns and monitors a wireguard tunnel @@ -65,7 +67,7 @@ impl WireguardMonitor { on_event: F, ) -> Result<WireguardMonitor> { let tunnel = Box::new(WgGoTunnel::start_tunnel(&config, log_path)?); - let router = routing::RouteManager::new().chain_err(|| ErrorKind::SetupRoutingError)?; + let router = routing::RouteManager::new().map_err(Error::SetupRoutingError)?; let event_callback = Box::new(on_event.clone()); let (close_msg_sender, close_msg_receiver) = mpsc::channel(); let mut monitor = WireguardMonitor { @@ -111,7 +113,7 @@ impl WireguardMonitor { pub fn wait(mut self) -> Result<()> { let wait_result = match self.close_msg_receiver.recv() { - Ok(CloseMsg::PingErr) => Err(ErrorKind::PingTimeoutError.into()), + Ok(CloseMsg::PingErr) => Err(Error::PingTimeoutError), Ok(CloseMsg::Stop) => Ok(()), Err(_) => Ok(()), }; @@ -148,7 +150,7 @@ impl WireguardMonitor { let default_node = self .router .get_default_route_node() - .chain_err(|| ErrorKind::SetupRoutingError)?; + .map_err(Error::SetupRoutingError)?; // route endpoints with specific routes for peer in config.peers.iter() { @@ -163,7 +165,7 @@ impl WireguardMonitor { self.router .add_routes(required_routes) - .chain_err(|| ErrorKind::SetupRoutingError) + .map_err(Error::SetupRoutingError) } fn tunnel_metadata(&self, config: &Config) -> TunnelMetadata { diff --git a/talpid-core/src/tunnel/wireguard/wireguard_go.rs b/talpid-core/src/tunnel/wireguard/wireguard_go.rs index e713a4a190..059147286d 100644 --- a/talpid-core/src/tunnel/wireguard/wireguard_go.rs +++ b/talpid-core/src/tunnel/wireguard/wireguard_go.rs @@ -1,4 +1,4 @@ -use super::{Config, ErrorKind, Result, ResultExt, Tunnel}; +use super::{Config, Error, Result, Tunnel}; use crate::network_interface::{NetworkInterface, TunnelDevice}; use std::{ffi::CString, fs, os::unix::io::AsRawFd, path::Path}; @@ -14,25 +14,24 @@ pub struct WgGoTunnel { impl WgGoTunnel { pub fn start_tunnel(config: &Config, log_path: Option<&Path>) -> Result<Self> { - let mut tunnel_device = - TunnelDevice::new().chain_err(|| ErrorKind::SetupTunnelDeviceError)?; + let mut tunnel_device = TunnelDevice::new().map_err(Error::SetupTunnelDeviceError)?; for ip in config.tunnel.addresses.iter() { tunnel_device .set_ip(*ip) - .chain_err(|| ErrorKind::SetupTunnelDeviceError)?; + .map_err(Error::SetupTunnelDeviceError)?; } tunnel_device .set_up(true) - .chain_err(|| ErrorKind::SetupTunnelDeviceError)?; + .map_err(Error::SetupTunnelDeviceError)?; let interface_name: String = tunnel_device.get_name().to_string(); let log_file = prepare_log_file(log_path)?; let wg_config_str = config.to_userspace_format(); let iface_name = - CString::new(interface_name.as_bytes()).chain_err(|| ErrorKind::InterfaceNameError)?; + CString::new(interface_name.as_bytes()).map_err(Error::InterfaceNameError)?; let handle = unsafe { wgTurnOnWithFd( @@ -46,7 +45,7 @@ impl WgGoTunnel { }; if handle < 0 { - bail!(ErrorKind::StartWireguardError(handle)); + return Err(Error::StartWireguardError { status: handle }); } Ok(WgGoTunnel { @@ -61,7 +60,7 @@ impl WgGoTunnel { if let Some(handle) = self.handle.take() { let status = unsafe { wgTurnOff(handle) }; if status < 0 { - bail!(ErrorKind::StopWireguardError(status)) + return Err(Error::StopWireguardError { status }); } } return Ok(()); @@ -77,8 +76,7 @@ impl Drop for WgGoTunnel { } fn prepare_log_file(log_path: Option<&Path>) -> Result<fs::File> { - fs::File::create(log_path.unwrap_or("/dev/null".as_ref())) - .chain_err(|| ErrorKind::PrepareLogFileError) + fs::File::create(log_path.unwrap_or("/dev/null".as_ref())).map_err(Error::PrepareLogFileError) } impl Tunnel for WgGoTunnel { diff --git a/talpid-core/src/tunnel_state_machine/connecting_state.rs b/talpid-core/src/tunnel_state_machine/connecting_state.rs index a843fa1fdd..f044c61df1 100644 --- a/talpid-core/src/tunnel_state_machine/connecting_state.rs +++ b/talpid-core/src/tunnel_state_machine/connecting_state.rs @@ -126,17 +126,11 @@ impl ConnectingState { } Err(error) => match error { #[cfg(windows)] - error @ tunnel::Error( - tunnel::ErrorKind::OpenVpnTunnelMonitoringError( - tunnel::openvpn::ErrorKind::DisabledTapAdapter, - ), - _, + error @ tunnel::Error::OpenVpnTunnelMonitoringError( + tunnel::openvpn::Error::DisabledTapAdapter, ) - | error @ tunnel::Error( - tunnel::ErrorKind::OpenVpnTunnelMonitoringError( - tunnel::openvpn::ErrorKind::MissingTapAdapter, - ), - _, + | error @ tunnel::Error::OpenVpnTunnelMonitoringError( + tunnel::openvpn::Error::MissingTapAdapter, ) => { warn!( "{}", @@ -351,8 +345,8 @@ impl TunnelState for ConnectingState { } Err(error) => { log::error!("Failed to start tunnel: {}", error); - let block_reason = match *error.kind() { - tunnel::ErrorKind::EnableIpv6Error => BlockReason::Ipv6Unavailable, + let block_reason = match error { + tunnel::Error::EnableIpv6Error => BlockReason::Ipv6Unavailable, _ => BlockReason::StartTunnelError, }; BlockedState::enter(shared_values, block_reason) diff --git a/talpid-openvpn-plugin/Cargo.toml b/talpid-openvpn-plugin/Cargo.toml index caf1e5637b..439d10131e 100644 --- a/talpid-openvpn-plugin/Cargo.toml +++ b/talpid-openvpn-plugin/Cargo.toml @@ -17,7 +17,7 @@ edition = "2018" crate-type = ["cdylib"] [dependencies] -error-chain = "0.12" +err-derive = "0.1.5" log = "0.4" env_logger = "0.6" jsonrpc-client-core = { git = "https://github.com/mullvad/jsonrpc-client-rs", rev = "68aac55b" } @@ -28,6 +28,7 @@ futures = "0.1" openvpn-plugin = { git = "https://github.com/mullvad/openvpn-plugin-rs", branch = "auth-failed-event", features = ["serde", "log"] } talpid-ipc = { path = "../talpid-ipc" } +talpid-types = { path = "../talpid-types" } [target.'cfg(windows)'.build-dependencies] diff --git a/talpid-openvpn-plugin/src/lib.rs b/talpid-openvpn-plugin/src/lib.rs index 34ddce52bb..c2080e294a 100644 --- a/talpid-openvpn-plugin/src/lib.rs +++ b/talpid-openvpn-plugin/src/lib.rs @@ -8,36 +8,36 @@ #![deny(rust_2018_idioms)] -#[macro_use] -extern crate error_chain; - -use error_chain::ChainedError; use openvpn_plugin::{openvpn_plugin, EventResult, EventType}; -use std::{collections::HashMap, ffi::CString, sync::Mutex}; - +use std::{collections::HashMap, ffi::CString, io, sync::Mutex}; +use talpid_types::ErrorExt; mod processing; use crate::processing::EventProcessor; -error_chain! { - errors { - InitHandleFailed { - description("Unable to initialize event processor") - } - InvalidEventType { - description("Invalid event type constant") - } - ParseEnvFailed { - description("Unable to parse environment variables from OpenVPN") - } - ParseArgsFailed { - description("Unable to parse arguments from OpenVPN") - } - EventProcessingFailed { - description("Failed to process the event") - } - } +#[derive(err_derive::Error, Debug)] +pub enum Error { + #[error(display = "No core server id given as first argument")] + MissingCoreServerId, + + #[error(display = "Failed to send an event to daemon over the IPC channel")] + SendEvent(#[error(cause)] jsonrpc_client_core::Error), + + #[error(display = "Connection is shut down")] + Shutdown, + + #[error(display = "Unable to start Tokio runtime")] + CreateRuntime(#[error(cause)] io::Error), + + #[error(display = "Unable to create IPC transport")] + CreateTransport(#[error(cause)] io::Error), + + #[error(display = "Unable to parse environment variables from OpenVPN")] + ParseEnvFailed(#[error(cause)] std::str::Utf8Error), + + #[error(display = "Unable to parse arguments from OpenVPN")] + ParseArgsFailed(#[error(cause)] std::str::Utf8Error), } @@ -63,7 +63,7 @@ pub struct Arguments { fn openvpn_open( args: Vec<CString>, _env: HashMap<CString, CString>, -) -> Result<(Vec<EventType>, Mutex<EventProcessor>)> { +) -> Result<(Vec<EventType>, Mutex<EventProcessor>), Error> { env_logger::init(); log::debug!("Initializing plugin"); @@ -72,20 +72,18 @@ fn openvpn_open( "Connecting back to talpid core at {}", arguments.ipc_socket_path ); - let processor = EventProcessor::new(arguments).chain_err(|| ErrorKind::InitHandleFailed)?; + let processor = EventProcessor::new(arguments)?; Ok((INTERESTING_EVENTS.to_vec(), Mutex::new(processor))) } -fn parse_args(args: &[CString]) -> Result<Arguments> { +fn parse_args(args: &[CString]) -> Result<Arguments, Error> { let mut args_iter = openvpn_plugin::ffi::parse::string_array_utf8(args) - .chain_err(|| ErrorKind::ParseArgsFailed)? + .map_err(Error::ParseArgsFailed)? .into_iter(); let _plugin_path = args_iter.next(); - let ipc_socket_path: String = args_iter - .next() - .ok_or_else(|| ErrorKind::Msg("No core server id given as first argument".to_owned()))?; + let ipc_socket_path: String = args_iter.next().ok_or_else(|| Error::MissingCoreServerId)?; Ok(Arguments { ipc_socket_path }) } @@ -100,17 +98,15 @@ fn openvpn_event( _args: Vec<CString>, env: HashMap<CString, CString>, handle: &mut Mutex<EventProcessor>, -) -> Result<EventResult> { +) -> Result<EventResult, Error> { log::debug!("Received event: {:?}", event); - let parsed_env = - openvpn_plugin::ffi::parse::env_utf8(&env).chain_err(|| ErrorKind::ParseEnvFailed)?; + let parsed_env = openvpn_plugin::ffi::parse::env_utf8(&env).map_err(Error::ParseEnvFailed)?; let result = handle .lock() .expect("failed to obtain mutex for EventProcessor") - .process_event(event, parsed_env) - .chain_err(|| ErrorKind::EventProcessingFailed); + .process_event(event, parsed_env); match result { Ok(()) => Ok(EventResult::Success), Err(e) => { diff --git a/talpid-openvpn-plugin/src/processing.rs b/talpid-openvpn-plugin/src/processing.rs index 4e1eb361a8..4124b6491c 100644 --- a/talpid-openvpn-plugin/src/processing.rs +++ b/talpid-openvpn-plugin/src/processing.rs @@ -1,4 +1,4 @@ -use super::Arguments; +use super::{Arguments, Error}; use jsonrpc_client_core::{ expand_params, jsonrpc_client, Future, Result as ClientResult, Transport, }; @@ -6,19 +6,6 @@ use jsonrpc_client_ipc::IpcTransport; use std::{collections::HashMap, sync::mpsc, thread}; use tokio::{reactor::Handle, runtime::Runtime}; -error_chain! { - errors { - IpcSendingError { - description("Failed while sending an event over the IPC channel") - } - - Shutdown { - description("Connection is shut down") - } - - } -} - /// Struct processing OpenVPN events and notifies listeners over IPC pub struct EventProcessor { @@ -27,23 +14,37 @@ pub struct EventProcessor { } impl EventProcessor { - pub fn new(arguments: Arguments) -> Result<EventProcessor> { + pub fn new(arguments: Arguments) -> Result<EventProcessor, Error> { log::trace!("Creating EventProcessor"); let (start_tx, start_rx) = mpsc::channel(); let (client_result_tx, client_result_rx) = mpsc::channel(); thread::spawn(move || { - let mut rt = Runtime::new().expect("failed to spawn runtime"); - + let mut rt = match Runtime::new().map_err(Error::CreateRuntime) { + Err(e) => { + let _ = start_tx.send(Err(e)); + return; + } + Ok(rt) => rt, + }; let (client, client_handle) = - IpcTransport::new(&arguments.ipc_socket_path, &Handle::current()) - .expect("Unable to create IPC transport") - .into_client(); + match IpcTransport::new(&arguments.ipc_socket_path, &Handle::default()) + .map_err(Error::CreateTransport) + .map(|transport| transport.into_client()) + { + Err(e) => { + let _ = start_tx.send(Err(e)); + return; + } + Ok((client, client_handle)) => (client, client_handle), + }; - let _ = start_tx.send(client_handle); + let _ = start_tx.send(Ok(client_handle)); let _ = client_result_tx.send(rt.block_on(client)); }); - let client_handle = start_rx.recv().chain_err(|| ErrorKind::Shutdown)?; + let client_handle = start_rx + .recv() + .expect("No start result from EventProcessor thread")?; let ipc_client = EventProxy::new(client_handle); Ok(EventProcessor { @@ -56,22 +57,22 @@ impl EventProcessor { &mut self, event: openvpn_plugin::EventType, env: HashMap<String, String>, - ) -> Result<()> { + ) -> Result<(), Error> { log::trace!("Processing \"{:?}\" event", event); let call_future = self .ipc_client .openvpn_event(event, env) - .map_err(|e| Error::with_chain(e, ErrorKind::IpcSendingError)); + .map_err(Error::SendEvent); call_future.wait()?; self.check_client_status() } - fn check_client_status(&mut self) -> Result<()> { + fn check_client_status(&mut self) -> Result<(), Error> { use std::sync::mpsc::TryRecvError::*; match self.client_result_rx.try_recv() { Err(Empty) => Ok(()), - Err(Disconnected) | Ok(Ok(())) => Err(ErrorKind::Shutdown.into()), - Ok(Err(e)) => Err(Error::with_chain(e, ErrorKind::IpcSendingError)), + Err(Disconnected) | Ok(Ok(())) => Err(Error::Shutdown), + Ok(Err(e)) => Err(Error::SendEvent(e)), } } } |
