Skip to main content

Crate mailrs_spf

Crate mailrs_spf 

Source
Expand description

§mailrs-spf

Crates.io docs.rs License

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 Record with Mechanisms
  • 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=spf1PermError per §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. in exists: / include: domain templates. Common patterns work because most SPF records use literal domains; macro-heavy records (some bulk-mailer providers use exists:%{ir}._spf.provider.com) will compute against the literal template string. Add macros feature 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.
  • ptr mechanism (§5.5) — RFC marks it not-recommended; we return PermError if 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):

OperationMedian
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;

Modules§

error
Error + result types per RFC 7208 §2.6.
evaluator
Evaluator: walks a Record’s mechanisms against an IP + DNS to produce a SpfResult (RFC 7208 §4).
record
SPF record parsing (RFC 7208 §4.6).
resolver
DNS resolver trait that the SPF evaluator uses to fetch records.