Skip to main content

falco_plugin/extract/
wrappers.rs

1use crate::base::wrappers::PluginWrapper;
2use crate::error::ffi_result::FfiResult;
3use crate::error::panic::catch_panic;
4use crate::event::EventInput;
5use crate::extract::ExtractPlugin;
6use crate::tables::LazyTableReader;
7use falco_event::events::AnyEventPayload;
8use falco_plugin_api::plugin_api__bindgen_ty_2 as extract_plugin_api;
9use falco_plugin_api::ss_plugin_rc;
10use falco_plugin_api::{ss_plugin_event_input, ss_plugin_rc_SS_PLUGIN_FAILURE};
11use falco_plugin_api::{ss_plugin_field_extract_input, ss_plugin_t};
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 an extract 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 `extract_plugin!` and `static_plugin!` macros, so you
25/// should never need to implement this trait manually.
26#[diagnostic::on_unimplemented(
27    message = "Extract plugin is not exported",
28    note = "use either `extract_plugin!` or `static_plugin!`"
29)]
30pub unsafe trait ExtractPluginExported {}
31
32pub trait ExtractPluginFallbackApi {
33    const EXTRACT_API: extract_plugin_api = extract_plugin_api {
34        get_extract_event_types: None,
35        get_extract_event_sources: None,
36        get_fields: None,
37        extract_fields: None,
38    };
39
40    const IMPLEMENTS_EXTRACT: bool = false;
41}
42impl<T> ExtractPluginFallbackApi for T {}
43
44#[allow(missing_debug_implementations)]
45pub struct ExtractPluginApi<T>(std::marker::PhantomData<T>);
46
47impl<T: ExtractPlugin> ExtractPluginApi<T> {
48    pub const EXTRACT_API: extract_plugin_api = extract_plugin_api {
49        get_extract_event_types: Some(plugin_get_extract_event_types::<T>),
50        get_extract_event_sources: Some(plugin_get_extract_event_sources::<T>),
51        get_fields: Some(plugin_get_fields::<T>),
52        extract_fields: Some(plugin_extract_fields::<T>),
53    };
54
55    pub const IMPLEMENTS_EXTRACT: bool = true;
56}
57
58pub extern "C" fn plugin_get_fields<T: ExtractPlugin>() -> *const c_char {
59    T::get_fields().as_ptr()
60}
61
62/// # Safety
63///
64/// All pointers must be valid
65pub unsafe extern "C" fn plugin_get_extract_event_types<T: ExtractPlugin>(
66    numtypes: *mut u32,
67    _plugin: *mut ss_plugin_t,
68) -> *mut u16 {
69    let types = T::Event::EVENT_TYPES;
70    unsafe { *numtypes = types.len() as u32 };
71    types.as_ptr().cast_mut() // TODO(spec): this should ****really**** be const
72}
73
74//noinspection DuplicatedCode
75pub extern "C" fn plugin_get_extract_event_sources<T: ExtractPlugin>() -> *const c_char {
76    static SOURCES: Mutex<BTreeMap<TypeId, CString>> = Mutex::new(BTreeMap::new());
77    let ty = TypeId::of::<T>();
78    let mut sources_map = SOURCES.lock().unwrap();
79    // we only generate the string once and never change or delete it
80    // so the pointer should remain valid for the static lifetime
81    sources_map
82        .entry(ty)
83        .or_insert_with(|| {
84            let sources = serde_json::to_string(T::Event::event_sources().as_slice())
85                .expect("failed to serialize event source array");
86            CString::new(sources.into_bytes()).expect("failed to add NUL to event source array")
87        })
88        .as_ptr()
89}
90
91/// # Safety
92///
93/// All pointers must be valid
94pub unsafe extern "C" fn plugin_extract_fields<T: ExtractPlugin>(
95    plugin: *mut ss_plugin_t,
96    event_input: *const ss_plugin_event_input,
97    extract_input: *const ss_plugin_field_extract_input,
98) -> ss_plugin_rc {
99    let plugin = plugin as *mut PluginWrapper<T>;
100    unsafe {
101        let Some(plugin) = plugin.as_mut() else {
102            return ss_plugin_rc_SS_PLUGIN_FAILURE;
103        };
104        let Some(actual_plugin) = &mut plugin.plugin else {
105            return ss_plugin_rc_SS_PLUGIN_FAILURE;
106        };
107
108        let Some(event_input) = event_input.as_ref() else {
109            return ss_plugin_rc_SS_PLUGIN_FAILURE;
110        };
111        let event_input = EventInput(*event_input, PhantomData);
112
113        let Some(extract_input) = extract_input.as_ref() else {
114            return ss_plugin_rc_SS_PLUGIN_FAILURE;
115        };
116
117        let fields =
118            std::slice::from_raw_parts_mut(extract_input.fields, extract_input.num_fields as usize);
119
120        let Some(reader_ext) = extract_input.table_reader_ext.as_ref() else {
121            return ss_plugin_rc_SS_PLUGIN_FAILURE;
122        };
123
124        let offsets = extract_input.value_offsets.as_mut();
125
126        let table_reader = LazyTableReader::new(reader_ext, actual_plugin.last_error.clone());
127
128        plugin.field_storage.reset();
129        catch_panic(AssertUnwindSafe(|| {
130            actual_plugin.plugin.extract_fields(
131                &event_input,
132                &table_reader,
133                fields,
134                offsets,
135                &plugin.field_storage,
136            )
137        }))
138        .rc(&mut plugin.error_buf)
139    }
140}
141
142/// # Register an extract plugin
143///
144/// This macro must be called at most once in a crate (it generates public functions with fixed
145/// `#[no_mangle`] names) with a type implementing [`ExtractPlugin`] as the sole parameter.
146#[macro_export]
147macro_rules! extract_plugin {
148    ($ty:ty) => {
149        unsafe impl $crate::extract::wrappers::ExtractPluginExported for $ty {}
150
151        $crate::wrap_ffi! {
152            #[unsafe(no_mangle)]
153            use $crate::extract::wrappers: <$ty>;
154
155            unsafe fn plugin_get_extract_event_sources() -> *const ::std::ffi::c_char;
156            unsafe fn plugin_get_extract_event_types(
157                numtypes: *mut u32,
158                plugin: *mut falco_plugin::api::ss_plugin_t,
159            ) -> *mut u16;
160            unsafe fn plugin_get_fields() -> *const ::std::ffi::c_char;
161            unsafe fn plugin_extract_fields(
162                plugin: *mut falco_plugin::api::ss_plugin_t,
163                event_input: *const falco_plugin::api::ss_plugin_event_input,
164                extract_input: *const falco_plugin::api::ss_plugin_field_extract_input,
165            ) -> i32;
166        }
167
168        #[allow(dead_code)]
169        fn __typecheck_plugin_extract_api() -> falco_plugin::api::plugin_api__bindgen_ty_2 {
170            falco_plugin::api::plugin_api__bindgen_ty_2 {
171                get_extract_event_sources: Some(plugin_get_extract_event_sources),
172                get_extract_event_types: Some(plugin_get_extract_event_types),
173                get_fields: Some(plugin_get_fields),
174                extract_fields: Some(plugin_extract_fields),
175            }
176        }
177    };
178}