diff options
| author | Linus Färnstrand <linus@mullvad.net> | 2017-03-06 19:41:33 +0100 |
|---|---|---|
| committer | Linus Färnstrand <linus@mullvad.net> | 2017-03-07 08:42:02 +0100 |
| commit | ae3458fdd2ee092924ce2f275e622f6a66fab903 (patch) | |
| tree | a6f5a196c0c0b7d3b5b9080ce7d80e9bfb4b2210 /talpid_openvpn_plugin/src | |
| parent | f085642d25c2f91095ce01d324a59b5cee23e8b0 (diff) | |
| download | mullvadvpn-ae3458fdd2ee092924ce2f275e622f6a66fab903.tar.xz mullvadvpn-ae3458fdd2ee092924ce2f275e622f6a66fab903.zip | |
Extract OpenVPN FFI stuff to separate crate
Diffstat (limited to 'talpid_openvpn_plugin/src')
| -rw-r--r-- | talpid_openvpn_plugin/src/ffi/consts.rs | 97 | ||||
| -rw-r--r-- | talpid_openvpn_plugin/src/ffi/mod.rs | 55 | ||||
| -rw-r--r-- | talpid_openvpn_plugin/src/ffi/parse.rs | 142 | ||||
| -rw-r--r-- | talpid_openvpn_plugin/src/ffi/structs.rs | 49 | ||||
| -rw-r--r-- | talpid_openvpn_plugin/src/lib.rs | 48 | ||||
| -rw-r--r-- | talpid_openvpn_plugin/src/processing.rs | 5 |
6 files changed, 26 insertions, 370 deletions
diff --git a/talpid_openvpn_plugin/src/ffi/consts.rs b/talpid_openvpn_plugin/src/ffi/consts.rs deleted file mode 100644 index cf5490afbc..0000000000 --- a/talpid_openvpn_plugin/src/ffi/consts.rs +++ /dev/null @@ -1,97 +0,0 @@ -/// Constants for OpenVPN. Taken from include/openvpn-plugin.h in the OpenVPN repository: -/// https://github.com/OpenVPN/openvpn/blob/master/include/openvpn-plugin.h.in - -use std::os::raw::c_int; - -error_chain!{ - errors { - InvalidEnumVariant(i: c_int) { - description("Integer does not match any enum variant") - display("{} is not a valid OPENVPN_PLUGIN_* constant", i) - } - } -} - -/// Enum whose variants correspond to the `OPENVPN_PLUGIN_*` event constants. -#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] -pub enum OpenVpnPluginEvent { - Up = 0, - Down = 1, - RouteUp = 2, - IpChange = 3, - TlsVerify = 4, - AuthUserPassVerify = 5, - ClientConnect = 6, - ClientDisconnect = 7, - LearnAddress = 8, - ClientConnectV2 = 9, - TlsFinal = 10, - EnablePf = 11, - RoutePredown = 12, - N = 13, -} - -impl OpenVpnPluginEvent { - /// Tries to parse an integer from C into a variant of `OpenVpnPluginEvent`. - pub fn from_int(i: c_int) -> Result<OpenVpnPluginEvent> { - if i >= OpenVpnPluginEvent::Up as c_int && i <= OpenVpnPluginEvent::N as c_int { - Ok(unsafe { ::std::mem::transmute_copy::<c_int, OpenVpnPluginEvent>(&i) }) - } else { - Err(ErrorKind::InvalidEnumVariant(i).into()) - } - } -} - - -// Return values. Returned from the plugin to OpenVPN to indicate success or failure. Can also -// Accept (success) or decline (error) an operation, such as incoming client connection attempt. -pub const OPENVPN_PLUGIN_FUNC_SUCCESS: c_int = 0; -pub const OPENVPN_PLUGIN_FUNC_ERROR: c_int = 1; -#[allow(dead_code)] -pub const OPENVPN_PLUGIN_FUNC_DEFERRED: c_int = 2; - - - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn from_int_first() { - let result = OpenVpnPluginEvent::from_int(0); - assert_matches!(result, Ok(OpenVpnPluginEvent::Up)); - } - - #[test] - fn from_int_last() { - let result = OpenVpnPluginEvent::from_int(13); - assert_matches!(result, Ok(OpenVpnPluginEvent::N)); - } - - #[test] - fn from_int_all_valid() { - for i in 0..13 { - if OpenVpnPluginEvent::from_int(i).is_err() { - panic!("{} not covered", i); - } - } - } - - #[test] - fn from_int_negative() { - let result = OpenVpnPluginEvent::from_int(-5); - assert_matches!(result, Err(Error(ErrorKind::InvalidEnumVariant(-5), _))); - } - - #[test] - fn from_int_invalid() { - let result = OpenVpnPluginEvent::from_int(14); - assert_matches!(result, Err(Error(ErrorKind::InvalidEnumVariant(14), _))); - } - - #[test] - fn event_enum_to_str() { - let result = format!("{:?}", OpenVpnPluginEvent::Up); - assert_eq!("Up", result); - } -} diff --git a/talpid_openvpn_plugin/src/ffi/mod.rs b/talpid_openvpn_plugin/src/ffi/mod.rs deleted file mode 100644 index fe046cde67..0000000000 --- a/talpid_openvpn_plugin/src/ffi/mod.rs +++ /dev/null @@ -1,55 +0,0 @@ -// FFI definitions for OpenVPN. See include/openvpn-plugin.h in the OpenVPN repository for -// the original declarations of these structs and functions along with documentation for them: -// https://github.com/OpenVPN/openvpn/blob/master/include/openvpn-plugin.h.in - -use std::os::raw::c_int; - -mod consts; -pub use self::consts::*; - -mod structs; -pub use self::structs::*; - -pub mod parse; - - - -/// Translates a collection of `OpenVpnPluginEvent` instances into a bitmask in the format OpenVPN -/// expects it. -pub fn events_to_bitmask(events: &[OpenVpnPluginEvent]) -> c_int { - let mut bitmask: c_int = 0; - for event in events { - bitmask |= 1 << (*event as i32); - } - bitmask -} - - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn events_to_bitmask_no_events() { - let result = events_to_bitmask(&[]); - assert_eq!(0, result); - } - - #[test] - fn events_to_bitmask_one_event() { - let result = events_to_bitmask(&[OpenVpnPluginEvent::Up]); - assert_eq!(0b1, result); - } - - #[test] - fn events_to_bitmask_another_event() { - let result = events_to_bitmask(&[OpenVpnPluginEvent::RouteUp]); - assert_eq!(0b100, result); - } - - #[test] - fn events_to_bitmask_many_events() { - let result = events_to_bitmask(&[OpenVpnPluginEvent::RouteUp, OpenVpnPluginEvent::N]); - assert_eq!((1 << 13) + (1 << 2), result); - } -} diff --git a/talpid_openvpn_plugin/src/ffi/parse.rs b/talpid_openvpn_plugin/src/ffi/parse.rs deleted file mode 100644 index 096e3ada55..0000000000 --- a/talpid_openvpn_plugin/src/ffi/parse.rs +++ /dev/null @@ -1,142 +0,0 @@ -use std::collections::HashMap; -use std::ffi::CStr; -use std::os::raw::c_char; - -error_chain!{ - errors { - Null { - description("Input is null pointer") - } - NoEqual(s: String) { - description("No equal sign in string") - display("No equal sign in \"{}\"", s) - } - } - foreign_links { - InvalidContent(::std::str::Utf8Error); - } -} - - -/// Parses a null-terminated C string array into a Vec<String> for safe usage. -/// -/// Returns an Err if given a null pointer or if a string is not valid utf-8. -/// -/// # Segfaults -/// -/// Can cause the program to crash if the pointer array starting at `ptr` is not correctly null -/// 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(ErrorKind::Null.into()) - } else { - let mut strings = Vec::new(); - while !(*ptr).is_null() { - let cstr = CStr::from_ptr(*ptr); - strings.push(cstr.to_str()?.to_owned()); - ptr = ptr.offset(1); - } - Ok(strings) - } -} - - -/// Parses a null-terminated array of C strings with "=" delimiters into a key-value map. -/// -/// The input environment has to contain null-terminated strings containing at least -/// one equal sign ("="). Every string is split at the first equal sign and added to the map with -/// the first part being the key and the second the value. -/// -/// If multiple entries have the same key, the last one will be in the result map. -/// -/// # Segfaults -/// -/// Uses `string_array` internally and will segfault for the same reasons as that function. -pub unsafe fn env(envptr: *const *const c_char) -> Result<HashMap<String, String>> { - let mut map = HashMap::new(); - for string in string_array(envptr)? { - let mut iter = string.splitn(2, '='); - let key = iter.next().unwrap(); - let value = iter.next().ok_or_else(|| Error::from(ErrorKind::NoEqual(string.clone())))?; - map.insert(key.to_owned(), value.to_owned()); - } - Ok(map) -} - - -#[cfg(test)] -mod tests { - use super::*; - use std::os::raw::c_char; - use std::ptr; - - #[test] - fn string_array_null() { - let result = unsafe { string_array(ptr::null()) }; - assert_matches!(result, Err(Error(ErrorKind::Null, _))); - } - - #[test] - fn string_array_empty() { - let ptr_arr = [ptr::null()]; - let result = unsafe { string_array(&ptr_arr as *const *const c_char).unwrap() }; - assert!(result.is_empty()); - } - - #[test] - fn string_array_no_space_trim() { - let test_str = " foobar \0"; - let ptr_arr = [test_str as *const _ as *const c_char, ptr::null()]; - let result = unsafe { string_array(&ptr_arr as *const *const c_char).unwrap() }; - assert_eq!([" foobar "], &result[..]); - } - - #[test] - fn string_array_two_strings() { - let test_str1 = "foobar\0"; - let test_str2 = "barbaz\0"; - let ptr_arr = [test_str1 as *const _ as *const c_char, - test_str2 as *const _ as *const c_char, - ptr::null()]; - let result = unsafe { string_array(&ptr_arr as *const *const c_char).unwrap() }; - assert_eq!(["foobar", "barbaz"], &result[..]); - } - - #[test] - fn env_one_value() { - let test_str = "var_a=value_b\0"; - let ptr_arr = [test_str as *const _ as *const c_char, ptr::null()]; - let result = unsafe { env(&ptr_arr as *const *const c_char).unwrap() }; - assert_eq!(1, result.len()); - assert_eq!(Some("value_b"), result.get("var_a").map(|s| &s[..])); - } - - #[test] - fn env_no_equal() { - let test_str = "foobar\0"; - let ptr_arr = [test_str as *const _ as *const c_char, ptr::null()]; - let result = unsafe { env(&ptr_arr as *const *const c_char) }; - assert_matches!(result, Err(Error(ErrorKind::NoEqual(_), _))); - } - - #[test] - fn env_double_equal() { - let test_str = "foo=bar=baz\0"; - let ptr_arr = [test_str as *const _ as *const c_char, ptr::null()]; - let env = unsafe { env(&ptr_arr as *const *const c_char).unwrap() }; - assert_eq!(1, env.len()); - assert_eq!(Some("bar=baz"), env.get("foo").map(|s| &s[..])); - } - - #[test] - fn env_two_same_key() { - let test_str1 = "foo=123\0"; - let test_str2 = "foo=abc\0"; - let ptr_arr = [test_str1 as *const _ as *const c_char, - test_str2 as *const _ as *const c_char, - ptr::null()]; - let env = unsafe { env(&ptr_arr as *const *const c_char).unwrap() }; - assert_eq!(1, env.len()); - assert_eq!(Some("abc"), env.get("foo").map(|s| &s[..])); - } -} diff --git a/talpid_openvpn_plugin/src/ffi/structs.rs b/talpid_openvpn_plugin/src/ffi/structs.rs deleted file mode 100644 index b80f7ae2a2..0000000000 --- a/talpid_openvpn_plugin/src/ffi/structs.rs +++ /dev/null @@ -1,49 +0,0 @@ -use std::os::raw::{c_char, c_int, c_uint, c_void}; - -/// Struct sent to `openvpn_plugin_open_v3` containing input values. -#[repr(C)] -pub struct openvpn_plugin_args_open_in { - type_mask: c_int, - pub argv: *const *const c_char, - envp: *const *const c_char, - callbacks: *const c_void, - ssl_api: ovpnSSLAPI, - ovpn_version: *const c_char, - ovpn_version_major: c_uint, - ovpn_version_minor: c_uint, - ovpn_version_patch: *const c_char, -} - -#[allow(dead_code)] -#[repr(C)] -enum ovpnSSLAPI { - None, - OpenSsl, - MbedTls, -} - -/// Struct used for returning values from `openvpn_plugin_open_v3` to OpenVPN. -#[repr(C)] -pub struct openvpn_plugin_args_open_return { - pub type_mask: c_int, - pub handle: *const c_void, - return_list: *const c_void, -} - -/// Struct sent to `openvpn_plugin_func_v3` containing input values. -#[repr(C)] -pub struct openvpn_plugin_args_func_in { - pub event_type: c_int, - argv: *const *const c_char, - pub envp: *const *const c_char, - pub handle: *const c_void, - per_client_context: *const c_void, - current_cert_depth: c_int, - current_cert: *const c_void, -} - -/// Struct used for returning values from `openvpn_plugin_func_v3` to OpenVPN. -#[repr(C)] -pub struct openvpn_plugin_args_func_return { - return_list: *const c_void, -} diff --git a/talpid_openvpn_plugin/src/lib.rs b/talpid_openvpn_plugin/src/lib.rs index 58343de791..1b6b1fec2b 100644 --- a/talpid_openvpn_plugin/src/lib.rs +++ b/talpid_openvpn_plugin/src/lib.rs @@ -5,18 +5,13 @@ extern crate error_chain; extern crate log; extern crate env_logger; -#[cfg(test)] -#[macro_use] -extern crate assert_matches; - +extern crate openvpn_ffi; extern crate talpid_ipc; use std::os::raw::{c_int, c_void}; -mod ffi; mod processing; -use ffi::OpenVpnPluginEvent; use processing::EventProcessor; @@ -43,8 +38,8 @@ error_chain!{ /// All the OpenVPN events this plugin will register for listening to. Edit this variable to change /// events. -pub static INTERESTING_EVENTS: &'static [OpenVpnPluginEvent] = &[OpenVpnPluginEvent::Up, - OpenVpnPluginEvent::RoutePredown]; +pub static INTERESTING_EVENTS: &'static [openvpn_ffi::OpenVpnPluginEvent] = + &[openvpn_ffi::OpenVpnPluginEvent::Up, openvpn_ffi::OpenVpnPluginEvent::RoutePredown]; /// Called by OpenVPN when the plugin is first loaded. @@ -53,29 +48,29 @@ pub static INTERESTING_EVENTS: &'static [OpenVpnPluginEvent] = &[OpenVpnPluginEv /// plugin. #[no_mangle] pub extern "C" fn openvpn_plugin_open_v3(_version: c_int, - args: *const ffi::openvpn_plugin_args_open_in, - retptr: *mut ffi::openvpn_plugin_args_open_return) + args: *const openvpn_ffi::openvpn_plugin_args_open_in, + retptr: *mut openvpn_ffi::openvpn_plugin_args_open_return) -> c_int { if init_logger().is_err() { - return ffi::OPENVPN_PLUGIN_FUNC_ERROR; + return openvpn_ffi::OPENVPN_PLUGIN_FUNC_ERROR; } match openvpn_plugin_open_v3_internal(args, retptr) { - Ok(_) => ffi::OPENVPN_PLUGIN_FUNC_SUCCESS, + Ok(_) => openvpn_ffi::OPENVPN_PLUGIN_FUNC_SUCCESS, Err(e) => { log_error("Unable to initialize plugin", &e); - ffi::OPENVPN_PLUGIN_FUNC_ERROR + openvpn_ffi::OPENVPN_PLUGIN_FUNC_ERROR } } } -fn openvpn_plugin_open_v3_internal(args: *const ffi::openvpn_plugin_args_open_in, - retptr: *mut ffi::openvpn_plugin_args_open_return) +fn openvpn_plugin_open_v3_internal(args: *const openvpn_ffi::openvpn_plugin_args_open_in, + retptr: *mut openvpn_ffi::openvpn_plugin_args_open_return) -> Result<()> { debug!("Initializing plugin"); let core_server_id = parse_args(args)?; let processor = EventProcessor::new(core_server_id).chain_err(|| ErrorKind::InitHandleFailed)?; unsafe { - (*retptr).type_mask = ffi::events_to_bitmask(INTERESTING_EVENTS); + (*retptr).type_mask = openvpn_ffi::events_to_bitmask(INTERESTING_EVENTS); // Converting the handle into a raw pointer will make it escape Rust deallocation. See // `openvpn_plugin_close_v1` for deallocation. (*retptr).handle = Box::into_raw(Box::new(processor)) as *const c_void; @@ -83,8 +78,9 @@ fn openvpn_plugin_open_v3_internal(args: *const ffi::openvpn_plugin_args_open_in Ok(()) } -fn parse_args(args: *const ffi::openvpn_plugin_args_open_in) -> Result<talpid_ipc::IpcServerId> { - let mut args_iter = unsafe { ffi::parse::string_array((*args).argv) } +fn parse_args(args: *const openvpn_ffi::openvpn_plugin_args_open_in) + -> Result<talpid_ipc::IpcServerId> { + let mut args_iter = unsafe { openvpn_ffi::parse::string_array((*args).argv) } .chain_err(|| ErrorKind::ParseArgsFailed)? .into_iter(); let _plugin_path = args_iter.next(); @@ -109,23 +105,25 @@ pub extern "C" fn openvpn_plugin_close_v1(handle: *const c_void) { /// `openvpn_plugin_open_v3` #[no_mangle] pub extern "C" fn openvpn_plugin_func_v3(_version: c_int, - args: *const ffi::openvpn_plugin_args_func_in, - _retptr: *const ffi::openvpn_plugin_args_func_return) + args: *const openvpn_ffi::openvpn_plugin_args_func_in, + _retptr: *const openvpn_ffi::openvpn_plugin_args_func_return) -> c_int { match openvpn_plugin_func_v3_internal(args) { - Ok(_) => ffi::OPENVPN_PLUGIN_FUNC_SUCCESS, + Ok(_) => openvpn_ffi::OPENVPN_PLUGIN_FUNC_SUCCESS, Err(e) => { log_error("Error while processing event", &e); - ffi::OPENVPN_PLUGIN_FUNC_ERROR + openvpn_ffi::OPENVPN_PLUGIN_FUNC_ERROR } } } -fn openvpn_plugin_func_v3_internal(args: *const ffi::openvpn_plugin_args_func_in) -> Result<()> { +fn openvpn_plugin_func_v3_internal(args: *const openvpn_ffi::openvpn_plugin_args_func_in) + -> Result<()> { let event_type = unsafe { (*args).event_type }; - let event = OpenVpnPluginEvent::from_int(event_type).chain_err(|| ErrorKind::InvalidEventType)?; + let event = openvpn_ffi::OpenVpnPluginEvent::from_int(event_type).chain_err(|| ErrorKind::InvalidEventType)?; debug!("Received event: {:?}", event); - let env = unsafe { ffi::parse::env((*args).envp) }.chain_err(|| ErrorKind::ParseEnvFailed)?; + let env = + unsafe { openvpn_ffi::parse::env((*args).envp) }.chain_err(|| ErrorKind::ParseEnvFailed)?; let mut handle = unsafe { Box::from_raw((*args).handle as *mut EventProcessor) }; handle.process_event(event, env).chain_err(|| ErrorKind::EventProcessingFailed)?; diff --git a/talpid_openvpn_plugin/src/processing.rs b/talpid_openvpn_plugin/src/processing.rs index 586d0574ec..68bab3b99c 100644 --- a/talpid_openvpn_plugin/src/processing.rs +++ b/talpid_openvpn_plugin/src/processing.rs @@ -1,9 +1,10 @@ -use ffi::OpenVpnPluginEvent; +use openvpn_ffi; use std::collections::HashMap; use talpid_ipc::{IpcClient, IpcServerId}; + error_chain! { errors { IpcSendingError { @@ -26,7 +27,7 @@ impl EventProcessor { } pub fn process_event(&mut self, - event: OpenVpnPluginEvent, + event: openvpn_ffi::OpenVpnPluginEvent, env: HashMap<String, String>) -> Result<()> { trace!("Processing \"{:?}\" event", event); |
