diff options
| -rw-r--r-- | talpid_cli/src/main.rs | 45 | ||||
| -rw-r--r-- | talpid_core/src/tunnel/mod.rs | 104 | ||||
| -rw-r--r-- | talpid_core/src/tunnel/openvpn.rs | 1 |
3 files changed, 119 insertions, 31 deletions
diff --git a/talpid_cli/src/main.rs b/talpid_cli/src/main.rs index 8433fa03e8..2f1e5c36dc 100644 --- a/talpid_cli/src/main.rs +++ b/talpid_cli/src/main.rs @@ -9,17 +9,14 @@ extern crate error_chain; extern crate log; extern crate env_logger; -use std::path::Path; use std::sync::Mutex; use std::sync::mpsc::{self, Receiver}; +use talpid_core::net::RemoteAddr; -use talpid_core::process::openvpn::OpenVpnCommand; -use talpid_core::tunnel::openvpn::{OpenVpnEvent, OpenVpnMonitor}; +use talpid_core::tunnel::{TunnelEvent, TunnelMonitor}; mod cli; -use cli::Args; - error_chain!{} @@ -28,52 +25,38 @@ quick_main!(run); fn run() -> Result<()> { init_logger()?; let args = cli::parse_args_or_exit(); - let command = create_openvpn_command(&args); - main_loop(&command, args.plugin_path.as_path()) + main_loop(&args.remotes) } pub fn init_logger() -> Result<()> { env_logger::init().chain_err(|| "Failed to bootstrap logging system") } -fn create_openvpn_command(args: &Args) -> OpenVpnCommand { - let mut command = OpenVpnCommand::new(&args.binary); - command - .config(&args.config) - .remotes(&args.remotes[..]) - .unwrap(); - command -} - -fn main_loop(command: &OpenVpnCommand, plugin_path: &Path) -> Result<()> { - let (monitor, rx) = create_openvpn_monitor(plugin_path)?; +fn main_loop(remotes: &[RemoteAddr]) -> Result<()> { + let mut remotes_iter = remotes.iter().cloned().cycle(); + let (monitor, rx) = create_tunnel_monitor()?; loop { - monitor.start(command.clone()).chain_err(|| "Unable to start OpenVPN")?; + monitor.start(remotes_iter.next().unwrap()).chain_err(|| "Unable to start OpenVPN")?; while let Ok(msg) = rx.recv() { match msg { - OpenVpnEvent::Shutdown(result) => { - println!( - "Monitored process exited. clean: {}", - result.map(|s| s.success()).unwrap_or(false) - ); + TunnelEvent::Shutdown => { + println!("Monitored process exited"); break; } - OpenVpnEvent::PluginEvent(event, env) => { - println!("OpenVPN event:\nEvent: {:?}\nENV: {:?}", event, env); - } + TunnelEvent::Up => println!("Tunnel UP"), + TunnelEvent::Down => println!("Tunnel DOWN"), } } std::thread::sleep(std::time::Duration::from_millis(500)); } } -fn create_openvpn_monitor(plugin_path: &Path) -> Result<(OpenVpnMonitor, Receiver<OpenVpnEvent>)> { +fn create_tunnel_monitor() -> Result<(TunnelMonitor, Receiver<TunnelEvent>)> { let (event_tx, event_rx) = mpsc::channel(); let event_tx_mutex = Mutex::new(event_tx); - let on_event = move |event: OpenVpnEvent| { + let on_event = move |event: TunnelEvent| { event_tx_mutex.lock().unwrap().send(event).expect("Unable to send on tx_lock"); }; - let monitor = OpenVpnMonitor::new(on_event, plugin_path) - .chain_err(|| "Unable to start OpenVPN monitor")?; + let monitor = TunnelMonitor::new(on_event).chain_err(|| "Unable to start OpenVPN monitor")?; Ok((monitor, event_rx)) } diff --git a/talpid_core/src/tunnel/mod.rs b/talpid_core/src/tunnel/mod.rs index 0b477ca1c4..c453c52056 100644 --- a/talpid_core/src/tunnel/mod.rs +++ b/talpid_core/src/tunnel/mod.rs @@ -1,2 +1,106 @@ +use net; +use openvpn_ffi::OpenVpnPluginEvent; +use process::openvpn::OpenVpnCommand; + /// A module for all OpenVPN related tunnel management. pub mod openvpn; + +use self::openvpn::{OpenVpnEvent, OpenVpnMonitor}; + +mod errors { + error_chain!{ + errors { + /// An error indicating there was an error listening for events from the VPN tunnel. + TunnelMonitoringError { + description("Error while setting up or processing events from the VPN tunnel") + } + /// An error indicating that there was an error when trying to start up a VPN tunnel. + TunnelStartError { + description("Error while trying to start the tunnel") + } + } + } +} +pub use self::errors::*; + + +/// Possible events from the VPN tunnel and the child process managing it. +pub enum TunnelEvent { + /// Sent when the tunnel comes up and is ready for traffic. + Up, + /// Sent when the tunnel goes down. + Down, + /// Sent when the process managing the tunnel exits. + Shutdown, +} + +impl TunnelEvent { + /// Converts an `OpenVpnEvent` to a `TunnelEvent`. + /// Returns `None` if there is no corresponding `TunnelEvent`. + pub fn from_openvpn_event(event: &OpenVpnEvent) -> Option<TunnelEvent> { + match *event { + OpenVpnEvent::PluginEvent(ref event, _) => Self::from_openvpn_plugin_event(event), + OpenVpnEvent::Shutdown(_) => Some(TunnelEvent::Shutdown), + } + } + + fn from_openvpn_plugin_event(event: &OpenVpnPluginEvent) -> Option<TunnelEvent> { + match *event { + OpenVpnPluginEvent::Up => Some(TunnelEvent::Up), + OpenVpnPluginEvent::RoutePredown => Some(TunnelEvent::Down), + _ => None, + } + } +} + + +/// Abstraction for monitoring a generic VPN tunnel. +pub struct TunnelMonitor { + monitor: OpenVpnMonitor, +} + +impl TunnelMonitor { + /// Creates a new `TunnelMonitor` with the given event callback. + pub fn new<L>(on_event: L) -> Result<Self> + where L: Fn(TunnelEvent) + Send + Sync + 'static + { + let on_openvpn_event = move |openvpn_event| { + // FIXME: This comment must be here to make rustfmt 0.8.3 not screw up. + match TunnelEvent::from_openvpn_event(&openvpn_event) { + Some(tunnel_event) => on_event(tunnel_event), + None => debug!("Ignoring OpenVpnEvent {:?}", openvpn_event), + } + }; + let monitor = openvpn::OpenVpnMonitor::new(on_openvpn_event, get_plugin_path()) + .chain_err(|| ErrorKind::TunnelMonitoringError)?; + Ok(TunnelMonitor { monitor }) + } + + /// Tries to start a VPN tunnel towards the given address. Will fail if there is a tunnel + /// running already. + pub fn start(&self, remote: net::RemoteAddr) -> Result<()> { + let mut cmd = OpenVpnCommand::new("openvpn"); + cmd.config(get_config_path()).remotes(remote).unwrap(); + self.monitor.start(cmd).chain_err(|| ErrorKind::TunnelStartError) + } +} + + +// TODO(linus): Temporary implementation for getting plugin path during development. +fn get_plugin_path() -> &'static str { + if cfg!(all(unix, not(target_os = "macos"))) { + "./target/debug/libtalpid_openvpn_plugin.so" + } else if cfg!(target_os = "macos") { + "./target/debug/libtalpid_openvpn_plugin.dylib" + } else if cfg!(windows) { + "./target/debug/libtalpid_openvpn_plugin.dll" + } else { + panic!("Unsupported platform"); + } +} + +// TODO(linus): Temporary implementation for getting hold of a config location. +// Manually place a working config here or change this string in order to test +fn get_config_path() -> &'static str { + "./openvpn.conf" +} diff --git a/talpid_core/src/tunnel/openvpn.rs b/talpid_core/src/tunnel/openvpn.rs index bcf89e3eb0..9d366ebb4a 100644 --- a/talpid_core/src/tunnel/openvpn.rs +++ b/talpid_core/src/tunnel/openvpn.rs @@ -33,6 +33,7 @@ pub use self::errors::*; /// Possible events from OpenVPN +#[derive(Debug)] pub enum OpenVpnEvent { /// An event from the plugin loaded into OpenVPN. PluginEvent(openvpn_ffi::OpenVpnPluginEvent, openvpn_ffi::OpenVpnEnv), |
