use std::ffi::{CStr, c_char};
use std::mem;
use std::mem::ManuallyDrop;
use yara_x::SourceCode;
use yara_x::errors::{CompileError, SerializationError, VariableError};
use crate::{_yrx_set_last_error, YRX_BUFFER, YRX_RESULT, YRX_RULES};
pub struct YRX_COMPILER<'a> {
inner: yara_x::Compiler<'a>,
flags: u32,
}
pub const YRX_COLORIZE_ERRORS: u32 = 1;
pub const YRX_RELAXED_RE_SYNTAX: u32 = 2;
pub const YRX_ERROR_ON_SLOW_PATTERN: u32 = 4;
pub const YRX_ERROR_ON_SLOW_LOOP: u32 = 8;
pub const YRX_ENABLE_CONDITION_OPTIMIZATION: u32 = 16;
pub const YRX_DISABLE_INCLUDES: u32 = 32;
fn _yrx_compiler_create<'a>(flags: u32) -> yara_x::Compiler<'a> {
let mut compiler = yara_x::Compiler::new();
if flags & YRX_RELAXED_RE_SYNTAX != 0 {
compiler.relaxed_re_syntax(true);
}
if flags & YRX_ENABLE_CONDITION_OPTIMIZATION != 0 {
compiler.condition_optimization(true);
}
if flags & YRX_COLORIZE_ERRORS != 0 {
compiler.colorize_errors(true);
}
if flags & YRX_ERROR_ON_SLOW_PATTERN != 0 {
compiler.error_on_slow_pattern(true);
}
if flags & YRX_ERROR_ON_SLOW_LOOP != 0 {
compiler.error_on_slow_loop(true);
}
if flags & YRX_DISABLE_INCLUDES != 0 {
compiler.enable_includes(false);
}
compiler
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn yrx_compiler_create(
flags: u32,
compiler: &mut *mut YRX_COMPILER,
) -> YRX_RESULT {
*compiler = Box::into_raw(Box::new(YRX_COMPILER {
inner: _yrx_compiler_create(flags),
flags,
}));
YRX_RESULT::YRX_SUCCESS
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn yrx_compiler_destroy(compiler: *mut YRX_COMPILER) {
drop(Box::from_raw(compiler))
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn yrx_compiler_add_source(
compiler: *mut YRX_COMPILER,
src: *const c_char,
) -> YRX_RESULT {
yrx_compiler_add_source_with_origin(compiler, src, std::ptr::null())
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn yrx_compiler_add_source_with_origin(
compiler: *mut YRX_COMPILER,
src: *const c_char,
origin: *const c_char,
) -> YRX_RESULT {
let compiler = if let Some(compiler) = compiler.as_mut() {
compiler
} else {
return YRX_RESULT::YRX_INVALID_ARGUMENT;
};
let src = CStr::from_ptr(src);
let mut src = SourceCode::from(src.to_bytes());
if !origin.is_null() {
let origin = CStr::from_ptr(origin);
src = match origin.to_str() {
Ok(origin) => src.with_origin(origin),
Err(_) => return YRX_RESULT::YRX_INVALID_ARGUMENT,
};
}
match compiler.inner.add_source(src) {
Ok(_) => {
_yrx_set_last_error::<CompileError>(None);
YRX_RESULT::YRX_SUCCESS
}
Err(err) => {
_yrx_set_last_error(Some(err));
YRX_RESULT::YRX_SYNTAX_ERROR
}
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn yrx_compiler_add_include_dir(
compiler: *mut YRX_COMPILER,
dir: *const c_char,
) -> YRX_RESULT {
let compiler = if let Some(compiler) = compiler.as_mut() {
compiler
} else {
return YRX_RESULT::YRX_INVALID_ARGUMENT;
};
let dir = if let Ok(dir) = CStr::from_ptr(dir).to_str() {
dir
} else {
return YRX_RESULT::YRX_INVALID_ARGUMENT;
};
compiler.inner.add_include_dir(dir);
YRX_RESULT::YRX_SUCCESS
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn yrx_compiler_ignore_module(
compiler: *mut YRX_COMPILER,
module: *const c_char,
) -> YRX_RESULT {
let compiler = if let Some(compiler) = compiler.as_mut() {
compiler
} else {
return YRX_RESULT::YRX_INVALID_ARGUMENT;
};
let module = if let Ok(module) = CStr::from_ptr(module).to_str() {
module
} else {
return YRX_RESULT::YRX_INVALID_ARGUMENT;
};
compiler.inner.ignore_module(module);
YRX_RESULT::YRX_SUCCESS
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn yrx_compiler_max_warnings(
compiler: *mut YRX_COMPILER,
n: usize,
) -> YRX_RESULT {
let compiler = if let Some(compiler) = compiler.as_mut() {
compiler
} else {
return YRX_RESULT::YRX_INVALID_ARGUMENT;
};
compiler.inner.max_warnings(n);
YRX_RESULT::YRX_SUCCESS
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn yrx_compiler_enable_feature(
compiler: *mut YRX_COMPILER,
feature: *const c_char,
) -> YRX_RESULT {
let compiler = if let Some(compiler) = compiler.as_mut() {
compiler
} else {
return YRX_RESULT::YRX_INVALID_ARGUMENT;
};
let feature = if let Ok(module) = CStr::from_ptr(feature).to_str() {
module
} else {
return YRX_RESULT::YRX_INVALID_ARGUMENT;
};
compiler.inner.enable_feature(feature);
YRX_RESULT::YRX_SUCCESS
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn yrx_compiler_ban_module(
compiler: *mut YRX_COMPILER,
module: *const c_char,
error_title: *const c_char,
error_msg: *const c_char,
) -> YRX_RESULT {
let compiler = if let Some(compiler) = compiler.as_mut() {
compiler
} else {
return YRX_RESULT::YRX_INVALID_ARGUMENT;
};
let module = if let Ok(module) = CStr::from_ptr(module).to_str() {
module
} else {
return YRX_RESULT::YRX_INVALID_ARGUMENT;
};
let err_title = if let Ok(err_title) = CStr::from_ptr(error_title).to_str()
{
err_title
} else {
return YRX_RESULT::YRX_INVALID_ARGUMENT;
};
let err_msg = if let Ok(err_msg) = CStr::from_ptr(error_msg).to_str() {
err_msg
} else {
return YRX_RESULT::YRX_INVALID_ARGUMENT;
};
compiler.inner.ban_module(module, err_title, err_msg);
YRX_RESULT::YRX_SUCCESS
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn yrx_compiler_new_namespace(
compiler: *mut YRX_COMPILER,
namespace: *const c_char,
) -> YRX_RESULT {
let compiler = if let Some(compiler) = compiler.as_mut() {
compiler
} else {
return YRX_RESULT::YRX_INVALID_ARGUMENT;
};
let namespace = if let Ok(namespace) = CStr::from_ptr(namespace).to_str() {
namespace
} else {
return YRX_RESULT::YRX_INVALID_ARGUMENT;
};
compiler.inner.new_namespace(namespace);
YRX_RESULT::YRX_SUCCESS
}
unsafe fn yrx_compiler_define_global<
T: TryInto<yara_x::Variable, Error = yara_x::errors::VariableError>,
>(
compiler: *mut YRX_COMPILER,
ident: *const c_char,
value: T,
) -> YRX_RESULT {
let compiler = if let Some(compiler) = compiler.as_mut() {
compiler
} else {
return YRX_RESULT::YRX_INVALID_ARGUMENT;
};
let ident = if let Ok(ident) = CStr::from_ptr(ident).to_str() {
ident
} else {
return YRX_RESULT::YRX_INVALID_ARGUMENT;
};
match compiler.inner.define_global(ident, value) {
Ok(_) => {
_yrx_set_last_error::<VariableError>(None);
YRX_RESULT::YRX_SUCCESS
}
Err(err) => {
_yrx_set_last_error(Some(err));
YRX_RESULT::YRX_VARIABLE_ERROR
}
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn yrx_compiler_define_global_str(
compiler: *mut YRX_COMPILER,
ident: *const c_char,
value: *const c_char,
) -> YRX_RESULT {
let value = if let Ok(value) = CStr::from_ptr(value).to_str() {
value
} else {
return YRX_RESULT::YRX_INVALID_ARGUMENT;
};
yrx_compiler_define_global(compiler, ident, value)
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn yrx_compiler_define_global_bool(
compiler: *mut YRX_COMPILER,
ident: *const c_char,
value: bool,
) -> YRX_RESULT {
yrx_compiler_define_global(compiler, ident, value)
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn yrx_compiler_define_global_int(
compiler: *mut YRX_COMPILER,
ident: *const c_char,
value: i64,
) -> YRX_RESULT {
yrx_compiler_define_global(compiler, ident, value)
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn yrx_compiler_define_global_float(
compiler: *mut YRX_COMPILER,
ident: *const c_char,
value: f64,
) -> YRX_RESULT {
yrx_compiler_define_global(compiler, ident, value)
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn yrx_compiler_define_global_json(
compiler: *mut YRX_COMPILER,
ident: *const c_char,
value: *const c_char,
) -> YRX_RESULT {
let value = if let Ok(value) = CStr::from_ptr(value).to_str() {
value
} else {
return YRX_RESULT::YRX_INVALID_ARGUMENT;
};
let value: serde_json::Value = match serde_json::from_str(value) {
Ok(json_value) => json_value,
Err(_) => return YRX_RESULT::YRX_INVALID_ARGUMENT,
};
yrx_compiler_define_global(compiler, ident, value)
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn yrx_compiler_errors_json(
compiler: *mut YRX_COMPILER,
buf: &mut *mut YRX_BUFFER,
) -> YRX_RESULT {
let compiler = if let Some(compiler) = compiler.as_mut() {
compiler
} else {
return YRX_RESULT::YRX_INVALID_ARGUMENT;
};
match serde_json::to_vec(compiler.inner.errors()) {
Ok(json) => {
let json = json.into_boxed_slice();
let mut json = ManuallyDrop::new(json);
*buf = Box::into_raw(Box::new(YRX_BUFFER {
data: json.as_mut_ptr(),
length: json.len(),
}));
_yrx_set_last_error::<SerializationError>(None);
YRX_RESULT::YRX_SUCCESS
}
Err(err) => {
_yrx_set_last_error(Some(err));
YRX_RESULT::YRX_SERIALIZATION_ERROR
}
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn yrx_compiler_warnings_json(
compiler: *mut YRX_COMPILER,
buf: &mut *mut YRX_BUFFER,
) -> YRX_RESULT {
let compiler = if let Some(compiler) = compiler.as_mut() {
compiler
} else {
return YRX_RESULT::YRX_INVALID_ARGUMENT;
};
match serde_json::to_vec(compiler.inner.warnings()) {
Ok(json) => {
let json = json.into_boxed_slice();
let mut json = ManuallyDrop::new(json);
*buf = Box::into_raw(Box::new(YRX_BUFFER {
data: json.as_mut_ptr(),
length: json.len(),
}));
_yrx_set_last_error::<SerializationError>(None);
YRX_RESULT::YRX_SUCCESS
}
Err(err) => {
_yrx_set_last_error(Some(err));
YRX_RESULT::YRX_SERIALIZATION_ERROR
}
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn yrx_compiler_build(
compiler: *mut YRX_COMPILER,
) -> *mut YRX_RULES {
let compiler = if let Some(compiler) = compiler.as_mut() {
compiler
} else {
return std::ptr::null_mut();
};
let compiler = mem::replace(
&mut compiler.inner,
_yrx_compiler_create(compiler.flags),
);
Box::into_raw(YRX_RULES::boxed(compiler.build()))
}