mod applayerevent;
mod applayerframetype;
+mod stringenum;
/// The `AppLayerEvent` derive macro generates a `AppLayerEvent` trait
/// implementation for enums that define AppLayerEvents.
pub fn derive_app_layer_frame_type(input: TokenStream) -> TokenStream {
applayerframetype::derive_app_layer_frame_type(input)
}
+
+#[proc_macro_derive(EnumStringU8, attributes(name))]
+pub fn derive_enum_string_u8(input: TokenStream) -> TokenStream {
+ stringenum::derive_enum_string::<u8>(input, "u8")
+}
+
+#[proc_macro_derive(EnumStringU16, attributes(name))]
+pub fn derive_enum_string_u16(input: TokenStream) -> TokenStream {
+ stringenum::derive_enum_string::<u16>(input, "u16")
+}
+
+#[proc_macro_derive(EnumStringU32, attributes(name))]
+pub fn derive_enum_string_u32(input: TokenStream) -> TokenStream {
+ stringenum::derive_enum_string::<u32>(input, "u32")
+}
--- /dev/null
+/* Copyright (C) 2023 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.
+ */
+
+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;
+
+pub fn derive_enum_string<T: std::str::FromStr + quote::ToTokens>(input: TokenStream, ustr: &str) -> TokenStream where <T as FromStr>::Err: std::fmt::Display {
+ let input = parse_macro_input!(input as DeriveInput);
+ let name = input.ident;
+ let mut values = Vec::new();
+ let mut names = Vec::new();
+ let mut fields = Vec::new();
+
+ if let syn::Data::Enum(ref data) = input.data {
+ for v in (&data.variants).into_iter() {
+ if let Some((_, val)) = &v.discriminant {
+ let fname = transform_name(&v.ident.to_string());
+ names.push(fname);
+ fields.push(v.ident.clone());
+ if let syn::Expr::Lit(l) = val {
+ if let syn::Lit::Int(li) = &l.lit {
+ if let Ok(value) = li.base10_parse::<T>() {
+ values.push(value);
+ } else {
+ panic!("EnumString requires explicit {}", ustr);
+ }
+ } else {
+ panic!("EnumString requires explicit literal integer");
+ }
+ } else {
+ panic!("EnumString requires explicit literal");
+ }
+ } else {
+ panic!("EnumString requires explicit values");
+ }
+ }
+ } else {
+ 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 crate_id = if is_suricata {
+ syn::Ident::new("crate", proc_macro2::Span::call_site())
+ } else {
+ syn::Ident::new("suricata", proc_macro2::Span::call_site())
+ };
+
+ let utype_str = syn::Ident::new(ustr, proc_macro2::Span::call_site());
+
+ let expanded = quote! {
+ impl #crate_id::detect::EnumString<#utype_str> for #name {
+ fn from_u(v: #utype_str) -> Option<Self> {
+ match v {
+ #( #values => Some(#name::#fields) ,)*
+ _ => None,
+ }
+ }
+ fn into_u(self) -> #utype_str {
+ match self {
+ #( #name::#fields => #values ,)*
+ }
+ }
+ fn to_str(&self) -> &'static str {
+ match *self {
+ #( #name::#fields => #names ,)*
+ }
+ }
+ fn from_str(s: &str) -> Option<Self> {
+ match s {
+ #( #names => Some(#name::#fields) ,)*
+ _ => None
+ }
+ }
+ }
+ };
+
+ proc_macro::TokenStream::from(expanded)
+}
pub mod uint;
pub mod uri;
pub mod requires;
+
+/// EnumString trait that will be implemented on enums that
+/// derive StringEnum.
+pub trait EnumString<T> {
+ /// Return the enum variant of the given numeric value.
+ fn from_u(v: T) -> Option<Self> where Self: Sized;
+
+ /// Convert the enum variant to the numeric value.
+ fn into_u(self) -> T;
+
+ /// Return the string for logging the enum value.
+ fn to_str(&self) -> &'static str;
+
+ /// Get an enum variant from parsing a string.
+ fn from_str(s: &str) -> Option<Self> where Self: Sized;
+}
+
+#[cfg(test)]
+mod test {
+ use super::*;
+ use suricata_derive::EnumStringU8;
+
+ #[derive(Clone, Debug, PartialEq, EnumStringU8)]
+ #[repr(u8)]
+ pub enum TestEnum {
+ Zero = 0,
+ BestValueEver = 42,
+ }
+
+ #[test]
+ fn test_enum_string_u8() {
+ assert_eq!(TestEnum::from_u(0), Some(TestEnum::Zero));
+ assert_eq!(TestEnum::from_u(1), None);
+ assert_eq!(TestEnum::from_u(42), Some(TestEnum::BestValueEver));
+ assert_eq!(TestEnum::Zero.into_u(), 0);
+ assert_eq!(TestEnum::BestValueEver.into_u(), 42);
+ assert_eq!(TestEnum::Zero.to_str(), "zero");
+ 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));
+ }
+}
use nom7::Err;
use nom7::IResult;
+use super::EnumString;
+
use std::ffi::CStr;
#[derive(PartialEq, Eq, Clone, Debug)]
pub mode: DetectUintMode,
}
+/// Parses a string for detection with integers, using enumeration strings
+///
+/// Needs to specify T1 the integer type (like u8)
+/// And the Enumeration for the stringer.
+/// Will try to parse numerical value first, as any integer detection keyword
+/// And if this fails, will resort to using the enumeration strings.
+///
+/// Returns Some DetectUintData on success, None on failure
+pub fn detect_parse_uint_enum<T1: DetectIntType, T2: EnumString<T1>>(s: &str) -> Option<DetectUintData<T1>> {
+ if let Ok((_, ctx)) = detect_parse_uint::<T1>(s) {
+ return Some(ctx);
+ }
+ if let Some(enum_val) = T2::from_str(s) {
+ let ctx = DetectUintData::<T1> {
+ arg1: enum_val.into_u(),
+ arg2: T1::min_value(),
+ mode: DetectUintMode::DetectUintModeEqual,
+ };
+ return Some(ctx);
+ }
+ return None;
+}
+
pub trait DetectIntType:
std::str::FromStr
+ std::cmp::PartialOrd
mod tests {
use super::*;
+ use suricata_derive::EnumStringU8;
+
+ #[derive(Clone, Debug, PartialEq, EnumStringU8)]
+ #[repr(u8)]
+ pub enum TestEnum {
+ Zero = 0,
+ BestValueEver = 42,
+ }
+
+ #[test]
+ fn test_detect_parse_uint_enum() {
+ let ctx = detect_parse_uint_enum::<u8, TestEnum>("best_value_ever").unwrap();
+ assert_eq!(ctx.arg1, 42);
+ assert_eq!(ctx.mode, DetectUintMode::DetectUintModeEqual);
+
+ let ctx = detect_parse_uint_enum::<u8, TestEnum>(">1").unwrap();
+ assert_eq!(ctx.arg1, 1);
+ assert_eq!(ctx.mode, DetectUintMode::DetectUintModeGt);
+ }
+
#[test]
fn test_parse_uint_hex() {
let (_, val) = detect_parse_uint::<u64>("0x100").unwrap();