1use std::error;
2use std::fmt;
3use std::ptr::null_mut;
4use std::time::Duration;
5
6use crate::object::{AsRawMutObject, Keyed, WafArray, WafMap, WafObject, WafOwned};
7
8pub struct Context {
13 pub(crate) raw: libddwaf_sys::ddwaf_context,
14 pub(crate) keepalive: Vec<WafMap>,
15}
16impl Context {
17 pub fn run(
24 &mut self,
25 mut persistent_data: Option<WafMap>,
26 ephemeral_data: Option<&WafMap>,
27 timeout: Duration,
28 ) -> Result<RunResult, RunError> {
29 let mut res = std::mem::MaybeUninit::<RunOutput>::uninit();
30 let persistent_ref = persistent_data
31 .as_mut()
32 .map_or(null_mut(), |f| unsafe {
34 std::ptr::from_mut(f.as_raw_mut()).cast()
35 });
36 let ephemeral_ref = ephemeral_data
37 .map(AsRef::<libddwaf_sys::ddwaf_object>::as_ref)
38 .map_or(null_mut(), |r| std::ptr::from_ref(r).cast_mut());
40
41 let status = unsafe {
42 libddwaf_sys::ddwaf_run(
43 self.raw,
44 persistent_ref,
45 ephemeral_ref,
46 res.as_mut_ptr().cast(),
47 timeout.as_micros().try_into().unwrap_or(u64::MAX),
48 )
49 };
50 match status {
51 libddwaf_sys::DDWAF_ERR_INTERNAL => {
52 if let Some(obj) = persistent_data {
55 self.keepalive.push(obj);
56 }
57 Err(RunError::InternalError)
58 }
59 libddwaf_sys::DDWAF_ERR_INVALID_OBJECT => Err(RunError::InvalidObject),
60 libddwaf_sys::DDWAF_ERR_INVALID_ARGUMENT => Err(RunError::InvalidArgument),
61 libddwaf_sys::DDWAF_OK => {
62 if let Some(obj) = persistent_data {
64 self.keepalive.push(obj);
65 }
66 Ok(RunResult::NoMatch(unsafe { res.assume_init() }))
67 }
68 libddwaf_sys::DDWAF_MATCH => {
69 if let Some(obj) = persistent_data {
71 self.keepalive.push(obj);
72 }
73 Ok(RunResult::Match(unsafe { res.assume_init() }))
74 }
75 unknown => unreachable!(
76 "Unexpected value returned by {}: 0x{:02X}",
77 stringify!(libddwaf_sys::ddwaf_run),
78 unknown
79 ),
80 }
81 }
82}
83impl Drop for Context {
84 fn drop(&mut self) {
85 unsafe { libddwaf_sys::ddwaf_context_destroy(self.raw) }
86 }
87}
88unsafe impl Send for Context {}
91unsafe impl Sync for Context {}
93
94#[derive(Debug)]
96pub enum RunResult {
97 NoMatch(RunOutput),
99 Match(RunOutput),
102}
103
104#[non_exhaustive]
106#[derive(Debug)]
107pub enum RunError {
108 InternalError,
110 InvalidObject,
112 InvalidArgument,
114}
115impl fmt::Display for RunError {
116 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
117 match self {
118 RunError::InternalError => write!(f, "The WAF encountered an internal error"),
119 RunError::InvalidObject => write!(f, "The WAF encountered an invalid object"),
120 RunError::InvalidArgument => write!(f, "The WAF encountered an invalid argument"),
121 }
122 }
123}
124impl error::Error for RunError {}
125
126#[repr(transparent)]
128pub struct RunOutput {
129 data: WafOwned<WafMap>,
130}
131impl RunOutput {
132 #[must_use]
135 pub fn timeout(&self) -> bool {
136 debug_assert!(self.data.is_valid());
137 self.data
138 .get(b"timeout")
139 .and_then(|o| o.to_bool())
140 .unwrap_or_default()
141 }
142
143 #[must_use]
146 pub fn keep(&self) -> bool {
147 debug_assert!(self.data.is_valid());
148 self.data
149 .get(b"keep")
150 .and_then(|o| o.to_bool())
151 .unwrap_or_default()
152 }
153
154 pub fn duration(&self) -> Duration {
157 debug_assert!(self.data.is_valid());
158 self.data
159 .get(b"duration")
160 .and_then(|o| o.to_u64())
161 .map(Duration::from_nanos)
162 .unwrap_or_default()
163 }
164
165 pub fn events(&self) -> Option<&Keyed<WafArray>> {
169 debug_assert!(self.data.is_valid());
170 self.data
171 .get(b"events")
172 .and_then(Keyed::<WafObject>::as_type)
173 }
174
175 pub fn actions(&self) -> Option<&Keyed<WafMap>> {
179 debug_assert!(self.data.is_valid());
180 self.data
181 .get(b"actions")
182 .and_then(Keyed::<WafObject>::as_type)
183 }
184
185 pub fn attributes(&self) -> Option<&Keyed<WafMap>> {
188 debug_assert!(self.data.is_valid());
189 self.data
190 .get(b"attributes")
191 .and_then(Keyed::<WafObject>::as_type)
192 }
193}
194impl fmt::Debug for RunOutput {
195 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
196 f.debug_struct("RunOutput")
197 .field("timeout", &self.timeout())
198 .field("keep", &self.keep())
199 .field("duration", &self.duration())
200 .field("events", &self.events())
201 .field("actions", &self.actions())
202 .field("attributes", &self.attributes())
203 .finish()
204 }
205}