diff --git a/crates/dns-resolver/src/lib.rs b/crates/dns-resolver/src/lib.rs index 42f7c1d1..3760809f 100644 --- a/crates/dns-resolver/src/lib.rs +++ b/crates/dns-resolver/src/lib.rs @@ -140,13 +140,13 @@ impl MailExchanger { Ok(mx) } - pub async fn resolve_addresses(&self) -> Vec { + pub async fn resolve_addresses(&self) -> ResolvedMxAddresses { let mut result = vec![]; for mx_host in &self.hosts { // '.' is a null mx; skip trying to resolve it if mx_host == "." { - continue; + return ResolvedMxAddresses::NullMx; } // Handle the literal address case @@ -174,10 +174,16 @@ impl MailExchanger { } } result.reverse(); - result + ResolvedMxAddresses::Addresses(result) } } +#[derive(Debug, Clone)] +pub enum ResolvedMxAddresses { + NullMx, + Addresses(Vec), +} + struct ByPreference { hosts: Vec, pref: u16, @@ -416,12 +422,14 @@ MailExchanger { k9::snapshot!( v4_loopback.resolve_addresses().await, r#" -[ - ResolvedAddress { - name: "127.0.0.1", - addr: 127.0.0.1, - }, -] +Addresses( + [ + ResolvedAddress { + name: "127.0.0.1", + addr: 127.0.0.1, + }, + ], +) "# ); @@ -447,12 +455,14 @@ MailExchanger { k9::snapshot!( v6_loopback_non_conforming.resolve_addresses().await, r#" -[ - ResolvedAddress { - name: "::1", - addr: ::1, - }, -] +Addresses( + [ + ResolvedAddress { + name: "::1", + addr: ::1, + }, + ], +) "# ); @@ -478,12 +488,14 @@ MailExchanger { k9::snapshot!( v6_loopback.resolve_addresses().await, r#" -[ - ResolvedAddress { - name: "::1", - addr: ::1, - }, -] +Addresses( + [ + ResolvedAddress { + name: "::1", + addr: ::1, + }, + ], +) "# ); } diff --git a/crates/kumod/src/smtp_dispatcher.rs b/crates/kumod/src/smtp_dispatcher.rs index 3d6aed33..3a3385ed 100644 --- a/crates/kumod/src/smtp_dispatcher.rs +++ b/crates/kumod/src/smtp_dispatcher.rs @@ -7,6 +7,7 @@ use crate::runtime::{rt_spawn, spawn}; use crate::spool::SpoolManager; use anyhow::Context; use async_trait::async_trait; +use dns_resolver::ResolvedMxAddresses; use kumo_log_types::ResolvedAddress; use message::Message; use rfc5321::{ClientError, EnhancedStatusCode, ForwardPath, Response, ReversePath, SmtpClient}; @@ -34,7 +35,7 @@ impl SmtpDispatcher { }, }; - let mut addresses = dispatcher + let addresses = dispatcher .mx .as_ref() .expect("to have mx when doing smtp") @@ -42,16 +43,35 @@ impl SmtpDispatcher { .await; tracing::trace!("mx resolved to {addresses:?}"); + let mut addresses = match addresses { + ResolvedMxAddresses::NullMx => { + dispatcher + .bulk_ready_queue_operation(Response { + code: 556, + enhanced_code: Some(EnhancedStatusCode { + class: 5, + subject: 1, + detail: 10, + }), + content: "Recipient address has a null MX".to_string(), + command: None, + }) + .await; + return Ok(None); + } + ResolvedMxAddresses::Addresses(a) => a, + }; + if addresses.is_empty() { dispatcher .bulk_ready_queue_operation(Response { - code: 556, + code: 451, enhanced_code: Some(EnhancedStatusCode { - class: 5, - subject: 1, - detail: 10, + class: 4, + subject: 4, + detail: 4, }), - content: "Recipient address has a null MX".to_string(), + content: "MX didn't resolve to any hosts".to_string(), command: None, }) .await;