diff --git a/.gitignore b/.gitignore index 2f3d22706..a4c6fa1f1 100644 --- a/.gitignore +++ b/.gitignore @@ -228,12 +228,13 @@ libclamav/c++/llvm/tools/llvmc/plugins/Base/Base.td debug/ target/ -# Generated by libclamav_rust/build.rs in IDEs -libclamav_rust/clamav_rust.h - # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html Cargo.lock # These are backup files generated by rustfmt **/*.rs.bk + +# The name of our generated ClamAV-Rust bindings file. +libclamav_rust/src/sys.rs + diff --git a/libclamav/others.h b/libclamav/others.h index bbdcc8349..76f9be212 100644 --- a/libclamav/others.h +++ b/libclamav/others.h @@ -856,6 +856,12 @@ inline void cli_dbgmsg(const char *str, ...) __attribute__((format(printf, 1, 2) inline void cli_dbgmsg(const char *str, ...); #endif +#ifdef __GNUC__ +void cli_dbgmsg_no_inline(const char *str, ...) __attribute__((format(printf, 1, 2))); +#else +void cli_dbgmsg_no_inline(const char *str, ...); +#endif + #ifdef HAVE_CLI_GETPAGESIZE #undef HAVE_CLI_GETPAGESIZE #endif diff --git a/libclamav/others_common.c b/libclamav/others_common.c index 241e0aa48..3cf42836f 100644 --- a/libclamav/others_common.c +++ b/libclamav/others_common.c @@ -180,6 +180,14 @@ inline void cli_dbgmsg(const char *str, ...) } } +void cli_dbgmsg_no_inline(const char *str, ...) +{ + if (UNLIKELY(cli_get_debug_flag())) { + MSGCODE(buff, len, "LibClamAV debug: "); + fputs(buff, stderr); + } +} + int cli_matchregex(const char *str, const char *regex) { regex_t reg; diff --git a/libclamav_rust/Cargo.toml b/libclamav_rust/Cargo.toml index e46eb1bef..fdd5ac4d2 100644 --- a/libclamav_rust/Cargo.toml +++ b/libclamav_rust/Cargo.toml @@ -19,3 +19,4 @@ name = "clamav_rust" [build-dependencies] cbindgen = "0.20" +bindgen = "0.59" diff --git a/libclamav_rust/build.rs b/libclamav_rust/build.rs index 710ed00ac..eb30454eb 100644 --- a/libclamav_rust/build.rs +++ b/libclamav_rust/build.rs @@ -1,6 +1,8 @@ use std::env; use std::path::{Path, PathBuf}; +use bindgen::builder; + // Note to maintainers: this is currently a hybrid of examination of the // CMake environment, and leaning on Rust `cfg` elements. Ideally, it // should be possible to work in this space (e.g., execute tests from an @@ -14,7 +16,7 @@ use std::path::{Path, PathBuf}; // A list of environment variables to query to determine additional libraries // that need to be linked to resolve dependencies. -const LIB_ENV_LINK: [&str; 12] = [ +const LIB_ENV_LINK: &[&str] = &[ "LIBSSL", "LIBCRYPTO", "LIBZ", @@ -30,13 +32,47 @@ const LIB_ENV_LINK: [&str; 12] = [ ]; // The same, but additional values to check on Windows platforms -const LIB_ENV_LINK_WINDOWS: [&str; 2] = ["LIBPTHREADW32", "LIBWIN32COMPAT"]; +const LIB_ENV_LINK_WINDOWS: &[&str] = &["LIBPTHREADW32", "LIBWIN32COMPAT"]; // Additional [verbatim] libraries to link on Windows platforms -const LIB_LINK_WINDOWS: [&str; 4] = ["wsock32", "ws2_32", "Shell32", "User32"]; +const LIB_LINK_WINDOWS: &[&str] = &["wsock32", "ws2_32", "Shell32", "User32"]; // Windows library names that must have the leading `lib` trimmed (if encountered) -const WINDOWS_TRIM_LOCAL_LIB: [&str; 2] = ["libclamav", "libclammspack"]; +const WINDOWS_TRIM_LOCAL_LIB: &[&str] = &["libclamav", "libclammspack"]; + +// Generate bindings for these functions: +const BINDGEN_FUNCTIONS: &[&str] = &[ + "cli_ctx", + "cli_warnmsg", + "cli_dbgmsg_no_inline", + "cli_infomsg_simple", + "cli_errmsg", + "cli_append_virus", + "cli_versig2", + "cli_getdsig", + "cli_get_debug_flag", +]; + +// Generate bindings for these types (structs, enums): +const BINDGEN_TYPES: &[&str] = &["cli_matcher", "cli_ac_data", "cli_ac_result"]; + +// Find the required functions and types in these headers: +const BINDGEN_HEADERS: &[&str] = &[ + "../libclamav/matcher.h", + "../libclamav/matcher-ac.h", + "../libclamav/others.h", + "../libclamav/dsig.h", +]; + +// Find the required headers in these directories: +const BINDGEN_INCLUDE_PATHS: &[&str] = &[ + "-I../libclamav", + "-I../libclamunrar_iface", + "-I../libclammspack", +]; + +// Write the bindings to this file: +const BINDGEN_OUTPUT_FILE: &str = "src/sys.rs"; const C_HEADER_OUTPUT: &str = "clamav_rust.h"; @@ -58,14 +94,54 @@ fn main() -> Result<(), &'static str> { // We only want to execute cbindgen for `cargo build`, not `cargo test`. // FindRust.cmake defines $CARGO_CMD so we can differentiate. - if "build" == env::var("CARGO_CMD").or(Ok("".to_string()))? { + let cargo_cmd = env::var("CARGO_CMD").unwrap_or("".into()); + if cargo_cmd == "build" { + execute_bindgen()?; execute_cbindgen()?; } else { - eprintln!("NOTE: Not performing cbindgen as CARGO_CMD != build"); + eprintln!("NOTE: Not generating bindings because CARGO_CMD != build"); } + Ok(()) } +/// Use bindgen to generate Rust bindings to call into C libraries. +fn execute_bindgen() -> Result<(), &'static str> { + let build_dir = PathBuf::from(env::var("CARGO_TARGET_DIR").unwrap_or(".".into())); + let build_include_path = format!("-I{}", build_dir.join("..").to_str().unwrap()); + + // Configure and generate bindings. + let mut builder = builder() + // Silence code-style warnings for generated bindings. + .raw_line("#![allow(non_snake_case, non_camel_case_types, non_upper_case_globals)]") + // Make the bindings pretty. + .rustfmt_bindings(true) + // Enable bindgen to find generated headers in the build directory, too. + .clang_arg(build_include_path); + + for &include_path in BINDGEN_INCLUDE_PATHS { + builder = builder.clang_arg(include_path); + } + for &header in BINDGEN_HEADERS { + builder = builder.header(header); + } + for &c_function in BINDGEN_FUNCTIONS { + builder = builder.allowlist_function(c_function); + } + for &c_type in BINDGEN_TYPES { + builder = builder.allowlist_type(c_type); + } + + // Generate! + let bindings = builder.generate().unwrap(); + + // Write the generated bindings to an output file. + bindings.write_to_file(BINDGEN_OUTPUT_FILE).unwrap(); + + Ok(()) +} + +/// Use cbindgen to generate C-header's for Rust static libraries. fn execute_cbindgen() -> Result<(), &'static str> { let crate_dir = env::var("CARGO_MANIFEST_DIR").or(Err("CARGO_MANIFEST_DIR not specified"))?; let build_dir = PathBuf::from(env::var("CARGO_TARGET_DIR").unwrap_or(".".into())); @@ -86,15 +162,15 @@ fn detect_clamav_build() -> Result<(), &'static str> { if search_and_link_lib("LIBCLAMAV")? { eprintln!("NOTE: LIBCLAMAV defined. Examining LIB* environment variables"); // Need to link with libclamav dependencies - for var in &LIB_ENV_LINK { + for var in LIB_ENV_LINK { let _ = search_and_link_lib(var); } if cfg!(windows) { - for var in &LIB_ENV_LINK_WINDOWS { + for var in LIB_ENV_LINK_WINDOWS { let _ = search_and_link_lib(var); } - for lib in &LIB_LINK_WINDOWS { + for lib in LIB_LINK_WINDOWS { println!("cargo:rustc-link-lib={}", lib); } } else { diff --git a/libclamav_rust/cbindgen.toml b/libclamav_rust/cbindgen.toml index 1da598531..61fb06e8d 100644 --- a/libclamav_rust/cbindgen.toml +++ b/libclamav_rust/cbindgen.toml @@ -17,15 +17,8 @@ after_includes = "" [export] include = ["cdiff_apply", "script2cdiff"] -exclude = [ - "cli_dbgmsg", - "cli_errmsg", - "cli_infomsg_simple", - "cli_warnmsg", - "cli_versig2", - "cli_get_debug_flag", - "cli_getdsig", -] +exclude = [] + # prefix = "CAPI_" item_types = [] renaming_overrides_prefixing = false diff --git a/libclamav_rust/src/cdiff.rs b/libclamav_rust/src/cdiff.rs index 4e8cf4dc1..f8d3383c0 100644 --- a/libclamav_rust/src/cdiff.rs +++ b/libclamav_rust/src/cdiff.rs @@ -24,7 +24,7 @@ use std::{ fs::{self, File, OpenOptions}, io::{prelude::*, BufReader, BufWriter, Read, Seek, SeekFrom, Write}, iter::*, - os::raw::{c_char, c_uchar}, + os::raw::c_char, process, }; @@ -34,6 +34,8 @@ use log::{debug, error, warn}; use sha2::{Digest, Sha256}; use thiserror::Error; +use crate::sys; + /// Maximum size of a digital signature const MAX_SIG_SIZE: usize = 350; @@ -255,28 +257,9 @@ impl<'a> XchgOp<'a> { } } -extern "C" { - fn cli_versig2( - digest: *const c_uchar, - dsig: *const c_char, - n: *const c_char, - e: *const c_char, - ) -> i32; - - fn cli_getdsig( - host: *const u8, - user: *const u8, - data: *const u8, - datalen: u32, - mode: u8, - ) -> *const c_char; - - fn cli_get_debug_flag() -> u8; -} - fn is_debug_enabled() -> bool { unsafe { - let debug_flag = cli_get_debug_flag(); + let debug_flag = sys::cli_get_debug_flag(); // Return true if debug_flag is not 0 !matches!(debug_flag, 0) } @@ -418,9 +401,9 @@ pub fn script2cdiff(script_file_name: &str, builder: &str, server: &str) -> Resu // These strings should not contain interior NULs let server = CString::new(server).unwrap(); let builder = CString::new(builder).unwrap(); - let dsig_ptr = cli_getdsig( - server.as_c_str().as_ptr() as *const u8, - builder.as_c_str().as_ptr() as *const u8, + let dsig_ptr = sys::cli_getdsig( + server.as_c_str().as_ptr() as *const c_char, + builder.as_c_str().as_ptr() as *const c_char, sha256.to_vec().as_ptr(), 32, 2, @@ -511,7 +494,7 @@ pub fn cdiff_apply(file: &mut File, mode: ApplyMode) -> Result<(), CdiffError> { let n = CString::new(PUBLIC_KEY_MODULUS).unwrap(); let e = CString::new(PUBLIC_KEY_EXPONENT).unwrap(); let versig_result = unsafe { - cli_versig2( + sys::cli_versig2( sha256.to_vec().as_ptr(), dsig_cstring.as_ptr(), n.as_ptr() as *const c_char, diff --git a/libclamav_rust/src/lib.rs b/libclamav_rust/src/lib.rs index 7e311e260..59f8071d4 100644 --- a/libclamav_rust/src/lib.rs +++ b/libclamav_rust/src/lib.rs @@ -20,6 +20,9 @@ * MA 02110-1301, USA. */ +/// cbindgen:ignore +pub mod sys; + pub mod cdiff; pub mod logging; pub mod util; diff --git a/libclamav_rust/src/logging.rs b/libclamav_rust/src/logging.rs index 3cd4844da..ca6835e91 100644 --- a/libclamav_rust/src/logging.rs +++ b/libclamav_rust/src/logging.rs @@ -21,16 +21,10 @@ */ use std::ffi::CString; -use std::os::raw::c_char; use log::{set_max_level, Level, LevelFilter, Metadata, Record}; -extern "C" { - fn cli_warnmsg(str: *const c_char, ...) -> (); - fn cli_dbgmsg(str: *const c_char, ...) -> (); - fn cli_infomsg_simple(str: *const c_char, ...) -> (); - fn cli_errmsg(str: *const c_char, ...) -> (); -} +use crate::sys; pub struct ClamLogger; @@ -46,16 +40,16 @@ impl log::Log for ClamLogger { match record.level() { Level::Debug => unsafe { - cli_dbgmsg(ptr); + sys::cli_dbgmsg_no_inline(ptr); }, Level::Error => unsafe { - cli_errmsg(ptr); + sys::cli_errmsg(ptr); }, Level::Info => unsafe { - cli_infomsg_simple(ptr); + sys::cli_infomsg_simple(ptr); }, Level::Warn => unsafe { - cli_warnmsg(ptr); + sys::cli_warnmsg(ptr); }, _ => {} }