diff --git a/crates/kumod/src/mod_kumo.rs b/crates/kumod/src/mod_kumo.rs index 85e0b0f5..1f765a85 100644 --- a/crates/kumod/src/mod_kumo.rs +++ b/crates/kumod/src/mod_kumo.rs @@ -1,8 +1,7 @@ use crate::dest_site::DestSiteConfig; use crate::lua_config::get_or_create_module; -use crate::smtp_server::RejectError; +use crate::smtp_server::{EsmtpListenerParams, RejectError}; use anyhow::Context; -use cidr::IpCidr; use mlua::{Function, Lua, LuaSerdeExt, Value}; use serde::Deserialize; use std::path::PathBuf; @@ -64,36 +63,6 @@ pub fn register(lua: &Lua) -> anyhow::Result<()> { Ok(()) } -#[derive(Deserialize)] -struct EsmtpListenerParams { - #[serde(default = "EsmtpListenerParams::default_listen")] - listen: String, - #[serde(default = "EsmtpListenerParams::default_hostname")] - hostname: String, - #[serde(default = "EsmtpListenerParams::default_relay_hosts")] - relay_hosts: Vec, -} - -impl EsmtpListenerParams { - fn default_relay_hosts() -> Vec { - vec![ - IpCidr::new("127.0.0.1".parse().unwrap(), 32).unwrap(), - IpCidr::new("::1".parse().unwrap(), 128).unwrap(), - ] - } - - fn default_listen() -> String { - "127.0.0.1:2025".to_string() - } - - fn default_hostname() -> String { - gethostname::gethostname() - .to_str() - .unwrap_or("localhost") - .to_string() - } -} - async fn start_esmtp_listener(params: EsmtpListenerParams) -> anyhow::Result<()> { use crate::smtp_server::SmtpServer; use tokio::net::TcpListener; @@ -106,12 +75,10 @@ async fn start_esmtp_listener(params: EsmtpListenerParams) -> anyhow::Result<()> loop { let (socket, peer_address) = listener.accept().await?; - let hostname = params.hostname.to_string(); - let relay_hosts = params.relay_hosts.clone(); + let params = params.clone(); crate::runtime::Runtime::run(move || { tokio::task::spawn_local(async move { - if let Err(err) = SmtpServer::run(socket, peer_address, relay_hosts, hostname).await - { + if let Err(err) = SmtpServer::run(socket, peer_address, params).await { tracing::error!("SmtpServer::run: {err:#}"); } }); diff --git a/crates/kumod/src/smtp_server.rs b/crates/kumod/src/smtp_server.rs index 0e138c35..96c180ce 100644 --- a/crates/kumod/src/smtp_server.rs +++ b/crates/kumod/src/smtp_server.rs @@ -7,6 +7,7 @@ use message::{EnvelopeAddress, Message}; use mlua::ToLuaMulti; use rfc5321::{AsyncReadAndWrite, BoxedAsyncReadAndWrite, Command}; use rustls::ServerConfig; +use serde::Deserialize; use std::fmt::Debug; use std::net::SocketAddr; use std::sync::Arc; @@ -15,6 +16,42 @@ use tokio::io::{AsyncReadExt, AsyncWriteExt}; use tokio_rustls::TlsAcceptor; use tracing::{error, instrument}; +#[derive(Deserialize, Clone, Debug)] +pub struct EsmtpListenerParams { + #[serde(default = "EsmtpListenerParams::default_listen")] + pub listen: String, + #[serde(default = "EsmtpListenerParams::default_hostname")] + pub hostname: String, + #[serde(default = "EsmtpListenerParams::default_relay_hosts")] + pub relay_hosts: Vec, + #[serde(default = "EsmtpListenerParams::default_banner")] + pub banner: String, +} + +impl EsmtpListenerParams { + fn default_relay_hosts() -> Vec { + vec![ + IpCidr::new("127.0.0.1".parse().unwrap(), 32).unwrap(), + IpCidr::new("::1".parse().unwrap(), 128).unwrap(), + ] + } + + fn default_listen() -> String { + "127.0.0.1:2025".to_string() + } + + fn default_hostname() -> String { + gethostname::gethostname() + .to_str() + .unwrap_or("localhost") + .to_string() + } + + fn default_banner() -> String { + "KumoMTA".to_string() + } +} + #[derive(Error, Debug, Clone)] #[error("{code} {message}")] #[must_use] @@ -57,11 +94,10 @@ pub struct SmtpServer { state: Option, said_hello: Option, config: LuaConfig, - hostname: String, peer_address: SocketAddr, - relay_hosts: Vec, tls_active: bool, read_buffer: Vec, + params: EsmtpListenerParams, } #[derive(Debug)] @@ -75,8 +111,7 @@ impl SmtpServer { pub async fn run( socket: T, peer_address: SocketAddr, - relay_hosts: Vec, - hostname: String, + params: EsmtpListenerParams, ) -> anyhow::Result<()> where T: AsyncReadAndWrite + Debug + Send + 'static, @@ -88,11 +123,10 @@ impl SmtpServer { state: None, said_hello: None, config, - hostname, peer_address, - relay_hosts, tls_active: false, read_buffer: Vec::with_capacity(1024), + params, }; tokio::task::spawn_local(async move { @@ -180,14 +214,17 @@ impl SmtpServer { // else we risk re-injecting messages received during enumeration. self.write_response( 421, - format!("{} 4.3.2 Hold on just a moment!", self.hostname), + format!("{} 4.3.2 Hold on just a moment!", self.params.hostname), ) .await?; return Ok(()); } - self.write_response(220, format!("{} KumoMTA\nW00t!\nYeah!", self.hostname)) - .await?; + self.write_response( + 220, + format!("{} {}", self.params.hostname, self.params.banner), + ) + .await?; loop { let line = match self.read_line().await? { ReadLine::Line(line) => line, @@ -217,7 +254,7 @@ impl SmtpServer { continue; } self.write_response(220, "Ready to Start TLS").await?; - let acceptor = build_tls_acceptor(&self.hostname)?; + let acceptor = build_tls_acceptor(&self.params.hostname)?; let socket = acceptor.accept(self.socket.take().unwrap()).await?; let socket: BoxedAsyncReadAndWrite = Box::new(socket); self.socket.replace(socket); @@ -243,7 +280,7 @@ impl SmtpServer { 250, format!( "{} Aloha {domain}\n{}", - self.hostname, + self.params.hostname, extensions.join("\n"), ), ) @@ -277,7 +314,7 @@ impl SmtpServer { } let mut relay_allowed = false; - for cidr in &self.relay_hosts { + for cidr in &self.params.relay_hosts { if cidr.contains(&self.peer_address.ip()) { relay_allowed = true; break; diff --git a/simple_policy.lua b/simple_policy.lua index 141a68f3..b1c76a36 100644 --- a/simple_policy.lua +++ b/simple_policy.lua @@ -13,6 +13,11 @@ kumo.on('init', function() -- override the default set of relay hosts relay_hosts = { '127.0.0.1', '192.168.1.0/24' }, + + -- Customize the banner. + -- The configured hostname will be automatically + -- prepended to this text. + banner = 'Welcome to KumoMTA!', } -- Define the default "data" spool location; this is where