clamd_client/
error.rs

1// for nicer doc links
2#[allow(unused_imports)]
3use crate::ClamdClient;
4use std::num::TryFromIntError;
5
6use tracing::trace;
7
8pub type Result<T> = std::result::Result<T, ClamdError>;
9
10/// Errors that can occur when using [`ClamdClient`].
11#[derive(Debug, thiserror::Error)]
12pub enum ClamdError {
13    /// Occurs when the custom set chunk size is larger than
14    /// [`std::u32::MAX`].
15    #[error("could not send chunk: too large: {0}")]
16    ChunkSizeError(#[source] TryFromIntError),
17    /// Occurs when the library cannot connect to the tcp or unix
18    /// socket. Contains underlying [`std::io::Error`].
19    #[error("could not connect to socket: {0}")]
20    ConnectError(#[source] std::io::Error),
21    /// Occurs when the clamd response is not valid Utf8.
22    #[error("could not decode clamav response: {0}")]
23    DecodingUtf8Error(#[source] std::string::FromUtf8Error),
24    /// Occurs when there was an [`std::io::Error`] while transfering
25    /// commands or data to/from clamd.
26    #[error("could not decode / encode clamav response: {0}")]
27    DecodingIoError(
28        #[from]
29        #[source]
30        std::io::Error,
31    ),
32
33    /// Occurs when the response from clamd is not what the library
34    /// expects. Contains the invalid response.
35    #[error("invalid response from clamd: {0}")]
36    InvalidResponse(String),
37    /// Occurs when there should be a response from clamd but it just
38    /// closed the connection without sending a response.
39    #[error("no response from clamd")]
40    NoResponse,
41    /// Occurs when we expect a longer response from clamd, but it
42    /// is somehow malformed. Contains the invalid response.
43    #[error("incomplete response from clamd: {0}")]
44    IncompleteResponse(String),
45    /// Occurs when everything between this library and clamd went
46    /// well but clamd seems to have found a virus signature. See also
47    /// [`ClamdError::scan_error`].
48    #[error("clamd returned error on scan, possible virus: {0}")]
49    ScanError(String),
50}
51
52impl ClamdError {
53    /// If you want to ignore any error but an actual malignent scan
54    /// result from clamd. I do not recommend using this without careful thought, as any other error
55    /// could hide that uploaded bytes are actually a virus.
56    /// # Example
57    /// ```rust
58    /// # use std::net::SocketAddr;
59    /// # use clamd_client::ClamdClientBuilder;
60    /// # use eyre::Result;
61    /// # async fn doc() -> eyre::Result<()> {
62    /// let address = "127.0.0.1:3310".parse::<SocketAddr>()?;
63    /// let mut clamd_client = ClamdClientBuilder::tcp_socket(&address).build();
64    ///
65    /// // This downloads a virus signature that is benign but trips clamd.
66    /// let eicar_bytes = reqwest::get("https://secure.eicar.org/eicarcom2.zip")
67    ///   .await?
68    ///   .bytes()
69    ///   .await?;
70    ///
71    /// let err = clamd_client.scan_bytes(&eicar_bytes).await.unwrap_err();
72    /// let msg = err.scan_error().unwrap();
73    /// println!("Eicar scan returned that its a virus: {}", msg);
74    /// # Ok(())
75    /// # }
76    /// ```
77    pub fn scan_error(self) -> Option<String> {
78        match self {
79            Self::ScanError(s) => Some(s),
80            _ => {
81                trace!("ignoring non-scan error {:?}", self);
82                None
83            }
84        }
85    }
86}