From 1e356315da8dfab9232ea2bb94bfa3d603746f65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jagoda=20Estera=20=C5=9Al=C4=85zak?= <128227338+j-g00da@users.noreply.github.com> Date: Mon, 20 Apr 2026 13:03:49 +0200 Subject: [PATCH] feat(smtp-server): Log malformed SMTP commands (#118) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Closes #110 Signed-off-by: Jagoda Ślązak --- filtermail/src/smtp_server.rs | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/filtermail/src/smtp_server.rs b/filtermail/src/smtp_server.rs index 63f899ab..1a7f8178 100644 --- a/filtermail/src/smtp_server.rs +++ b/filtermail/src/smtp_server.rs @@ -1,6 +1,6 @@ //! A simplified SMTP server implementation for internal communication. -use crate::utils::extract_address; +use crate::utils::{extract_address, log_eml}; use async_trait::async_trait; use std::sync::Arc; use tokio::io::{AsyncBufReadExt, AsyncWriteExt, BufReader, BufWriter}; @@ -103,7 +103,9 @@ where // Note: this will kill the connection if any line doesn't end with CRLF. // This is intentional as stray LF most likely means an attempt to exploit the server. let Some(cmd) = line.strip_suffix("\r\n") else { - log::warn!("Malformed command without CRLF ending! Closing connection."); + log::warn!( + "Malformed command without CRLF ending! Received: {line:?} Closing connection." + ); break 'connection; }; @@ -171,6 +173,15 @@ where if !data_line.ends_with("\r\n") { log::warn!("Malformed DATA line without CRLF ending! Closing connection."); + data.extend_from_slice(data_line.as_bytes()); + let eml_path = log_eml("malformed-data", &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}"); break 'connection; }