summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorLinus Färnstrand <linus@mullvad.net>2017-02-01 10:47:49 +0100
committerLinus Färnstrand <linus@mullvad.net>2017-02-01 10:47:49 +0100
commit1c15ddfd97afeba755ab591c205785f9be7608e1 (patch)
tree02415cc0ae86eb1997c355d9e4eddc2b197c106b
parentdb300b94dc777333345ad68dfc2b823d625f3a4b (diff)
parent1d0695e78ce42a1ba4634898e0ce3cc2e7ad604d (diff)
downloadmullvadvpn-1c15ddfd97afeba755ab591c205785f9be7608e1.tar.xz
mullvadvpn-1c15ddfd97afeba755ab591c205785f9be7608e1.zip
Merge branch 'parse-c-strings'
-rw-r--r--.travis.yml5
-rw-r--r--appveyor.yml3
-rw-r--r--talpid_openvpn_plugin/Cargo.lock91
-rw-r--r--talpid_openvpn_plugin/Cargo.toml1
-rw-r--r--talpid_openvpn_plugin/src/ffi/mod.rs2
-rw-r--r--talpid_openvpn_plugin/src/ffi/parse.rs154
-rw-r--r--talpid_openvpn_plugin/src/lib.rs3
7 files changed, 257 insertions, 2 deletions
diff --git a/.travis.yml b/.travis.yml
index 3a9cd49e98..c3763c550f 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -16,7 +16,10 @@ script:
- cargo build --verbose
- cargo test --verbose
- cargo fmt -- --write-mode=diff
- - cd talpid_cli/
+ - cd talpid_openvpn_plugin/
+ - cargo test --verbose
+ - cargo fmt -- --write-mode=diff
+ - cd ../talpid_cli/
- cargo test --verbose
- cargo fmt -- --write-mode=diff
diff --git a/appveyor.yml b/appveyor.yml
index 34a8906cce..d3af2daebb 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -45,7 +45,8 @@ test_script:
cargo build --target %TARGET% --release && echo "RELEASE BUILD DONE" &&
cargo test --target %TARGET% && echo "DEBUG TESTING DONE" &&
cargo test --target %TARGET% --release && echo "RELEASE TESTING DONE" &&
- cd talpid_cli/ && cargo test --target %TARGET%
+ cd talpid_openvpn_plugin/ && cargo test --target %TARGET%
+ cd ../talpid_cli/ && cargo test --target %TARGET%
)
# Cache build binaries for faster builds next time
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..de8e7a16a7
--- /dev/null
+++ b/talpid_openvpn_plugin/src/ffi/parse.rs
@@ -0,0 +1,154 @@
+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(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(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;
+
+ /// 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_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_pat!(Err(Error(ErrorKind::NoEqual(_), _)), result);
+ }
+
+ #[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/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