summaryrefslogtreecommitdiffhomepage
path: root/src
diff options
context:
space:
mode:
authorLinus Färnstrand <linus@mullvad.net>2017-04-07 09:18:04 +0200
committerLinus Färnstrand <linus@mullvad.net>2017-04-07 09:26:35 +0200
commit30fad8d78135e72cf671d1b01ee1b1c6643ff21c (patch)
tree64b629ff5a03c107980f983d2fe3e3b0fc4736e8 /src
parent5701c32ba8ff9ab7fbc2712d813287c812bc5aef (diff)
downloadmullvadvpn-30fad8d78135e72cf671d1b01ee1b1c6643ff21c.tar.xz
mullvadvpn-30fad8d78135e72cf671d1b01ee1b1c6643ff21c.zip
Move talpid_core from root to member
Diffstat (limited to 'src')
-rw-r--r--src/lib.rs23
-rw-r--r--src/net.rs253
-rw-r--r--src/process/mod.rs31
-rw-r--r--src/process/monitor.rs267
-rw-r--r--src/process/openvpn.rs310
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), _)));
- }
-}