Skip to main content

falco_plugin/parse/
wrappers.rs

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