falco_plugin/plugin/extract/
wrappers.rs

1use crate::plugin::base::PluginWrapper;
2use crate::plugin::error::ffi_result::FfiResult;
3use crate::plugin::event::EventInput;
4use crate::plugin::extract::ExtractPlugin;
5use crate::tables::LazyTableReader;
6use falco_plugin_api::plugin_api__bindgen_ty_2 as extract_plugin_api;
7use falco_plugin_api::ss_plugin_rc;
8use falco_plugin_api::{ss_plugin_event_input, ss_plugin_rc_SS_PLUGIN_FAILURE};
9use falco_plugin_api::{ss_plugin_field_extract_input, ss_plugin_t};
10use std::any::TypeId;
11use std::collections::BTreeMap;
12use std::ffi::{c_char, CString};
13use std::sync::Mutex;
14
15/// Marker trait to mark an extract 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 `extract_plugin!` and `static_plugin!` macros, so you
21/// should never need to implement this trait manually.
22#[diagnostic::on_unimplemented(
23    message = "Extract plugin is not exported",
24    note = "use either `extract_plugin!` or `static_plugin!`"
25)]
26pub unsafe trait ExtractPluginExported {}
27
28pub trait ExtractPluginFallbackApi {
29    const EXTRACT_API: extract_plugin_api = extract_plugin_api {
30        get_extract_event_types: None,
31        get_extract_event_sources: None,
32        get_fields: None,
33        extract_fields: None,
34    };
35
36    const IMPLEMENTS_EXTRACT: bool = false;
37}
38impl<T> ExtractPluginFallbackApi for T {}
39
40#[allow(missing_debug_implementations)]
41pub struct ExtractPluginApi<T>(std::marker::PhantomData<T>);
42
43impl<T: ExtractPlugin> ExtractPluginApi<T> {
44    pub const EXTRACT_API: extract_plugin_api = extract_plugin_api {
45        get_extract_event_types: Some(plugin_get_extract_event_types::<T>),
46        get_extract_event_sources: Some(plugin_get_extract_event_sources::<T>),
47        get_fields: Some(plugin_get_fields::<T>),
48        extract_fields: Some(plugin_extract_fields::<T>),
49    };
50
51    pub const IMPLEMENTS_EXTRACT: bool = true;
52}
53
54pub extern "C-unwind" fn plugin_get_fields<T: ExtractPlugin>() -> *const c_char {
55    T::get_fields().as_ptr()
56}
57
58/// # Safety
59///
60/// All pointers must be valid
61pub unsafe extern "C-unwind" fn plugin_get_extract_event_types<T: ExtractPlugin>(
62    numtypes: *mut u32,
63    _plugin: *mut ss_plugin_t,
64) -> *mut u16 {
65    let types = T::EVENT_TYPES;
66    unsafe { *numtypes = types.len() as u32 };
67    types.as_ptr() as *const u16 as *mut u16 // TODO(spec): this should ****really**** be const
68}
69
70//noinspection DuplicatedCode
71pub extern "C-unwind" fn plugin_get_extract_event_sources<T: ExtractPlugin>() -> *const c_char {
72    static SOURCES: Mutex<BTreeMap<TypeId, CString>> = Mutex::new(BTreeMap::new());
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_extract_fields<T: ExtractPlugin>(
91    plugin: *mut ss_plugin_t,
92    event_input: *const ss_plugin_event_input,
93    extract_input: *const ss_plugin_field_extract_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_input) = event_input.as_ref() else {
105            return ss_plugin_rc_SS_PLUGIN_FAILURE;
106        };
107        let event_input = EventInput(*event_input);
108
109        let Some(extract_input) = extract_input.as_ref() else {
110            return ss_plugin_rc_SS_PLUGIN_FAILURE;
111        };
112
113        let fields =
114            std::slice::from_raw_parts_mut(extract_input.fields, extract_input.num_fields as usize);
115
116        let Some(reader_ext) = extract_input.table_reader_ext.as_ref() else {
117            return ss_plugin_rc_SS_PLUGIN_FAILURE;
118        };
119
120        let table_reader = LazyTableReader::new(reader_ext, actual_plugin.last_error.clone());
121
122        plugin.field_storage.reset();
123        actual_plugin
124            .plugin
125            .extract_fields(
126                &event_input,
127                &table_reader,
128                fields,
129                &mut plugin.field_storage,
130            )
131            .rc(&mut plugin.error_buf)
132    }
133}
134
135/// # Register an extract plugin
136///
137/// This macro must be called at most once in a crate (it generates public functions with fixed
138/// `#[no_mangle`] names) with a type implementing [`ExtractPlugin`] as the sole parameter.
139#[macro_export]
140macro_rules! extract_plugin {
141    ($ty:ty) => {
142        unsafe impl $crate::internals::extract::wrappers::ExtractPluginExported for $ty {}
143
144        $crate::wrap_ffi! {
145            #[no_mangle]
146            use $crate::internals::extract::wrappers: <$ty>;
147
148            unsafe fn plugin_get_extract_event_sources() -> *const ::std::ffi::c_char;
149            unsafe fn plugin_get_extract_event_types(
150                numtypes: *mut u32,
151                plugin: *mut falco_plugin::api::ss_plugin_t,
152            ) -> *mut u16;
153            unsafe fn plugin_get_fields() -> *const ::std::ffi::c_char;
154            unsafe fn plugin_extract_fields(
155                plugin: *mut falco_plugin::api::ss_plugin_t,
156                event_input: *const falco_plugin::api::ss_plugin_event_input,
157                extract_input: *const falco_plugin::api::ss_plugin_field_extract_input,
158            ) -> i32;
159        }
160
161        #[allow(dead_code)]
162        fn __typecheck_plugin_extract_api() -> falco_plugin::api::plugin_api__bindgen_ty_2 {
163            falco_plugin::api::plugin_api__bindgen_ty_2 {
164                get_extract_event_sources: Some(plugin_get_extract_event_sources),
165                get_extract_event_types: Some(plugin_get_extract_event_types),
166                get_fields: Some(plugin_get_fields),
167                extract_fields: Some(plugin_extract_fields),
168            }
169        }
170    };
171}