Expand description
§Rust ClamAV Client
A simple ClamAV client for sending files, in-memory data, and data streams to clamd for antivirus scanning. It provides both a synchronous API and asynchronous functions compatible with async-std, smol, and Tokio.
Check out the examples below, the integration tests, or the API documentation to learn how to use this library.
The integration tests run against a lightweight ClamAV daemon that’s available as a separate GitHub Action.
§Installation
Add this to your Cargo.toml:
[dependencies]
clamav-client = "2.2.0"To use the async functions in clamav_client::tokio, add this to your Cargo.toml:
[dependencies]
clamav-client = { version = "2.2.0", features = ["tokio"] }To scan Tokio streams, enable the tokio-stream feature and add this to your Cargo.toml:
[dependencies]
clamav-client = { version = "2.2.0", features = ["tokio-stream"] }Support for smol is available since version 2.1.0 by enabling the smol feature:
[dependencies]
clamav-client = { version = "2.2.0", features = ["smol"] }Support for async-std is still available, but async-std itself has been discontinued in favor of smol. You can enable it with the async-std feature:
[dependencies]
clamav-client = { version = "2.2.0", features = ["async-std"] }§Migrations
§Migrate to 1.x
The *_socket and *_tcp functions were deprecated in version 0.5.0 and have been removed in version 1.0.0.
§Migrate to 0.5.x
The *_socket and *_tcp functions have been deprecated in favor of more general functions with the same name, but without the suffixes. These updated functions, such as ping, scan_buffer, and scan_file, now have the connection type (TCP or Unix socket) as a parameter, effectively replacing the host_address and socket_path parameters.
For example,
let clamd_host_address = "localhost:3310";
let result = clamav_client::scan_file_tcp("README.md", clamd_host_address, None);
assert!(result.is_ok());becomes:
let clamd_tcp = clamav_client::Tcp{ host_address: "localhost:3310" };
let result = clamav_client::scan_file("README.md", clamd_tcp, None);
assert!(result.is_ok());§Examples
§Usage
let clamd_tcp = clamav_client::Tcp{ host_address: "localhost:3310" };
// Ping clamd to make sure the server is available and accepting TCP connections
let clamd_available = match clamav_client::ping(clamd_tcp) {
Ok(ping_response) => ping_response == clamav_client::PONG,
Err(_) => false,
};
if !clamd_available {
println!("Cannot ping clamd at {}", clamd_tcp.host_address);
return;
}
assert!(clamd_available);
// Scan file for viruses
let file_path = "tests/data/eicar.txt";
let scan_file_response = clamav_client::scan_file(file_path, clamd_tcp, None).unwrap();
let file_clean = clamav_client::clean(&scan_file_response).unwrap();
if file_clean {
println!("No virus found in {}", file_path);
} else {
println!("The file {} is infected!", file_path);
}
assert!(!file_clean);
// Scan in-memory data for viruses
let buffer = br#"X5O!P%@AP[4\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H*"#;
let scan_buffer_response = clamav_client::scan_buffer(buffer, clamd_tcp, None).unwrap();
let data_clean = clamav_client::clean(&scan_buffer_response).unwrap();
if data_clean {
println!("No virus found");
} else {
println!("The data is infected!");
}
assert!(!data_clean);§Usage - Async with tokio
#[cfg(feature = "tokio-stream")]
tokio::runtime::Builder::new_current_thread().enable_all().build().unwrap().block_on(async {
let clamd_tcp = clamav_client::tokio::Tcp{ host_address: "localhost:3310" };
// Ping clamd asynchronously and await the result
let clamd_available = match clamav_client::tokio::ping(clamd_tcp).await {
Ok(ping_response) => ping_response == clamav_client::PONG,
Err(_) => false,
};
if !clamd_available {
println!("Cannot ping clamd at {}", clamd_tcp.host_address);
return;
}
assert!(clamd_available);
let file_path = "tests/data/eicar.txt";
let buffer = br#"X5O!P%@AP[4\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H*"#;
let file = tokio::fs::File::open(file_path).await.unwrap();
let stream = tokio_util::io::ReaderStream::new(file);
// Concurrently scan a file, a data buffer, and a file stream for viruses
let (scan_file_result, scan_buffer_result, scan_stream_result) = tokio::join!(
clamav_client::tokio::scan_file(file_path, clamd_tcp, None),
clamav_client::tokio::scan_buffer(buffer, clamd_tcp, None),
clamav_client::tokio::scan_stream(stream, clamd_tcp, None)
);
let scan_file_response = scan_file_result.unwrap();
let file_clean = clamav_client::clean(&scan_file_response).unwrap();
if file_clean {
println!("No virus found in {}", file_path);
} else {
println!("The file {} is infected!", file_path);
}
assert!(!file_clean);
let scan_buffer_response = scan_buffer_result.unwrap();
let data_clean = clamav_client::clean(&scan_buffer_response).unwrap();
if data_clean {
println!("No virus found");
} else {
println!("The data buffer is infected!");
}
assert!(!data_clean);
let scan_stream_response = scan_stream_result.unwrap();
let stream_clean = clamav_client::clean(&scan_stream_response).unwrap();
if stream_clean {
println!("No virus found");
} else {
println!("The file stream is infected!");
}
assert!(!stream_clean);
})§Usage - Async with smol
#[cfg(feature = "smol")]
smol::block_on(async {
let clamd_tcp = clamav_client::smol::Tcp{ host_address: "localhost:3310" };
// Ping clamd asynchronously and await the result
let clamd_available = match clamav_client::smol::ping(clamd_tcp).await {
Ok(ping_response) => ping_response == clamav_client::PONG,
Err(_) => false,
};
if !clamd_available {
println!("Cannot ping clamd at {}", clamd_tcp.host_address);
return;
}
assert!(clamd_available);
// Scan a file for viruses
let file_path = "tests/data/eicar.txt";
let scan_file_result = clamav_client::smol::scan_file(file_path, clamd_tcp, None).await;
let scan_file_response = scan_file_result.unwrap();
let file_clean = clamav_client::clean(&scan_file_response).unwrap();
if file_clean {
println!("No virus found in {}", file_path);
} else {
println!("The file {} is infected!", file_path);
}
assert!(!file_clean);
})§Usage - Async with async-std
#[cfg(feature = "async-std")]
async_std::task::block_on(async {
let clamd_tcp = clamav_client::async_std::Tcp{ host_address: "localhost:3310" };
// Ping clamd asynchronously and await the result
let clamd_available = match clamav_client::async_std::ping(clamd_tcp).await {
Ok(ping_response) => ping_response == clamav_client::PONG,
Err(_) => false,
};
if !clamd_available {
println!("Cannot ping clamd at {}", clamd_tcp.host_address);
return;
}
assert!(clamd_available);
// Scan a file for viruses
let file_path = "tests/data/eicar.txt";
let scan_file_result = clamav_client::async_std::scan_file(file_path, clamd_tcp, None).await;
let scan_file_response = scan_file_result.unwrap();
let file_clean = clamav_client::clean(&scan_file_response).unwrap();
if file_clean {
println!("No virus found in {}", file_path);
} else {
println!("The file {} is infected!", file_path);
}
assert!(!file_clean);
})More examples can be found in the tests.
§Links
§Development
§Testing locally
For the tests to pass, you should start clamd as follows:
clamd -F --config-file=clamd/clamd.conf
and then run cargo test --all-features to cover all tests.
It doesn’t really matter how you start clamd, as long as the options from clamd.conf are the same for your configuration.
§Contributing
Contributions are welcome!
§Contributors
Modules§
- async_
std - Use the feature flag “async-std” to enable this module
- smol
- Use the feature flag “smol” to enable this module
- tokio
- Use the feature flag “tokio” or “tokio-stream” to enable this module
Structs§
- Socket
- Use a Unix socket connection to communicate with a ClamAV server
- Tcp
- Use a TCP connection to communicate with a ClamAV server
Constants§
Traits§
- Transport
Protocol - The communication protocol to use
Functions§
- clean
- Checks whether the ClamAV response indicates that the scanned content is clean or contains a virus
- get_
version - Gets the version number from ClamAV
- ping
- Sends a ping request to ClamAV
- reload
- Reloads the virus databases
- scan_
buffer - Scans a data buffer for viruses
- scan_
file - Scans a file for viruses
- shutdown
- Shuts down a ClamAV server
Type Aliases§
- IoResult
- Custom result type
- Utf8
Result - Custom result type