yara_x_capi/lib.rs
1/*! C bindings for the YARA-X library.
2
3This crate defines the C-compatible API that C/C++ programs can use for
4interfacing with the YARA-X Rust library. A header file for this library
5(`yara_x.h`) will be automatically generated by [`cbindgen`][1], during
6compilation, together with dynamic-linking and static-linking versions of
7the library.
8
9# How to build and install
10
11You will need [`cargo-c`][2] for building this library, if you didn't install
12it before, this is the first step:
13```text
14cargo install cargo-c
15```
16
17You will also need the `openssl` library, depending on your platform you
18can choose one of the following methods:
19
20Ubuntu:
21
22```text
23sudo apt install libssl-dev
24```
25
26MacOS (using [`brew`][3]):
27
28```text
29brew install openssl@3
30```
31
32Windows (using [`vcpkg`][4]):
33
34```text
35git clone https://github.com/microsoft/vcpkg.git
36cd vcpkg
37bootstrap-vcpkg.bat
38vcpkg install openssl:x64-windows-static
39set OPENSSL_DIR=%cd%\installed\x64-windows-static
40```
41
42Once you have installed the pre-requisites, go to the root directory
43of the YARA-X repository and type:
44
45```text
46cargo cinstall -p yara-x-capi --release
47```
48
49The command above will put the library and header files in the correct path
50in your system (usually `/usr/local/lib` and `/usr/local/include` for Linux
51and macOS users), and will generate a `.pc` file so that `pkg-config` knows
52about the library.
53
54In Linux and macOS you can check if everything went fine by compiling a simple
55test program, like this:
56
57```text
58cat <<EOF > test.c
59#include <yara_x.h>
60int main() {
61 YRX_RULES* rules;
62 yrx_compile("rule dummy { condition: true }", &rules);
63 yrx_rules_destroy(rules);
64}
65EOF
66```
67
68```text
69gcc `pkg-config --cflags yara_x_capi` `pkg-config --libs yara_x_capi` test.c
70```
71
72The compilation should succeed without errors.
73
74Windows users can find all the files you need for importing the YARA-X library
75in your project in the `target/x86_64-pc-windows-msvc/release` directory. This
76includes:
77
78* A header file (`yara_x.h`)
79* A [module definition file][4] (`yara_x_capi.def`)
80* A DLL file (`yara_x_capi.dll`) with its corresponding import library (`yara_x_capi.dll.lib`)
81* A static library (`yara_x_capi.lib`)
82
83
84[1]: https://github.com/mozilla/cbindgen
85[2]: https://github.com/lu-zero/cargo-c
86[3]: https://brew.sh
87[4]: https://vcpkg.io/
88[4]: https://learn.microsoft.com/en-us/cpp/build/reference/module-definition-dot-def-files
89 */
90
91#![deny(missing_docs)]
92#![allow(non_camel_case_types)]
93#![allow(unsafe_op_in_unsafe_fn)]
94#![allow(clippy::missing_safety_doc)]
95#![allow(clippy::not_unsafe_ptr_arg_deref)]
96
97use std::cell::RefCell;
98use std::ffi::{CStr, CString, c_char};
99use std::ptr::slice_from_raw_parts_mut;
100
101use yara_x::errors::CompileError;
102
103pub use metadata::*;
104pub use pattern::*;
105pub use rule::*;
106pub use rules::*;
107pub use scanner::*;
108
109mod compiler;
110mod metadata;
111mod pattern;
112mod rule;
113mod rules;
114mod scanner;
115
116#[cfg(test)]
117mod tests;
118
119thread_local! {
120 static LAST_ERROR: RefCell<Option<CString>> = const { RefCell::new(None) };
121}
122
123fn _yrx_set_last_error<E>(err: Option<E>)
124where
125 E: ToString,
126{
127 LAST_ERROR.set(err.map(|err| CString::new(err.to_string()).unwrap()))
128}
129
130/// Error codes returned by functions in this API.
131#[derive(PartialEq, Debug)]
132#[repr(C)]
133pub enum YRX_RESULT {
134 /// Everything was OK.
135 YRX_SUCCESS,
136 /// A syntax error occurred while compiling YARA rules.
137 YRX_SYNTAX_ERROR,
138 /// An error occurred while defining or setting a global variable. This may
139 /// happen when a variable is defined twice and when you try to set a value
140 /// that doesn't correspond to the variable's type.
141 YRX_VARIABLE_ERROR,
142 /// An error occurred during a scan operation.
143 YRX_SCAN_ERROR,
144 /// A scan operation was aborted due to a timeout.
145 YRX_SCAN_TIMEOUT,
146 /// An error indicating that some of the arguments passed to a function is
147 /// invalid. Usually indicates a nil pointer to a scanner or compiler.
148 YRX_INVALID_ARGUMENT,
149 /// An error indicating that some of the strings passed to a function is
150 /// not valid UTF-8.
151 YRX_INVALID_UTF8,
152 /// An error indicating that a scanner that was already in multi-block
153 /// mode has been used as a standard scanner.
154 YRX_INVALID_STATE,
155 /// An error occurred while serializing/deserializing YARA rules.
156 YRX_SERIALIZATION_ERROR,
157 /// An error returned when a rule doesn't have any metadata.
158 YRX_NO_METADATA,
159 /// An error returned in cases where some API is not supported because the
160 /// library was not built with the required features.
161 YRX_NOT_SUPPORTED,
162}
163
164/// Returns the error message for the most recent function in this API
165/// invoked by the current thread.
166///
167/// The returned pointer is only valid until this thread calls some other
168/// function, as it can modify the last error and render the pointer to
169/// a previous error message invalid. Also, the pointer will be null if
170/// the most recent function was successfully.
171#[unsafe(no_mangle)]
172pub unsafe extern "C" fn yrx_last_error() -> *const c_char {
173 LAST_ERROR.with_borrow(|err| {
174 if let Some(err) = err { err.as_ptr() } else { std::ptr::null() }
175 })
176}
177
178/// Contains information about a pattern match.
179#[repr(C)]
180pub struct YRX_MATCH {
181 /// Offset within the data where the match occurred.
182 pub offset: usize,
183 /// Length of the match.
184 pub length: usize,
185}
186
187/// Represents a buffer with arbitrary data.
188#[repr(C)]
189pub struct YRX_BUFFER {
190 /// Pointer to the data contained in the buffer.
191 pub data: *mut u8,
192 /// Length of data in bytes.
193 pub length: usize,
194}
195
196impl Drop for YRX_BUFFER {
197 fn drop(&mut self) {
198 unsafe {
199 drop(Box::from_raw(slice_from_raw_parts_mut(
200 self.data,
201 self.length,
202 )));
203 }
204 }
205}
206
207/// Destroys a [`YRX_BUFFER`] object.
208#[unsafe(no_mangle)]
209pub unsafe extern "C" fn yrx_buffer_destroy(buf: *mut YRX_BUFFER) {
210 drop(Box::from_raw(buf));
211}
212
213/// Compiles YARA source code and creates a [`YRX_RULES`] object that contains
214/// the compiled rules.
215///
216/// The rules must be destroyed with [`yrx_rules_destroy`].
217#[unsafe(no_mangle)]
218pub unsafe extern "C" fn yrx_compile(
219 src: *const c_char,
220 rules: &mut *mut YRX_RULES,
221) -> YRX_RESULT {
222 let c_str = CStr::from_ptr(src);
223 match yara_x::compile(c_str.to_bytes()) {
224 Ok(r) => {
225 *rules = Box::into_raw(YRX_RULES::boxed(r));
226 _yrx_set_last_error::<CompileError>(None);
227 YRX_RESULT::YRX_SUCCESS
228 }
229 Err(err) => {
230 _yrx_set_last_error(Some(err));
231 YRX_RESULT::YRX_SYNTAX_ERROR
232 }
233 }
234}
235
236/// Finalizes YARA-X.
237///
238/// This function only needs to be called in a very specific scenario:
239/// when YARA-X is used as a dynamically loaded library (`.so`, `.dll`,
240/// `.dylib`) **and** that library must be unloaded at runtime.
241///
242/// Its primary purpose is to remove the process-wide signal handlers
243/// installed by the [wasmtime] engine.
244///
245/// # Safety
246///
247/// This function is **unsafe** to call under normal circumstances. It has
248/// strict preconditions that must be met:
249///
250/// - There must be no other active `wasmtime` engines in the process. This
251/// applies not only to clones of the engine used by YARA-X (which should not
252/// exist because YARA-X uses a single copy of its engine), but to *any*
253/// `wasmtime` engine, since global state shared by all engines is torn
254/// down.
255///
256/// - On Unix platforms, no other signal handlers may have been installed
257/// for signals intercepted by `wasmtime`. If other handlers have been set,
258/// `wasmtime` cannot reliably restore the original state, which may lead
259/// to undefined behavior.
260///
261/// [wasmtime]: https://wasmtime.dev/
262#[unsafe(no_mangle)]
263pub unsafe extern "C" fn yrx_finalize() {
264 yara_x::finalize();
265}