use std::ffi::CStr;
use std::os::raw::{c_char, c_void};
use coraza_sys::*;
use crate::matched_rule::{LogLevel, MatchedRule, Severity};
pub(crate) struct CallbackContext {
#[allow(clippy::type_complexity)]
pub debug_log: Option<Box<dyn Fn(LogLevel, &str, &str) + Send>>,
pub error: Option<Box<dyn Fn(MatchedRule) + Send>>,
}
impl CallbackContext {
pub fn new() -> Self {
Self {
debug_log: None,
error: None,
}
}
}
pub(crate) unsafe extern "C" fn debug_log_trampoline(
ctx: *mut c_void,
level: coraza_debug_log_level_t,
msg: *const c_char,
fields: *const c_char,
) {
if ctx.is_null() {
return;
}
let ctx = unsafe { &*(ctx as *const CallbackContext) };
if let Some(ref cb) = ctx.debug_log {
let msg_str;
let msg = if msg.is_null() {
""
} else {
msg_str = unsafe { CStr::from_ptr(msg) }.to_string_lossy();
&msg_str
};
let fields_str;
let fields = if fields.is_null() {
""
} else {
fields_str = unsafe { CStr::from_ptr(fields) }.to_string_lossy();
&fields_str
};
cb(LogLevel::from(level), msg, fields);
}
}
pub(crate) unsafe extern "C" fn error_trampoline(ctx: *mut c_void, rule: coraza_matched_rule_t) {
if ctx.is_null() {
return;
}
let ctx = unsafe { &mut *(ctx as *mut CallbackContext) };
if let Some(ref cb) = ctx.error {
let matched = extract_matched_rule(rule);
cb(matched);
}
}
unsafe fn extract_matched_rule(rule: coraza_matched_rule_t) -> MatchedRule {
let severity = unsafe { coraza_matched_rule_get_severity(rule) };
let rule_id = unsafe { coraza_matched_rule_get_id(rule) };
let error_log = unsafe { coraza_matched_rule_get_error_log(rule) };
let message = if error_log.is_null() {
String::new()
} else {
let s = unsafe { CStr::from_ptr(error_log) }
.to_string_lossy()
.into_owned();
unsafe { coraza_free_string(error_log) };
s
};
MatchedRule {
message,
severity: Severity::from(severity),
rule_id,
uri: String::new(), client_ip: String::new(), server_ip: String::new(), disruptive: rule_id != 0, }
}