]> git.ipfire.org Git - thirdparty/suricata.git/commitdiff
detect/bytemath: convert parser to Rust
authorJeff Lucovsky <jlucovsky@oisf.net>
Wed, 8 Jun 2022 12:03:44 +0000 (08:03 -0400)
committerVictor Julien <vjulien@oisf.net>
Fri, 23 Sep 2022 13:51:23 +0000 (15:51 +0200)
Issue: 5077

This commit
- Converts the PCRE based parser to Rust.
- Adds unit tests to the new Rust modules
- Removes the PCRE parser from detect-bytemath.c
- Adjusts the C source modules to refer to the Rust definitions
- Includes the multiply operator (missing from the C parser)

rust/src/detect_parser/byte_math.rs [new file with mode: 0644]
rust/src/detect_parser/error.rs [new file with mode: 0644]
rust/src/detect_parser/mod.rs [new file with mode: 0644]
rust/src/detect_parser/parser.rs [new file with mode: 0644]
rust/src/lib.rs
src/detect-byte.c
src/detect-bytemath.c
src/detect-bytemath.h
src/detect-engine-content-inspection.c

diff --git a/rust/src/detect_parser/byte_math.rs b/rust/src/detect_parser/byte_math.rs
new file mode 100644 (file)
index 0000000..09e3820
--- /dev/null
@@ -0,0 +1,1149 @@
+/* Copyright (C) 2022 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.
+ */
+
+// Author: Jeff Lucovsky <jlucovsky@oisf.net>
+
+use crate::detect_parser::error::RuleParseError;
+use crate::detect_parser::parser::{parse_token, take_until_whitespace};
+use std::ffi::{CStr, CString};
+use std::os::raw::c_char;
+
+use nom7::bytes::complete::tag;
+use nom7::character::complete::multispace0;
+use nom7::sequence::preceded;
+use nom7::{Err, IResult};
+use std::str;
+
+pub const DETECT_BYTEMATH_FLAG_RELATIVE: u8 = 0x01;
+pub const DETECT_BYTEMATH_FLAG_STRING: u8 = 0x02;
+pub const DETECT_BYTEMATH_FLAG_BITMASK: u8 = 0x04;
+pub const DETECT_BYTEMATH_FLAG_ENDIAN: u8 = 0x08;
+pub const DETECT_BYTEMATH_FLAG_RVALUE_VAR: u8 = 0x10;
+
+// Ensure required values are provided
+const DETECT_BYTEMATH_FLAG_NBYTES: u8 = 0x1;
+const DETECT_BYTEMATH_FLAG_OFFSET: u8 = 0x2;
+const DETECT_BYTEMATH_FLAG_OPER: u8 = 0x4;
+const DETECT_BYTEMATH_FLAG_RVALUE: u8 = 0x8;
+const DETECT_BYTEMATH_FLAG_RESULT: u8 = 0x10;
+const DETECT_BYTEMATH_FLAG_REQUIRED: u8 = DETECT_BYTEMATH_FLAG_RESULT
+    | DETECT_BYTEMATH_FLAG_RVALUE
+    | DETECT_BYTEMATH_FLAG_NBYTES
+    | DETECT_BYTEMATH_FLAG_OFFSET
+    | DETECT_BYTEMATH_FLAG_OPER;
+
+#[repr(u8)]
+#[derive(Copy, Clone, Debug, PartialEq)]
+// operators: +, -, /, *, <<, >>
+pub enum ByteMathOperator {
+    OperatorNone = 1,
+    Addition = 2,
+    Subtraction = 3,
+    Division = 4,
+    Multiplication = 5,
+    LeftShift = 6,
+    RightShift = 7,
+}
+
+#[repr(u8)]
+#[derive(Copy, Clone, Debug, PartialEq)]
+// endian <big|little|dce>
+pub enum ByteMathEndian {
+    EndianNone = 0,
+    BigEndian = 1,
+    LittleEndian = 2,
+    EndianDCE = 3,
+}
+pub const DETECT_BYTEMATH_ENDIAN_DEFAULT: ByteMathEndian = ByteMathEndian::BigEndian;
+
+#[repr(u8)]
+#[derive(Copy, Clone, Debug, PartialEq)]
+pub enum ByteMathBase {
+    BaseNone = 0,
+    BaseOct = 8,
+    BaseDec = 10,
+    BaseHex = 16,
+}
+const BASE_DEFAULT: ByteMathBase = ByteMathBase::BaseDec;
+
+// Fixed position parameter count: bytes, offset, oper, rvalue, result
+// result is not parsed with the fixed position parameters as it's
+// often swapped with optional parameters
+pub const DETECT_BYTEMATH_FIXED_PARAM_COUNT: usize = 5;
+// Optional parameters: endian, relative, string, dce, bitmask
+pub const DETECT_BYTEMATH_MAX_PARAM_COUNT: usize = 10;
+
+#[derive(Debug)]
+enum ResultValue {
+    Numeric(u64),
+    String(String),
+}
+
+#[repr(C)]
+#[derive(Debug)]
+pub struct DetectByteMathData {
+    rvalue_str: *const c_char,
+    result: *const c_char,
+    rvalue: u32,
+    offset: i32,
+    bitmask_val: u32,
+    bitmask_shift_count: u16,
+    id: u16,
+    flags: u8,
+    local_id: u8,
+    nbytes: u8,
+    oper: ByteMathOperator,
+    endian: ByteMathEndian, // big, little, dce
+    base: ByteMathBase,     // From string or dce
+}
+
+impl Drop for DetectByteMathData {
+    fn drop(&mut self) {
+        unsafe {
+            if !self.result.is_null() {
+                let _ = CString::from_raw(self.result as *mut c_char);
+            }
+            if !self.rvalue_str.is_null() {
+                let _ = CString::from_raw(self.rvalue_str as *mut c_char);
+            }
+        }
+    }
+}
+
+impl Default for DetectByteMathData {
+    fn default() -> Self {
+        DetectByteMathData {
+            local_id: 0,
+            flags: 0,
+            nbytes: 0,
+            offset: 0,
+            oper: ByteMathOperator::OperatorNone,
+            rvalue_str: std::ptr::null_mut(),
+            rvalue: 0,
+            result: std::ptr::null_mut(),
+            endian: DETECT_BYTEMATH_ENDIAN_DEFAULT,
+            base: BASE_DEFAULT,
+            bitmask_val: 0,
+            bitmask_shift_count: 0,
+            id: 0,
+        }
+    }
+}
+
+impl DetectByteMathData {
+    pub fn new() -> Self {
+        Self {
+            ..Default::default()
+        }
+    }
+}
+
+fn get_string_value(value: &str) -> Result<ByteMathBase, ()> {
+    let res = match value {
+        "hex" => ByteMathBase::BaseHex,
+        "oct" => ByteMathBase::BaseOct,
+        "dec" => ByteMathBase::BaseDec,
+        _ => return Err(()),
+    };
+
+    Ok(res)
+}
+
+fn get_oper_value(value: &str) -> Result<ByteMathOperator, ()> {
+    let res = match value {
+        "+" => ByteMathOperator::Addition,
+        "-" => ByteMathOperator::Subtraction,
+        "/" => ByteMathOperator::Division,
+        "*" => ByteMathOperator::Multiplication,
+        "<<" => ByteMathOperator::LeftShift,
+        ">>" => ByteMathOperator::RightShift,
+        _ => return Err(()),
+    };
+
+    Ok(res)
+}
+
+fn get_endian_value(value: &str) -> Result<ByteMathEndian, ()> {
+    let res = match value {
+        "big" => ByteMathEndian::BigEndian,
+        "little" => ByteMathEndian::LittleEndian,
+        "dce" => ByteMathEndian::EndianDCE,
+        _ => return Err(()),
+    };
+
+    Ok(res)
+}
+
+// Parsed as a u64 for validation with u32 {min,max} so values greater than uint32
+// are not treated as a string value.
+fn parse_rvalue(input: &str) -> IResult<&str, ResultValue, RuleParseError<&str>> {
+    let (input, rvalue) = parse_token(input)?;
+    if let Ok(val) = rvalue.parse::<u64>() {
+        Ok((input, ResultValue::Numeric(val)))
+    } else {
+        Ok((input, ResultValue::String(rvalue.to_string())))
+    }
+}
+
+fn parse_bytemath(input: &str) -> IResult<&str, DetectByteMathData, RuleParseError<&str>> {
+    // Inner utility function for easy error creation.
+    fn make_error(reason: String) -> nom7::Err<RuleParseError<&'static str>> {
+        Err::Error(RuleParseError::InvalidByteMath(reason))
+    }
+    let (_, values) = nom7::multi::separated_list1(
+        tag(","),
+        preceded(multispace0, nom7::bytes::complete::is_not(",")),
+    )(input)?;
+
+    if values.len() < DETECT_BYTEMATH_FIXED_PARAM_COUNT
+        || values.len() > DETECT_BYTEMATH_MAX_PARAM_COUNT
+    {
+        return Err(make_error(format!("Incorrect argument string; at least {} values must be specified but no more than {}: {:?}",
+            DETECT_BYTEMATH_FIXED_PARAM_COUNT, DETECT_BYTEMATH_MAX_PARAM_COUNT, input)));
+    }
+
+    let mut required_flags: u8 = 0;
+    let mut byte_math = DetectByteMathData::new();
+    //for value in &values[0..] {
+    for value in values {
+        let (mut val, mut name) = take_until_whitespace(value)?;
+        val = val.trim();
+        name = name.trim();
+        match name {
+            "oper" => {
+                if 0 != (required_flags & DETECT_BYTEMATH_FLAG_OPER) {
+                    return Err(make_error("operator already set".to_string()));
+                }
+                byte_math.oper = match get_oper_value(val) {
+                    Ok(val) => val,
+                    Err(_) => {
+                        return Err(make_error(format!("unknown oper value {}", val)));
+                    }
+                };
+                required_flags |= DETECT_BYTEMATH_FLAG_OPER;
+            }
+            "result" => {
+                if 0 != (required_flags & DETECT_BYTEMATH_FLAG_RESULT) {
+                    return Err(make_error("result already set".to_string()));
+                }
+                let tmp: String = val
+                    .parse()
+                    .map_err(|_| make_error(format!("invalid result: {}", val)))?;
+                match CString::new(tmp) {
+                    Ok(strval) => {
+                        byte_math.result = strval.into_raw();
+                        required_flags |= DETECT_BYTEMATH_FLAG_RESULT;
+                    }
+                    _ => {
+                        return Err(make_error(
+                            "parse string not safely convertible to C".to_string(),
+                        ));
+                    }
+                }
+            }
+            "rvalue" => {
+                if 0 != (required_flags & DETECT_BYTEMATH_FLAG_RVALUE) {
+                    return Err(make_error("rvalue already set".to_string()));
+                }
+                let (_, res) = parse_rvalue(val)?;
+                match res {
+                    ResultValue::Numeric(val) => {
+                        if val >= u32::MIN.into() && val <= u32::MAX.into() {
+                            byte_math.rvalue = val as u32
+                        } else {
+                            return Err(make_error(format!(
+                                "invalid rvalue value: must be between {} and {}: {}",
+                                1,
+                                u32::MAX,
+                                val
+                            )));
+                        }
+                    }
+                    ResultValue::String(val) => match CString::new(val) {
+                        Ok(newval) => {
+                            byte_math.rvalue_str = newval.into_raw();
+                            byte_math.flags |= DETECT_BYTEMATH_FLAG_RVALUE_VAR;
+                        }
+                        _ => {
+                            return Err(make_error(
+                                "parse string not safely convertible to C".to_string(),
+                            ))
+                        }
+                    },
+                }
+                required_flags |= DETECT_BYTEMATH_FLAG_RVALUE;
+            }
+            "endian" => {
+                if 0 != (byte_math.flags & DETECT_BYTEMATH_FLAG_ENDIAN) {
+                    return Err(make_error("endianess already set".to_string()));
+                }
+                byte_math.endian = match get_endian_value(val) {
+                    Ok(val) => val,
+                    Err(_) => {
+                        return Err(make_error(format!("invalid endian value: {}", val)));
+                    }
+                };
+                byte_math.flags |= DETECT_BYTEMATH_FLAG_ENDIAN;
+            }
+            "dce" => {
+                if 0 != (byte_math.flags & DETECT_BYTEMATH_FLAG_ENDIAN) {
+                    return Err(make_error("endianess already set".to_string()));
+                }
+                byte_math.flags |= DETECT_BYTEMATH_FLAG_ENDIAN;
+                byte_math.endian = ByteMathEndian::EndianDCE;
+            }
+            "string" => {
+                if 0 != (byte_math.flags & DETECT_BYTEMATH_FLAG_STRING) {
+                    return Err(make_error("string already set".to_string()));
+                }
+                byte_math.base = match get_string_value(val) {
+                    Ok(val) => val,
+                    Err(_) => {
+                        return Err(make_error(format!("invalid string value: {}", val)));
+                    }
+                };
+                byte_math.flags |= DETECT_BYTEMATH_FLAG_STRING;
+            }
+            "relative" => {
+                if 0 != (byte_math.flags & DETECT_BYTEMATH_FLAG_RELATIVE) {
+                    return Err(make_error("relative already set".to_string()));
+                }
+                byte_math.flags |= DETECT_BYTEMATH_FLAG_RELATIVE;
+            }
+            "bitmask" => {
+                if 0 != (byte_math.flags & DETECT_BYTEMATH_FLAG_BITMASK) {
+                    return Err(make_error("bitmask already set".to_string()));
+                }
+                let trimmed = if val.starts_with("0x") || val.starts_with("0X") {
+                    &val[2..]
+                } else {
+                    val
+                };
+
+                let val = u32::from_str_radix(trimmed, 16)
+                    .map_err(|_| make_error(format!("invalid bitmask value: {}", value)))?;
+                byte_math.bitmask_val = val;
+                byte_math.flags |= DETECT_BYTEMATH_FLAG_BITMASK;
+            }
+            "offset" => {
+                if 0 != (required_flags & DETECT_BYTEMATH_FLAG_OFFSET) {
+                    return Err(make_error("offset already set".to_string()));
+                }
+                byte_math.offset = val
+                    .parse::<i32>()
+                    .map_err(|_| make_error(format!("invalid offset value: {}", val)))?;
+                if byte_math.offset > 65535 || byte_math.offset < -65535 {
+                    return Err(make_error(format!(
+                        "invalid offset value: must be between -65535 and 65535: {}",
+                        val
+                    )));
+                }
+                required_flags |= DETECT_BYTEMATH_FLAG_OFFSET;
+            }
+            "bytes" => {
+                if 0 != (required_flags & DETECT_BYTEMATH_FLAG_NBYTES) {
+                    return Err(make_error("nbytes already set".to_string()));
+                }
+                byte_math.nbytes = val
+                    .parse()
+                    .map_err(|_| make_error(format!("invalid bytes value: {}", val)))?;
+                if byte_math.nbytes < 1 || byte_math.nbytes > 10 {
+                    return Err(make_error(format!(
+                        "invalid bytes value: must be between 1 and 10: {}",
+                        byte_math.nbytes
+                    )));
+                }
+                required_flags |= DETECT_BYTEMATH_FLAG_NBYTES;
+            }
+            _ => {
+                return Err(make_error(format!("unknown byte_math keyword: {}", name)));
+            }
+        };
+    }
+
+    // Ensure required values are present
+    if (required_flags & DETECT_BYTEMATH_FLAG_REQUIRED) != DETECT_BYTEMATH_FLAG_REQUIRED {
+        return Err(make_error(format!(
+            "required byte_math parameters missing: \"{:?}\"",
+            input
+        )));
+    }
+
+    // Using left/right shift further restricts the value of nbytes. Note that
+    // validation has already ensured nbytes is in [1..10]
+    match byte_math.oper {
+        ByteMathOperator::LeftShift | ByteMathOperator::RightShift => {
+            if byte_math.nbytes > 4 {
+                return Err(make_error(format!("nbytes must be 1 through 4 (inclusive) when used with \"<<\" or \">>\"; {} is not valid", byte_math.nbytes)));
+            }
+        }
+        _ => {}
+    };
+
+    Ok((input, byte_math))
+}
+
+/// Intermediary function between the C code and the parsing functions.
+#[no_mangle]
+pub unsafe extern "C" fn ScByteMathParse(c_arg: *const c_char) -> *mut DetectByteMathData {
+    if c_arg.is_null() {
+        return std::ptr::null_mut();
+    }
+
+    let arg = match CStr::from_ptr(c_arg).to_str() {
+        Ok(arg) => arg,
+        Err(_) => {
+            return std::ptr::null_mut();
+        }
+    };
+    match parse_bytemath(arg) {
+        Ok((_, detect)) => return Box::into_raw(Box::new(detect)) as *mut DetectByteMathData,
+        Err(_) => return std::ptr::null_mut(),
+    }
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn ScByteMathFree(ptr: *mut DetectByteMathData) {
+    if !ptr.is_null() {
+        let _ = Box::from_raw(ptr as *mut DetectByteMathData);
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    // structure equality only used by test cases
+    impl PartialEq for DetectByteMathData {
+        fn eq(&self, other: &Self) -> bool {
+            let mut res: bool = false;
+
+            if !self.rvalue_str.is_null() && !other.rvalue_str.is_null() {
+                let s_val = unsafe { CStr::from_ptr(self.rvalue_str) };
+                let o_val = unsafe { CStr::from_ptr(other.rvalue_str) };
+                res = s_val == o_val;
+            } else if !self.rvalue_str.is_null() || !other.rvalue_str.is_null() {
+                return false;
+            }
+
+            if !self.result.is_null() && !self.result.is_null() {
+                let s_val = unsafe { CStr::from_ptr(self.result) };
+                let o_val = unsafe { CStr::from_ptr(other.result) };
+                res = s_val == o_val;
+            } else if !self.result.is_null() || !self.result.is_null() {
+                return false;
+            }
+
+            res && self.local_id == other.local_id
+                && self.flags == other.flags
+                && self.nbytes == other.nbytes
+                && self.offset == other.offset
+                && self.oper == other.oper
+                && self.rvalue == other.rvalue
+                && self.endian == other.endian
+                && self.base == other.base
+                && self.bitmask_val == other.bitmask_val
+                && self.bitmask_shift_count == other.bitmask_shift_count
+                && self.id == other.id
+        }
+    }
+
+    fn valid_test(
+        args: &str, nbytes: u8, offset: i32, oper: ByteMathOperator, rvalue_str: &str, rvalue: u32,
+        result: &str, base: ByteMathBase, endian: ByteMathEndian, bitmask_val: u32, flags: u8,
+    ) {
+        let bmd = DetectByteMathData {
+            nbytes: nbytes,
+            offset: offset,
+            oper: oper,
+            rvalue_str: if rvalue_str != "" {
+                CString::new(rvalue_str).unwrap().into_raw()
+            } else {
+                std::ptr::null_mut()
+            },
+            rvalue: rvalue,
+            result: CString::new(result).unwrap().into_raw(),
+            base: base,
+            endian: endian,
+            bitmask_val: bitmask_val,
+            flags: flags,
+            ..Default::default()
+        };
+
+        match parse_bytemath(args) {
+            Ok((_, val)) => {
+                assert_eq!(val, bmd);
+            }
+            Err(_) => {
+                assert!(false);
+            }
+        }
+    }
+
+    #[test]
+    fn test_parser_valid() {
+        valid_test(
+            "bytes 4, offset 3933, oper +, rvalue myrvalue, result myresult, dce, string dec",
+            4,
+            3933,
+            ByteMathOperator::Addition,
+            "myrvalue",
+            0,
+            "myresult",
+            ByteMathBase::BaseDec,
+            ByteMathEndian::EndianDCE,
+            0,
+            DETECT_BYTEMATH_FLAG_RVALUE_VAR
+                | DETECT_BYTEMATH_FLAG_STRING
+                | DETECT_BYTEMATH_FLAG_ENDIAN,
+        );
+
+        valid_test(
+            "bytes 4, offset 3933, oper +, rvalue 99, result other, dce, string dec",
+            4,
+            3933,
+            ByteMathOperator::Addition,
+            "",
+            99,
+            "other",
+            ByteMathBase::BaseDec,
+            ByteMathEndian::EndianDCE,
+            0,
+            DETECT_BYTEMATH_FLAG_STRING | DETECT_BYTEMATH_FLAG_ENDIAN,
+        );
+
+        valid_test(
+            "bytes 4, offset -3933, oper +, rvalue myrvalue, result foo",
+            4,
+            -3933,
+            ByteMathOperator::Addition,
+            "rvalue",
+            0,
+            "foo",
+            BASE_DEFAULT,
+            ByteMathEndian::BigEndian,
+            0,
+            DETECT_BYTEMATH_FLAG_RVALUE_VAR,
+        );
+
+        // Out of order
+        valid_test(
+            "string dec, endian big, result other, rvalue 99, oper +, offset 3933, bytes 4",
+            4,
+            3933,
+            ByteMathOperator::Addition,
+            "",
+            99,
+            "other",
+            ByteMathBase::BaseDec,
+            ByteMathEndian::BigEndian,
+            0,
+            DETECT_BYTEMATH_FLAG_STRING | DETECT_BYTEMATH_FLAG_ENDIAN,
+        );
+    }
+
+    #[test]
+    fn test_parser_string_valid() {
+        let mut bmd = DetectByteMathData {
+            nbytes: 4,
+            offset: 3933,
+            oper: ByteMathOperator::Addition,
+            rvalue_str: CString::new("myrvalue").unwrap().into_raw(),
+            rvalue: 0,
+            result: CString::new("foo").unwrap().into_raw(),
+            endian: DETECT_BYTEMATH_ENDIAN_DEFAULT,
+            base: ByteMathBase::BaseDec,
+            flags: DETECT_BYTEMATH_FLAG_RVALUE_VAR | DETECT_BYTEMATH_FLAG_STRING,
+            ..Default::default()
+        };
+
+        match parse_bytemath(
+            "bytes 4, offset 3933, oper +, rvalue myrvalue, result foo, string dec",
+        ) {
+            Ok((_, val)) => {
+                assert_eq!(val, bmd);
+            }
+            Err(_) => {
+                assert!(false);
+            }
+        }
+
+        bmd.flags = DETECT_BYTEMATH_FLAG_RVALUE_VAR;
+        bmd.base = BASE_DEFAULT;
+        match parse_bytemath("bytes 4, offset 3933, oper +, rvalue myrvalue, result foo") {
+            Ok((_, val)) => {
+                assert_eq!(val, bmd);
+            }
+            Err(_) => {
+                assert!(false);
+            }
+        }
+
+        bmd.flags = DETECT_BYTEMATH_FLAG_RVALUE_VAR | DETECT_BYTEMATH_FLAG_STRING;
+        bmd.base = ByteMathBase::BaseHex;
+        match parse_bytemath(
+            "bytes 4, offset 3933, oper +, rvalue myrvalue, result foo, string hex",
+        ) {
+            Ok((_, val)) => {
+                assert_eq!(val, bmd);
+            }
+            Err(_) => {
+                assert!(false);
+            }
+        }
+
+        bmd.base = ByteMathBase::BaseOct;
+        match parse_bytemath(
+            "bytes 4, offset 3933, oper +, rvalue myrvalue, result foo, string oct",
+        ) {
+            Ok((_, val)) => {
+                assert_eq!(val, bmd);
+            }
+            Err(_) => {
+                assert!(false);
+            }
+        }
+    }
+
+    #[test]
+    fn test_parser_string_invalid() {
+        assert_eq!(
+            true,
+            parse_bytemath(
+                "bytes 4, offset 3933, oper +, rvalue myrvalue, result foo, string decimal"
+            )
+            .is_err()
+        );
+        assert_eq!(
+            true,
+            parse_bytemath(
+                "bytes 4, offset 3933, oper +, rvalue myrvalue, result foo, string hexadecimal"
+            )
+            .is_err()
+        );
+        assert_eq!(
+            true,
+            parse_bytemath(
+                "bytes 4, offset 3933, oper +, rvalue myrvalue, result foo, string octal"
+            )
+            .is_err()
+        );
+    }
+
+    #[test]
+    // bytes must be between 1 and 10; when combined with rshift/lshift, must be 4 or less
+    fn test_parser_bytes_invalid() {
+        assert_eq!(
+            true,
+            parse_bytemath("bytes 0, offset 3933, oper +, rvalue myrvalue, result foo").is_err()
+        );
+        assert_eq!(
+            true,
+            parse_bytemath("bytes 11, offset 3933, oper +, rvalue myrvalue, result foo").is_err()
+        );
+        assert_eq!(
+            true,
+            parse_bytemath("bytes 5, offset 3933, oper >>, rvalue myrvalue, result foo").is_err()
+        );
+        assert_eq!(
+            true,
+            parse_bytemath("bytes 5, offset 3933, oper <<, rvalue myrvalue, result foo").is_err()
+        );
+    }
+
+    #[test]
+    fn test_parser_bitmask_invalid() {
+        assert_eq!(
+            true,
+            parse_bytemath("bytes 4, offset 3933, oper +, rvalue myrvalue, result foo, bitmask 0x")
+                .is_err()
+        );
+        assert_eq!(
+            true,
+            parse_bytemath(
+                "bytes 4, offset 3933, oper +, rvalue myrvalue, result foo, bitmask x12345678"
+            )
+            .is_err()
+        );
+        assert_eq!(
+            true,
+            parse_bytemath(
+                "bytes 4, offset 3933, oper +, rvalue myrvalue, result foo, bitmask X12345678"
+            )
+            .is_err()
+        );
+        assert_eq!(
+            true,
+            parse_bytemath(
+                "bytes 4, offset 3933, oper +, rvalue myrvalue, result foo, bitmask 0x123456789012"
+            )
+            .is_err()
+        );
+        assert_eq!(
+            true,
+            parse_bytemath("bytes 4, offset 3933, oper +, rvalue myrvalue, result foo, bitmask 0q")
+                .is_err()
+        );
+        assert_eq!(
+            true,
+            parse_bytemath(
+                "bytes 4, offset 3933, oper +, rvalue myrvalue, result foo, bitmask maple"
+            )
+            .is_err()
+        );
+        assert_eq!(
+            true,
+            parse_bytemath(
+                "bytes 4, offset 3933, oper +, rvalue myrvalue, result foo, bitmask 0xGHIJKLMN"
+            )
+            .is_err()
+        );
+        assert_eq!(
+            true,
+            parse_bytemath(
+                "bytes 4, offset 3933, oper +, rvalue myrvalue, result foo, bitmask #*#*@-"
+            )
+            .is_err()
+        );
+    }
+
+    #[test]
+    fn test_parser_bitmask_valid() {
+        let mut bmd = DetectByteMathData {
+            nbytes: 4,
+            offset: 3933,
+            oper: ByteMathOperator::Addition,
+            rvalue_str: CString::new("myrvalue").unwrap().into_raw(),
+            rvalue: 0,
+            result: CString::new("foo").unwrap().into_raw(),
+            endian: ByteMathEndian::BigEndian,
+            base: BASE_DEFAULT,
+            flags: DETECT_BYTEMATH_FLAG_RVALUE_VAR | DETECT_BYTEMATH_FLAG_BITMASK,
+            ..Default::default()
+        };
+
+        bmd.bitmask_val = 0x12345678;
+        match parse_bytemath(
+            "bytes 4, offset 3933, oper +, rvalue myrvalue, result foo, bitmask 0x12345678",
+        ) {
+            Ok((_, val)) => {
+                assert_eq!(val, bmd);
+            }
+            Err(_) => {
+                assert!(false);
+            }
+        }
+
+        bmd.bitmask_val = 0xffff1234;
+        match parse_bytemath(
+            "bytes 4, offset 3933, oper +, rvalue myrvalue, result foo, bitmask ffff1234",
+        ) {
+            Ok((_, val)) => {
+                assert_eq!(val, bmd);
+            }
+            Err(_) => {
+                assert!(false);
+            }
+        }
+
+        bmd.bitmask_val = 0xffff1234;
+        match parse_bytemath(
+            "bytes 4, offset 3933, oper +, rvalue myrvalue, result foo, bitmask 0Xffff1234",
+        ) {
+            Ok((_, val)) => {
+                assert_eq!(val, bmd);
+            }
+            Err(_) => {
+                assert!(false);
+            }
+        }
+    }
+    #[test]
+    fn test_parser_endian_valid() {
+        let mut bmd = DetectByteMathData {
+            nbytes: 4,
+            offset: 3933,
+            oper: ByteMathOperator::Addition,
+            rvalue_str: CString::new("myrvalue").unwrap().into_raw(),
+            rvalue: 0,
+            result: CString::new("foo").unwrap().into_raw(),
+            endian: ByteMathEndian::BigEndian,
+            base: BASE_DEFAULT,
+            flags: DETECT_BYTEMATH_FLAG_RVALUE_VAR | DETECT_BYTEMATH_FLAG_ENDIAN,
+            ..Default::default()
+        };
+
+        match parse_bytemath(
+            "bytes 4, offset 3933, oper +, rvalue myrvalue, result foo, endian big",
+        ) {
+            Ok((_, val)) => {
+                assert_eq!(val, bmd);
+            }
+            Err(_) => {
+                assert!(false);
+            }
+        }
+
+        bmd.endian = ByteMathEndian::LittleEndian;
+        match parse_bytemath(
+            "bytes 4, offset 3933, oper +, rvalue myrvalue, result foo, endian little",
+        ) {
+            Ok((_, val)) => {
+                assert_eq!(val, bmd);
+            }
+            Err(_) => {
+                assert!(false);
+            }
+        }
+
+        bmd.endian = ByteMathEndian::EndianDCE;
+        match parse_bytemath("bytes 4, offset 3933, oper +, rvalue myrvalue, result foo, dce") {
+            Ok((_, val)) => {
+                assert_eq!(val, bmd);
+            }
+            Err(_) => {
+                assert!(false);
+            }
+        }
+
+        bmd.endian = DETECT_BYTEMATH_ENDIAN_DEFAULT;
+        bmd.flags = DETECT_BYTEMATH_FLAG_RVALUE_VAR;
+        match parse_bytemath("bytes 4, offset 3933, oper +, rvalue myrvalue, result foo") {
+            Ok((_, val)) => {
+                assert_eq!(val, bmd);
+            }
+            Err(_) => {
+                assert!(false);
+            }
+        }
+    }
+
+    #[test]
+    fn test_parser_endian_invalid() {
+        assert_eq!(
+            true,
+            parse_bytemath(
+                "bytes 4, offset 3933, oper +, rvalue myrvalue, result foo, endian bigger"
+            )
+            .is_err()
+        );
+        assert_eq!(
+            true,
+            parse_bytemath(
+                "bytes 4, offset 3933, oper +, rvalue myrvalue, result foo, endian smaller"
+            )
+            .is_err()
+        );
+
+        // endianess can only be specified once
+        assert_eq!(
+            true,
+            parse_bytemath(
+                "bytes 4, offset 3933, oper +, rvalue myrvalue, result foo, endian big, dce"
+            )
+            .is_err()
+        );
+        assert_eq!(
+            true,
+            parse_bytemath(
+                "bytes 4, offset 3933, oper +, rvalue myrvalue, result foo, endian small, endian big"
+            )
+            .is_err()
+        );
+        assert_eq!(
+            true,
+            parse_bytemath(
+                "bytes 4, offset 3933, oper +, rvalue myrvalue, result foo, endian small, dce"
+            )
+            .is_err()
+        );
+    }
+
+    #[test]
+    fn test_parser_oper_valid() {
+        let mut bmd = DetectByteMathData {
+            nbytes: 4,
+            offset: 3933,
+            oper: ByteMathOperator::Addition,
+            rvalue_str: CString::new("myrvalue").unwrap().into_raw(),
+            rvalue: 0,
+            result: CString::new("foo").unwrap().into_raw(),
+            endian: ByteMathEndian::BigEndian,
+            base: BASE_DEFAULT,
+            flags: DETECT_BYTEMATH_FLAG_RVALUE_VAR,
+            ..Default::default()
+        };
+
+        match parse_bytemath("bytes 4, offset 3933, oper +, rvalue myrvalue, result foo") {
+            Ok((_, val)) => {
+                assert_eq!(val, bmd);
+            }
+            Err(_) => {
+                assert!(false);
+            }
+        }
+
+        bmd.oper = ByteMathOperator::Subtraction;
+        match parse_bytemath("bytes 4, offset 3933, oper -, rvalue myrvalue, result foo") {
+            Ok((_, val)) => {
+                assert_eq!(val, bmd);
+            }
+            Err(_) => {
+                assert!(false);
+            }
+        }
+
+        bmd.oper = ByteMathOperator::Multiplication;
+        match parse_bytemath("bytes 4, offset 3933, oper *, rvalue myrvalue, result foo") {
+            Ok((_, val)) => {
+                assert_eq!(val, bmd);
+            }
+            Err(_) => {
+                assert!(false);
+            }
+        }
+        bmd.oper = ByteMathOperator::Division;
+        match parse_bytemath("bytes 4, offset 3933, oper /, rvalue myrvalue, result foo") {
+            Ok((_, val)) => {
+                assert_eq!(val, bmd);
+            }
+            Err(_) => {
+                assert!(false);
+            }
+        }
+        bmd.oper = ByteMathOperator::RightShift;
+        match parse_bytemath("bytes 4, offset 3933, oper >>, rvalue myrvalue, result foo") {
+            Ok((_, val)) => {
+                assert_eq!(val, bmd);
+            }
+            Err(_) => {
+                assert!(false);
+            }
+        }
+        bmd.oper = ByteMathOperator::LeftShift;
+        match parse_bytemath("bytes 4, offset 3933, oper <<, rvalue myrvalue, result foo") {
+            Ok((_, val)) => {
+                assert_eq!(val, bmd);
+            }
+            Err(_) => {
+                assert!(false);
+            }
+        }
+    }
+
+    #[test]
+    fn test_parser_oper_invalid() {
+        assert_eq!(
+            true,
+            parse_bytemath("bytes 4, offset 0, oper !, rvalue myvalue, result foo").is_err()
+        );
+        assert_eq!(
+            true,
+            parse_bytemath("bytes 4, offset 0, oper ^, rvalue myvalue, result foo").is_err()
+        );
+        assert_eq!(
+            true,
+            parse_bytemath("bytes 4, offset 0, oper <>, rvalue myvalue, result foo").is_err()
+        );
+        assert_eq!(
+            true,
+            parse_bytemath("bytes 4, offset 0, oper ><, rvalue myvalue, result foo").is_err()
+        );
+        assert_eq!(
+            true,
+            parse_bytemath("bytes 4, offset 0, oper <, rvalue myvalue, result foo").is_err()
+        );
+        assert_eq!(
+            true,
+            parse_bytemath("bytes 4, offset 0, oper >, rvalue myvalue, result foo").is_err()
+        );
+    }
+
+    #[test]
+    fn test_parser_rvalue_valid() {
+        let mut bmd = DetectByteMathData {
+            nbytes: 4,
+            offset: 47303,
+            oper: ByteMathOperator::Multiplication,
+            rvalue_str: std::ptr::null_mut(),
+            rvalue: 4294967295,
+            result: CString::new("foo").unwrap().into_raw(),
+            endian: DETECT_BYTEMATH_ENDIAN_DEFAULT,
+            base: BASE_DEFAULT,
+            ..Default::default()
+        };
+
+        match parse_bytemath("bytes 4, offset 47303, oper *, rvalue 4294967295      , result foo") {
+            Ok((_, val)) => {
+                assert_eq!(val, bmd);
+            }
+            Err(_) => {
+                assert!(false);
+            }
+        }
+
+        bmd.rvalue = 1;
+        match parse_bytemath("bytes 4, offset 47303, oper *, rvalue 1, result foo") {
+            Ok((_, val)) => {
+                assert_eq!(val, bmd);
+            }
+            Err(_) => {
+                assert!(false);
+            }
+        }
+        bmd.rvalue = 0;
+        match parse_bytemath("bytes 4, offset 47303, oper *, rvalue 0, result foo") {
+            Ok((_, val)) => {
+                assert_eq!(val, bmd);
+            }
+            Err(_) => {
+                assert!(false);
+            }
+        }
+    }
+
+    #[test]
+    fn test_parser_rvalue_invalid() {
+        assert_eq!(
+            true,
+            parse_bytemath("bytes 4, offset 47303, oper *, rvalue 4294967296, result foo").is_err()
+        );
+    }
+
+    #[test]
+    fn test_parser_offset_valid() {
+        let mut bmd = DetectByteMathData {
+            nbytes: 4,
+            offset: -65535,
+            oper: ByteMathOperator::Multiplication,
+            rvalue_str: CString::new("myrvalue").unwrap().into_raw(),
+            rvalue: 0,
+            result: CString::new("foo").unwrap().into_raw(),
+            endian: DETECT_BYTEMATH_ENDIAN_DEFAULT,
+            base: BASE_DEFAULT,
+            flags: DETECT_BYTEMATH_FLAG_RVALUE_VAR,
+            ..Default::default()
+        };
+
+        match parse_bytemath("bytes 4, offset -65535, oper *, rvalue myrvalue, result foo") {
+            Ok((_, val)) => {
+                assert_eq!(val, bmd);
+            }
+            Err(_) => {
+                assert!(false);
+            }
+        }
+
+        bmd.offset = 65535;
+        match parse_bytemath("bytes 4, offset 65535, oper *, rvalue myrvalue, result foo") {
+            Ok((_, val)) => {
+                assert_eq!(val, bmd);
+            }
+            Err(_) => {
+                assert!(false);
+            }
+        }
+    }
+
+    #[test]
+    // offset: numeric values must be between -65535 and 65535
+    fn test_parser_offset_invalid() {
+        assert_eq!(
+            true,
+            parse_bytemath("bytes 4, offset -70000, oper *, rvalue myvalue, result foo").is_err()
+        );
+        assert_eq!(
+            true,
+            parse_bytemath("bytes 4, offset 70000, oper +, rvalue myvalue, result foo").is_err()
+        );
+    }
+
+    #[test]
+    fn test_parser_incomplete_args() {
+        assert_eq!(true, parse_bytemath("").is_err());
+        assert_eq!(true, parse_bytemath("bytes 4").is_err());
+        assert_eq!(true, parse_bytemath("bytes 4, offset 0").is_err());
+        assert_eq!(true, parse_bytemath("bytes 4, offset 0, oper <<").is_err());
+    }
+
+    #[test]
+    fn test_parser_missing_required() {
+        assert_eq!(
+            true,
+            parse_bytemath("endian big, offset 3933, oper +, rvalue myrvalue, result foo").is_err()
+        );
+        assert_eq!(
+            true,
+            parse_bytemath("bytes 4, endian big, oper +, rvalue myrvalue, result foo,").is_err()
+        );
+        assert_eq!(
+            true,
+            parse_bytemath("bytes 4, offset 3933, endian big, rvalue myrvalue, result foo")
+                .is_err()
+        );
+        assert_eq!(
+            true,
+            parse_bytemath("bytes 4, offset 3933, oper +, endian big, result foo").is_err()
+        );
+        assert_eq!(
+            true,
+            parse_bytemath("bytes 4, offset 3933, oper +, rvalue myrvalue, endian big").is_err()
+        );
+    }
+
+    #[test]
+    fn test_parser_invalid_args() {
+        assert_eq!(true, parse_bytemath("monkey banana").is_err());
+        assert_eq!(true, parse_bytemath("bytes nan").is_err());
+        assert_eq!(true, parse_bytemath("bytes 4, offset nan").is_err());
+        assert_eq!(true, parse_bytemath("bytes 4, offset 0, three 3, four 4, five 5, six 6, seven 7, eight 8, nine 9, ten 10, eleven 11").is_err());
+        assert_eq!(
+            true,
+            parse_bytemath("bytes 4, offset 0, oper ><, rvalue myrvalue").is_err()
+        );
+        assert_eq!(
+            true,
+            parse_bytemath("bytes 4, offset 0, oper +, rvalue myrvalue, endian endian").is_err()
+        );
+    }
+    #[test]
+    fn test_parser_multiple() {
+        assert_eq!(
+            true,
+            parse_bytemath(
+                "bytes 4, bytes 4, offset 0, oper +, rvalue myrvalue, result myresult, endian big"
+            )
+            .is_err()
+        );
+        assert_eq!(
+            true,
+            parse_bytemath(
+                "bytes 4, offset 0, offset 0, oper +, rvalue myrvalue, result myresult, endian big"
+            )
+            .is_err()
+        );
+        assert_eq!(
+            true,
+            parse_bytemath(
+                "bytes 4, offset 0, oper +, oper +, rvalue myrvalue, result myresult, endian big"
+            )
+            .is_err()
+        );
+        assert_eq!(true, parse_bytemath("bytes 4, offset 0, oper +, rvalue myrvalue, rvalue myrvalue, result myresult, endian big").is_err());
+        assert_eq!(true, parse_bytemath("bytes 4, offset 0, oper +, rvalue myrvalue, result myresult, result myresult, endian big").is_err());
+        assert_eq!(true, parse_bytemath("bytes 4, offset 0, oper +, rvalue myrvalue, result myresult, endian big, endian big").is_err());
+    }
+}
diff --git a/rust/src/detect_parser/error.rs b/rust/src/detect_parser/error.rs
new file mode 100644 (file)
index 0000000..d0738c6
--- /dev/null
@@ -0,0 +1,37 @@
+/* Copyright (C) 2022 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.
+ */
+
+use nom7::error::{ErrorKind, ParseError};
+
+/// Custom rule parse errors.
+///
+/// Implemented based on the Nom example for implementing custom errors.
+#[derive(Debug, PartialEq)]
+pub enum RuleParseError<I> {
+    InvalidByteMath(String),
+
+    Nom(I, ErrorKind),
+}
+impl<I> ParseError<I> for RuleParseError<I> {
+    fn from_error_kind(input: I, kind: ErrorKind) -> Self {
+        RuleParseError::Nom(input, kind)
+    }
+
+    fn append(_: I, _: ErrorKind, other: Self) -> Self {
+        other
+    }
+}
diff --git a/rust/src/detect_parser/mod.rs b/rust/src/detect_parser/mod.rs
new file mode 100644 (file)
index 0000000..308e01d
--- /dev/null
@@ -0,0 +1,20 @@
+/* Copyright (C) 2022 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.
+ */
+
+pub mod byte_math;
+pub mod error;
+pub mod parser;
diff --git a/rust/src/detect_parser/parser.rs b/rust/src/detect_parser/parser.rs
new file mode 100644 (file)
index 0000000..eccf8fa
--- /dev/null
@@ -0,0 +1,38 @@
+/* Copyright (C) 2022 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.
+ */
+
+use crate::detect_parser::error::RuleParseError;
+
+use nom7::bytes::complete::is_not;
+use nom7::character::complete::multispace0;
+use nom7::sequence::preceded;
+use nom7::IResult;
+
+static WHITESPACE: &str = " \t\r\n";
+/// Parse all characters up until the next whitespace character.
+pub fn take_until_whitespace(input: &str) -> IResult<&str, &str, RuleParseError<&str>> {
+    nom7::bytes::complete::is_not(WHITESPACE)(input)
+}
+
+/// Parse the next token ignoring leading whitespace.
+///
+/// A token is the next sequence of chars until a terminating character. Leading whitespace
+/// is ignore.
+pub fn parse_token(input: &str) -> IResult<&str, &str, RuleParseError<&str>> {
+    let terminators = "\n\r\t,;: ";
+    preceded(multispace0, is_not(terminators))(input)
+}
index a8b729bf61039b3ab54d76d8a61cd70b25360b2d..d0ea2af89bb62f511a1b89c109d50ee7a114a2e8 100644 (file)
@@ -113,6 +113,7 @@ pub mod smb;
 pub mod krb;
 pub mod dcerpc;
 pub mod modbus;
+pub mod detect_parser;
 
 pub mod ike;
 pub mod snmp;
index a1560aac238ad49eb02034227dde20a03127d3ec..19fdc472229ecc7fe3a46ee104169d1b4e9a0beb 100644 (file)
@@ -25,7 +25,7 @@
 #include "detect-byte.h"
 #include "detect-byte-extract.h"
 #include "detect-bytemath.h"
-
+#include "rust.h"
 /**
  * \brief Used to retrieve args from BM.
  *
index 649b5dc09588f24b1200c5ac3e2dd5c955d0f273..23071219253734f19913054c20013e678b473670 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (C) 2020 Open Information Security Foundation
+/* Copyright (C) 2020-2022 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
 #include "detect-engine.h"
 #include "detect-engine-mpm.h"
 #include "detect-engine-state.h"
+#include "detect-engine-build.h"
 #include "detect-content.h"
 #include "detect-pcre.h"
 #include "detect-byte.h"
 #include "detect-bytemath.h"
-#include "detect-isdataat.h"
-#include "detect-engine-build.h"
 
+#include "app-layer-parser.h"
 #include "app-layer-protos.h"
+#include "rust-bindings.h"
 
 #include "flow.h"
 #include "flow-var.h"
 #include "util-unittest-helper.h"
 #include "util-spm.h"
 
-/* the default value of endianess to be used, if none's specified */
-#define DETECT_BYTEMATH_ENDIAN_DEFAULT DETECT_BYTEMATH_ENDIAN_BIG
-
-/* the base to be used if string mode is specified.  These options would be
- * specified in DetectByteMathData->base */
-#define DETECT_BYTEMATH_BASE_NONE  0
-#define DETECT_BYTEMATH_BASE_OCT   8
-#define DETECT_BYTEMATH_BASE_DEC  10
-#define DETECT_BYTEMATH_BASE_HEX  16
-#define DETECT_BYTEMATH_BASE_DEFAULT DETECT_BYTEMATH_BASE_DEC
-
-/* the max no of bytes that can be extracted in string mode - (string, hex)
- * (string, oct) or (string, dec) */
-#define STRING_MAX_BYTES_TO_EXTRACT_FOR_OCT 23
-#define STRING_MAX_BYTES_TO_EXTRACT_FOR_DEC 20
-#define STRING_MAX_BYTES_TO_EXTRACT_FOR_HEX 14
-
-/* the max no of bytes that can be extracted in non-string mode */
-#define NO_STRING_MAX_BYTES_TO_EXTRACT 4
-
-#define PARSE_REGEX                                                                                \
-    "^"                                                                                            \
-    "\\s*(bytes)\\s*(\\d+)\\s*"                                                                    \
-    ",\\s*(offset)\\s*([-]?\\d+)\\s*"                                                              \
-    ",\\s*(oper)\\s*([-+\\/]{1}|<<|>>)\\s*"                                                        \
-    ",\\s*(rvalue)\\s*(\\w+)\\s*"                                                                  \
-    ",\\s*(result)\\s*(\\w+)\\s*"                                                                  \
-    "(?:,\\s*(relative)\\s*)?"                                                                     \
-    "(?:,\\s*(endian)\\s*(big|little)\\s*)?"                                                       \
-    "(?:,\\s*(string)\\s*(hex|dec|oct)\\s*)?"                                                      \
-    "(?:,\\s*(dce)\\s*)?"                                                                          \
-    "(?:,\\s*(bitmask)\\s*(0?[xX]?[0-9a-fA-F]{2,8})\\s*)?"                                         \
-    "$"
-
-/* Mandatory value group numbers -- kw values not needed */
-//#define BYTES_KW     1
-#define BYTES_VAL      2
-//#define OFFSET_KW    3
-#define OFFSET_VAL     4
-//#define OPER_KW              5
-#define OPER_VAL       6
-//#define RVALUE_KW    7
-#define RVALUE_VAL     8
-//#define RESULT_KW    9
-#define RESULT_VAL     10
-
-/* Optional value group numbers */
-#define RELATIVE_KW    11
-#define ENDIAN_KW      12
-#define ENDIAN_VAL     13
-#define STRING_KW      14
-#define STRING_VAL     15
-#define DCE_KW         16
-#define BITMASK_KW     17
-#define BITMASK_VAL    18
-
-#define MIN_GROUP      10
-#define MAX_GROUP      19
-
-static DetectParseRegex parse_regex;
-
 static int DetectByteMathSetup(DetectEngineCtx *, Signature *, const char *);
 #ifdef UNITTESTS
 static void DetectByteMathRegisterTests(void);
 #endif
 static void DetectByteMathFree(DetectEngineCtx *, void *);
 
+#define DETECT_BYTEMATH_ENDIAN_DEFAULT (uint8_t) BigEndian
+#define DETECT_BYTEMATH_BASE_DEFAULT   (uint8_t) BaseDec
 /**
  * \brief Registers the keyword handlers for the "byte_math" keyword.
  */
@@ -132,7 +74,6 @@ void DetectBytemathRegister(void)
 #ifdef UNITTESTS
     sigmatch_table[DETECT_BYTEMATH].RegisterTests = DetectByteMathRegisterTests;
 #endif
-    DetectSetupParseRegexes(PARSE_REGEX, &parse_regex);
 }
 
 int DetectByteMathDoMatch(DetectEngineThreadCtx *det_ctx, const SigMatchData *smd,
@@ -196,8 +137,8 @@ int DetectByteMathDoMatch(DetectEngineThreadCtx *det_ctx, const SigMatchData *sm
             }
         }
     } else {
-        int endianness = (endian == DETECT_BYTEMATH_ENDIAN_BIG) ?
-                          BYTE_BIG_ENDIAN : BYTE_LITTLE_ENDIAN;
+        ByteMathEndian bme = endian;
+        int endianness = (bme == BigEndian) ? BYTE_BIG_ENDIAN : BYTE_LITTLE_ENDIAN;
         extbytes = ByteExtractUint64(&val, endianness, data->nbytes, ptr);
         if (extbytes != data->nbytes) {
             SCLogDebug("error extracting %d bytes of numeric data: %d",
@@ -212,24 +153,24 @@ int DetectByteMathDoMatch(DetectEngineThreadCtx *det_ctx, const SigMatchData *sm
     det_ctx->buffer_offset = ptr - payload;
 
     switch (data->oper) {
-        case DETECT_BYTEMATH_OPERATOR_NONE:
+        case OperatorNone:
             break;
-        case DETECT_BYTEMATH_OPERATOR_PLUS:
+        case Addition:
             val += rvalue;
             break;
-        case DETECT_BYTEMATH_OPERATOR_MINUS:
+        case Subtraction:
             val -= rvalue;
             break;
-        case DETECT_BYTEMATH_OPERATOR_DIVIDE:
+        case Division:
             val /= rvalue;
             break;
-        case DETECT_BYTEMATH_OPERATOR_MULTIPLY:
+        case Multiplication:
             val *= rvalue;
             break;
-        case DETECT_BYTEMATH_OPERATOR_LSHIFT:
+        case LeftShift:
             val <<= rvalue;
             break;
-        case DETECT_BYTEMATH_OPERATOR_RSHIFT:
+        case RightShift:
             val >>= rvalue;
             break;
     }
@@ -258,277 +199,26 @@ int DetectByteMathDoMatch(DetectEngineThreadCtx *det_ctx, const SigMatchData *sm
  */
 static DetectByteMathData *DetectByteMathParse(DetectEngineCtx *de_ctx, const char *arg, char **rvalue)
 {
-    DetectByteMathData *bmd = NULL;
-    int ret, res;
-    size_t pcre2len;
-    char tmp_str[128] = "";
-
-    ret = DetectParsePcreExec(&parse_regex, arg, 0, 0);
-    if (ret < MIN_GROUP || ret > MAX_GROUP) {
-        SCLogError(SC_ERR_PCRE_PARSE, "byte_math parse error; invalid value: ret %" PRId32
-                   ", string \"%s\"", ret, arg);
-        goto error;
-    }
-
-    bmd = SCCalloc(1, sizeof(DetectByteMathData));
-    if (unlikely(bmd == NULL))
-        goto error;
-
-    /* no of bytes to extract */
-    pcre2len = sizeof(tmp_str);
-    res = pcre2_substring_copy_bynumber(
-            parse_regex.match, BYTES_VAL, (PCRE2_UCHAR8 *)tmp_str, &pcre2len);
-    if (res < 0) {
-        SCLogError(SC_ERR_PCRE_GET_SUBSTRING,
-                "pcre2_substring_copy_bynumber failed "
-                "for \"nbytes\" value: \"%s\"",
-                tmp_str);
-        goto error;
-    }
-
-    res = ByteExtractStringUint8(&bmd->nbytes, 10,
-                                strlen(tmp_str),
-                                (const char *)tmp_str);
-    if (res < 1 || bmd->nbytes > 10) {
-        SCLogError(SC_ERR_INVALID_SIGNATURE, "byte_math invalid bytes "
-                   "value \"%s\" specified.", tmp_str);
-        goto error;
-    }
-
-    /* offset */
-    pcre2len = sizeof(tmp_str);
-    res = pcre2_substring_copy_bynumber(
-            parse_regex.match, OFFSET_VAL, (PCRE2_UCHAR8 *)tmp_str, &pcre2len);
-    if (res < 0) {
-        SCLogError(SC_ERR_PCRE_GET_SUBSTRING,
-                "pcre2_substring_copy_bynumber failed "
-                "for \"offset\" value: \"%s\"",
-                tmp_str);
-        goto error;
-    }
-
-    if (StringParseI32RangeCheck(
-                &bmd->offset, 10, strlen(tmp_str), (const char *)tmp_str, -65535, 65535) < 0) {
-        SCLogError(SC_ERR_INVALID_SIGNATURE, "byte_math invalid offset "
-                   "value \"%s\"", tmp_str);
-        goto error;
+    DetectByteMathData *bmd;
+    if ((bmd = ScByteMathParse(arg)) == NULL) {
+        SCLogError(SC_ERR_PCRE_PARSE, "invalid bytemath values");
+        return NULL;
     }
 
-    /* operator */
-    pcre2len = sizeof(tmp_str);
-    res = pcre2_substring_copy_bynumber(
-            parse_regex.match, OPER_VAL, (PCRE2_UCHAR8 *)tmp_str, &pcre2len);
-    if (res < 0) {
-        SCLogError(SC_ERR_PCRE_GET_SUBSTRING,
-                "pcre2_substring_copy_bynumber failed "
-                "for \"operator\" value of byte_math: \"%s\"",
-                tmp_str);
-        goto error;
-    }
-
-    if (strcmp(tmp_str, "+") == 0) {
-        bmd->oper = DETECT_BYTEMATH_OPERATOR_PLUS;
-    } else if (strcmp(tmp_str, "-") == 0) {
-        bmd->oper = DETECT_BYTEMATH_OPERATOR_MINUS;
-    } else if (strcmp(tmp_str, "/") == 0) {
-        bmd->oper = DETECT_BYTEMATH_OPERATOR_DIVIDE;
-    } else if (strcmp(tmp_str, "*") == 0) {
-        bmd->oper = DETECT_BYTEMATH_OPERATOR_MULTIPLY;
-    } else if (strcmp(tmp_str, "<<") == 0) {
-        bmd->oper = DETECT_BYTEMATH_OPERATOR_LSHIFT;
-    } else if (strcmp(tmp_str, ">>") == 0) {
-        bmd->oper = DETECT_BYTEMATH_OPERATOR_RSHIFT;
-    }
-
-    /* rvalue */
-    pcre2len = sizeof(tmp_str);
-    res = pcre2_substring_copy_bynumber(
-            parse_regex.match, RVALUE_VAL, (PCRE2_UCHAR8 *)tmp_str, &pcre2len);
-    if (res < 0) {
-        SCLogError(SC_ERR_PCRE_GET_SUBSTRING,
-                "pcre2_substring_copy_bynumber failed "
-                "for \"rvalue\" to byte_math: \"%s\"",
-                tmp_str);
-        goto error;
-    }
-
-    if (*tmp_str != '-' && isalpha((unsigned char)*tmp_str)) {
+    if (bmd->rvalue_str) {
         if (rvalue == NULL) {
             SCLogError(SC_ERR_INVALID_SIGNATURE, "byte_math supplied with "
                        "var name for rvalue. \"rvalue\" argument supplied to "
                        "this function must be non-NULL");
             goto error;
         }
-        *rvalue = SCStrdup(tmp_str);
+        *rvalue = SCStrdup(bmd->rvalue_str);
         if (*rvalue == NULL) {
             goto error;
         }
-    } else {
-        if (ByteExtractStringUint32(&bmd->rvalue, 10, strlen(tmp_str), (const char *)tmp_str) < 0) {
-            SCLogError(SC_ERR_INVALID_SIGNATURE, "byte_math invalid rvalue "
-                       "value \"%s\"", tmp_str);
-            goto error;
-        }
     }
 
-    /* result */
-    pcre2len = sizeof(tmp_str);
-    res = pcre2_substring_copy_bynumber(
-            parse_regex.match, RESULT_VAL, (PCRE2_UCHAR8 *)tmp_str, &pcre2len);
-    if (res < 0) {
-        SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre2_substring_copy_bynumber failed "
-                                              "for \"result\" to byte_math");
-        goto error;
-    }
-    if (!isalpha(*tmp_str)) {
-        SCLogError(SC_ERR_INVALID_SIGNATURE, "byte_math result must be "
-                   "a variable name. Unable to find \"%s\"", tmp_str);
-        goto error;
-    }
-
-    bmd->result = SCStrdup(tmp_str);
-    if (bmd->result == NULL)
-        goto error;
-
-    /* optional value handling:
-     * relative - 11
-     * endian <val> - 12-13
-     * string <val> - 14-15
-     * dce - 16
-     * bitmask <val> - 17-18
-     */
-
-    if (ret > RELATIVE_KW) {
-        pcre2len = sizeof(tmp_str);
-        res = SC_Pcre2SubstringCopy(
-                parse_regex.match, RELATIVE_KW, (PCRE2_UCHAR8 *)tmp_str, &pcre2len);
-
-        if (res < 0) {
-            SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre2_substring_copy_bynumber failed "
-                                                  "for byte_math \"relative\" arg");
-            goto error;
-        }
-
-        if (tmp_str[0] != '\0') {
-            bmd->flags |= DETECT_BYTEMATH_FLAG_RELATIVE;
-        }
-    }
-
-    if (ret > ENDIAN_VAL) {
-        pcre2len = sizeof(tmp_str);
-        res = SC_Pcre2SubstringCopy(
-                parse_regex.match, ENDIAN_KW, (PCRE2_UCHAR8 *)tmp_str, &pcre2len);
-        if (res < 0) {
-            SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre2_substring_copy_bynumber failed "
-                                                  "for byte_math \"endian\" arg");
-            goto error;
-        }
-
-        if (tmp_str[0] != '\0') {
-            bmd->flags |= DETECT_BYTEMATH_FLAG_ENDIAN;
-        }
-
-        pcre2len = sizeof(tmp_str);
-        res = SC_Pcre2SubstringCopy(
-                parse_regex.match, ENDIAN_VAL, (PCRE2_UCHAR8 *)tmp_str, &pcre2len);
-        if (res < 0) {
-            SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre2_substring_copy_bynumber failed "
-                                                  "for byte_math \"endian\" value");
-            goto error;
-        }
-
-        /* Since the parse succeeded, there's a value */
-        if (strcmp("big", tmp_str) == 0)
-            bmd->endian = DETECT_BYTEMATH_ENDIAN_BIG;
-        else if (strcmp("little", tmp_str) == 0)
-            bmd->endian = DETECT_BYTEMATH_ENDIAN_LITTLE;
-    }
-
-    if (ret > STRING_VAL) {
-        pcre2len = sizeof(tmp_str);
-        res = SC_Pcre2SubstringCopy(
-                parse_regex.match, STRING_KW, (PCRE2_UCHAR8 *)tmp_str, &pcre2len);
-        if (res < 0) {
-            SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre2_substring_copy_bynumber failed "
-                                                  "for byte_math \"string\" arg");
-            goto error;
-        }
-
-        if (tmp_str[0] != '\0') {
-            bmd->flags |= DETECT_BYTEMATH_FLAG_STRING;
-        }
-
-        pcre2len = sizeof(tmp_str);
-        res = SC_Pcre2SubstringCopy(
-                parse_regex.match, STRING_VAL, (PCRE2_UCHAR8 *)tmp_str, &pcre2len);
-        if (res < 0) {
-            SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre2_substring_copy_bynumber failed "
-                                                  "for byte_math \"string\" value");
-            goto error;
-        }
-
-        /* Since the parse succeeded, there's a value */
-        if (strcmp("hex", tmp_str) == 0) {
-            bmd->base = DETECT_BYTEMATH_BASE_HEX;
-        } else if (strcmp("oct", tmp_str) == 0) {
-            bmd->base = DETECT_BYTEMATH_BASE_OCT;
-        } else if (strcmp("dec", tmp_str) == 0) {
-            bmd->base = DETECT_BYTEMATH_BASE_DEC;
-        }
-    }
-
-    if (ret > DCE_KW) {
-        pcre2len = sizeof(tmp_str);
-        res = SC_Pcre2SubstringCopy(parse_regex.match, DCE_KW, (PCRE2_UCHAR8 *)tmp_str, &pcre2len);
-        if (res < 0) {
-            SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre2_substring_copy_bynumber failed "
-                                                  "for byte_math \"dce\" arg");
-            goto error;
-        }
-
-        if (tmp_str[0] != '\0') {
-            bmd->flags |= DETECT_BYTEMATH_FLAG_ENDIAN;
-            bmd->endian = DETECT_BYTEMATH_ENDIAN_DCE;
-        }
-    }
-
-    if (ret > BITMASK_VAL) {
-        pcre2len = sizeof(tmp_str);
-        res = pcre2_substring_copy_bynumber(
-                parse_regex.match, BITMASK_KW, (PCRE2_UCHAR8 *)tmp_str, &pcre2len);
-        if (res < 0) {
-            SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre2_substring_copy_bynumber failed "
-                                                  "for byte_math \"bitmask\" arg");
-            goto error;
-        }
-
-        if (tmp_str[0] != '\0') {
-            bmd->flags |= DETECT_BYTEMATH_FLAG_BITMASK;
-        }
-
-        /* bitmask value*/
-        pcre2len = sizeof(tmp_str);
-        res = pcre2_substring_copy_bynumber(
-                parse_regex.match, BITMASK_VAL, (PCRE2_UCHAR8 *)tmp_str, &pcre2len);
-        if (res < 0) {
-            SCLogError(SC_ERR_PCRE_GET_SUBSTRING,
-                    "pcre2_substring_copy_bynumber failed "
-                    "for bitmask value: \"%s\"",
-                    tmp_str);
-            goto error;
-        }
-
-        res = ByteExtractStringUint32(&bmd->bitmask_val, 16, strlen(tmp_str), tmp_str);
-        if (res < 0) {
-            SCLogError(SC_ERR_INVALID_SIGNATURE, "Unable to extract bitmask "
-                       "value: \"%s\"", tmp_str);
-            goto error;
-        }
-
-        /* determine how many trailing 0's are in the bitmask. This will be used
-         * to rshift the value after applying the bitmask
-         */
-        bmd->bitmask_shift_count = 0;
+    if (bmd->flags & DETECT_BYTEMATH_FLAG_BITMASK) {
         if (bmd->bitmask_val) {
             uint32_t bmask = bmd->bitmask_val;
             while (!(bmask & 0x1)){
@@ -538,43 +228,6 @@ static DetectByteMathData *DetectByteMathParse(DetectEngineCtx *de_ctx, const ch
         }
     }
 
-    if (bmd->endian == DETECT_BYTEMATH_ENDIAN_DCE) {
-        switch (bmd->nbytes) {
-            case 1:
-            case 2:
-            case 4:
-                break;
-            default:
-                SCLogError(SC_ERR_INVALID_SIGNATURE, "nbytes must be 1, 2, or 4 "
-                           "when used with \"dce\"; %d is not valid", bmd->nbytes);
-                goto error;
-                break;
-        }
-    }
-
-    switch (bmd->oper) {
-        case DETECT_BYTEMATH_OPERATOR_LSHIFT:
-        case DETECT_BYTEMATH_OPERATOR_RSHIFT:
-            /* nbytes has already been validated to be in the range [1, 10] */
-            if (bmd->nbytes > 4) {
-                SCLogError(SC_ERR_INVALID_SIGNATURE, "nbytes must be 1 through 4 (inclusive) "
-                           "when used with \"<<\" or \">>\"; %d is not valid", bmd->nbytes);
-                goto error;
-            }
-            break;
-
-        default:
-            break;
-    }
-
-    /* Set defaults for endian and base if needed */
-    if (!(bmd->flags & DETECT_BYTEMATH_FLAG_ENDIAN)) {
-        bmd->endian = DETECT_BYTEMATH_ENDIAN_DEFAULT;
-    }
-    if (!(bmd->flags & DETECT_BYTEMATH_FLAG_STRING)) {
-        bmd->base = DETECT_BYTEMATH_BASE_DEFAULT;
-    }
-
     return bmd;
 
  error:
@@ -621,7 +274,7 @@ static int DetectByteMathSetup(DetectEngineCtx *de_ctx, Signature *s, const char
                 goto error;
             }
         }
-    } else if (data->endian == DETECT_BYTEMATH_ENDIAN_DCE) {
+    } else if (data->endian == EndianDCE) {
         if (data->flags & DETECT_BYTEMATH_FLAG_RELATIVE) {
             prev_pm = DetectGetLastSMFromLists(s, DETECT_CONTENT, DETECT_PCRE,
                                                DETECT_BYTETEST, DETECT_BYTEJUMP,
@@ -662,14 +315,12 @@ static int DetectByteMathSetup(DetectEngineCtx *de_ctx, Signature *s, const char
         sm_list = DETECT_SM_LIST_PMATCH;
     }
 
-    if (data->endian == DETECT_BYTEMATH_ENDIAN_DCE) {
+    if (data->endian == EndianDCE) {
         if (DetectSignatureSetAppProto(s, ALPROTO_DCERPC) != 0)
             goto error;
 
-        if ((data->flags & DETECT_BYTEMATH_FLAG_STRING) ||
-            (data->base == DETECT_BYTEMATH_BASE_DEC) ||
-            (data->base == DETECT_BYTEMATH_BASE_HEX) ||
-            (data->base == DETECT_BYTEMATH_BASE_OCT) ) {
+        if ((data->flags & DETECT_BYTEMATH_FLAG_STRING) || (data->base == BaseDec) ||
+                (data->base == BaseHex) || (data->base == BaseOct)) {
             SCLogError(SC_ERR_CONFLICTING_RULE_KEYWORDS, "Invalid option. "
                        "A bytemath keyword with dce holds other invalid modifiers.");
             goto error;
@@ -684,7 +335,7 @@ static int DetectByteMathSetup(DetectEngineCtx *de_ctx, Signature *s, const char
             goto error;
         }
         data->rvalue = index;
-        data->flags |= DETECT_BYTEMATH_RVALUE_VAR;
+        data->flags |= DETECT_BYTEMATH_FLAG_RVALUE_VAR;
         SCFree(rvalue);
         rvalue = NULL;
     }
@@ -738,14 +389,7 @@ static int DetectByteMathSetup(DetectEngineCtx *de_ctx, Signature *s, const char
  */
 static void DetectByteMathFree(DetectEngineCtx *de_ctx, void *ptr)
 {
-    if (ptr != NULL) {
-        DetectByteMathData *bmd = ptr;
-        if (bmd->result != NULL)
-            SCFree((void *)bmd->result);
-        SCFree(bmd);
-    }
-
-    return;
+    ScByteMathFree(ptr);
 }
 
 /**
@@ -790,7 +434,7 @@ static int DetectByteMathParseTest01(void)
 
     FAIL_IF_NOT(bmd->nbytes == 4);
     FAIL_IF_NOT(bmd->offset == 2);
-    FAIL_IF_NOT(bmd->oper == DETECT_BYTEMATH_OPERATOR_PLUS);
+    FAIL_IF_NOT(bmd->oper == Addition);
     FAIL_IF_NOT(bmd->rvalue == 10);
     FAIL_IF_NOT(strcmp(bmd->result, "bar") == 0);
     FAIL_IF_NOT(bmd->endian == DETECT_BYTEMATH_ENDIAN_DEFAULT);
@@ -855,7 +499,7 @@ static int DetectByteMathParseTest06(void)
     FAIL_IF(bmd == NULL);
     FAIL_IF_NOT(bmd->nbytes == 4);
     FAIL_IF_NOT(bmd->offset == 0);
-    FAIL_IF_NOT(bmd->oper == DETECT_BYTEMATH_OPERATOR_PLUS);
+    FAIL_IF_NOT(bmd->oper == Addition);
     FAIL_IF_NOT(bmd->rvalue == 248);
     FAIL_IF_NOT(strcmp(bmd->result, "var") == 0);
     FAIL_IF_NOT(bmd->flags == flags);
@@ -877,7 +521,7 @@ static int DetectByteMathParseTest07(void)
     FAIL_IF_NOT(rvalue);
     FAIL_IF_NOT(bmd->nbytes == 4);
     FAIL_IF_NOT(bmd->offset == 2);
-    FAIL_IF_NOT(bmd->oper == DETECT_BYTEMATH_OPERATOR_PLUS);
+    FAIL_IF_NOT(bmd->oper == Addition);
     FAIL_IF_NOT(strcmp(rvalue, "foo") == 0);
     FAIL_IF_NOT(strcmp(bmd->result, "bar") == 0);
     FAIL_IF_NOT(bmd->endian == DETECT_BYTEMATH_ENDIAN_DEFAULT);
@@ -911,7 +555,7 @@ static int DetectByteMathParseTest09(void)
 
     FAIL_IF_NOT(bmd->nbytes == 4);
     FAIL_IF_NOT(bmd->offset == 2);
-    FAIL_IF_NOT(bmd->oper == DETECT_BYTEMATH_OPERATOR_PLUS);
+    FAIL_IF_NOT(bmd->oper == Addition);
     FAIL_IF_NOT(bmd->rvalue == 39);
     FAIL_IF_NOT(strcmp(bmd->result, "bar") == 0);
     FAIL_IF_NOT(bmd->flags == flags);
@@ -934,11 +578,11 @@ static int DetectByteMathParseTest10(void)
     FAIL_IF(bmd == NULL);
     FAIL_IF_NOT(bmd->nbytes == 4);
     FAIL_IF_NOT(bmd->offset == 2);
-    FAIL_IF_NOT(bmd->oper == DETECT_BYTEMATH_OPERATOR_PLUS);
+    FAIL_IF_NOT(bmd->oper == Addition);
     FAIL_IF_NOT(bmd->rvalue == 39);
     FAIL_IF_NOT(strcmp(bmd->result, "bar") == 0);
     FAIL_IF_NOT(bmd->flags == flags);
-    FAIL_IF_NOT(bmd->endian == DETECT_BYTEMATH_ENDIAN_BIG);
+    FAIL_IF_NOT(bmd->endian == BigEndian);
     FAIL_IF_NOT(bmd->base == DETECT_BYTEMATH_BASE_DEFAULT);
 
     DetectByteMathFree(NULL, bmd);
@@ -957,11 +601,11 @@ static int DetectByteMathParseTest11(void)
     FAIL_IF(bmd == NULL);
     FAIL_IF_NOT(bmd->nbytes == 4);
     FAIL_IF_NOT(bmd->offset == 2);
-    FAIL_IF_NOT(bmd->oper == DETECT_BYTEMATH_OPERATOR_PLUS);
+    FAIL_IF_NOT(bmd->oper == Addition);
     FAIL_IF_NOT(bmd->rvalue == 39);
     FAIL_IF_NOT(strcmp(bmd->result, "bar") == 0);
     FAIL_IF_NOT(bmd->flags == flags);
-    FAIL_IF_NOT(bmd->endian == DETECT_BYTEMATH_ENDIAN_DCE);
+    FAIL_IF_NOT(bmd->endian == EndianDCE);
     FAIL_IF_NOT(bmd->base == DETECT_BYTEMATH_BASE_DEFAULT);
 
     DetectByteMathFree(NULL, bmd);
@@ -980,12 +624,12 @@ static int DetectByteMathParseTest12(void)
     FAIL_IF(bmd == NULL);
     FAIL_IF_NOT(bmd->nbytes == 4);
     FAIL_IF_NOT(bmd->offset == 2);
-    FAIL_IF_NOT(bmd->oper == DETECT_BYTEMATH_OPERATOR_PLUS);
+    FAIL_IF_NOT(bmd->oper == Addition);
     FAIL_IF_NOT(bmd->rvalue == 39);
     FAIL_IF_NOT(strcmp(bmd->result, "bar") == 0);
     FAIL_IF_NOT(bmd->flags == flags);
-    FAIL_IF_NOT(bmd->endian == DETECT_BYTEMATH_ENDIAN_BIG);
-    FAIL_IF_NOT(bmd->base == DETECT_BYTEMATH_BASE_DEC);
+    FAIL_IF_NOT(bmd->endian == BigEndian);
+    FAIL_IF_NOT(bmd->base == BaseDec);
 
     DetectByteMathFree(NULL, bmd);
 
@@ -1006,14 +650,14 @@ static int DetectByteMathParseTest13(void)
     FAIL_IF(bmd == NULL);
     FAIL_IF_NOT(bmd->nbytes == 4);
     FAIL_IF_NOT(bmd->offset == 2);
-    FAIL_IF_NOT(bmd->oper == DETECT_BYTEMATH_OPERATOR_PLUS);
+    FAIL_IF_NOT(bmd->oper == Addition);
     FAIL_IF_NOT(bmd->rvalue == 39);
     FAIL_IF_NOT(strcmp(bmd->result, "bar") == 0);
     FAIL_IF_NOT(bmd->bitmask_val == 0x8f40);
     FAIL_IF_NOT(bmd->bitmask_shift_count == 6);
     FAIL_IF_NOT(bmd->flags == flags);
-    FAIL_IF_NOT(bmd->endian == DETECT_BYTEMATH_ENDIAN_BIG);
-    FAIL_IF_NOT(bmd->base == DETECT_BYTEMATH_BASE_DEC);
+    FAIL_IF_NOT(bmd->endian == BigEndian);
+    FAIL_IF_NOT(bmd->base == BaseDec);
 
     DetectByteMathFree(NULL, bmd);
 
@@ -1059,14 +703,14 @@ static int DetectByteMathParseTest16(void)
     FAIL_IF(bmd == NULL);
     FAIL_IF_NOT(bmd->nbytes == 4);
     FAIL_IF_NOT(bmd->offset == -2);
-    FAIL_IF_NOT(bmd->oper == DETECT_BYTEMATH_OPERATOR_PLUS);
+    FAIL_IF_NOT(bmd->oper == Addition);
     FAIL_IF_NOT(bmd->rvalue == 39);
     FAIL_IF_NOT(strcmp(bmd->result, "bar") == 0);
     FAIL_IF_NOT(bmd->bitmask_val == 0x8f40);
     FAIL_IF_NOT(bmd->bitmask_shift_count == 6);
     FAIL_IF_NOT(bmd->flags == flags);
-    FAIL_IF_NOT(bmd->endian == DETECT_BYTEMATH_ENDIAN_BIG);
-    FAIL_IF_NOT(bmd->base == DETECT_BYTEMATH_BASE_DEC);
+    FAIL_IF_NOT(bmd->endian == BigEndian);
+    FAIL_IF_NOT(bmd->base == BaseDec);
 
     DetectByteMathFree(NULL, bmd);
 
@@ -1289,11 +933,11 @@ static int DetectByteMathContext01(void)
 
     de_ctx->flags |= DE_QUIET;
     s = de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
-                                   "(msg:\"Testing bytemath_body\"; "
-                                   "content:\"|00 04 93 F3|\"; "
-                                   "content:\"|00 00 00 07|\"; distance:4; within:4;"
-                                   "byte_math:bytes 4, offset 0, oper +, rvalue"
-                                   "248, result var, relative; sid:1;)");
+                                           "(msg:\"Testing bytemath_body\"; "
+                                           "content:\"|00 04 93 F3|\"; "
+                                           "content:\"|00 00 00 07|\"; distance:4; within:4;"
+                                           "byte_math:bytes 4, offset 0, oper +, rvalue "
+                                           "248, result var, relative; sid:1;)");
 
     FAIL_IF(de_ctx->sig_list == NULL);
 
@@ -1319,9 +963,9 @@ static int DetectByteMathContext01(void)
     FAIL_IF_NOT(bmd->rvalue == 248);
     FAIL_IF_NOT(strcmp(bmd->result, "var") == 0);
     FAIL_IF_NOT(bmd->flags == DETECT_BYTEMATH_FLAG_RELATIVE);
-    FAIL_IF_NOT(bmd->endian == DETECT_BYTEMATH_ENDIAN_BIG);
-    FAIL_IF_NOT(bmd->oper == DETECT_BYTEMATH_OPERATOR_PLUS);
-    FAIL_IF_NOT(bmd->base == DETECT_BYTEMATH_BASE_DEC);
+    FAIL_IF_NOT(bmd->endian == BigEndian);
+    FAIL_IF_NOT(bmd->oper == Addition);
+    FAIL_IF_NOT(bmd->base == BaseDec);
 
     DetectEngineCtxFree(de_ctx);
 
index 6db91c94ab6e02a13c70f2787c031ad2060ea3ea..099bd81f10625fb054cfe715e7743dd9eb2727e7 100644 (file)
 #ifndef __DETECT_BYTEMATH_H__
 #define __DETECT_BYTEMATH_H__
 
-/* flags */
-#define DETECT_BYTEMATH_FLAG_RELATIVE   0x01
-#define DETECT_BYTEMATH_FLAG_STRING     0x02
-#define DETECT_BYTEMATH_FLAG_BITMASK    0x04
-#define DETECT_BYTEMATH_FLAG_ENDIAN     0x08
-#define DETECT_BYTEMATH_RVALUE_VAR      0x10
-
-/* endian value to be used.  Would be stored in DetectByteMathData->endian */
-#define DETECT_BYTEMATH_ENDIAN_NONE    0
-#define DETECT_BYTEMATH_ENDIAN_BIG     1
-#define DETECT_BYTEMATH_ENDIAN_LITTLE  2
-#define DETECT_BYTEMATH_ENDIAN_DCE     3
-
-#define DETECT_BYTEMATH_OPERATOR_NONE     1
-#define DETECT_BYTEMATH_OPERATOR_PLUS     2
-#define DETECT_BYTEMATH_OPERATOR_MINUS    3
-#define DETECT_BYTEMATH_OPERATOR_DIVIDE   4
-#define DETECT_BYTEMATH_OPERATOR_MULTIPLY 5
-#define DETECT_BYTEMATH_OPERATOR_LSHIFT   6
-#define DETECT_BYTEMATH_OPERATOR_RSHIFT   7
-
-/**
- * \brief Holds data related to byte_math keyword.
- */
-typedef struct DetectByteMathData_ {
-    /* local id used by other keywords in the sig to reference this */
-    uint8_t local_id;
-    uint8_t nbytes;
-    int32_t offset;
-
-    uint32_t rvalue;
-
-    /* "result" variable, if present */
-    const char *result; /* consumed */
-
-    uint8_t flags;
-    uint8_t endian;
-    uint8_t base;
-    uint8_t oper;
-
-    uint32_t bitmask_val;
-
-    uint16_t bitmask_shift_count;
-    /* unique id used to reference this byte_math keyword */
-    uint16_t id;
-
-} DetectByteMathData;
-
 void DetectBytemathRegister(void);
 
 SigMatch *DetectByteMathRetrieveSMVar(const char *, const Signature *);
index 94d3f5f7760f923b4b8067bd16b062ca4f66f1c7..529f5155d161a825be04492ab132f90c216b5a64 100644 (file)
@@ -59,6 +59,8 @@
 #include "util-unittest-helper.h"
 #include "util-profiling.h"
 
+#include "rust.h"
+
 #ifdef HAVE_LUA
 #include "util-lua.h"
 #endif
@@ -541,20 +543,18 @@ uint8_t DetectEngineContentInspection(DetectEngineCtx *de_ctx, DetectEngineThrea
 
         /* if we have dce enabled we will have to use the endianness
          * specified by the dce header */
-        if ((bmd->flags & DETECT_BYTEMATH_FLAG_ENDIAN) &&
-            endian == DETECT_BYTEMATH_ENDIAN_DCE &&
-            flags & (DETECT_CI_FLAGS_DCE_LE|DETECT_CI_FLAGS_DCE_BE)) {
+        if ((bmd->flags & DETECT_BYTEMATH_FLAG_ENDIAN) && endian == (int)EndianDCE &&
+                flags & (DETECT_CI_FLAGS_DCE_LE | DETECT_CI_FLAGS_DCE_BE)) {
 
             /* enable the endianness flag temporarily.  once we are done
              * processing we reset the flags to the original value*/
-            endian |= ((flags & DETECT_CI_FLAGS_DCE_LE) ?
-                       DETECT_BYTEMATH_ENDIAN_LITTLE : DETECT_BYTEMATH_ENDIAN_BIG);
+            endian |= (uint8_t)((flags & DETECT_CI_FLAGS_DCE_LE) ? LittleEndian : BigEndian);
         }
         uint64_t rvalue;
-        if (bmd->flags & DETECT_BYTEMATH_RVALUE_VAR) {
-             rvalue = det_ctx->byte_values[bmd->local_id];
+        if (bmd->flags & DETECT_BYTEMATH_FLAG_RVALUE_VAR) {
+            rvalue = det_ctx->byte_values[bmd->local_id];
         } else {
-             rvalue = bmd->rvalue;
+            rvalue = bmd->rvalue;
         }
 
         DEBUG_VALIDATE_BUG_ON(buffer_len > UINT16_MAX);