yara 0.32.0

Rust bindings for VirusTotal/yara
use std::ffi::{CStr, CString};
use std::os::raw::{c_char, c_int, c_void};
#[cfg(unix)]
use std::os::unix::io::AsRawFd;
#[cfg(windows)]
use std::os::windows::io::AsRawHandle;
use std::path::Path;
use std::ptr;

use yara_sys::{YR_COMPILER, YR_RULE, YR_RULES};

use crate::errors::*;

pub fn compiler_create<'a>() -> Result<&'a mut YR_COMPILER, YaraError> {
    let mut pointer: *mut YR_COMPILER = ptr::null_mut();
    let result = unsafe { yara_sys::yr_compiler_create(&mut pointer) };

    yara_sys::Error::from_code(result)
        .map(|()| unsafe { &mut *pointer })
        .map_err(|e| e.into())
}

pub fn compiler_destroy(compiler_ptr: *mut YR_COMPILER) {
    unsafe {
        yara_sys::yr_compiler_destroy(compiler_ptr);
    }
}

pub fn compiler_add_string(
    compiler: *mut YR_COMPILER,
    string: &str,
    namespace: Option<&str>,
) -> Result<(), Error> {
    let string = CString::new(string).unwrap();
    let namespace = namespace.map(|n| CString::new(n).unwrap());
    let mut errors = Vec::<CompileError>::new();
    unsafe {
        yara_sys::yr_compiler_set_callback(
            compiler,
            Some(compile_callback),
            &mut errors as *mut Vec<_> as _,
        )
    };
    let result = unsafe {
        yara_sys::yr_compiler_add_string(
            compiler,
            string.as_ptr(),
            namespace.as_ref().map_or(ptr::null(), |s| s.as_ptr()),
        )
    };

    compile_result(result, errors)
}

fn compile_result(compile_result: i32, messages: Vec<CompileError>) -> Result<(), Error> {
    if compile_result == 0 || messages.iter().all(|c| c.level != CompileErrorLevel::Error) {
        Ok(())
    } else {
        Err(CompileErrors::new(messages).into())
    }
}

#[cfg(unix)]
pub fn compiler_add_file<P: AsRef<Path>, F: AsRawFd>(
    compiler: *mut YR_COMPILER,
    file: &F,
    path: P,
    namespace: Option<&str>,
) -> Result<(), Error> {
    let path = CString::new(path.as_ref().as_os_str().to_str().unwrap()).unwrap();
    let namespace = namespace.map(|n| CString::new(n).unwrap());
    let mut errors = Vec::<CompileError>::new();
    unsafe {
        yara_sys::yr_compiler_set_callback(
            compiler,
            Some(compile_callback),
            &mut errors as *mut Vec<_> as _,
        )
    };

    let fd = file.as_raw_fd();
    let result = unsafe {
        yara_sys::yr_compiler_add_fd(
            compiler,
            fd,
            namespace.as_ref().map_or(ptr::null(), |n| n.as_ptr()),
            path.as_ptr(),
        )
    };
    compile_result(result, errors)
}

#[cfg(windows)]
pub fn compiler_add_file<P: AsRef<Path>, F: AsRawHandle>(
    compiler: *mut YR_COMPILER,
    file: &F,
    path: P,
    namespace: Option<&str>,
) -> Result<(), Error> {
    let path = CString::new(path.as_ref().as_os_str().to_str().unwrap()).unwrap();
    let namespace = namespace.map(|n| CString::new(n).unwrap());
    let mut errors = Vec::<CompileError>::new();
    unsafe {
        yara_sys::yr_compiler_set_callback(
            compiler,
            Some(compile_callback),
            &mut errors as *mut Vec<_> as _,
        )
    };

    let handle = file.as_raw_handle();
    let result = unsafe {
        yara_sys::yr_compiler_add_fd(
            compiler,
            handle,
            namespace.as_ref().map_or(ptr::null(), |n| n.as_ptr()),
            path.as_ptr(),
        )
    };
    compile_result(result, errors)
}

extern "C" fn compile_callback(
    error_level: c_int,
    filename: *const c_char,
    line_number: c_int,
    _rule: *const YR_RULE,
    message: *const c_char,
    user_data: *mut c_void,
) {
    let errors: &mut Vec<CompileError> = unsafe { &mut *(user_data as *mut Vec<CompileError>) };
    let message = unsafe { CStr::from_ptr(message) }.to_str().unwrap();
    let filename = if !filename.is_null() {
        Some(unsafe { CStr::from_ptr(filename) }.to_str().unwrap())
    } else {
        None
    };
    errors.push(CompileError {
        level: CompileErrorLevel::from_code(error_level),
        filename: filename.map(|s| s.to_string()),
        line: line_number as usize,
        message: message.to_owned(),
    });
}

pub fn compiler_define_integer_variable(
    compiler: *mut YR_COMPILER,
    identifier: &str,
    value: i64,
) -> Result<(), YaraError> {
    let identifier = CString::new(identifier).unwrap();
    let result = unsafe {
        yara_sys::yr_compiler_define_integer_variable(compiler, identifier.as_ptr(), value)
    };
    yara_sys::Error::from_code(result).map_err(Into::into)
}

pub fn compiler_define_float_variable(
    compiler: *mut YR_COMPILER,
    identifier: &str,
    value: f64,
) -> Result<(), YaraError> {
    let identifier = CString::new(identifier).unwrap();
    let result = unsafe {
        yara_sys::yr_compiler_define_float_variable(compiler, identifier.as_ptr(), value)
    };
    yara_sys::Error::from_code(result).map_err(Into::into)
}

pub fn compiler_define_boolean_variable(
    compiler: *mut YR_COMPILER,
    identifier: &str,
    value: bool,
) -> Result<(), YaraError> {
    let identifier = CString::new(identifier).unwrap();
    let value = i32::from(value);
    let result = unsafe {
        yara_sys::yr_compiler_define_boolean_variable(compiler, identifier.as_ptr(), value)
    };
    yara_sys::Error::from_code(result).map_err(Into::into)
}

pub fn compiler_define_str_variable(
    compiler: *mut YR_COMPILER,
    identifier: &str,
    value: &str,
) -> Result<(), YaraError> {
    let identifier = CString::new(identifier).unwrap();
    let value = CString::new(value).unwrap();
    let result = unsafe {
        yara_sys::yr_compiler_define_string_variable(compiler, identifier.as_ptr(), value.as_ptr())
    };
    yara_sys::Error::from_code(result).map_err(Into::into)
}

pub fn compiler_define_cstr_variable(
    compiler: *mut YR_COMPILER,
    identifier: &str,
    value: &CStr,
) -> Result<(), YaraError> {
    let identifier = CString::new(identifier).unwrap();
    let result = unsafe {
        yara_sys::yr_compiler_define_string_variable(compiler, identifier.as_ptr(), value.as_ptr())
    };
    yara_sys::Error::from_code(result).map_err(Into::into)
}

pub fn compiler_get_rules(compiler: *mut YR_COMPILER) -> Result<*mut YR_RULES, YaraError> {
    let mut pointer = ptr::null_mut();
    let result = unsafe { yara_sys::yr_compiler_get_rules(compiler, &mut pointer) };

    yara_sys::Error::from_code(result)
        .map(|()| pointer)
        .map_err(Into::into)
}