use crate::extract::EventInput;
use crate::plugin::base::Plugin;
use crate::plugin::extract::schema::ExtractFieldInfo;
use crate::plugin::extract::wrappers::ExtractPluginExported;
use crate::tables::LazyTableReader;
use falco_event::events::{AnyEventPayload, RawEvent};
use falco_plugin_api::{ss_plugin_extract_field, ss_plugin_extract_value_offsets};
use std::any::TypeId;
use std::collections::BTreeMap;
use std::ffi::{CStr, CString};
use std::ops::Range;
use std::sync::Mutex;
mod extractor_fn;
pub mod fields;
pub mod schema;
#[doc(hidden)]
pub mod wrappers;
#[derive(Debug, Clone, Eq, PartialEq)]
pub enum ExtractFieldRequestArg<'a> {
None,
Int(u64),
String(&'a CStr),
}
pub trait ExtractField {
unsafe fn key_unchecked(&self) -> ExtractFieldRequestArg<'_>;
}
impl ExtractField for ss_plugin_extract_field {
unsafe fn key_unchecked(&self) -> ExtractFieldRequestArg<'_> {
if self.arg_present == 0 {
return ExtractFieldRequestArg::None;
}
if self.arg_key.is_null() {
return ExtractFieldRequestArg::Int(self.arg_index);
}
unsafe { ExtractFieldRequestArg::String(CStr::from_ptr(self.arg_key)) }
}
}
#[allow(clippy::reversed_empty_ranges)]
pub const INVALID_RANGE: Range<usize> = 1..0;
pub const UNSPECIFIED_RANGE: Range<usize> = 0..0;
const PLUGIN_EVENT_PAYLOAD_OFFSET: usize = 38;
#[derive(Debug, Eq, PartialEq)]
pub enum ExtractByteRange {
NotRequested,
Requested,
Found(Range<usize>),
}
impl ExtractByteRange {
pub fn in_plugin_data(range: Range<usize>) -> Self {
Self::Found(
PLUGIN_EVENT_PAYLOAD_OFFSET + range.start..PLUGIN_EVENT_PAYLOAD_OFFSET + range.end,
)
}
}
#[derive(Debug)]
pub struct ExtractRequest<'c, 'e, 'r, 't, P: ExtractPlugin> {
pub context: &'c mut P::ExtractContext,
pub event: &'e EventInput<'r, P::Event<'r>>,
pub table_reader: &'t LazyTableReader<'t>,
pub offset: &'c mut ExtractByteRange,
}
pub trait ExtractPlugin: Plugin + ExtractPluginExported + Sized
where
Self: 'static,
{
type Event<'a>: AnyEventPayload + TryFrom<&'a RawEvent<'a>>;
type ExtractContext: Default + 'static;
const EXTRACT_FIELDS: &'static [ExtractFieldInfo<Self>];
fn get_fields() -> &'static CStr {
static FIELD_SCHEMA: Mutex<BTreeMap<TypeId, CString>> = Mutex::new(BTreeMap::new());
let ty = TypeId::of::<Self>();
let mut schema_map = FIELD_SCHEMA.lock().unwrap();
unsafe {
CStr::from_ptr(
schema_map
.entry(ty)
.or_insert_with(|| {
let schema = serde_json::to_string_pretty(&Self::EXTRACT_FIELDS)
.expect("failed to serialize extraction schema");
CString::new(schema.into_bytes())
.expect("failed to add NUL to extraction schema")
})
.as_ptr(),
)
}
}
fn extract_fields<'a>(
&'a mut self,
event_input: &EventInput<'a, Self::Event<'a>>,
table_reader: &LazyTableReader,
fields: &mut [ss_plugin_extract_field],
offsets: Option<&mut ss_plugin_extract_value_offsets>,
storage: &'a bumpalo::Bump,
) -> Result<(), anyhow::Error> {
let mut context = Self::ExtractContext::default();
let (mut offset_vec, mut length_vec) = if offsets.is_some() {
(
Some(bumpalo::collections::Vec::with_capacity_in(
fields.len(),
storage,
)),
Some(bumpalo::collections::Vec::with_capacity_in(
fields.len(),
storage,
)),
)
} else {
(None, None)
};
let mut any_offsets = false;
for req in fields {
let info = Self::EXTRACT_FIELDS
.get(req.field_id as usize)
.ok_or_else(|| anyhow::anyhow!("field index out of bounds"))?;
let mut offset = if offsets.is_some() {
ExtractByteRange::Requested
} else {
ExtractByteRange::NotRequested
};
let request = ExtractRequest::<Self> {
context: &mut context,
event: event_input,
table_reader,
offset: &mut offset,
};
info.func.call(self, req, request, storage)?;
if let (Some(offsets_vec), Some(lengths_vec)) =
(offset_vec.as_mut(), length_vec.as_mut())
{
let range = match offset {
ExtractByteRange::Found(range) => {
any_offsets = true;
range
}
_ => INVALID_RANGE,
};
offsets_vec.push(range.start as u32);
lengths_vec.push(range.end.wrapping_sub(range.start) as u32);
}
}
fn pointer_to_vec<T>(v: &Option<bumpalo::collections::Vec<T>>) -> *mut T {
match v {
None => std::ptr::null_mut(),
Some(v) => v.as_ptr().cast_mut(),
}
}
if let Some(offsets) = offsets {
if any_offsets {
offsets.start = pointer_to_vec(&offset_vec);
offsets.length = pointer_to_vec(&length_vec);
}
}
Ok(())
}
}