/* Copyright (C) 2017 Open Information Security Foundation * * You can copy, redistribute or modify this Program under the terms of * the GNU General Public License version 2 as published by the Free * Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * version 2 along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. */ //! Module for retrieving configuration details. use nom8::{ character::complete::{multispace0, not_line_ending}, combinator::verify, number::complete::double, sequence::preceded, IResult, Parser, }; use std::ffi::{CStr, CString}; use std::os::raw::c_char; use std::os::raw::c_int; use std::ptr; use std::str; use suricata_sys::sys::SCConfGetChildValue; use suricata_sys::sys::SCConfGetChildValueBool; use suricata_sys::sys::SCConfGetFirstNode; use suricata_sys::sys::SCConfGetNextNode; use suricata_sys::sys::SCConfGetNode; use suricata_sys::sys::SCConfGetValueNode; use suricata_sys::sys::SCConfNode; use suricata_sys::sys::SCConfNodeLookupChild; pub fn conf_get_node(key: &str) -> Option { let key = if let Ok(key) = CString::new(key) { key } else { return None; }; let node = unsafe { SCConfGetNode(key.as_ptr()) }; if node.is_null() { None } else { Some(ConfNode::wrap(node)) } } pub use suricata_ffi::conf::{conf_get, conf_get_bool}; /// Wrap a Suricata ConfNode and expose some of its methods with a /// Rust friendly interface. pub struct ConfNode { pub conf: *const SCConfNode, } impl ConfNode { pub fn wrap(conf: *const SCConfNode) -> Self { return Self { conf }; } pub fn get_child_node(&self, key: &str) -> Option { let node = unsafe { let s = CString::new(key).unwrap(); SCConfNodeLookupChild(self.conf, s.as_ptr()) }; if node.is_null() { None } else { Some(ConfNode::wrap(node)) } } pub fn first(&self) -> Option { let node = unsafe { SCConfGetFirstNode(self.conf) }; if node.is_null() { None } else { Some(ConfNode::wrap(node)) } } pub fn next(&self) -> Option { let node = unsafe { SCConfGetNextNode(self.conf) }; if node.is_null() { None } else { Some(ConfNode::wrap(node)) } } pub fn value(&self) -> &str { let vptr = unsafe { SCConfGetValueNode(self.conf) }; let value = std::str::from_utf8(unsafe { CStr::from_ptr(vptr).to_bytes() }).unwrap(); return value; } pub fn get_child_value(&self, key: &str) -> Option<&str> { let mut vptr: *const c_char = ptr::null_mut(); unsafe { let s = CString::new(key).unwrap(); if SCConfGetChildValue(self.conf, s.as_ptr(), &mut vptr) != 1 { return None; } } if vptr.is_null() { return None; } let value = str::from_utf8(unsafe { CStr::from_ptr(vptr).to_bytes() }).unwrap(); return Some(value); } pub fn get_child_bool(&self, key: &str) -> bool { let mut vptr: c_int = 0; unsafe { let s = CString::new(key).unwrap(); if SCConfGetChildValueBool(self.conf, s.as_ptr(), &mut vptr) != 1 { return false; } } return vptr == 1; } } const BYTE: u64 = 1; const KILOBYTE: u64 = 1024; const MEGABYTE: u64 = 1_048_576; const GIGABYTE: u64 = 1_073_741_824; /// Helper function to retrieve memory unit from a string slice /// /// Return value: u64 /// /// # Arguments /// /// * `unit` - A string slice possibly containing memory unit fn get_memunit(unit: &str) -> u64 { let unit = &unit.to_lowercase()[..]; match unit { "b" => BYTE, "kb" => KILOBYTE, "kib" => KILOBYTE, "mb" => MEGABYTE, "mib" => MEGABYTE, "gb" => GIGABYTE, "gib" => GIGABYTE, _ => 0, } } /// Parses memory units from human readable form to machine readable /// /// Return value: /// Result => Ok(u64) /// => Err(error string) /// /// # Arguments /// /// * `arg` - A string slice that holds the value parsed from the config /// /// Note that kb/mb/gb and kib/mib/gib are equivalent pub fn get_memval(arg: &str) -> Result { let arg = arg.trim(); let val: f64; let mut unit: &str; let r: IResult<&str, (f64, &str)> = ( preceded(multispace0, double), preceded(multispace0, verify(not_line_ending, |c: &str| c.len() < 4)), ) .parse(arg); if let Ok(r) = r { val = (r.1).0; unit = (r.1).1; } else { return Err("Error parsing the memory value"); } if unit.is_empty() { unit = "B"; } let unit = get_memunit(unit); if unit == 0 { return Err("Invalid memory unit"); } let res = val * unit as f64; Ok(res as u64) } #[cfg(test)] mod tests { use super::*; #[test] fn test_memval_nospace() { let s = "10"; let res = 10; assert_eq!(Ok(10), get_memval(s)); let s = "10kb"; assert_eq!(Ok(res * KILOBYTE), get_memval(s)); let s = "10Kb"; assert_eq!(Ok(res * KILOBYTE), get_memval(s)); let s = "10Kib"; assert_eq!(Ok(res * KILOBYTE), get_memval(s)); let s = "10KB"; assert_eq!(Ok(res * KILOBYTE), get_memval(s)); let s = "10mb"; assert_eq!(Ok(res * MEGABYTE), get_memval(s)); let s = "10mib"; assert_eq!(Ok(res * MEGABYTE), get_memval(s)); let s = "10gb"; assert_eq!(Ok(res * GIGABYTE), get_memval(s)); let s = "10gib"; assert_eq!(Ok(res * GIGABYTE), get_memval(s)); } #[test] fn test_memval_space_start() { let s = " 10"; let res = 10; assert_eq!(Ok(res), get_memval(s)); let s = " 10Kb"; assert_eq!(Ok(res * KILOBYTE), get_memval(s)); let s = " 10mb"; assert_eq!(Ok(res * MEGABYTE), get_memval(s)); let s = " 10Gb"; assert_eq!(Ok(res * GIGABYTE), get_memval(s)); let s = " 30b"; assert_eq!(Ok(30), get_memval(s)); } #[test] fn test_memval_space_end() { let s = " 10 "; let res = 10; assert_eq!(Ok(res), get_memval(s)); let s = "10Kb "; assert_eq!(Ok(res * KILOBYTE), get_memval(s)); let s = "10mb "; assert_eq!(Ok(res * MEGABYTE), get_memval(s)); let s = " 10Gb "; assert_eq!(Ok(res * GIGABYTE), get_memval(s)); let s = " 30b "; assert_eq!(Ok(30), get_memval(s)); } #[test] fn test_memval_space_in_bw() { let s = " 10 "; let res = 10; assert_eq!(Ok(res), get_memval(s)); let s = "10 Kb "; assert_eq!(Ok(res * KILOBYTE), get_memval(s)); let s = "10 mb"; assert_eq!(Ok(res * MEGABYTE), get_memval(s)); let s = " 10 Gb "; assert_eq!(Ok(res * GIGABYTE), get_memval(s)); let s = "30 b"; assert_eq!(Ok(30), get_memval(s)); } #[test] fn test_memval_float_val() { let s = " 10.5 "; assert_eq!(Ok(10), get_memval(s)); let s = "10.8Kb "; assert_eq!(Ok((10.8 * KILOBYTE as f64) as u64), get_memval(s)); let s = "10.4 mb "; assert_eq!(Ok((10.4 * MEGABYTE as f64) as u64), get_memval(s)); let s = " 10.5Gb "; assert_eq!(Ok((10.5 * GIGABYTE as f64) as u64), get_memval(s)); let s = " 30.0 b "; assert_eq!(Ok(30), get_memval(s)); } #[test] fn test_memval_erroneous_val() { let s = "5eb"; assert!(get_memval(s).is_err()); let s = "5 1kb"; assert!(get_memval(s).is_err()); let s = "61k b"; assert!(get_memval(s).is_err()); let s = "8 8 k b"; assert!(get_memval(s).is_err()); } }