1use std::error;
2use std::fmt;
3use std::time::Duration;
4
5use crate::object::get_default_allocator;
6use crate::object::WafOwnedOutputAllocator;
7use crate::object::{AsRawMutObject, Keyed, WafArray, WafMap, WafObject};
8
9pub struct Context {
14 pub(crate) raw: libddwaf_sys::ddwaf_context,
15}
16
17pub struct Subcontext {
24 pub(crate) raw: libddwaf_sys::ddwaf_subcontext,
25}
26
27pub trait RunnableContext {
29 fn run(&mut self, data: WafMap, timeout: Duration) -> Result<RunResult, RunError>;
36
37 fn run_batches(&mut self, data: WafArray, timeout: Duration) -> Result<RunResult, RunError>;
46}
47
48type RunFunc<S> = unsafe extern "C" fn(
49 S,
50 *mut libddwaf_sys::ddwaf_object,
51 libddwaf_sys::ddwaf_allocator,
52 *mut libddwaf_sys::ddwaf_object,
53 u64,
54) -> libddwaf_sys::DDWAF_RET_CODE;
55
56fn run<S>(
57 raw_self: S,
58 func: RunFunc<S>,
59 func_name: &'static str,
60 mut data: impl AsRawMutObject,
61 timeout: Duration,
62) -> Result<RunResult, RunError> {
63 let mut res = std::mem::MaybeUninit::<RunOutput>::uninit();
64
65 let data_ptr = unsafe { data.as_raw_mut() };
66
67 let status = unsafe {
68 func(
69 raw_self,
70 data_ptr,
71 get_default_allocator().into(),
72 res.as_mut_ptr().cast(),
73 timeout.as_micros().try_into().unwrap_or(u64::MAX),
74 )
75 };
76 match status {
77 libddwaf_sys::DDWAF_ERR_INTERNAL => {
78 std::mem::forget(data);
81 Err(RunError::InternalError)
82 }
83 libddwaf_sys::DDWAF_ERR_INVALID_OBJECT => {
84 std::mem::forget(data);
86 Err(RunError::InvalidObject)
87 }
88 libddwaf_sys::DDWAF_ERR_INVALID_ARGUMENT => Err(RunError::InvalidArgument),
89 libddwaf_sys::DDWAF_OK => {
90 std::mem::forget(data);
92 Ok(RunResult::NoMatch(unsafe { res.assume_init() }))
93 }
94 libddwaf_sys::DDWAF_MATCH => {
95 std::mem::forget(data);
97 Ok(RunResult::Match(unsafe { res.assume_init() }))
98 }
99 unknown => unreachable!(
100 "Unexpected value returned by {}: 0x{:02X}",
101 func_name,
102 unknown
103 ),
104 }
105}
106impl RunnableContext for Context {
107 fn run(&mut self, data: WafMap, timeout: Duration) -> Result<RunResult, RunError> {
108 run(
109 self.raw,
110 libddwaf_sys::ddwaf_context_eval,
111 stringify!(libddwaf_sys::ddwaf_context_eval),
112 data,
113 timeout,
114 )
115 }
116
117 fn run_batches(&mut self, data: WafArray, timeout: Duration) -> Result<RunResult, RunError> {
118 run(
119 self.raw,
120 libddwaf_sys::ddwaf_context_multieval,
121 stringify!(libddwaf_sys::ddwaf_context_multieval),
122 data,
123 timeout,
124 )
125 }
126}
127impl Context {
128 pub fn new_subcontext(&self) -> Result<Subcontext, InternalError> {
134 let raw = unsafe { libddwaf_sys::ddwaf_subcontext_init(self.raw) };
135 if raw.is_null() {
136 Err(InternalError {})
137 } else {
138 Ok(Subcontext { raw })
139 }
140 }
141}
142impl RunnableContext for Subcontext {
143 fn run(&mut self, data: WafMap, timeout: Duration) -> Result<RunResult, RunError> {
144 run(
145 self.raw,
146 libddwaf_sys::ddwaf_subcontext_eval,
147 stringify!(libddwaf_sys::ddwaf_subcontext_eval),
148 data,
149 timeout,
150 )
151 }
152
153 fn run_batches(&mut self, data: WafArray, timeout: Duration) -> Result<RunResult, RunError> {
154 run(
155 self.raw,
156 libddwaf_sys::ddwaf_subcontext_multieval,
157 stringify!(libddwaf_sys::ddwaf_subcontext_multieval),
158 data,
159 timeout,
160 )
161 }
162}
163impl Drop for Context {
164 fn drop(&mut self) {
165 unsafe { libddwaf_sys::ddwaf_context_destroy(self.raw) }
166 }
167}
168impl Drop for Subcontext {
169 fn drop(&mut self) {
170 unsafe { libddwaf_sys::ddwaf_subcontext_destroy(self.raw) }
171 }
172}
173
174unsafe impl Send for Context {}
178unsafe impl Sync for Context {}
182
183unsafe impl Send for Subcontext {}
185unsafe impl Sync for Subcontext {}
187
188#[derive(Debug)]
190pub enum RunResult {
191 NoMatch(RunOutput),
193 Match(RunOutput),
196}
197
198#[non_exhaustive]
200#[derive(Debug)]
201pub enum RunError {
202 InternalError,
204 InvalidObject,
206 InvalidArgument,
208}
209impl fmt::Display for RunError {
210 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
211 match self {
212 RunError::InternalError => write!(f, "The WAF encountered an internal error"),
213 RunError::InvalidObject => write!(f, "The WAF encountered an invalid object"),
214 RunError::InvalidArgument => write!(f, "The WAF encountered an invalid argument"),
215 }
216 }
217}
218impl error::Error for RunError {}
219
220#[derive(Debug)]
222pub struct InternalError {}
223impl fmt::Display for InternalError {
224 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
225 write!(
226 f,
227 "An unexpected internal error occurred in the WAF; check the error logs"
228 )
229 }
230}
231impl error::Error for InternalError {}
232
233#[repr(transparent)]
235pub struct RunOutput {
236 data: WafOwnedOutputAllocator<WafMap>,
237}
238impl RunOutput {
239 #[must_use]
242 pub fn timeout(&self) -> bool {
243 debug_assert!(self.data.is_valid());
244 self.data
245 .get_bstr(b"timeout")
246 .and_then(|o| o.to_bool())
247 .unwrap_or_default()
248 }
249
250 #[must_use]
253 pub fn keep(&self) -> bool {
254 debug_assert!(self.data.is_valid());
255 self.data
256 .get_bstr(b"keep")
257 .and_then(|o| o.to_bool())
258 .unwrap_or_default()
259 }
260
261 pub fn duration(&self) -> Duration {
264 debug_assert!(self.data.is_valid());
265 self.data
266 .get_bstr(b"duration")
267 .and_then(|o| o.to_u64())
268 .map(Duration::from_nanos)
269 .unwrap_or_default()
270 }
271
272 #[must_use]
274 pub fn evaluated(&self) -> u64 {
275 debug_assert!(self.data.is_valid());
276 self.data
277 .get_bstr(b"evaluated")
278 .and_then(|o| o.to_u64())
279 .unwrap_or_default()
280 }
281
282 pub fn events(&self) -> Option<&Keyed<WafArray>> {
286 debug_assert!(self.data.is_valid());
287 self.data
288 .get_bstr(b"events")
289 .and_then(Keyed::<WafObject>::as_type)
290 }
291
292 pub fn actions(&self) -> Option<&Keyed<WafMap>> {
296 debug_assert!(self.data.is_valid());
297 self.data
298 .get_bstr(b"actions")
299 .and_then(Keyed::<WafObject>::as_type)
300 }
301
302 pub fn attributes(&self) -> Option<&Keyed<WafMap>> {
305 debug_assert!(self.data.is_valid());
306 self.data
307 .get_bstr(b"attributes")
308 .and_then(Keyed::<WafObject>::as_type)
309 }
310}
311impl fmt::Debug for RunOutput {
312 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
313 f.debug_struct("RunOutput")
314 .field("timeout", &self.timeout())
315 .field("keep", &self.keep())
316 .field("duration", &self.duration())
317 .field("evaluated", &self.evaluated())
318 .field("events", &self.events())
319 .field("actions", &self.actions())
320 .field("attributes", &self.attributes())
321 .finish()
322 }
323}