Use bindgen to generate Rust-bindings for some libclamav internal
functions and structures in an new "sys.rs" module.
"sys.rs" will be generated at build-time and unfortunately must be
dropped in the source/libclamav_rust/src directory rather than under the
build directory. As far as I know, Cargo/Rust provide no way to set an
include path to the build directory to separately place generated files.
TODO: Verify if this is true and move sys.rs to the build directory if
possible.
Using the new bindings with:
- the logging module.
- the cdiff module.
Also:
- Removed clamav_rust.h from .gitignore, because we generate it in the
build directory now.
- Removed the hand-written bindings from the cbindgen exclusions.
lib.rs has an annotation that prevents cbindgen from looking at sys.rs.
- Fixed a `u8` -> `c_char` type issue in cdiff in the cli_getdsig() call
parameters.
| ... | ... |
@@ -228,12 +228,13 @@ libclamav/c++/llvm/tools/llvmc/plugins/Base/Base.td |
| 228 | 228 |
debug/ |
| 229 | 229 |
target/ |
| 230 | 230 |
|
| 231 |
-# Generated by libclamav_rust/build.rs in IDEs |
|
| 232 |
-libclamav_rust/clamav_rust.h |
|
| 233 |
- |
|
| 234 | 231 |
# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries |
| 235 | 232 |
# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html |
| 236 | 233 |
Cargo.lock |
| 237 | 234 |
|
| 238 | 235 |
# These are backup files generated by rustfmt |
| 239 | 236 |
**/*.rs.bk |
| 237 |
+ |
|
| 238 |
+# The name of our generated ClamAV-Rust bindings file. |
|
| 239 |
+libclamav_rust/src/sys.rs |
|
| 240 |
+ |
| ... | ... |
@@ -856,6 +856,12 @@ inline void cli_dbgmsg(const char *str, ...) __attribute__((format(printf, 1, 2) |
| 856 | 856 |
inline void cli_dbgmsg(const char *str, ...); |
| 857 | 857 |
#endif |
| 858 | 858 |
|
| 859 |
+#ifdef __GNUC__ |
|
| 860 |
+void cli_dbgmsg_no_inline(const char *str, ...) __attribute__((format(printf, 1, 2))); |
|
| 861 |
+#else |
|
| 862 |
+void cli_dbgmsg_no_inline(const char *str, ...); |
|
| 863 |
+#endif |
|
| 864 |
+ |
|
| 859 | 865 |
#ifdef HAVE_CLI_GETPAGESIZE |
| 860 | 866 |
#undef HAVE_CLI_GETPAGESIZE |
| 861 | 867 |
#endif |
| ... | ... |
@@ -180,6 +180,14 @@ inline void cli_dbgmsg(const char *str, ...) |
| 180 | 180 |
} |
| 181 | 181 |
} |
| 182 | 182 |
|
| 183 |
+void cli_dbgmsg_no_inline(const char *str, ...) |
|
| 184 |
+{
|
|
| 185 |
+ if (UNLIKELY(cli_get_debug_flag())) {
|
|
| 186 |
+ MSGCODE(buff, len, "LibClamAV debug: "); |
|
| 187 |
+ fputs(buff, stderr); |
|
| 188 |
+ } |
|
| 189 |
+} |
|
| 190 |
+ |
|
| 183 | 191 |
int cli_matchregex(const char *str, const char *regex) |
| 184 | 192 |
{
|
| 185 | 193 |
regex_t reg; |
| ... | ... |
@@ -1,6 +1,8 @@ |
| 1 | 1 |
use std::env; |
| 2 | 2 |
use std::path::{Path, PathBuf};
|
| 3 | 3 |
|
| 4 |
+use bindgen::builder; |
|
| 5 |
+ |
|
| 4 | 6 |
// Note to maintainers: this is currently a hybrid of examination of the |
| 5 | 7 |
// CMake environment, and leaning on Rust `cfg` elements. Ideally, it |
| 6 | 8 |
// should be possible to work in this space (e.g., execute tests from an |
| ... | ... |
@@ -14,7 +16,7 @@ use std::path::{Path, PathBuf};
|
| 14 | 14 |
|
| 15 | 15 |
// A list of environment variables to query to determine additional libraries |
| 16 | 16 |
// that need to be linked to resolve dependencies. |
| 17 |
-const LIB_ENV_LINK: [&str; 12] = [ |
|
| 17 |
+const LIB_ENV_LINK: &[&str] = &[ |
|
| 18 | 18 |
"LIBSSL", |
| 19 | 19 |
"LIBCRYPTO", |
| 20 | 20 |
"LIBZ", |
| ... | ... |
@@ -30,13 +32,47 @@ const LIB_ENV_LINK: [&str; 12] = [ |
| 30 | 30 |
]; |
| 31 | 31 |
|
| 32 | 32 |
// The same, but additional values to check on Windows platforms |
| 33 |
-const LIB_ENV_LINK_WINDOWS: [&str; 2] = ["LIBPTHREADW32", "LIBWIN32COMPAT"]; |
|
| 33 |
+const LIB_ENV_LINK_WINDOWS: &[&str] = &["LIBPTHREADW32", "LIBWIN32COMPAT"]; |
|
| 34 | 34 |
|
| 35 | 35 |
// Additional [verbatim] libraries to link on Windows platforms |
| 36 |
-const LIB_LINK_WINDOWS: [&str; 4] = ["wsock32", "ws2_32", "Shell32", "User32"]; |
|
| 36 |
+const LIB_LINK_WINDOWS: &[&str] = &["wsock32", "ws2_32", "Shell32", "User32"]; |
|
| 37 | 37 |
|
| 38 | 38 |
// Windows library names that must have the leading `lib` trimmed (if encountered) |
| 39 |
-const WINDOWS_TRIM_LOCAL_LIB: [&str; 2] = ["libclamav", "libclammspack"]; |
|
| 39 |
+const WINDOWS_TRIM_LOCAL_LIB: &[&str] = &["libclamav", "libclammspack"]; |
|
| 40 |
+ |
|
| 41 |
+// Generate bindings for these functions: |
|
| 42 |
+const BINDGEN_FUNCTIONS: &[&str] = &[ |
|
| 43 |
+ "cli_ctx", |
|
| 44 |
+ "cli_warnmsg", |
|
| 45 |
+ "cli_dbgmsg_no_inline", |
|
| 46 |
+ "cli_infomsg_simple", |
|
| 47 |
+ "cli_errmsg", |
|
| 48 |
+ "cli_append_virus", |
|
| 49 |
+ "cli_versig2", |
|
| 50 |
+ "cli_getdsig", |
|
| 51 |
+ "cli_get_debug_flag", |
|
| 52 |
+]; |
|
| 53 |
+ |
|
| 54 |
+// Generate bindings for these types (structs, enums): |
|
| 55 |
+const BINDGEN_TYPES: &[&str] = &["cli_matcher", "cli_ac_data", "cli_ac_result"]; |
|
| 56 |
+ |
|
| 57 |
+// Find the required functions and types in these headers: |
|
| 58 |
+const BINDGEN_HEADERS: &[&str] = &[ |
|
| 59 |
+ "../libclamav/matcher.h", |
|
| 60 |
+ "../libclamav/matcher-ac.h", |
|
| 61 |
+ "../libclamav/others.h", |
|
| 62 |
+ "../libclamav/dsig.h", |
|
| 63 |
+]; |
|
| 64 |
+ |
|
| 65 |
+// Find the required headers in these directories: |
|
| 66 |
+const BINDGEN_INCLUDE_PATHS: &[&str] = &[ |
|
| 67 |
+ "-I../libclamav", |
|
| 68 |
+ "-I../libclamunrar_iface", |
|
| 69 |
+ "-I../libclammspack", |
|
| 70 |
+]; |
|
| 71 |
+ |
|
| 72 |
+// Write the bindings to this file: |
|
| 73 |
+const BINDGEN_OUTPUT_FILE: &str = "src/sys.rs"; |
|
| 40 | 74 |
|
| 41 | 75 |
const C_HEADER_OUTPUT: &str = "clamav_rust.h"; |
| 42 | 76 |
|
| ... | ... |
@@ -58,14 +94,54 @@ fn main() -> Result<(), &'static str> {
|
| 58 | 58 |
|
| 59 | 59 |
// We only want to execute cbindgen for `cargo build`, not `cargo test`. |
| 60 | 60 |
// FindRust.cmake defines $CARGO_CMD so we can differentiate. |
| 61 |
- if "build" == env::var("CARGO_CMD").or(Ok("".to_string()))? {
|
|
| 61 |
+ let cargo_cmd = env::var("CARGO_CMD").unwrap_or("".into());
|
|
| 62 |
+ if cargo_cmd == "build" {
|
|
| 63 |
+ execute_bindgen()?; |
|
| 62 | 64 |
execute_cbindgen()?; |
| 63 | 65 |
} else {
|
| 64 |
- eprintln!("NOTE: Not performing cbindgen as CARGO_CMD != build");
|
|
| 66 |
+ eprintln!("NOTE: Not generating bindings because CARGO_CMD != build");
|
|
| 65 | 67 |
} |
| 68 |
+ |
|
| 69 |
+ Ok(()) |
|
| 70 |
+} |
|
| 71 |
+ |
|
| 72 |
+/// Use bindgen to generate Rust bindings to call into C libraries. |
|
| 73 |
+fn execute_bindgen() -> Result<(), &'static str> {
|
|
| 74 |
+ let build_dir = PathBuf::from(env::var("CARGO_TARGET_DIR").unwrap_or(".".into()));
|
|
| 75 |
+ let build_include_path = format!("-I{}", build_dir.join("..").to_str().unwrap());
|
|
| 76 |
+ |
|
| 77 |
+ // Configure and generate bindings. |
|
| 78 |
+ let mut builder = builder() |
|
| 79 |
+ // Silence code-style warnings for generated bindings. |
|
| 80 |
+ .raw_line("#![allow(non_snake_case, non_camel_case_types, non_upper_case_globals)]")
|
|
| 81 |
+ // Make the bindings pretty. |
|
| 82 |
+ .rustfmt_bindings(true) |
|
| 83 |
+ // Enable bindgen to find generated headers in the build directory, too. |
|
| 84 |
+ .clang_arg(build_include_path); |
|
| 85 |
+ |
|
| 86 |
+ for &include_path in BINDGEN_INCLUDE_PATHS {
|
|
| 87 |
+ builder = builder.clang_arg(include_path); |
|
| 88 |
+ } |
|
| 89 |
+ for &header in BINDGEN_HEADERS {
|
|
| 90 |
+ builder = builder.header(header); |
|
| 91 |
+ } |
|
| 92 |
+ for &c_function in BINDGEN_FUNCTIONS {
|
|
| 93 |
+ builder = builder.allowlist_function(c_function); |
|
| 94 |
+ } |
|
| 95 |
+ for &c_type in BINDGEN_TYPES {
|
|
| 96 |
+ builder = builder.allowlist_type(c_type); |
|
| 97 |
+ } |
|
| 98 |
+ |
|
| 99 |
+ // Generate! |
|
| 100 |
+ let bindings = builder.generate().unwrap(); |
|
| 101 |
+ |
|
| 102 |
+ // Write the generated bindings to an output file. |
|
| 103 |
+ bindings.write_to_file(BINDGEN_OUTPUT_FILE).unwrap(); |
|
| 104 |
+ |
|
| 66 | 105 |
Ok(()) |
| 67 | 106 |
} |
| 68 | 107 |
|
| 108 |
+/// Use cbindgen to generate C-header's for Rust static libraries. |
|
| 69 | 109 |
fn execute_cbindgen() -> Result<(), &'static str> {
|
| 70 | 110 |
let crate_dir = env::var("CARGO_MANIFEST_DIR").or(Err("CARGO_MANIFEST_DIR not specified"))?;
|
| 71 | 111 |
let build_dir = PathBuf::from(env::var("CARGO_TARGET_DIR").unwrap_or(".".into()));
|
| ... | ... |
@@ -86,15 +162,15 @@ fn detect_clamav_build() -> Result<(), &'static str> {
|
| 86 | 86 |
if search_and_link_lib("LIBCLAMAV")? {
|
| 87 | 87 |
eprintln!("NOTE: LIBCLAMAV defined. Examining LIB* environment variables");
|
| 88 | 88 |
// Need to link with libclamav dependencies |
| 89 |
- for var in &LIB_ENV_LINK {
|
|
| 89 |
+ for var in LIB_ENV_LINK {
|
|
| 90 | 90 |
let _ = search_and_link_lib(var); |
| 91 | 91 |
} |
| 92 | 92 |
|
| 93 | 93 |
if cfg!(windows) {
|
| 94 |
- for var in &LIB_ENV_LINK_WINDOWS {
|
|
| 94 |
+ for var in LIB_ENV_LINK_WINDOWS {
|
|
| 95 | 95 |
let _ = search_and_link_lib(var); |
| 96 | 96 |
} |
| 97 |
- for lib in &LIB_LINK_WINDOWS {
|
|
| 97 |
+ for lib in LIB_LINK_WINDOWS {
|
|
| 98 | 98 |
println!("cargo:rustc-link-lib={}", lib);
|
| 99 | 99 |
} |
| 100 | 100 |
} else {
|
| ... | ... |
@@ -17,15 +17,8 @@ after_includes = "" |
| 17 | 17 |
|
| 18 | 18 |
[export] |
| 19 | 19 |
include = ["cdiff_apply", "script2cdiff"] |
| 20 |
-exclude = [ |
|
| 21 |
- "cli_dbgmsg", |
|
| 22 |
- "cli_errmsg", |
|
| 23 |
- "cli_infomsg_simple", |
|
| 24 |
- "cli_warnmsg", |
|
| 25 |
- "cli_versig2", |
|
| 26 |
- "cli_get_debug_flag", |
|
| 27 |
- "cli_getdsig", |
|
| 28 |
-] |
|
| 20 |
+exclude = [] |
|
| 21 |
+ |
|
| 29 | 22 |
# prefix = "CAPI_" |
| 30 | 23 |
item_types = [] |
| 31 | 24 |
renaming_overrides_prefixing = false |
| ... | ... |
@@ -24,7 +24,7 @@ use std::{
|
| 24 | 24 |
fs::{self, File, OpenOptions},
|
| 25 | 25 |
io::{prelude::*, BufReader, BufWriter, Read, Seek, SeekFrom, Write},
|
| 26 | 26 |
iter::*, |
| 27 |
- os::raw::{c_char, c_uchar},
|
|
| 27 |
+ os::raw::c_char, |
|
| 28 | 28 |
process, |
| 29 | 29 |
}; |
| 30 | 30 |
|
| ... | ... |
@@ -34,6 +34,8 @@ use log::{debug, error, warn};
|
| 34 | 34 |
use sha2::{Digest, Sha256};
|
| 35 | 35 |
use thiserror::Error; |
| 36 | 36 |
|
| 37 |
+use crate::sys; |
|
| 38 |
+ |
|
| 37 | 39 |
/// Maximum size of a digital signature |
| 38 | 40 |
const MAX_SIG_SIZE: usize = 350; |
| 39 | 41 |
|
| ... | ... |
@@ -255,28 +257,9 @@ impl<'a> XchgOp<'a> {
|
| 255 | 255 |
} |
| 256 | 256 |
} |
| 257 | 257 |
|
| 258 |
-extern "C" {
|
|
| 259 |
- fn cli_versig2( |
|
| 260 |
- digest: *const c_uchar, |
|
| 261 |
- dsig: *const c_char, |
|
| 262 |
- n: *const c_char, |
|
| 263 |
- e: *const c_char, |
|
| 264 |
- ) -> i32; |
|
| 265 |
- |
|
| 266 |
- fn cli_getdsig( |
|
| 267 |
- host: *const u8, |
|
| 268 |
- user: *const u8, |
|
| 269 |
- data: *const u8, |
|
| 270 |
- datalen: u32, |
|
| 271 |
- mode: u8, |
|
| 272 |
- ) -> *const c_char; |
|
| 273 |
- |
|
| 274 |
- fn cli_get_debug_flag() -> u8; |
|
| 275 |
-} |
|
| 276 |
- |
|
| 277 | 258 |
fn is_debug_enabled() -> bool {
|
| 278 | 259 |
unsafe {
|
| 279 |
- let debug_flag = cli_get_debug_flag(); |
|
| 260 |
+ let debug_flag = sys::cli_get_debug_flag(); |
|
| 280 | 261 |
// Return true if debug_flag is not 0 |
| 281 | 262 |
!matches!(debug_flag, 0) |
| 282 | 263 |
} |
| ... | ... |
@@ -418,9 +401,9 @@ pub fn script2cdiff(script_file_name: &str, builder: &str, server: &str) -> Resu |
| 418 | 418 |
// These strings should not contain interior NULs |
| 419 | 419 |
let server = CString::new(server).unwrap(); |
| 420 | 420 |
let builder = CString::new(builder).unwrap(); |
| 421 |
- let dsig_ptr = cli_getdsig( |
|
| 422 |
- server.as_c_str().as_ptr() as *const u8, |
|
| 423 |
- builder.as_c_str().as_ptr() as *const u8, |
|
| 421 |
+ let dsig_ptr = sys::cli_getdsig( |
|
| 422 |
+ server.as_c_str().as_ptr() as *const c_char, |
|
| 423 |
+ builder.as_c_str().as_ptr() as *const c_char, |
|
| 424 | 424 |
sha256.to_vec().as_ptr(), |
| 425 | 425 |
32, |
| 426 | 426 |
2, |
| ... | ... |
@@ -511,7 +494,7 @@ pub fn cdiff_apply(file: &mut File, mode: ApplyMode) -> Result<(), CdiffError> {
|
| 511 | 511 |
let n = CString::new(PUBLIC_KEY_MODULUS).unwrap(); |
| 512 | 512 |
let e = CString::new(PUBLIC_KEY_EXPONENT).unwrap(); |
| 513 | 513 |
let versig_result = unsafe {
|
| 514 |
- cli_versig2( |
|
| 514 |
+ sys::cli_versig2( |
|
| 515 | 515 |
sha256.to_vec().as_ptr(), |
| 516 | 516 |
dsig_cstring.as_ptr(), |
| 517 | 517 |
n.as_ptr() as *const c_char, |
| ... | ... |
@@ -21,16 +21,10 @@ |
| 21 | 21 |
*/ |
| 22 | 22 |
|
| 23 | 23 |
use std::ffi::CString; |
| 24 |
-use std::os::raw::c_char; |
|
| 25 | 24 |
|
| 26 | 25 |
use log::{set_max_level, Level, LevelFilter, Metadata, Record};
|
| 27 | 26 |
|
| 28 |
-extern "C" {
|
|
| 29 |
- fn cli_warnmsg(str: *const c_char, ...) -> (); |
|
| 30 |
- fn cli_dbgmsg(str: *const c_char, ...) -> (); |
|
| 31 |
- fn cli_infomsg_simple(str: *const c_char, ...) -> (); |
|
| 32 |
- fn cli_errmsg(str: *const c_char, ...) -> (); |
|
| 33 |
-} |
|
| 27 |
+use crate::sys; |
|
| 34 | 28 |
|
| 35 | 29 |
pub struct ClamLogger; |
| 36 | 30 |
|
| ... | ... |
@@ -46,16 +40,16 @@ impl log::Log for ClamLogger {
|
| 46 | 46 |
|
| 47 | 47 |
match record.level() {
|
| 48 | 48 |
Level::Debug => unsafe {
|
| 49 |
- cli_dbgmsg(ptr); |
|
| 49 |
+ sys::cli_dbgmsg_no_inline(ptr); |
|
| 50 | 50 |
}, |
| 51 | 51 |
Level::Error => unsafe {
|
| 52 |
- cli_errmsg(ptr); |
|
| 52 |
+ sys::cli_errmsg(ptr); |
|
| 53 | 53 |
}, |
| 54 | 54 |
Level::Info => unsafe {
|
| 55 |
- cli_infomsg_simple(ptr); |
|
| 55 |
+ sys::cli_infomsg_simple(ptr); |
|
| 56 | 56 |
}, |
| 57 | 57 |
Level::Warn => unsafe {
|
| 58 |
- cli_warnmsg(ptr); |
|
| 58 |
+ sys::cli_warnmsg(ptr); |
|
| 59 | 59 |
}, |
| 60 | 60 |
_ => {}
|
| 61 | 61 |
} |