summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock2
-rw-r--r--Cargo.toml1
-rw-r--r--src/lib.rs3
-rw-r--r--src/net.rs74
-rw-r--r--src/process/monitor.rs77
-rw-r--r--talpid_cli/Cargo.toml1
-rw-r--r--talpid_cli/src/main.rs54
-rw-r--r--talpid_openvpn_plugin/src/ffi/parse.rs2
8 files changed, 102 insertions, 112 deletions
diff --git a/Cargo.lock b/Cargo.lock
index a13f3df512..4fb8fb2009 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -129,6 +129,7 @@ name = "talpid_cli"
version = "0.0.0"
dependencies = [
"clap 2.20.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "error-chain 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
"talpid_core 0.0.0",
]
@@ -138,6 +139,7 @@ version = "0.0.0"
dependencies = [
"assert_matches 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
"clonablechild 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "error-chain 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
diff --git a/Cargo.toml b/Cargo.toml
index 902b80ead6..f0bba952ec 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -5,6 +5,7 @@ authors = ["Linus Färnstrand <linus@mullvad.net>"]
[dependencies]
clonablechild = "0.1"
+error-chain = "0.8"
[dev-dependencies]
assert_matches = "1.0"
diff --git a/src/lib.rs b/src/lib.rs
index 81225be473..f8e5e96a46 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -8,6 +8,9 @@ extern crate assert_matches;
extern crate clonablechild;
+#[macro_use]
+extern crate error_chain;
+
/// Working with processes.
pub mod process;
diff --git a/src/net.rs b/src/net.rs
index 2f5d98e29a..fb119ea8af 100644
--- a/src/net.rs
+++ b/src/net.rs
@@ -1,14 +1,24 @@
-use std::error::Error;
use std::fmt;
use std::io;
use std::iter;
use std::net::SocketAddr;
-use std::num::ParseIntError;
use std::option;
use std::slice;
use std::str::FromStr;
use std::vec;
+
+error_chain! {
+ errors {
+ /// Error indicating parsing the address failed
+ AddrParse(s: String) {
+ description("Invalid address format")
+ display("Unable to parse address. {}", s)
+ }
+ }
+}
+
+
/// Representation of a TCP or UDP endpoint. The IP level address is represented by either an IP
/// directly or a hostname/domain. The IP level address together with a port becomes a socket
/// address.
@@ -44,19 +54,23 @@ impl RemoteAddr {
}
}
- fn from_domain_str(s: &str) -> Result<Self, AddrParseError> {
+ fn from_domain_str(s: &str) -> Result<Self> {
let (address, port_str) = Self::split_at_last_colon(s)?;
- let port = u16::from_str(port_str)?;
+ let port = u16::from_str(port_str).chain_err(|| {
+ ErrorKind::AddrParse(format!("Invalid port: \"{}\"", port_str))
+ })?;
if address.is_empty() || address.contains(':') {
- return Err(AddrParseError(()));
+ let msg = format!("Invalid IP or domain: \"{}\"", address);
+ return Err(ErrorKind::AddrParse(msg).into());
}
Ok(RemoteAddr::Domain(address.to_owned(), port))
}
- fn split_at_last_colon(s: &str) -> Result<(&str, &str), AddrParseError> {
+ fn split_at_last_colon(s: &str) -> Result<(&str, &str)> {
let mut iter = s.rsplitn(2, ':');
let port = iter.next().unwrap();
- let address = iter.next().ok_or(AddrParseError(()))?;
+ let address = iter.next()
+ .ok_or_else(|| Error::from(ErrorKind::AddrParse("No colon".to_owned())))?;
Ok((address, port))
}
}
@@ -68,8 +82,8 @@ impl From<SocketAddr> for RemoteAddr {
}
impl FromStr for RemoteAddr {
- type Err = AddrParseError;
- fn from_str(s: &str) -> Result<Self, Self::Err> {
+ type Err = Error;
+ fn from_str(s: &str) -> Result<Self> {
if let Ok(addr) = SocketAddr::from_str(s) {
Ok(RemoteAddr::from(addr))
} else {
@@ -78,7 +92,6 @@ impl FromStr for RemoteAddr {
}
}
-
impl fmt::Display for RemoteAddr {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
match *self {
@@ -88,29 +101,6 @@ impl fmt::Display for RemoteAddr {
}
}
-/// Representation of the errors that can happen when parsing a string into a `RemoteAddr`.
-#[derive(Debug, Clone, PartialEq, Eq)]
-pub struct AddrParseError(());
-
-impl From<ParseIntError> for AddrParseError {
- fn from(_: ParseIntError) -> Self {
- AddrParseError(())
- }
-}
-
-impl fmt::Display for AddrParseError {
- fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
- fmt.write_str(self.description())
- }
-}
-
-impl Error for AddrParseError {
- fn description(&self) -> &str {
- "Invalid remote address format"
- }
-}
-
-
/// A trait for objects which can be converted to one or more `RemoteAddr` values.
pub trait ToRemoteAddrs {
/// Returned iterator over remote addresses which this type may correspond
@@ -200,7 +190,8 @@ mod remote_addr_tests {
#[test]
fn from_ipv6_str_without_brackets() {
- assert!(RemoteAddr::from_str("fe80::1:1337").is_err());
+ let result = RemoteAddr::from_str("fe80::1:1337");
+ assert_matches!(result, Err(Error(ErrorKind::AddrParse(_), _)));
}
#[test]
@@ -212,27 +203,32 @@ mod remote_addr_tests {
#[test]
fn from_ipv6_str_without_port() {
- assert!(RemoteAddr::from_str("fe80::1").is_err());
+ let result = RemoteAddr::from_str("fe80::1");
+ assert_matches!(result, Err(Error(ErrorKind::AddrParse(_), _)));
}
#[test]
fn from_str_no_colon() {
- assert!(RemoteAddr::from_str("example.com").is_err());
+ let result = RemoteAddr::from_str("example.com");
+ assert_matches!(result, Err(Error(ErrorKind::AddrParse(_), _)));
}
#[test]
fn from_str_invalid_port_large() {
- assert!(RemoteAddr::from_str("example.com:99999").is_err());
+ let result = RemoteAddr::from_str("example.com:99999");
+ assert_matches!(result, Err(Error(ErrorKind::AddrParse(_), _)));
}
#[test]
fn from_str_empty_address() {
- assert!(RemoteAddr::from_str(":100").is_err());
+ let result = RemoteAddr::from_str(":100");
+ assert_matches!(result, Err(Error(ErrorKind::AddrParse(_), _)));
}
#[test]
fn from_str_empty_port() {
- assert!(RemoteAddr::from_str("example.com:").is_err());
+ let result = RemoteAddr::from_str("example.com:");
+ assert_matches!(result, Err(Error(ErrorKind::AddrParse(_), _)));
}
#[test]
diff --git a/src/process/monitor.rs b/src/process/monitor.rs
index e8d32a7973..8caac5cc79 100644
--- a/src/process/monitor.rs
+++ b/src/process/monitor.rs
@@ -1,11 +1,27 @@
-use std::error::Error;
-use std::fmt;
use std::io;
use std::process::{ChildStdout, ChildStderr};
use std::sync::{Arc, Mutex};
use std::thread;
+error_chain! {
+ errors {
+ /// The transition could not be made because the state machine was not in a state that
+ /// could transition to the desired state.
+ InvalidState {
+ description("Invalid state for desired transition")
+ }
+ /// Error representing a failure in spawning the child process
+ Spawn {
+ description("Unable to spawn child process")
+ }
+ /// Error representing a failure in sending a kill signal to the child process
+ Kill {
+ description("Unable to send kill signal to process")
+ }
+ }
+}
+
/// Trait for objects that represent child processes that `ChildMonitor` can monitor
pub trait MonitoredChild: Clone + Send + 'static {
/// Waits for the child to exit completely, returning if the child exited cleanly or not.
@@ -31,49 +47,6 @@ pub trait ChildSpawner: Send + 'static {
}
-/// Type alias for results of transitions in the `ChildMonitor` state machine.
-pub type TransitionResult<T> = Result<T, TransitionError>;
-
-/// Error type for transitions in the `ChildMonitor` state machine.
-#[derive(Debug)]
-pub enum TransitionError {
- /// The transition could not be made because the state machine was not in a state that could
- /// transition to the desired state.
- InvalidState,
-
- /// The transition failed because of an `io::Error`.
- IoError(io::Error),
-}
-
-impl From<io::Error> for TransitionError {
- fn from(error: io::Error) -> Self {
- TransitionError::IoError(error)
- }
-}
-
-impl fmt::Display for TransitionError {
- fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
- fmt.write_str(self.description())
- }
-}
-
-impl Error for TransitionError {
- fn description(&self) -> &str {
- match *self {
- TransitionError::InvalidState => "Invalid state for desired transition",
- TransitionError::IoError(..) => "Transition failed due to IO error",
- }
- }
-
- fn cause(&self) -> Option<&Error> {
- match *self {
- TransitionError::IoError(ref e) => Some(e),
- _ => None,
- }
- }
-}
-
-
enum State<C: MonitoredChild> {
Stopped,
Running(RunningState<C>),
@@ -104,14 +77,12 @@ impl<S: ChildSpawner> ChildMonitor<S> {
/// Starts the child process and begins to monitor it. `listener` will be called as soon as the
/// child process exits.
- pub fn start<L>(&mut self,
- listener: L)
- -> TransitionResult<(Option<ChildStdout>, Option<ChildStderr>)>
+ pub fn start<L>(&mut self, listener: L) -> Result<(Option<ChildStdout>, Option<ChildStderr>)>
where L: FnMut(bool) + Send + 'static
{
let mut state_lock = self.state.lock().unwrap();
if let State::Stopped = *state_lock {
- let mut child = self.spawner.spawn()?;
+ let mut child = self.spawner.spawn().chain_err(|| ErrorKind::Spawn)?;
let io = (child.stdout(), child.stderr());
let thread_handle = self.spawn_monitor(child.clone(), listener);
*state_lock = State::Running(RunningState {
@@ -120,7 +91,7 @@ impl<S: ChildSpawner> ChildMonitor<S> {
});
Ok(io)
} else {
- Err(TransitionError::InvalidState)
+ Err(ErrorKind::InvalidState.into())
}
}
@@ -139,13 +110,13 @@ impl<S: ChildSpawner> ChildMonitor<S> {
}
/// Sends a kill signal to the child process.
- pub fn stop(&self) -> TransitionResult<()> {
+ pub fn stop(&self) -> Result<()> {
let state_lock = self.state.lock().unwrap();
if let State::Running(ref running_state) = *state_lock {
- running_state.child.kill()?;
+ running_state.child.kill().chain_err(|| ErrorKind::Kill)?;
Ok(())
} else {
- Err(TransitionError::InvalidState)
+ Err(ErrorKind::InvalidState.into())
}
}
}
diff --git a/talpid_cli/Cargo.toml b/talpid_cli/Cargo.toml
index 5abed624a4..637b1f6699 100644
--- a/talpid_cli/Cargo.toml
+++ b/talpid_cli/Cargo.toml
@@ -5,6 +5,7 @@ authors = ["Linus Färnstrand <linus@mullvad.net>"]
[dependencies]
clap = "2.20"
+error-chain = "0.8"
[dependencies.talpid_core]
path = "../"
diff --git a/talpid_cli/src/main.rs b/talpid_cli/src/main.rs
index a174d7ed37..4a4239f622 100644
--- a/talpid_cli/src/main.rs
+++ b/talpid_cli/src/main.rs
@@ -1,34 +1,49 @@
+// `error_chain!` can recurse deeply
+#![recursion_limit = "1024"]
+
extern crate talpid_core;
#[macro_use]
extern crate clap;
+#[macro_use]
+extern crate error_chain;
use std::io::{self, Read, Write};
use std::sync::mpsc::{self, Receiver};
use std::thread;
use talpid_core::process::OpenVpnCommand;
-use talpid_core::process::monitor::{ChildMonitor, TransitionResult, ChildSpawner};
+use talpid_core::process::monitor::{ChildMonitor, ChildSpawner};
mod cli;
use cli::Args;
-/// Macro for printing to stderr. Will simply do nothing if the printing fails for some reason.
-macro_rules! eprintln {
- ($($arg:tt)*) => (
- use std::io::Write;
- let _ = writeln!(&mut ::std::io::stderr(), $($arg)* );
- )
+
+error_chain! {
+ links {
+ Monitor(talpid_core::process::monitor::Error, talpid_core::process::monitor::ErrorKind);
+ }
}
+
fn main() {
- let args = cli::parse_args_or_exit();
+ if let Err(ref e) = run() {
+ println!("error: {}", e);
+ for e in e.iter().skip(1) {
+ println!("caused by: {}", e);
+ }
+ if let Some(backtrace) = e.backtrace() {
+ println!("backtrace: {:?}", backtrace);
+ }
+ ::std::process::exit(1);
+ }
+}
+fn run() -> Result<()> {
+ let args = cli::parse_args_or_exit();
let command = create_openvpn_command(&args);
let monitor = ChildMonitor::new(command);
- if let Err(e) = main_loop(monitor) {
- eprintln!("OpenVPN failed: {}", e);
- }
+ main_loop(monitor)
}
fn create_openvpn_command(args: &Args) -> OpenVpnCommand {
@@ -41,27 +56,28 @@ fn create_openvpn_command(args: &Args) -> OpenVpnCommand {
command
}
-fn main_loop<S>(mut monitor: ChildMonitor<S>) -> TransitionResult<()>
+fn main_loop<S>(mut monitor: ChildMonitor<S>) -> Result<()>
where S: ChildSpawner
{
loop {
- let rx = start_monitor(&mut monitor)?;
+ let rx = start_monitor(&mut monitor).chain_err(|| "Unable to start OpenVPN")?;
let clean_exit = rx.recv().unwrap();
println!("Monitored process exited. clean: {}", clean_exit);
std::thread::sleep(std::time::Duration::from_millis(500));
}
}
-fn start_monitor<S>(monitor: &mut ChildMonitor<S>) -> TransitionResult<Receiver<bool>>
+fn start_monitor<S>(monitor: &mut ChildMonitor<S>) -> Result<Receiver<bool>>
where S: ChildSpawner
{
let (tx, rx) = mpsc::channel();
let callback = move |clean| tx.send(clean).unwrap();
- monitor.start(callback).map(|(stdout, stderr)| {
- stdout.map(|stream| pass_io(stream, io::stdout()));
- stderr.map(|stream| pass_io(stream, io::stderr()));
- rx
- })
+ Ok(monitor.start(callback)
+ .map(|(stdout, stderr)| {
+ stdout.map(|stream| pass_io(stream, io::stdout()));
+ stderr.map(|stream| pass_io(stream, io::stderr()));
+ rx
+ })?)
}
fn pass_io<I, O>(mut input: I, mut output: O)
diff --git a/talpid_openvpn_plugin/src/ffi/parse.rs b/talpid_openvpn_plugin/src/ffi/parse.rs
index db26335d27..d694f1efb7 100644
--- a/talpid_openvpn_plugin/src/ffi/parse.rs
+++ b/talpid_openvpn_plugin/src/ffi/parse.rs
@@ -28,7 +28,7 @@ error_chain!{
/// terminated. Likewise, if any string pointed to is not properly null-terminated it may crash.
pub unsafe fn string_array(mut ptr: *const *const c_char) -> Result<Vec<String>> {
if ptr.is_null() {
- Err(Error::from(ErrorKind::Null))
+ Err(ErrorKind::Null.into())
} else {
let mut strings = Vec::new();
while !(*ptr).is_null() {