diff --git a/rust/derive/src/lib.rs b/rust/derive/src/lib.rs index ea314f51f6..6b6c1b4e01 100644 --- a/rust/derive/src/lib.rs +++ b/rust/derive/src/lib.rs @@ -59,7 +59,7 @@ pub fn derive_app_layer_state(input: TokenStream) -> TokenStream { applayerstate::derive_app_layer_state(input) } -#[proc_macro_derive(EnumStringU8, attributes(name))] +#[proc_macro_derive(EnumStringU8, attributes(name, suricata))] pub fn derive_enum_string_u8(input: TokenStream) -> TokenStream { stringenum::derive_enum_string::(input, "u8") } @@ -69,7 +69,7 @@ pub fn derive_enum_string_u16(input: TokenStream) -> TokenStream { stringenum::derive_enum_string::(input, "u16") } -#[proc_macro_derive(EnumStringU32, attributes(name))] +#[proc_macro_derive(EnumStringU32, attributes(name, suricata))] pub fn derive_enum_string_u32(input: TokenStream) -> TokenStream { stringenum::derive_enum_string::(input, "u32") } diff --git a/rust/derive/src/stringenum.rs b/rust/derive/src/stringenum.rs index 8eb33e43e6..6930e57a8b 100644 --- a/rust/derive/src/stringenum.rs +++ b/rust/derive/src/stringenum.rs @@ -15,14 +15,75 @@ * 02110-1301, USA. */ +//! String-enumeration derive macro + +/* +String enumeration allows a compact representation, and ensure that +there is no discrepancy between what is logged, and what can be parser +in signature keywords. + +Usage: just derive EnumStringUX with X being the number of bits used in repr + + #[derive(EnumStringU8)] + #[repr(u8)] + pub enum ExampleEnum { + Zero = 0, + BestValueEver = 42, + } + +Then, get 4 functions to translate from u8 <-> ExampleEnum <-> string +- from_u : takes an unsigned and return an Option +- into_u : takes an ExampleEnum and return an unsigned +- from_str : takes a string and return an Option +- into_str : takes an ExampleEnum and return a string (&'static str) + +Case: +snake_case is used by default: +ExampleEnum::BestValueEver.to_str() is "best_value_ever" + +Strings parsed to get an enumeration value are case insensitive + +Parameters: +The enum can have a suricata attribute, which is a list of named values +- enum_string_style can be: + - UPPERCASE : uppercase is used for logging and parsing + - LOG_UPPERCASE : uppercase is only used for logging, snake_case for parsing + +With UPPERCASE +ExampleEnum::BestValueEver.to_str() is "BESTVALUEEVER" (without underscores) +*/ + extern crate proc_macro; use super::applayerevent::transform_name; use proc_macro::TokenStream; use quote::quote; -use syn::{self, parse_macro_input, DeriveInput}; use std::str::FromStr; +use syn::{self, parse_macro_input, DeriveInput}; + +fn get_attr_enum_string_style(attr: &syn::Attribute) -> String { + let meta = attr.parse_meta().unwrap(); + if let syn::Meta::List(l) = meta { + for n in l.nested { + if let syn::NestedMeta::Meta(syn::Meta::NameValue(nv)) = n { + if nv.path.is_ident("enum_string_style") { + if let syn::Lit::Str(s) = nv.lit { + return s.value(); + } + panic!("enum_string_style invalid syntax"); + } + } + } + panic!("no enum_string_style"); + } + panic!("suricata attribute is not a list"); +} -pub fn derive_enum_string(input: TokenStream, ustr: &str) -> TokenStream where ::Err: std::fmt::Display { +pub fn derive_enum_string( + input: TokenStream, ustr: &str, +) -> TokenStream +where + ::Err: std::fmt::Display, +{ let input = parse_macro_input!(input as DeriveInput); let name = input.ident; let mut values = Vec::new(); @@ -30,13 +91,37 @@ pub fn derive_enum_string(input: TokenSt let mut names_upper = Vec::new(); let mut fields = Vec::new(); + let mut enum_string_style = String::from(""); + if let syn::Data::Enum(ref data) = input.data { + for attr in input.attrs.iter() { + if attr.path.is_ident("suricata") { + enum_string_style = get_attr_enum_string_style(attr); + } + } for v in (&data.variants).into_iter() { if let Some((_, val)) = &v.discriminant { - let fname = transform_name(&v.ident.to_string()); - let fnameu = fname.to_ascii_uppercase(); - names.push(fname); - names_upper.push(fnameu); + match &enum_string_style[..] { + "UPPERCASE" => { + names.push(v.ident.to_string().to_ascii_uppercase()); + names_upper.push(v.ident.to_string().to_ascii_uppercase()); + } + "LOG_UPPERCASE" => { + names.push(v.ident.to_string().to_ascii_uppercase()); + let fname = transform_name(&v.ident.to_string()); + let fnameu = fname.to_ascii_uppercase(); + names_upper.push(fnameu); + } + "" => { // snake_case + let fname = transform_name(&v.ident.to_string()); + let fnameu = fname.to_ascii_uppercase(); + names.push(fname); + names_upper.push(fnameu); + } + _ => { + panic!("EnumString style unknown {}", enum_string_style); + } + }; fields.push(v.ident.clone()); if let syn::Expr::Lit(l) = val { if let syn::Lit::Int(li) = &l.lit { @@ -59,7 +144,9 @@ pub fn derive_enum_string(input: TokenSt panic!("EnumString can only be derived for enums"); } - let is_suricata = std::env::var("CARGO_PKG_NAME").map(|var| var == "suricata").unwrap_or(false); + let is_suricata = std::env::var("CARGO_PKG_NAME") + .map(|var| var == "suricata") + .unwrap_or(false); let crate_id = if is_suricata { syn::Ident::new("crate", proc_macro2::Span::call_site()) } else { diff --git a/rust/src/detect/mod.rs b/rust/src/detect/mod.rs index e42217d25e..5264927c70 100644 --- a/rust/src/detect/mod.rs +++ b/rust/src/detect/mod.rs @@ -19,23 +19,23 @@ pub mod byte_extract; pub mod byte_math; +pub mod datasets; pub mod entropy; pub mod error; +pub mod float; pub mod flow; pub mod iprep; pub mod parser; pub mod requires; pub mod stream_size; +pub mod tojson; pub mod transforms; pub mod uint; -pub mod float; pub mod uri; -pub mod tojson; pub mod vlan; -pub mod datasets; -use std::os::raw::c_int; use std::ffi::CString; +use std::os::raw::c_int; use suricata_sys::sys::{ DetectEngineCtx, SCDetectHelperKeywordRegister, SCDetectHelperKeywordSetCleanCString, @@ -46,7 +46,9 @@ use suricata_sys::sys::{ /// derive StringEnum. pub trait EnumString { /// Return the enum variant of the given numeric value. - fn from_u(v: T) -> Option where Self: Sized; + fn from_u(v: T) -> Option + where + Self: Sized; /// Convert the enum variant to the numeric value. fn into_u(self) -> T; @@ -55,7 +57,9 @@ pub trait EnumString { fn to_str(&self) -> &'static str; /// Get an enum variant from parsing a string. - fn from_str(s: &str) -> Option where Self: Sized; + fn from_str(s: &str) -> Option + where + Self: Sized; } /// Rust app-layer light version of SigTableElmt for simple sticky buffer @@ -138,7 +142,6 @@ pub const SIGMATCH_INFO_MULTI_UINT: u32 = 0x80000; // BIT_U32(19) pub const SIGMATCH_INFO_ENUM_UINT: u32 = 0x100000; // BIT_U32(20) pub const SIGMATCH_INFO_BITFLAGS_UINT: u32 = 0x200000; // BIT_U32(21) - #[repr(u8)] #[derive(Copy, Clone, Debug, PartialEq, Eq)] // endian @@ -201,6 +204,56 @@ mod test { assert_eq!(TestEnum::BestValueEver.to_str(), "best_value_ever"); assert_eq!(TestEnum::from_str("zero"), Some(TestEnum::Zero)); assert_eq!(TestEnum::from_str("nope"), None); - assert_eq!(TestEnum::from_str("best_value_ever"), Some(TestEnum::BestValueEver)); + assert_eq!( + TestEnum::from_str("best_value_ever"), + Some(TestEnum::BestValueEver) + ); + } + + #[derive(Clone, Debug, PartialEq, EnumStringU8)] + #[repr(u8)] + #[suricata(enum_string_style = "LOG_UPPERCASE")] + pub enum TestEnumLogUppercase { + Zero = 0, + BestValueEver = 42, + } + + #[test] + fn test_enum_string_log_uppercase() { + assert_eq!(TestEnumLogUppercase::Zero.to_str(), "ZERO"); + assert_eq!( + TestEnumLogUppercase::BestValueEver.to_str(), + "BESTVALUEEVER" + ); + assert_eq!( + TestEnumLogUppercase::from_str("zero"), + Some(TestEnumLogUppercase::Zero) + ); + assert_eq!( + TestEnumLogUppercase::from_str("BEST_VALUE_EVER"), + Some(TestEnumLogUppercase::BestValueEver) + ); + } + + #[derive(Clone, Debug, PartialEq, EnumStringU8)] + #[repr(u8)] + #[suricata(enum_string_style = "UPPERCASE")] + pub enum TestEnumUppercase { + Zero = 0, + BestValueEver = 42, + } + + #[test] + fn test_enum_string_uppercase() { + assert_eq!(TestEnumUppercase::Zero.to_str(), "ZERO"); + assert_eq!(TestEnumUppercase::BestValueEver.to_str(), "BESTVALUEEVER"); + assert_eq!( + TestEnumUppercase::from_str("zero"), + Some(TestEnumUppercase::Zero) + ); + assert_eq!( + TestEnumUppercase::from_str("BESTVALUEEVER"), + Some(TestEnumUppercase::BestValueEver) + ); } }