Skip to content

toblux/rust-clamav-client

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

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.

Build status Crates.io

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

About

ClamAV client library with optional support for async-std, smol, and Tokio

Topics

Resources

License

Stars

Watchers

Forks

Contributors 4

  •  
  •  
  •  
  •  

Languages