]> git.ipfire.org Git - people/ms/suricata.git/commitdiff
rust: derive crate: for custom derives
authorJason Ish <jason.ish@oisf.net>
Mon, 13 Jan 2020 04:56:47 +0000 (22:56 -0600)
committerVictor Julien <victor@inliniac.net>
Wed, 1 Sep 2021 06:33:52 +0000 (08:33 +0200)
Currently has one derive, AppLayerEvent to be used like:

  #[derive(AppLayerEvent)]
  pub enum DNSEvent {
      MalformedData,
      NotRequest,
      NotResponse,
      ZFlagSet,
  }

Code will be generated to:
- Convert enum to a c type string
- Convert string to enum variant
- Convert id to enum variant

configure.ac
rust/.gitignore
rust/Cargo.toml.in
rust/Makefile.am
rust/derive/.gitignore [new file with mode: 0644]
rust/derive/Cargo.toml.in [new file with mode: 0644]
rust/derive/src/applayerevent.rs [new file with mode: 0644]
rust/derive/src/lib.rs [new file with mode: 0644]

index 30d2b4f0930077d08a7fc5434980842d6f2d5cee..5d9411a46d8fce9c9484430f4d680da56470f3a9 100644 (file)
@@ -2694,7 +2694,7 @@ AC_SUBST(enable_non_bundled_htp)
 
 AM_CONDITIONAL([BUILD_SHARED_LIBRARY], [test "x$enable_shared" = "xyes"] && [test "x$can_build_shared_library" = "xyes"])
 
-AC_CONFIG_FILES(Makefile src/Makefile rust/Makefile rust/Cargo.toml rust/.cargo/config)
+AC_CONFIG_FILES(Makefile src/Makefile rust/Makefile rust/Cargo.toml rust/derive/Cargo.toml rust/.cargo/config)
 AC_CONFIG_FILES(qa/Makefile qa/coccinelle/Makefile)
 AC_CONFIG_FILES(rules/Makefile doc/Makefile doc/userguide/Makefile doc/devguide/Makefile)
 AC_CONFIG_FILES(contrib/Makefile contrib/file_processor/Makefile contrib/file_processor/Action/Makefile contrib/file_processor/Processor/Makefile)
index 09a7c19e6a66273651508214ee1c034336e427e5..1c81617b24f152c5306067a2bf42a26e35f01d16 100644 (file)
@@ -1,6 +1,6 @@
 !Cargo.toml.in
+Cargo.toml
 /.cargo/config
-/Cargo.toml
 /Cargo.lock
 /target
 /vendor
index 9681b6ac0cc0d49eeb82db96a112813405339738..60feabb1d8b4a6c9bd0e31face9b3be9f4600e12 100644 (file)
@@ -3,6 +3,9 @@ name = "suricata"
 version = "@PACKAGE_VERSION@"
 edition = "2018"
 
+[workspace]
+members = [".", "./derive"]
+
 [lib]
 crate-type = ["staticlib", "rlib"]
 path = "@e_rustdir@/src/lib.rs"
@@ -50,5 +53,7 @@ md-5 = "~0.9.1"
 regex = "~1.4.2"
 lazy_static = "~1.4.0"
 
+suricata-derive = { path = "./derive" }
+
 [dev-dependencies]
 test-case = "~1.1.0"
index a8c360e4234358f856c09e7b9e645ada090efdec..8efd135efcacec3566636e7cbbdfb563206b8522 100644 (file)
@@ -73,7 +73,7 @@ maintainerclean-local:
 check:
        CARGO_HOME="$(CARGO_HOME)" @rustup_home@ \
                CARGO_TARGET_DIR="$(abs_top_builddir)/rust/target" \
-               $(CARGO) test $(RELEASE) --features "$(RUST_FEATURES)"
+               $(CARGO) test --all $(RELEASE) --features "$(RUST_FEATURES)"
 
 if HAVE_CARGO_VENDOR
 vendor:
