falco_plugin/plugin/parse/
wrappers.rs

1use crate::parse::EventInput;
2use crate::plugin::base::PluginWrapper;
3use crate::plugin::error::ffi_result::FfiResult;
4use crate::plugin::parse::{ParseInput, ParsePlugin};
5use falco_plugin_api::plugin_api__bindgen_ty_3 as parse_plugin_api;
6use falco_plugin_api::{
7    ss_plugin_event_input, ss_plugin_event_parse_input, ss_plugin_rc,
8    ss_plugin_rc_SS_PLUGIN_FAILURE, ss_plugin_t,
9};
10use std::any::TypeId;
11use std::collections::BTreeMap;
12use std::ffi::{c_char, CString};
13use std::sync::Mutex;
14
15/// Marker trait to mark a parse plugin as exported to the API
16///
17/// # Safety
18///
19/// Only implement this trait if you export the plugin either statically or dynamically
20/// to the plugin API. This is handled by the `parse_plugin!` and `static_plugin!` macros, so you
21/// should never need to implement this trait manually.
22#[diagnostic::on_unimplemented(
23    message = "Parse plugin is not exported",
24    note = "use either `parse_plugin!` or `static_plugin!`"
25)]
26pub unsafe trait ParsePluginExported {}
27
28pub trait ParsePluginFallbackApi {
29    const PARSE_API: parse_plugin_api = parse_plugin_api {
30        get_parse_event_types: None,
31        get_parse_event_sources: None,
32        parse_event: None,
33    };
34
35    const IMPLEMENTS_PARSE: bool = false;
36}
37impl<T> ParsePluginFallbackApi for T {}
38
39#[allow(missing_debug_implementations)]
40pub struct ParsePluginApi<T>(std::marker::PhantomData<T>);
41
42impl<T: ParsePlugin + 'static> ParsePluginApi<T> {
43    pub const PARSE_API: parse_plugin_api = parse_plugin_api {
44        get_parse_event_types: Some(plugin_get_parse_event_types::<T>),
45        get_parse_event_sources: Some(plugin_get_parse_event_sources::<T>),
46        parse_event: Some(plugin_parse_event::<T>),
47    };
48
49    pub const IMPLEMENTS_PARSE: bool = true;
50}
51
52/// # Safety
53///
54/// All pointers must be valid
55pub unsafe extern "C-unwind" fn plugin_get_parse_event_types<T: ParsePlugin>(
56    numtypes: *mut u32,
57    _plugin: *mut ss_plugin_t,
58) -> *mut u16 {
59    let types = T::EVENT_TYPES;
60    if let Some(numtypes) = numtypes.as_mut() {
61        *numtypes = types.len() as u32;
62        types.as_ptr() as *const u16 as *mut u16 // this should ****really**** be const
63    } else {
64        std::ptr::null_mut()
65    }
66}
67
68//noinspection DuplicatedCode
69pub extern "C-unwind" fn plugin_get_parse_event_sources<T: ParsePlugin + 'static>() -> *const c_char
70{
71    static SOURCES: Mutex<BTreeMap<TypeId, CString>> = Mutex::new(BTreeMap::new());
72
73    let ty = TypeId::of::<T>();
74    let mut sources_map = SOURCES.lock().unwrap();
75    // we only generate the string once and never change or delete it
76    // so the pointer should remain valid for the static lifetime
77    sources_map
78        .entry(ty)
79        .or_insert_with(|| {
80            let sources = serde_json::to_string(T::EVENT_SOURCES)
81                .expect("failed to serialize event source array");
82            CString::new(sources.into_bytes()).expect("failed to add NUL to event source array")
83        })
84        .as_ptr()
85}
86
87/// # Safety
88///
89/// All pointers must be valid
90pub unsafe extern "C-unwind" fn plugin_parse_event<T: ParsePlugin>(
91    plugin: *mut ss_plugin_t,
92    event: *const ss_plugin_event_input,
93    parse_input: *const ss_plugin_event_parse_input,
94) -> ss_plugin_rc {
95    let plugin = plugin as *mut PluginWrapper<T>;
96    unsafe {
97        let Some(plugin) = plugin.as_mut() else {
98            return ss_plugin_rc_SS_PLUGIN_FAILURE;
99        };
100        let Some(ref mut actual_plugin) = &mut plugin.plugin else {
101            return ss_plugin_rc_SS_PLUGIN_FAILURE;
102        };
103
104        let Some(event) = event.as_ref() else {
105            return ss_plugin_rc_SS_PLUGIN_FAILURE;
106        };
107        let event = EventInput(*event);
108
109        let Ok(parse_input) = ParseInput::try_from(parse_input, actual_plugin.last_error.clone())
110        else {
111            return ss_plugin_rc_SS_PLUGIN_FAILURE;
112        };
113
114        actual_plugin
115            .plugin
116            .parse_event(&event, &parse_input)
117            .rc(&mut plugin.error_buf)
118    }
119}
120
121/// # Register an event parsing plugin
122///
123/// This macro must be called at most once in a crate (it generates public functions with fixed
124/// `#[no_mangle]` names) with a type implementing [`ParsePlugin`] as the sole parameter.
125#[macro_export]
126macro_rules! parse_plugin {
127    ($ty:ty) => {
128        unsafe impl $crate::internals::parse::wrappers::ParsePluginExported for $ty {}
129
130        $crate::wrap_ffi! {
131            #[no_mangle]
132            use $crate::internals::parse::wrappers: <$ty>;
133
134            unsafe fn plugin_get_parse_event_types(
135                numtypes: *mut u32,
136                plugin: *mut falco_plugin::api::ss_plugin_t,
137            ) -> *mut u16;
138            unsafe fn plugin_get_parse_event_sources() -> *const ::std::ffi::c_char;
139            unsafe fn plugin_parse_event(
140                plugin: *mut falco_plugin::api::ss_plugin_t,
141                event_input: *const falco_plugin::api::ss_plugin_event_input,
142                parse_input: *const falco_plugin::api::ss_plugin_event_parse_input,
143            ) -> i32;
144        }
145
146        #[allow(dead_code)]
147        fn __typecheck_plugin_parse_api() -> falco_plugin::api::plugin_api__bindgen_ty_3 {
148            falco_plugin::api::plugin_api__bindgen_ty_3 {
149                get_parse_event_types: Some(plugin_get_parse_event_types),
150                get_parse_event_sources: Some(plugin_get_parse_event_sources),
151                parse_event: Some(plugin_parse_event),
152            }
153        }
154    };
155}