From e2d050501355d16fb662124f794b65823ec15700 Mon Sep 17 00:00:00 2001 From: Maurus Decimus <11444311+mdecimus@users.noreply.github.com> Date: Thu, 9 Jul 2026 09:48:05 +0200 Subject: [PATCH] Disable DANE when there are no TCP resolvers configured --- CHANGELOG.md | 1 + crates/common/src/config/smtp/mod.rs | 2 +- crates/common/src/config/smtp/resolver.rs | 26 +++++++++++++++-------- 3 files changed, 19 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fe197e5e..300fffae 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ If you are upgrading from v0.16.x, replace the binary (or run `docker pull`). If ## Fixed - OAuth resource indicators: Accept `imap`, `smtp`, `pop3` and `sieve` as valid resource indicators for OAuth access tokens. +- PostgreSQL: Incomplete channel binding implementation. ## [0.16.12] - 2026-07-06 diff --git a/crates/common/src/config/smtp/mod.rs b/crates/common/src/config/smtp/mod.rs index 64f78989..64574d4e 100644 --- a/crates/common/src/config/smtp/mod.rs +++ b/crates/common/src/config/smtp/mod.rs @@ -73,7 +73,7 @@ impl SmtpConfig { concat!( "The configured DNS resolver cannot validate DNSSEC. ", "DANE has been disabled to avoid deferring mail. ", - "Configure a DNSSEC-validating resolver to enable DANE." + "Ensure the resolver is DNSSEC-capable and reachable over TCP." ), ); } diff --git a/crates/common/src/config/smtp/resolver.rs b/crates/common/src/config/smtp/resolver.rs index 0b72642a..38c7ffbe 100644 --- a/crates/common/src/config/smtp/resolver.rs +++ b/crates/common/src/config/smtp/resolver.rs @@ -4,6 +4,7 @@ * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL */ +use ahash::AHashMap; use mail_auth::{ MessageAuthenticator, hickory_resolver::{ @@ -25,6 +26,7 @@ use serde::{Deserialize, Serialize}; use std::{ fmt::Display, hash::{DefaultHasher, Hash, Hasher}, + net::IpAddr, str::FromStr, sync::Arc, }; @@ -139,6 +141,7 @@ impl Resolvers { }, DnsResolver::Custom(resolver) => { resolver_config = ResolverConfig::default(); + let mut nameservers: AHashMap> = AHashMap::new(); for server in resolver.servers { let ip = server.address.into_inner(); @@ -152,11 +155,11 @@ impl Resolvers { }; let mut connection = ConnectionConfig::new(protocol); connection.port = port; - resolver_config.add_name_server(NameServerConfig::new( - ip, - true, - vec![connection], - )); + nameservers.entry(ip).or_default().push(connection); + } + + for (ip, connections) in nameservers { + resolver_config.add_name_server(NameServerConfig::new(ip, true, connections)); } opts.num_concurrent_reqs = resolver.concurrency as usize; @@ -223,19 +226,24 @@ impl Resolvers { }; Resolvers { - dns: MessageAuthenticator::new(resolver_config, opts).unwrap(), #[cfg(not(feature = "test_mode"))] - dnssec_available: dnssec_capable(&dnssec.resolver).await, + dnssec_available: ensure_dnssec(&resolver_config, &dnssec.resolver).await, #[cfg(feature = "test_mode")] dnssec_available: true, + dns: MessageAuthenticator::new(resolver_config, opts).unwrap(), dnssec, } } } #[cfg(not(feature = "test_mode"))] -async fn dnssec_capable(resolver: &TokioResolver) -> bool { - resolver +async fn ensure_dnssec(config: &ResolverConfig, resolver: &TokioResolver) -> bool { + config.name_servers().iter().any(|name_server| { + name_server + .connections + .iter() + .any(|connection| !matches!(connection.protocol, ProtocolConfig::Udp)) + }) && resolver .lookup( hickory_proto::rr::Name::root(), hickory_proto::rr::RecordType::DNSKEY,