diff --git a/rust/derive/.gitignore b/rust/derive/.gitignore
new file mode 100644 (file)
index 0000000..ea8c4bf
--- /dev/null
@@ -0,0 +1 @@
+/target
diff --git a/rust/derive/Cargo.toml.in b/rust/derive/Cargo.toml.in
new file mode 100644 (file)
index 0000000..b60ab19
--- /dev/null
@@ -0,0 +1,13 @@
+[package]
+name = "suricata-derive"
+version = "@PACKAGE_VERSION@"
+edition = "2018"
+
+[lib]
+proc-macro = true
+path = "@e_rustdir@/derive/src/lib.rs"
+
+[dependencies]
+proc-macro2 = "1.0"
+quote = "1.0"
+syn = "1.0"
diff --git a/rust/derive/src/applayerevent.rs b/rust/derive/src/applayerevent.rs
new file mode 100644 (file)
index 0000000..999fa2e
--- /dev/null
@@ -0,0 +1,108 @@
+/* Copyright (C) 2020 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 proc_macro::TokenStream;
+use quote::quote;
+use syn::{self, parse_macro_input, DeriveInput};
+
+pub fn derive_app_layer_event(input: TokenStream) -> TokenStream {
+    let input = parse_macro_input!(input as DeriveInput);
+    let name = input.ident;
+
+    let mut fields = Vec::new();
+    let mut vals = Vec::new();
+    let mut cstrings = Vec::new();
+    let mut names = Vec::new();
+
+    match input.data {
+        syn::Data::Enum(ref data) => {
+            for (i, v) in (&data.variants).into_iter().enumerate() {
+                fields.push(v.ident.clone());
+                let name = transform_name(&v.ident.to_string());
+                let cname = format!("{}\0", name);
+                names.push(name);
+                cstrings.push(cname);
+                vals.push(i as i32);
+            }
+        }
+        _ => panic!("AppLayerEvent can only be derived for enums"),
+    }
+
+    let expanded = quote! {
+        impl crate::applayer::AppLayerEvent for #name {
+            fn from_id(id: i32) -> Option<#name> {
+                match id {
+                    #( #vals => Some(#name::#fields) ,)*
+                    _ => None,
+                }
+            }
+
+            fn as_i32(&self) -> i32 {
+                match *self {
+                    #( #name::#fields => #vals ,)*
+                }
+            }
+
+            fn to_cstring(&self) -> &str {
+                match *self {
+                    #( #name::#fields => #cstrings ,)*
+                }
+            }
+
+            fn from_string(s: &str) -> Option<#name> {
+                match s {
+                    #( #names => Some(#name::#fields) ,)*
+                    _ => None
+                }
+            }
+        }
+    };
+
+    proc_macro::TokenStream::from(expanded)
+}
+
+/// Transform names such as "OneTwoThree" to "one_two_three".
+pub fn transform_name(in_name: &str) -> String {
+    let mut out = String::new();
+    for (i, c) in in_name.chars().enumerate() {
+        if i == 0 {
+            out.push_str(&c.to_lowercase().to_string());
+        } else if c.is_uppercase() {
+            out.push('_');
+            out.push_str(&c.to_lowercase().to_string());
+        } else {
+            out.push(c);
+        }
+    }
+    out
+}
+
+#[cfg(test)]
+mod test {
+    use super::*;
+
+    #[test]
+    fn test_transform_name() {
+        assert_eq!(transform_name("One"), "one".to_string());
+        assert_eq!(transform_name("SomeEvent"), "some_event".to_string());
+        assert_eq!(
+            transform_name("UnassignedMsgType"),
+            "unassigned_msg_type".to_string()
+        );
+    }
+}
diff --git a/rust/derive/src/lib.rs b/rust/derive/src/lib.rs
new file mode 100644 (file)
index 0000000..8d06017
--- /dev/null
@@ -0,0 +1,42 @@
+/* Copyright (C) 2020 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 proc_macro::TokenStream;
+
+mod applayerevent;
+
+/// The `AppLayerEvent` derive macro generates a `AppLayerEvent` trait
+/// implementation for enums that define AppLayerEvents.
+///
+/// Example usage (DNS app-layer events):
+///
+/// #[derive(AppLayerEvent)]
+/// enum {
+///     MalformedData,
+///     NotRequest,
+///     NotResponse,
+///     ZFlagSet,
+/// }
+///
+/// The enum variants must follow the naming convention of OneTwoThree
+/// for proper conversion to the name used in rules (one_tow_three).
+#[proc_macro_derive(AppLayerEvent)]
+pub fn derive_app_layer_event(input: TokenStream) -> TokenStream {
+    applayerevent::derive_app_layer_event(input)
+}