From 34622edbd2eae736e651d3dc6958b8b5a1bf7ea7 Mon Sep 17 00:00:00 2001 From: Wez Furlong Date: Sun, 5 Oct 2025 07:02:46 +0100 Subject: [PATCH] spf: allow for underscores in domain names when including Use the more relaxed form of Name parsing so that domains with underscores (which are in common use with email) do not generate an error at runtime when processing spf include rules. --- crates/kumo-spf/src/lib.rs | 2 +- crates/kumo-spf/src/record.rs | 3 +-- crates/kumo-spf/src/tests.rs | 24 ++++++++++++++++++++++++ 3 files changed, 26 insertions(+), 3 deletions(-) diff --git a/crates/kumo-spf/src/lib.rs b/crates/kumo-spf/src/lib.rs index 0e2ec4d6..333c93d6 100644 --- a/crates/kumo-spf/src/lib.rs +++ b/crates/kumo-spf/src/lib.rs @@ -177,7 +177,7 @@ impl<'a> SpfContext<'a> { } pub async fn check(&self, resolver: &dyn Resolver, initial: bool) -> SpfResult { - let name = match Name::from_utf8(self.domain) { + let name = match Name::from_str_relaxed(self.domain) { Ok(name) => name, Err(_) => { // Per , invalid diff --git a/crates/kumo-spf/src/record.rs b/crates/kumo-spf/src/record.rs index 6fcaf6e6..cf58addc 100644 --- a/crates/kumo-spf/src/record.rs +++ b/crates/kumo-spf/src/record.rs @@ -4,7 +4,6 @@ use dns_resolver::Resolver; use hickory_resolver::Name; use std::fmt; use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; -use std::str::FromStr; #[derive(Debug, Default)] pub(crate) struct Record { @@ -229,7 +228,7 @@ impl Directive { } .matches(cx.client_ip, IpAddr::V6(*ip6_network)), Mechanism::Ptr { domain } => { - let domain = match Name::from_str(&cx.domain(domain.as_ref())?) { + let domain = match Name::from_str_relaxed(&cx.domain(domain.as_ref())?) { Ok(domain) => domain, Err(err) => { return Err(SpfResult { diff --git a/crates/kumo-spf/src/tests.rs b/crates/kumo-spf/src/tests.rs index 9018c476..1d57c7ba 100644 --- a/crates/kumo-spf/src/tests.rs +++ b/crates/kumo-spf/src/tests.rs @@ -124,6 +124,30 @@ async fn mx() { ); } +#[tokio::test] +async fn underscores() { + let resolver = TestResolver::default() + .with_zone(EXAMPLE_COM) + .with_txt( + "under_score.com", + "v=spf1 ip4:192.0.2.128/28 -all".to_string(), + ) + .with_txt( + "example.com", + "v=spf1 include:under_score.com -all".to_string(), + ); + + let result = evaluate_ip(Ipv4Addr::from([192, 0, 2, 65]), &resolver).await; + k9::assert_equal!( + &result, + &SpfResult { + disposition: SpfDisposition::Fail, + context: "matched '-all' directive".to_owned(), + }, + "{result:?}" + ); +} + /// https://www.rfc-editor.org/rfc/rfc7208#appendix-A.1 #[tokio::test] async fn ip4() {