From 27d1ee98ce2c47e747e18242f2b057812f90fd26 Mon Sep 17 00:00:00 2001 From: Jason Ish Date: Sun, 12 Jan 2020 22:56:47 -0600 Subject: [PATCH] rust: derive crate: for custom derives 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 | 2 +- rust/.gitignore | 2 +- rust/Cargo.toml.in | 5 ++ rust/Makefile.am | 2 +- rust/derive/.gitignore | 1 + rust/derive/Cargo.toml.in | 13 ++++ rust/derive/src/applayerevent.rs | 108 +++++++++++++++++++++++++++++++ rust/derive/src/lib.rs | 42 ++++++++++++ 8 files changed, 172 insertions(+), 3 deletions(-) create mode 100644 rust/derive/.gitignore create mode 100644 rust/derive/Cargo.toml.in create mode 100644 rust/derive/src/applayerevent.rs create mode 100644 rust/derive/src/lib.rs diff --git a/configure.ac b/configure.ac index 30d2b4f093..5d9411a46d 100644 --- a/configure.ac +++ b/configure.ac @@ -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) diff --git a/rust/.gitignore b/rust/.gitignore index 09a7c19e6a..1c81617b24 100644 --- a/rust/.gitignore +++ b/rust/.gitignore @@ -1,6 +1,6 @@ !Cargo.toml.in +Cargo.toml /.cargo/config -/Cargo.toml /Cargo.lock /target /vendor diff --git a/rust/Cargo.toml.in b/rust/Cargo.toml.in index 9681b6ac0c..60feabb1d8 100644 --- a/rust/Cargo.toml.in +++ b/rust/Cargo.toml.in @@ -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" diff --git a/rust/Makefile.am b/rust/Makefile.am index a8c360e423..8efd135efc 100644 --- a/rust/Makefile.am +++ b/rust/Makefile.am @@ -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 index 0000000000..ea8c4bf7f3 --- /dev/null +++ b/rust/derive/.gitignore @@ -0,0 +1 @@ +/target diff --git a/rust/derive/Cargo.toml.in b/rust/derive/Cargo.toml.in new file mode 100644 index 0000000000..b60ab198f3 --- /dev/null +++ b/rust/derive/Cargo.toml.in @@ -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 index 0000000000..999fa2ea9c --- /dev/null +++ b/rust/derive/src/applayerevent.rs @@ -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 index 0000000000..8d0601701e --- /dev/null +++ b/rust/derive/src/lib.rs @@ -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) +} -- 2.47.2