diff --git a/filtermail/src/inbound.rs b/filtermail/src/inbound.rs index a1d5b879..28d0f51f 100644 --- a/filtermail/src/inbound.rs +++ b/filtermail/src/inbound.rs @@ -6,7 +6,7 @@ use crate::dkim_verifier::DkimVerifier; use crate::message::{check_encrypted, is_securejoin}; pub use crate::smtp_server::Envelope; use crate::smtp_server::SmtpHandler; -use crate::utils::{AddressDomain, extract_address}; +use crate::utils::{AddressDomain, extract_address, log_eml}; use async_trait::async_trait; use mailparse::{MailHeaderMap, parse_mail}; use std::str::FromStr; @@ -69,8 +69,18 @@ impl SmtpHandler for IncomingBeforeQueueHandler { } } AddressDomain::Name(domain) => { - if !self.skip_dkim { - self.dkim_verifier.verify(&envelope.data, &domain).await?; + if !self.skip_dkim + && let Err(e) = self.dkim_verifier.verify(&envelope.data, &domain).await + { + let eml_path = log_eml("dkim-verify", &envelope.data) + .await + .map(|path| path.to_string_lossy().to_string()) + .unwrap_or_else(|e| { + log::error!("Failed to save rejected message to file: {e}"); + "ERR".to_string() + }); + log::info!("Rejected message stored at: {eml_path}"); + return Err(e); } } } diff --git a/filtermail/src/utils.rs b/filtermail/src/utils.rs index 230886e4..d9a0adfa 100644 --- a/filtermail/src/utils.rs +++ b/filtermail/src/utils.rs @@ -1,4 +1,5 @@ use mailparse::MailAddr; +use std::path::PathBuf; use std::str::FromStr; /// Extracts the first email address found in SMTP command or email header. @@ -75,6 +76,23 @@ impl FromStr for AddressDomain { } } +/// Logs email to `/tmp/filtermail-rejected//.eml` +/// and returns the file path. +/// +/// Returns [`crate::error::Error`] on IO error. +pub async fn log_eml(reason: &str, data: &[u8]) -> Result { + let timestamp = std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .unwrap_or_default() + .as_secs(); + let filename = format!("{timestamp}.eml"); + let mut path = PathBuf::from(format!("/tmp/filtermail-rejected/{reason}")); + tokio::fs::create_dir_all(&path).await?; + path.push(filename); + tokio::fs::write(&path, data).await?; + Ok(path) +} + #[cfg(test)] mod tests { use super::*;