openvpn_plugin/ffi/
parse.rs1use std::collections::HashMap;
10use std::error::Error;
11use std::ffi::{CStr, CString};
12use std::fmt;
13use std::os::raw::c_char;
14use std::str::Utf8Error;
15
16#[derive(Debug, Clone, Eq, PartialEq, Hash)]
18pub enum ParseError {
19 NullPtr,
21 NoEqual(CString),
24}
25
26impl fmt::Display for ParseError {
27 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
28 match *self {
29 ParseError::NullPtr => "Input is null pointer".fmt(f),
30 ParseError::NoEqual(ref s) => write!(f, "No equal sign in \"{}\"", s.to_string_lossy()),
31 }
32 }
33}
34
35impl Error for ParseError {}
36
37
38pub unsafe fn string_array(mut ptr: *const *const c_char) -> Result<Vec<CString>, ParseError> {
47 if ptr.is_null() {
48 Err(ParseError::NullPtr)
49 } else {
50 let mut strings = Vec::new();
51 while !(*ptr).is_null() {
52 strings.push(CStr::from_ptr(*ptr).to_owned());
53 ptr = ptr.offset(1);
54 }
55 Ok(strings)
56 }
57}
58
59pub fn string_array_utf8(strings: &[CString]) -> Result<Vec<String>, Utf8Error> {
62 strings
63 .iter()
64 .map(|s| s.to_str().map(|s| s.to_owned()))
65 .collect()
66}
67
68
69pub unsafe fn env(envptr: *const *const c_char) -> Result<HashMap<CString, CString>, ParseError> {
81 let mut map = HashMap::new();
82 for string in string_array(envptr)? {
83 let string_bytes = string.as_bytes();
84 let equal_index = string_bytes
85 .iter()
86 .position(|&c| c == b'=')
87 .ok_or_else(|| ParseError::NoEqual(string.clone()))?;
88
89 let key = CString::new(&string_bytes[..equal_index]).unwrap();
91 let value = CString::new(&string_bytes[equal_index + 1..]).unwrap();
92 map.insert(key, value);
93 }
94 Ok(map)
95}
96
97pub fn env_utf8(env: &HashMap<CString, CString>) -> Result<HashMap<String, String>, Utf8Error> {
100 let mut output_env = HashMap::with_capacity(env.len());
101 for (key, value) in env {
102 output_env.insert(key.to_str()?.to_owned(), value.to_str()?.to_owned());
103 }
104 Ok(output_env)
105}
106
107
108#[cfg(test)]
109mod tests {
110 use super::*;
111 use std::os::raw::c_char;
112 use std::ptr;
113
114 #[test]
115 fn string_array_null() {
116 assert_eq!(Err(ParseError::NullPtr), unsafe {
117 string_array(ptr::null())
118 });
119 }
120
121 #[test]
122 fn string_array_empty() {
123 let ptr_arr = [ptr::null()];
124 let result = unsafe { string_array(&ptr_arr as *const *const c_char).unwrap() };
125 assert!(result.is_empty());
126 }
127
128 #[test]
129 fn string_array_no_space_trim() {
130 let test_str = " foobar \0";
131 let ptr_arr = [test_str as *const _ as *const c_char, ptr::null()];
132 let result = unsafe { string_array(&ptr_arr as *const *const c_char).unwrap() };
133 assert_eq!([CString::new(" foobar ").unwrap()], &result[..]);
134 }
135
136 #[test]
137 fn string_array_two_strings() {
138 let test_str1 = "foo\0";
139 let test_str2 = "bar\0";
140 let ptr_arr = [
141 test_str1 as *const _ as *const c_char,
142 test_str2 as *const _ as *const c_char,
143 ptr::null(),
144 ];
145 let result = unsafe { string_array(&ptr_arr as *const *const c_char).unwrap() };
146 assert_eq!(
147 [CString::new("foo").unwrap(), CString::new("bar").unwrap()],
148 &result[..]
149 );
150 }
151
152 #[test]
153 fn string_array_utf8_happy_path() {
154 let array = &[CString::new("foo").unwrap(), CString::new("bar").unwrap()];
155 let result = string_array_utf8(array).unwrap();
156 assert_eq!(vec!["foo", "bar"], result);
157 }
158
159 #[test]
160 fn string_array_utf8_invalid() {
161 let array = &[CString::new(vec![192]).unwrap()];
163 assert!(string_array_utf8(array).is_err());
164 }
165
166 #[test]
167 fn env_one_value() {
168 let test_str = "var_a=value_b\0";
169 let key = CString::new("var_a").unwrap();
170 let value = CString::new("value_b").unwrap();
171
172 let ptr_arr = [test_str as *const _ as *const c_char, ptr::null()];
173 let result = unsafe { env(&ptr_arr as *const *const c_char).unwrap() };
174 assert_eq!(1, result.len());
175 assert_eq!(Some(&value), result.get(&key));
176 }
177
178 #[test]
179 fn env_no_equal() {
180 let test_str = "foobar\0";
181 let ptr_arr = [test_str as *const _ as *const c_char, ptr::null()];
182 let result = unsafe { env(&ptr_arr as *const *const c_char) };
183 assert_eq!(
184 result,
185 Err(ParseError::NoEqual(CString::new("foobar").unwrap()))
186 );
187 }
188
189 #[test]
190 fn env_double_equal() {
191 let test_str = "foo=bar=baz\0";
192 let key = CString::new("foo").unwrap();
193 let value = CString::new("bar=baz").unwrap();
194
195 let ptr_arr = [test_str as *const _ as *const c_char, ptr::null()];
196 let env = unsafe { env(&ptr_arr as *const *const c_char).unwrap() };
197 assert_eq!(1, env.len());
198 assert_eq!(Some(&value), env.get(&key));
199 }
200
201 #[test]
202 fn env_two_same_key() {
203 let test_str1 = "foo=123\0";
204 let test_str2 = "foo=abc\0";
205 let ptr_arr = [
206 test_str1 as *const _ as *const c_char,
207 test_str2 as *const _ as *const c_char,
208 ptr::null(),
209 ];
210 let key = CString::new("foo").unwrap();
211 let value = CString::new("abc").unwrap();
212
213 let env = unsafe { env(&ptr_arr as *const *const c_char).unwrap() };
214 assert_eq!(1, env.len());
215 assert_eq!(Some(&value), env.get(&key));
216 }
217
218 #[test]
219 fn env_utf8_happy_path() {
220 let mut env = HashMap::new();
221 env.insert(CString::new("foo").unwrap(), CString::new("bar").unwrap());
222 env.insert(CString::new("baz").unwrap(), CString::new("123").unwrap());
223 let result = env_utf8(&env).unwrap();
224 assert_eq!("bar", result.get("foo").unwrap());
225 assert_eq!("123", result.get("baz").unwrap());
226 }
227
228 #[test]
229 fn env_utf8_invalid() {
230 let mut env = HashMap::new();
232 env.insert(
233 CString::new(vec![192]).unwrap(),
234 CString::new("bar").unwrap(),
235 );
236 assert!(env_utf8(&env).is_err());
237 }
238}