Expand description
§mailrs-spf
RFC 7208 Sender Policy Framework verifier. Pure-Rust evaluator with a
pluggable DNS resolver trait; ships an optional
hickory-resolver-backed implementation behind the hickory feature.
Pairs with mailrs-rfc5322
and mailrs-dmarc to give
mailrs a full owned email-auth stack — replacing the SPF half of
mail-auth with shape we control.
§Quickstart
use mailrs_spf::{verify, VerifyInput, SpfResult, HickoryResolver};
use hickory_resolver::TokioResolver;
let resolver_inner = TokioResolver::builder_tokio()?.build();
let resolver = HickoryResolver::new(resolver_inner);
let input = VerifyInput {
ip: "203.0.113.42".parse()?,
helo: "mta.example.com".into(),
mail_from: "alice@example.com".into(),
};
let result = verify(&resolver, &input).await;
match result {
SpfResult::Pass => { /* accept */ }
SpfResult::Fail => { /* reject 5xx with SPF reason */ }
SpfResult::SoftFail => { /* accept but tag suspicious */ }
SpfResult::Neutral | SpfResult::None => { /* no policy / no record */ }
SpfResult::PermError | SpfResult::TempError => { /* see RFC 7208 §8 */ }
}§What this crate does
- Parse SPF TXT records into a typed
RecordwithMechanisms - Evaluate against
(IP, HELO, MAIL FROM)per RFC 7208 §4 - All seven result values:
none / pass / fail / softfail / neutral / permerror / temperror - Mechanism support:
all,ip4,ip6,a,mx,include,exists - Qualifier support:
+(default),-,~,? - DNS lookup budget (≤10 per RFC §4.6.4) + recursion depth cap
- Multi-record detection (multiple
v=spf1→PermErrorper §4.5) - DNS resolver trait so callers plug their own DNS (hickory included behind a feature flag)
§What this crate does not (yet)
These are out-of-scope for 1.0 and deferred to 1.x minors:
- Macro expansion (RFC 7208 §7) —
%{i},%{s},%{d}, etc. inexists:/include:domain templates. Common patterns work because most SPF records use literal domains; macro-heavy records (some bulk-mailer providers useexists:%{ir}._spf.provider.com) will compute against the literal template string. Addmacrosfeature when expansion is needed. redirect=modifier (RFC 7208 §6.1) — would extend the lookup to another domain. Detected and skipped without erroring.exp=modifier (§6.2) — explanation text on Fail. Detected and skipped.ptrmechanism (§5.5) — RFC marks it not-recommended; we returnPermErrorif a record uses it.
These are intentional v1 scope limits, not bugs. None of them affect the common case (literal-domain records from major senders); add as 1.x minors when a use case demands.
§Why a new crate?
mail-auth covers SPF + DKIM + DMARC + ARC in one crate. We use it
in mailrs’s inbound pipeline. The shape works but:
- The combined surface is heavy for the SPF use case alone
- We can’t measure its perf cleanly against
mailrs-rfc5322(the underlying message-parsing layer) - Owning SPF + DKIM + DMARC as separate, focused stones lets us tune each per the dep-audit doc
mailrs-dmarc already exists. This crate carves the SPF half;
mailrs-dkim will follow.
§Performance
Measured (criterion, M-series Mac, release):
| Operation | Median |
|---|---|
Record::parse (simple v=spf1 ip4 -all) | 82 ns |
Record::parse (complex 8-mechanism record) | 484 ns |
verify pass-path (no real DNS) | 244 ns |
The verify number is the pure CPU work; actual production verify
is dominated by DNS round-trips (typical 5-50 ms). Reproduce:
cargo bench -p mailrs-spf --bench spf.
§License
Apache-2.0 OR MIT. Module layout:
Re-exports§
pub use error::SpfError;pub use error::SpfResult;pub use evaluator::verify;pub use evaluator::VerifyInput;pub use record::Mechanism;pub use record::Qualifier;pub use record::Record;pub use resolver::SpfResolver;pub use resolver::hickory::HickoryResolver;