diff options
| author | Linus Färnstrand <linus@mullvad.net> | 2017-01-25 23:10:45 +0100 |
|---|---|---|
| committer | Linus Färnstrand <linus@mullvad.net> | 2017-01-31 13:47:23 +0100 |
| commit | c7cdc75ee6e4232967256973f0192b6105f2ffc8 (patch) | |
| tree | 4f216b60b0270550b01da1e3c0667ba178ef5fa2 | |
| parent | db300b94dc777333345ad68dfc2b823d625f3a4b (diff) | |
| download | mullvadvpn-c7cdc75ee6e4232967256973f0192b6105f2ffc8.tar.xz mullvadvpn-c7cdc75ee6e4232967256973f0192b6105f2ffc8.zip | |
Add parse module with array and env functions
| -rw-r--r-- | talpid_openvpn_plugin/Cargo.lock | 91 | ||||
| -rw-r--r-- | talpid_openvpn_plugin/Cargo.toml | 1 | ||||
| -rw-r--r-- | talpid_openvpn_plugin/src/ffi/mod.rs | 2 | ||||
| -rw-r--r-- | talpid_openvpn_plugin/src/ffi/parse.rs | 130 | ||||
| -rw-r--r-- | talpid_openvpn_plugin/src/lib.rs | 3 |
5 files changed, 227 insertions, 0 deletions
diff --git a/talpid_openvpn_plugin/Cargo.lock b/talpid_openvpn_plugin/Cargo.lock index 6bbe450750..78d4f1057a 100644 --- a/talpid_openvpn_plugin/Cargo.lock +++ b/talpid_openvpn_plugin/Cargo.lock @@ -2,13 +2,104 @@ name = "talpid_openvpn_plugin" version = "0.1.0" dependencies = [ + "error-chain 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] +name = "backtrace" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "backtrace-sys 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dbghelp-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.20 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-demangle 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "backtrace-sys" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "gcc 0.3.42 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.20 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "cfg-if" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "dbghelp-sys" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "error-chain" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "backtrace 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "gcc" +version = "0.3.42" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "kernel32-sys" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] name = "lazy_static" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "libc" +version = "0.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "rustc-demangle" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "winapi" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "winapi-build" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + [metadata] +"checksum backtrace 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f551bc2ddd53aea015d453ef0b635af89444afa5ed2405dd0b2062ad5d600d80" +"checksum backtrace-sys 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "3602e8d8c43336088a8505fa55cae2b3884a9be29440863a11528a42f46f6bb7" +"checksum cfg-if 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "de1e760d7b6535af4241fca8bd8adf68e2e7edacc6b29f5d399050c5e48cf88c" +"checksum dbghelp-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "97590ba53bcb8ac28279161ca943a924d1fd4a8fb3fa63302591647c4fc5b850" +"checksum error-chain 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "6930e04918388a9a2e41d518c25cf679ccafe26733fb4127dbf21993f2575d46" +"checksum gcc 0.3.42 (registry+https://github.com/rust-lang/crates.io-index)" = "291055c78f59ca3d84c99026c9501c469413d386bb46be1e1cf1d285cd1db3b0" +"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" "checksum lazy_static 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6abe0ee2e758cd6bc8a2cd56726359007748fbf4128da998b65d0b70f881e19b" +"checksum libc 0.2.20 (registry+https://github.com/rust-lang/crates.io-index)" = "684f330624d8c3784fb9558ca46c4ce488073a8d22450415c5eb4f4cfb0d11b5" +"checksum rustc-demangle 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "1430d286cadb237c17c885e25447c982c97113926bb579f4379c0eca8d9586dc" +"checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" +"checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" diff --git a/talpid_openvpn_plugin/Cargo.toml b/talpid_openvpn_plugin/Cargo.toml index be65a23f4a..8eff4c9c27 100644 --- a/talpid_openvpn_plugin/Cargo.toml +++ b/talpid_openvpn_plugin/Cargo.toml @@ -8,3 +8,4 @@ crate-type = ["dylib"] [dependencies] lazy_static = "0.2" +error-chain = "0.8" diff --git a/talpid_openvpn_plugin/src/ffi/mod.rs b/talpid_openvpn_plugin/src/ffi/mod.rs index 019decf157..cf3b7fb42c 100644 --- a/talpid_openvpn_plugin/src/ffi/mod.rs +++ b/talpid_openvpn_plugin/src/ffi/mod.rs @@ -9,6 +9,8 @@ use std::os::raw::{c_char, c_int, c_uint, c_void}; mod consts; use self::consts::*; +mod parse; + /// Struct sent to `openvpn_plugin_open_v3` containing input values. #[repr(C)] diff --git a/talpid_openvpn_plugin/src/ffi/parse.rs b/talpid_openvpn_plugin/src/ffi/parse.rs new file mode 100644 index 0000000000..72b46316f0 --- /dev/null +++ b/talpid_openvpn_plugin/src/ffi/parse.rs @@ -0,0 +1,130 @@ +use std::collections::HashMap; +use std::ffi::CStr; +use std::os::raw::c_char; + +error_chain!{ + errors { + Null { + description("Null pointer") + } + NoEqual { + description("No equal sign in string") + } + } + 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(Error::from(ErrorKind::Null)) + } 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(Error::from(ErrorKind::NoEqual))?; + 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; + + /// Assert macro that fails if the given pattern does not match the given expression + /// TODO(Linus): Very useful macro for asserting on patterns where the type does not implement + /// Eq. Thus it should probably be moved to a general place/crate when needed in more places. + macro_rules! assert_pat { + ($expected:pat, $actual:expr) => {{ + if let $expected = $actual {} else { + let msg = stringify!($expected); + panic!("Expected {}. Got {:?}", msg, $actual); + } + }} + } + + #[test] + fn string_array_null() { + let result = unsafe { string_array(ptr::null()) }; + assert_pat!(Err(Error(ErrorKind::Null, _)), result); + } + + #[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_one_string() { + 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 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_pat!(Err(Error(ErrorKind::NoEqual, _)), result); + } + + #[test] + fn env_double_equal_trailing_space() { + 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[..])); + } +} diff --git a/talpid_openvpn_plugin/src/lib.rs b/talpid_openvpn_plugin/src/lib.rs index a917ead7a5..91fcd34396 100644 --- a/talpid_openvpn_plugin/src/lib.rs +++ b/talpid_openvpn_plugin/src/lib.rs @@ -1,6 +1,9 @@ #[macro_use] extern crate lazy_static; +#[macro_use] +extern crate error_chain; + mod ffi; /// Publicly export the functions making up the public interface of the plugin. These are the C FFI |
