diff options
| author | Linus Färnstrand <linus@mullvad.net> | 2017-04-07 09:18:04 +0200 |
|---|---|---|
| committer | Linus Färnstrand <linus@mullvad.net> | 2017-04-07 09:26:35 +0200 |
| commit | 30fad8d78135e72cf671d1b01ee1b1c6643ff21c (patch) | |
| tree | 64b629ff5a03c107980f983d2fe3e3b0fc4736e8 /src | |
| parent | 5701c32ba8ff9ab7fbc2712d813287c812bc5aef (diff) | |
| download | mullvadvpn-30fad8d78135e72cf671d1b01ee1b1c6643ff21c.tar.xz mullvadvpn-30fad8d78135e72cf671d1b01ee1b1c6643ff21c.zip | |
Move talpid_core from root to member
Diffstat (limited to 'src')
| -rw-r--r-- | src/lib.rs | 23 | ||||
| -rw-r--r-- | src/net.rs | 253 | ||||
| -rw-r--r-- | src/process/mod.rs | 31 | ||||
| -rw-r--r-- | src/process/monitor.rs | 267 | ||||
| -rw-r--r-- | src/process/openvpn.rs | 310 |
5 files changed, 0 insertions, 884 deletions
diff --git a/src/lib.rs b/src/lib.rs deleted file mode 100644 index 8fe76a5a69..0000000000 --- a/src/lib.rs +++ /dev/null @@ -1,23 +0,0 @@ -#![deny(missing_docs)] - -//! The core components of the talpidaemon VPN client. - -#[cfg(test)] -#[macro_use] -extern crate assert_matches; - -extern crate clonablechild; - -#[macro_use] -extern crate log; - -#[macro_use] -extern crate error_chain; - -extern crate talpid_ipc; - -/// Working with processes. -pub mod process; - -/// Network primitives. -pub mod net; diff --git a/src/net.rs b/src/net.rs deleted file mode 100644 index 3701ae641e..0000000000 --- a/src/net.rs +++ /dev/null @@ -1,253 +0,0 @@ -use std::fmt; -use std::io; -use std::iter; -use std::net::SocketAddr; -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. -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub enum RemoteAddr { - /// Endpoint represented by an IP and a port. - SocketAddr(SocketAddr), - /// Endpoint represented by a hostname or domain and a port. - Domain(String, u16), -} - -impl RemoteAddr { - /// Constructs a new `RemoteAddr` from the given address (hostname or domain) and port. To - /// construct a `RemoteAddr` based on IP rather than domain, use the From<SocketAddr> impl. - pub fn new(address: &str, port: u16) -> Self { - RemoteAddr::Domain(address.to_owned(), port) - } - - /// Returns the address associated with this `RemoteAddr`. If it is backed by an IP that will - /// be formatted as a string. - pub fn address(&self) -> String { - match *self { - RemoteAddr::SocketAddr(ref addr) => addr.ip().to_string(), - RemoteAddr::Domain(ref address, _) => address.to_owned(), - } - } - - /// Returns the port associated with this `RemoteAddr`. - pub fn port(&self) -> u16 { - match *self { - RemoteAddr::SocketAddr(addr) => addr.port(), - RemoteAddr::Domain(_, port) => port, - } - } - - 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).chain_err(|| { - ErrorKind::AddrParse(format!("Invalid port: \"{}\"", port_str)) - })?; - if address.is_empty() || address.contains(':') { - let msg = format!("Invalid IP or domain: \"{}\"", address); - bail!(ErrorKind::AddrParse(msg)); - } - Ok(RemoteAddr::Domain(address.to_owned(), port)) - } - - 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_else(|| Error::from(ErrorKind::AddrParse("No colon".to_owned())))?; - Ok((address, port)) - } -} - -impl From<SocketAddr> for RemoteAddr { - fn from(socket_addr: SocketAddr) -> Self { - RemoteAddr::SocketAddr(socket_addr) - } -} - -impl FromStr for RemoteAddr { - type Err = Error; - fn from_str(s: &str) -> Result<Self> { - if let Ok(addr) = SocketAddr::from_str(s) { - Ok(RemoteAddr::from(addr)) - } else { - Self::from_domain_str(s) - } - } -} - -impl fmt::Display for RemoteAddr { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - match *self { - RemoteAddr::SocketAddr(ref addr) => addr.fmt(fmt), - RemoteAddr::Domain(ref address, ref port) => write!(fmt, "{}:{}", address, port), - } - } -} - -/// 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 - /// to. - type Iter: Iterator<Item = RemoteAddr>; - - /// Converts this object to an iterator of parsed `RemoteAddr`s. - /// - /// # Errors - /// - /// Any errors encountered during parsing will be returned as an `Err`. - fn to_remote_addrs(&self) -> io::Result<Self::Iter>; -} - -impl ToRemoteAddrs for RemoteAddr { - type Iter = option::IntoIter<RemoteAddr>; - - fn to_remote_addrs(&self) -> io::Result<Self::Iter> { - Ok(Some(self.clone()).into_iter()) - } -} - -impl<'a> ToRemoteAddrs for &'a [RemoteAddr] { - type Iter = iter::Cloned<slice::Iter<'a, RemoteAddr>>; - - fn to_remote_addrs(&self) -> io::Result<Self::Iter> { - Ok(self.iter().cloned()) - } -} - -impl<'a> ToRemoteAddrs for &'a str { - type Iter = option::IntoIter<RemoteAddr>; - - fn to_remote_addrs(&self) -> io::Result<Self::Iter> { - let parsed_addr = str_to_remote_addr(self)?; - Ok(Some(parsed_addr).into_iter()) - } -} - -impl<'a> ToRemoteAddrs for &'a [&'a str] { - type Iter = vec::IntoIter<RemoteAddr>; - - fn to_remote_addrs(&self) -> io::Result<Self::Iter> { - let mut addrs = Vec::with_capacity(self.len()); - for addr in self.iter() { - addrs.push(str_to_remote_addr(addr)?); - } - Ok(addrs.into_iter()) - } -} - -fn str_to_remote_addr(s: &str) -> io::Result<RemoteAddr> { - RemoteAddr::from_str(s) - .map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, e.description())) -} - - - -#[cfg(test)] -mod remote_addr_tests { - use super::*; - - use std::net::{SocketAddr, SocketAddrV4, SocketAddrV6}; - use std::str::FromStr; - - #[test] - fn new_and_getters() { - let testee = RemoteAddr::new("a_domain", 543); - assert_eq!("a_domain", testee.address()); - assert_eq!(543, testee.port()); - } - - #[test] - fn from_socket_addr() { - let socket_addr = SocketAddr::from_str("10.0.1.1:76").unwrap(); - let testee: RemoteAddr = socket_addr.into(); - assert_eq!("10.0.1.1", testee.address()); - assert_eq!(76, testee.port()); - } - - #[test] - fn from_str() { - let testee = RemoteAddr::from_str("example.com:3333").unwrap(); - assert_eq!("example.com", testee.address()); - assert_eq!(3333, testee.port()); - } - - #[test] - fn from_ipv6_str_without_brackets() { - let result = RemoteAddr::from_str("fe80::1:1337"); - assert_matches!(result, Err(Error(ErrorKind::AddrParse(_), _))); - } - - #[test] - fn from_ipv6_str_with_brackets() { - let testee = RemoteAddr::from_str("[fe80::1]:1337").unwrap(); - assert_eq!("fe80::1", testee.address()); - assert_eq!(1337, testee.port()); - } - - #[test] - fn from_ipv6_str_without_port() { - let result = RemoteAddr::from_str("fe80::1"); - assert_matches!(result, Err(Error(ErrorKind::AddrParse(_), _))); - } - - #[test] - fn from_str_no_colon() { - let result = RemoteAddr::from_str("example.com"); - assert_matches!(result, Err(Error(ErrorKind::AddrParse(_), _))); - } - - #[test] - fn from_str_invalid_port_large() { - let result = RemoteAddr::from_str("example.com:99999"); - assert_matches!(result, Err(Error(ErrorKind::AddrParse(_), _))); - } - - #[test] - fn from_str_empty_address() { - let result = RemoteAddr::from_str(":100"); - assert_matches!(result, Err(Error(ErrorKind::AddrParse(_), _))); - } - - #[test] - fn from_str_empty_port() { - let result = RemoteAddr::from_str("example.com:"); - assert_matches!(result, Err(Error(ErrorKind::AddrParse(_), _))); - } - - #[test] - fn to_string_domain() { - let testee = RemoteAddr::new("example.com", 3333); - assert_eq!("example.com:3333", testee.to_string()); - } - - #[test] - fn to_string_ipv4() { - let socket_addr = SocketAddr::V4(SocketAddrV4::from_str("127.1.2.3:1337").unwrap()); - let testee = RemoteAddr::from(socket_addr); - assert_eq!("127.1.2.3:1337", testee.to_string()); - } - - #[test] - fn to_string_ipv6() { - let socket_addr = SocketAddr::V6(SocketAddrV6::from_str("[2001:beef::1]:9876").unwrap()); - let testee = RemoteAddr::from(socket_addr); - assert_eq!("[2001:beef::1]:9876", testee.to_string()); - } -} diff --git a/src/process/mod.rs b/src/process/mod.rs deleted file mode 100644 index 3259f90524..0000000000 --- a/src/process/mod.rs +++ /dev/null @@ -1,31 +0,0 @@ -use std::io; - -use std::process::{ChildStderr, ChildStdout}; - -/// A module for monitoring child processes and get notified of events on them. -pub mod monitor; -use self::monitor::MonitoredChild; - -/// A module for all OpenVPN related process management. -pub mod openvpn; - -use clonablechild::ClonableChild; - - -impl MonitoredChild for ClonableChild { - fn wait(&self) -> io::Result<bool> { - ClonableChild::wait(self).map(|exit_status| exit_status.success()) - } - - fn kill(&self) -> io::Result<()> { - ClonableChild::kill(self) - } - - fn stdout(&mut self) -> Option<ChildStdout> { - self.stdout() - } - - fn stderr(&mut self) -> Option<ChildStderr> { - self.stderr() - } -} diff --git a/src/process/monitor.rs b/src/process/monitor.rs deleted file mode 100644 index 3a6f9fb65f..0000000000 --- a/src/process/monitor.rs +++ /dev/null @@ -1,267 +0,0 @@ -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. - fn wait(&self) -> io::Result<bool>; - - /// Forces the child to exit. - fn kill(&self) -> io::Result<()>; - - /// Retreives the stdout stream for the child. - fn stdout(&mut self) -> Option<ChildStdout>; - - /// Retreives the stderr stream for the child. - fn stderr(&mut self) -> Option<ChildStderr>; -} - -/// Trait for objects that can spawn any type of child process object implementing `MonitoredChild`. -pub trait ChildSpawner: Send + 'static { - /// The type of child being spawned. - type Child: MonitoredChild; - - /// Spawns the child process, returning a handle to it on success. - fn spawn(&mut self) -> io::Result<Self::Child>; -} - - -enum State<C: MonitoredChild> { - Stopped, - Running(RunningState<C>), -} - -struct RunningState<C: MonitoredChild> { - child: C, - thread_handle: Option<thread::JoinHandle<()>>, -} - -/// A child process monitor. Takes care of starting and monitoring a child process and runs the -/// listener on child exit. -pub struct ChildMonitor<S: ChildSpawner> { - spawner: S, - state: Arc<Mutex<State<S::Child>>>, -} - -impl<S: ChildSpawner> ChildMonitor<S> { - /// Creates a new `ChildMonitor` that spawns processes with the given `spawner`. The new - /// `ChildMonitor` will be in the stopped state and not start any process until you call - /// `start()`. - pub fn new(spawner: S) -> Self { - ChildMonitor { - spawner: spawner, - state: Arc::new(Mutex::new(State::Stopped)), - } - } - - /// 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) -> 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().chain_err(|| ErrorKind::Spawn)?; - let io = (child.stdout(), child.stderr()); - let thread_handle = self.spawn_monitor(child.clone(), listener); - *state_lock = State::Running(RunningState { - child: child, - thread_handle: Some(thread_handle), - }); - Ok(io) - } else { - bail!(ErrorKind::InvalidState); - } - } - - fn spawn_monitor<L>(&self, child: S::Child, mut listener: L) -> thread::JoinHandle<()> - where L: FnMut(bool) + Send + 'static - { - let state_mutex = self.state.clone(); - thread::spawn(move || { - let success = child.wait().unwrap_or(false); - { - let mut state_lock = state_mutex.lock().unwrap(); - *state_lock = State::Stopped; - } - listener(success); - }) - } - - /// Sends a kill signal to the child process. - 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().chain_err(|| ErrorKind::Kill)?; - Ok(()) - } else { - bail!(ErrorKind::InvalidState); - } - } -} - -impl<S: ChildSpawner> Drop for ChildMonitor<S> { - fn drop(&mut self) { - let thread_handle = { - let mut state_lock = self.state.lock().unwrap(); - if let State::Running(ref mut state) = *state_lock { - let _ = state.child.kill(); - state.thread_handle.take() - } else { - None - } - }; - if let Some(thread_handle) = thread_handle { - let _ = thread_handle.join(); - } - } -} - - -#[cfg(test)] -mod child_monitor_tests { - use super::*; - use std::io; - use std::process::{ChildStdout, ChildStderr}; - use std::sync::{Arc, Mutex}; - use std::sync::mpsc; - use std::thread; - use std::time::Duration; - - #[derive(Clone)] - struct MockChild { - died: Arc<Mutex<bool>>, - } - - impl MockChild { - pub fn instant_exit() -> Self { - Self::new(true) - } - - pub fn alive_until_kill() -> Self { - Self::new(false) - } - - fn new(died: bool) -> Self { - MockChild { died: Arc::new(Mutex::new(died)) } - } - } - - impl MonitoredChild for MockChild { - fn wait(&self) -> io::Result<bool> { - loop { - if *self.died.lock().unwrap() { - break; - } - thread::sleep(Duration::new(0, 1_000_000)); - } - Ok(true) - } - - fn kill(&self) -> io::Result<()> { - *self.died.lock().unwrap() = true; - Ok(()) - } - - fn stdout(&mut self) -> Option<ChildStdout> { - None - } - - fn stderr(&mut self) -> Option<ChildStderr> { - None - } - } - - struct MockChildSpawner { - spawn_result: Option<MockChild>, - } - - impl MockChildSpawner { - pub fn new(spawn_result: Option<MockChild>) -> Self { - MockChildSpawner { spawn_result: spawn_result } - } - } - - impl ChildSpawner for MockChildSpawner { - type Child = MockChild; - - fn spawn(&mut self) -> io::Result<MockChild> { - self.spawn_result - .clone() - .ok_or(io::Error::new(io::ErrorKind::Other, "Mocking a failed process spawn")) - } - } - - /// Tries to recv a message from the given `$rx` for one second and tries to match it with the - /// given expected value, `$expected` - macro_rules! assert_event { - ($rx:ident, $expected:pat) => {{ - let result = $rx.recv_timeout(Duration::new(1, 0)); - assert_matches!(result, $expected); - }} - } - - #[test] - fn normal_start() { - let spawner = MockChildSpawner::new(Some(MockChild::instant_exit())); - let mut testee = ChildMonitor::new(spawner); - - let (tx, rx) = mpsc::channel(); - assert!(testee.start(move |success| tx.send(success).unwrap()).is_ok()); - assert_event!(rx, Ok(true)); - } - - #[test] - fn start_failed() { - let spawner = MockChildSpawner::new(None); - let mut testee = ChildMonitor::new(spawner); - - let (tx, rx) = mpsc::channel(); - assert!(testee.start(move |success| tx.send(success).unwrap()).is_err()); - // Make sure that the listener is not kept anywhere. Failing to start should drop the - // listener - assert_event!(rx, Err(mpsc::RecvTimeoutError::Disconnected)); - } - - #[test] - fn normal_stop() { - let spawner = MockChildSpawner::new(Some(MockChild::alive_until_kill())); - let mut testee = ChildMonitor::new(spawner); - - let (tx, rx) = mpsc::channel(); - assert!(testee.start(move |success| tx.send(success).unwrap()).is_ok()); - assert_event!(rx, Err(mpsc::RecvTimeoutError::Timeout)); - - assert!(testee.stop().is_ok()); - assert_event!(rx, Ok(true)); - } - - #[test] - fn stop_without_start() { - let spawner = MockChildSpawner::new(Some(MockChild::alive_until_kill())); - let testee = ChildMonitor::new(spawner); - - assert_matches!(testee.stop(), Err(Error(ErrorKind::InvalidState, _))); - } -} diff --git a/src/process/openvpn.rs b/src/process/openvpn.rs deleted file mode 100644 index e002d2b9c3..0000000000 --- a/src/process/openvpn.rs +++ /dev/null @@ -1,310 +0,0 @@ -extern crate openvpn_ffi; - -use super::monitor::{ChildSpawner, ChildMonitor}; - -use clonablechild::{ClonableChild, ChildExt}; - -use net::{RemoteAddr, ToRemoteAddrs}; - -use std::collections::HashMap; -use std::ffi::{OsString, OsStr}; -use std::fmt; -use std::io; -use std::ops::DerefMut; -use std::path::{Path, PathBuf}; -use std::process::{Command, Child, Stdio, ChildStdout, ChildStderr}; -use std::sync::{Arc, Mutex}; - -use talpid_ipc; - -error_chain!{ - errors { - /// Error while communicating with the OpenVPN plugin - PluginCommunicationError - } - links { - ChildMonitorError(::process::monitor::Error, ::process::monitor::ErrorKind) - #[doc="Something went wrong in the underlying ChildMonitor"]; - } -} - -/// An OpenVPN process builder, providing control over the different arguments that the OpenVPN -/// binary accepts. -#[derive(Clone)] -pub struct OpenVpnCommand { - openvpn_bin: OsString, - config: Option<PathBuf>, - remotes: Vec<RemoteAddr>, - plugin: Option<(PathBuf, Vec<String>)>, - pipe_output: bool, -} - -impl OpenVpnCommand { - /// Constructs a new `OpenVpnCommand` for launching OpenVPN processes from the binary at - /// `openvpn_bin`. - pub fn new<P: AsRef<OsStr>>(openvpn_bin: P) -> Self { - OpenVpnCommand { - openvpn_bin: OsString::from(openvpn_bin.as_ref()), - config: None, - remotes: vec![], - plugin: None, - pipe_output: true, - } - } - - /// Sets what configuration file will be given to OpenVPN - pub fn config<P: AsRef<Path>>(&mut self, path: P) -> &mut Self { - self.config = Some(path.as_ref().to_path_buf()); - self - } - - /// Sets the addresses that OpenVPN will connect to. See OpenVPN documentation for how multiple - /// remotes are handled. - pub fn remotes<A: ToRemoteAddrs>(&mut self, remotes: A) -> io::Result<&mut Self> { - self.remotes = remotes.to_remote_addrs()?.collect(); - Ok(self) - } - - /// Sets a plugin and its arguments that OpenVPN will be started with. - pub fn plugin<P: AsRef<Path>>(&mut self, path: P, args: Vec<String>) -> &mut Self { - self.plugin = Some((path.as_ref().to_path_buf(), args)); - self - } - - /// If piping the standard streams, stdout and stderr will be available to the parent process. - /// This is the default behavior. If you want the equivalence of attaching the child streams to - /// /dev/null, invoke this method with false. - pub fn pipe_output(&mut self, pipe_output: bool) -> &mut Self { - self.pipe_output = pipe_output; - self - } - - /// Executes the OpenVPN process as a child process, returning a handle to it. - pub fn spawn(&self) -> io::Result<Child> { - let mut command = self.create_command(); - let args = self.get_arguments(); - command.args(&args); - debug!("Spawning: {}", &self); - command.spawn() - } - - fn create_command(&self) -> Command { - let mut command = Command::new(&self.openvpn_bin); - command.stdin(Stdio::null()) - .stdout(self.get_output_pipe_policy()) - .stderr(self.get_output_pipe_policy()); - command - } - - fn get_output_pipe_policy(&self) -> Stdio { - if self.pipe_output { - Stdio::piped() - } else { - Stdio::null() - } - } - - /// Returns all arguments that the subprocess would be spawned with. - pub fn get_arguments(&self) -> Vec<OsString> { - let mut args = vec![]; - if let Some(ref config) = self.config { - args.push(OsString::from("--config")); - args.push(OsString::from(config.as_os_str())); - } - for remote in &self.remotes { - args.push(OsString::from("--remote")); - args.push(OsString::from(remote.address())); - args.push(OsString::from(remote.port().to_string())); - } - if let Some((ref path, ref plugin_args)) = self.plugin { - args.push(OsString::from("--plugin")); - args.push(OsString::from(path)); - args.extend(plugin_args.iter().map(OsString::from)); - } - args - } -} - -impl fmt::Display for OpenVpnCommand { - /// Format the program and arguments of an `OpenVpnCommand` for display. Any non-utf8 data is - /// lossily converted using the utf8 replacement character. - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - fmt.write_str(&self.openvpn_bin.to_string_lossy())?; - for arg in self.get_arguments().iter().map(|arg| arg.to_string_lossy()) { - write_argument(fmt, &arg)?; - } - Ok(()) - } -} - -fn write_argument(fmt: &mut fmt::Formatter, arg: &str) -> fmt::Result { - fmt.write_str(" ")?; - let quote = arg.contains(char::is_whitespace); - if quote { - fmt.write_str("\"")?; - } - fmt.write_str(arg)?; - if quote { - fmt.write_str("\"")?; - } - Ok(()) -} - - -impl ChildSpawner for OpenVpnCommand { - type Child = ClonableChild; - - fn spawn(&mut self) -> io::Result<ClonableChild> { - OpenVpnCommand::spawn(self).map(|child| child.into_clonable()) - } -} - - -/// Possible events from OpenVPN -pub enum OpenVpnEvent { - /// An event from the plugin loaded into OpenVPN. - PluginEvent(Result<(openvpn_ffi::OpenVpnPluginEvent, HashMap<String, String>)>), - /// The OpenVPN process exited. The bool indicates if the process exited cleanly. - Shutdown(bool), -} - -/// A struct able to start and monitor OpenVPN processes. -pub struct OpenVpnMonitor { - command: OpenVpnCommand, - plugin_path: PathBuf, - monitor: ChildMonitor<OpenVpnCommand>, -} - -impl OpenVpnMonitor { - /// Creates a new `OpenVpnMonitor` based on the given command - pub fn new<P: AsRef<Path>>(command: OpenVpnCommand, plugin_path: P) -> Self { - OpenVpnMonitor { - command: command.clone(), - plugin_path: plugin_path.as_ref().to_path_buf(), - monitor: ChildMonitor::new(command), - } - } - - /// Starts OpenVPN and begins to monitor it. - pub fn start<L>(&mut self, listener: L) -> Result<(Option<ChildStdout>, Option<ChildStderr>)> - where L: FnMut(OpenVpnEvent) + Send + 'static - { - let shared_listener = Arc::new(Mutex::new(listener)); - self.start_plugin_listener(shared_listener.clone())?; - self.start_child_monitor(shared_listener) - } - - fn start_plugin_listener<L>(&mut self, shared_listener: Arc<Mutex<L>>) -> Result<()> - where L: FnMut(OpenVpnEvent) + Send + 'static - { - let server_id = talpid_ipc::start_new_server(move |msg| { - let chained_msg = msg.chain_err(|| ErrorKind::PluginCommunicationError); - let mut listener = shared_listener.lock().unwrap(); - (listener.deref_mut())(OpenVpnEvent::PluginEvent(chained_msg)); - }).chain_err(|| ErrorKind::PluginCommunicationError)?; - self.command.plugin(&self.plugin_path, vec![server_id]); - Ok(()) - } - - fn start_child_monitor<L>(&mut self, - shared_listener: Arc<Mutex<L>>) - -> Result<(Option<ChildStdout>, Option<ChildStderr>)> - where L: FnMut(OpenVpnEvent) + Send + 'static - { - let callback = move |clean_exit| { - let mut listener = shared_listener.lock().unwrap(); - (listener.deref_mut())(OpenVpnEvent::Shutdown(clean_exit)); - }; - - self.monitor = ChildMonitor::new(self.command.clone()); - Ok(self.monitor.start(callback)?) - } - - /// Forwards a stop call to the underlying `ChildMonitor`. - pub fn stop(&self) -> Result<()> { - Ok(self.monitor.stop()?) - } -} - - -#[cfg(test)] -mod openvpn_command_tests { - use super::OpenVpnCommand; - use net::RemoteAddr; - use std::ffi::OsString; - - #[test] - fn no_arguments() { - let testee_args = OpenVpnCommand::new("").get_arguments(); - assert_eq!(0, testee_args.len()); - } - - #[test] - fn passes_one_remote() { - let remote = RemoteAddr::new("example.com", 3333); - - let testee_args = OpenVpnCommand::new("").remotes(remote).unwrap().get_arguments(); - - assert!(testee_args.contains(&OsString::from("example.com"))); - assert!(testee_args.contains(&OsString::from("3333"))); - } - - #[test] - fn passes_two_remotes() { - let remotes = vec![RemoteAddr::new("127.0.0.1", 998), RemoteAddr::new("fe80::1", 1337)]; - - let testee_args = OpenVpnCommand::new("").remotes(&remotes[..]).unwrap().get_arguments(); - - assert!(testee_args.contains(&OsString::from("127.0.0.1"))); - assert!(testee_args.contains(&OsString::from("998"))); - assert!(testee_args.contains(&OsString::from("fe80::1"))); - assert!(testee_args.contains(&OsString::from("1337"))); - } - - #[test] - fn accepts_str() { - assert!(OpenVpnCommand::new("").remotes("10.0.0.1:1377").is_ok()); - } - - #[test] - fn accepts_slice_of_str() { - let remotes = ["10.0.0.1:1337", "127.0.0.1:99"]; - - let testee_args = OpenVpnCommand::new("").remotes(&remotes[..]).unwrap().get_arguments(); - - assert!(testee_args.contains(&OsString::from("10.0.0.1"))); - assert!(testee_args.contains(&OsString::from("1337"))); - assert!(testee_args.contains(&OsString::from("127.0.0.1"))); - assert!(testee_args.contains(&OsString::from("99"))); - } - - #[test] - fn passes_plugin_path() { - let path = "./a/path"; - let testee_args = OpenVpnCommand::new("").plugin(path, vec![]).get_arguments(); - assert!(testee_args.contains(&OsString::from("./a/path"))); - } - - #[test] - fn passes_plugin_args() { - let args = vec![String::from("123"), String::from("cde")]; - let testee_args = OpenVpnCommand::new("").plugin("", args).get_arguments(); - assert!(testee_args.contains(&OsString::from("123"))); - assert!(testee_args.contains(&OsString::from("cde"))); - } -} - - -#[cfg(test)] -mod openvpn_monitor_tests { - use super::*; - - #[test] - fn stop_without_start() { - let command = OpenVpnCommand::new(""); - let testee = OpenVpnMonitor::new(command, ""); - - use super::super::monitor::ErrorKind::InvalidState as MInvalidState; - assert_matches!(testee.stop(), Err(Error(ErrorKind::ChildMonitorError(MInvalidState), _))); - } -} |
