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
38type RunFunc<S> = unsafe extern "C" fn(
39 S,
40 *mut libddwaf_sys::ddwaf_object,
41 libddwaf_sys::ddwaf_allocator,
42 *mut libddwaf_sys::ddwaf_object,
43 u64,
44) -> libddwaf_sys::DDWAF_RET_CODE;
45
46fn run<S>(
47 raw_self: S,
48 func: RunFunc<S>,
49 mut data: WafMap,
50 timeout: Duration,
51) -> Result<RunResult, RunError> {
52 let mut res = std::mem::MaybeUninit::<RunOutput>::uninit();
53
54 let data_ptr = unsafe { data.as_raw_mut() };
55
56 let status = unsafe {
57 func(
58 raw_self,
59 data_ptr,
60 get_default_allocator().into(),
61 res.as_mut_ptr().cast(),
62 timeout.as_micros().try_into().unwrap_or(u64::MAX),
63 )
64 };
65 match status {
66 libddwaf_sys::DDWAF_ERR_INTERNAL => {
67 std::mem::forget(data);
70 Err(RunError::InternalError)
71 }
72 libddwaf_sys::DDWAF_ERR_INVALID_OBJECT => Err(RunError::InvalidObject),
73 libddwaf_sys::DDWAF_ERR_INVALID_ARGUMENT => Err(RunError::InvalidArgument),
74 libddwaf_sys::DDWAF_OK => {
75 std::mem::forget(data);
77 Ok(RunResult::NoMatch(unsafe { res.assume_init() }))
78 }
79 libddwaf_sys::DDWAF_MATCH => {
80 std::mem::forget(data);
82 Ok(RunResult::Match(unsafe { res.assume_init() }))
83 }
84 unknown => unreachable!(
85 "Unexpected value returned by {}: 0x{:02X}",
86 stringify!(libddwaf_sys::ddwaf_run),
87 unknown
88 ),
89 }
90}
91impl RunnableContext for Context {
92 fn run(&mut self, data: WafMap, timeout: Duration) -> Result<RunResult, RunError> {
93 run(self.raw, libddwaf_sys::ddwaf_context_eval, data, timeout)
94 }
95}
96impl Context {
97 pub fn new_subcontext(&self) -> Result<Subcontext, InternalError> {
103 let raw = unsafe { libddwaf_sys::ddwaf_subcontext_init(self.raw) };
104 if raw.is_null() {
105 Err(InternalError {})
106 } else {
107 Ok(Subcontext { raw })
108 }
109 }
110}
111impl RunnableContext for Subcontext {
112 fn run(&mut self, data: WafMap, timeout: Duration) -> Result<RunResult, RunError> {
113 run(self.raw, libddwaf_sys::ddwaf_subcontext_eval, data, timeout)
114 }
115}
116impl Drop for Context {
117 fn drop(&mut self) {
118 unsafe { libddwaf_sys::ddwaf_context_destroy(self.raw) }
119 }
120}
121impl Drop for Subcontext {
122 fn drop(&mut self) {
123 unsafe { libddwaf_sys::ddwaf_subcontext_destroy(self.raw) }
124 }
125}
126
127unsafe impl Send for Context {}
131unsafe impl Sync for Context {}
135
136unsafe impl Send for Subcontext {}
138unsafe impl Sync for Subcontext {}
140
141#[derive(Debug)]
143pub enum RunResult {
144 NoMatch(RunOutput),
146 Match(RunOutput),
149}
150
151#[non_exhaustive]
153#[derive(Debug)]
154pub enum RunError {
155 InternalError,
157 InvalidObject,
159 InvalidArgument,
161}
162impl fmt::Display for RunError {
163 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
164 match self {
165 RunError::InternalError => write!(f, "The WAF encountered an internal error"),
166 RunError::InvalidObject => write!(f, "The WAF encountered an invalid object"),
167 RunError::InvalidArgument => write!(f, "The WAF encountered an invalid argument"),
168 }
169 }
170}
171impl error::Error for RunError {}
172
173#[derive(Debug)]
175pub struct InternalError {}
176impl fmt::Display for InternalError {
177 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
178 write!(
179 f,
180 "An unexpected internal error occurred in the WAF; check the error logs"
181 )
182 }
183}
184impl error::Error for InternalError {}
185
186#[repr(transparent)]
188pub struct RunOutput {
189 data: WafOwnedOutputAllocator<WafMap>,
190}
191impl RunOutput {
192 #[must_use]
195 pub fn timeout(&self) -> bool {
196 debug_assert!(self.data.is_valid());
197 self.data
198 .get_bstr(b"timeout")
199 .and_then(|o| o.to_bool())
200 .unwrap_or_default()
201 }
202
203 #[must_use]
206 pub fn keep(&self) -> bool {
207 debug_assert!(self.data.is_valid());
208 self.data
209 .get_bstr(b"keep")
210 .and_then(|o| o.to_bool())
211 .unwrap_or_default()
212 }
213
214 pub fn duration(&self) -> Duration {
217 debug_assert!(self.data.is_valid());
218 self.data
219 .get_bstr(b"duration")
220 .and_then(|o| o.to_u64())
221 .map(Duration::from_nanos)
222 .unwrap_or_default()
223 }
224
225 pub fn events(&self) -> Option<&Keyed<WafArray>> {
229 debug_assert!(self.data.is_valid());
230 self.data
231 .get_bstr(b"events")
232 .and_then(Keyed::<WafObject>::as_type)
233 }
234
235 pub fn actions(&self) -> Option<&Keyed<WafMap>> {
239 debug_assert!(self.data.is_valid());
240 self.data
241 .get_bstr(b"actions")
242 .and_then(Keyed::<WafObject>::as_type)
243 }
244
245 pub fn attributes(&self) -> Option<&Keyed<WafMap>> {
248 debug_assert!(self.data.is_valid());
249 self.data
250 .get_bstr(b"attributes")
251 .and_then(Keyed::<WafObject>::as_type)
252 }
253}
254impl fmt::Debug for RunOutput {
255 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
256 f.debug_struct("RunOutput")
257 .field("timeout", &self.timeout())
258 .field("keep", &self.keep())
259 .field("duration", &self.duration())
260 .field("events", &self.events())
261 .field("actions", &self.actions())
262 .field("attributes", &self.attributes())
263 .finish()
264 }
265}