summaryrefslogtreecommitdiffhomepage
path: root/talpid_openvpn_plugin/src
diff options
context:
space:
mode:
authorLinus Färnstrand <linus@mullvad.net>2017-03-06 19:41:33 +0100
committerLinus Färnstrand <linus@mullvad.net>2017-03-07 08:42:02 +0100
commitae3458fdd2ee092924ce2f275e622f6a66fab903 (patch)
treea6f5a196c0c0b7d3b5b9080ce7d80e9bfb4b2210 /talpid_openvpn_plugin/src
parentf085642d25c2f91095ce01d324a59b5cee23e8b0 (diff)
downloadmullvadvpn-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.rs97
-rw-r--r--talpid_openvpn_plugin/src/ffi/mod.rs55
-rw-r--r--talpid_openvpn_plugin/src/ffi/parse.rs142
-rw-r--r--talpid_openvpn_plugin/src/ffi/structs.rs49
-rw-r--r--talpid_openvpn_plugin/src/lib.rs48
-rw-r--r--talpid_openvpn_plugin/src/processing.rs5
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);