mailrs-shield
SMTP server anti-spam primitives in three modules: DNSBL lookups, greylisting policy, and FCrDNS (forward-confirmed reverse DNS) checks — async, transport-agnostic, mostly zero-I/O.
Extracted from mailrs so any Rust mail server can drop these in without re-implementing the same DNS-walking patterns. The Rust ecosystem currently has no dedicated crates for any of these three primitives.
What's inside
shield::dnsbl — DNS blocklist queries
Look up an inbound client IP against zones like Spamhaus ZEN, Barracuda, etc. Comes with an in-process TTL cache so repeat connections don't re-query.
use TokioResolver;
use check_dnsbl;
use IpAddr;
# async
shield::greylist — Greylisting policy
Pure policy (Harris 2003 / RFC 6647): defer the first time you see a (client_ip, sender, recipient) triplet, accept after the configured delay if the sender retries. Legitimate MTAs queue and retry; most spam bots don't.
use ;
let cfg = default; // 5-minute initial delay, 36-day pass window
assert_eq!;
assert_eq!;
assert_eq!;
The optional redis-store feature (on by default) ships a GreylistDb that combines Redis (hot cache) + Postgres (cold backup) behind a single check() call:
#
# async
Disable the feature to plug in your own store — the trait surface is just "given a key + clock, look up the first-seen timestamp."
shield::ptr — FCrDNS check
Score an inbound client by whether its IP's reverse DNS forward-resolves back to a name matching the EHLO domain. Returns 0.0 on full match, 1.0 on no match — easy to fold into a spam score.
use TokioResolver;
use check_client_ptr;
use IpAddr;
# async
Performance
Microbenchmarks for the pure helpers (no live resolver hits) live in benches/ops.rs. Measured with criterion 0.8 on Apple Silicon (M-series), cargo bench, release profile.
| Operation | Median | Notes |
|---|---|---|
dnsbl::reverse_ipv4(1.2.3.4) |
~110 ns | builds the 4.3.2.1.zen.spamhaus.org.-shape name |
dnsbl::interpret_spamhaus(127.0.0.2) |
~700 ps | match-arm dispatch, no allocation |
greylist::evaluate_triplet(first seen) |
~850 ps | always defers |
greylist::evaluate_triplet(retry within delay) |
~1.5 ns | timestamp delta + comparison |
greylist::triplet_key(ip, sender, rcpt) |
~120 ns | one format! + lowercase normalization |
ptr::ptr_score_from_names(match) |
~85 ns | scans the candidate names for the EHLO domain |
ptr::ptr_score_from_names(no match) |
~200 ns | runs the full FCrDNS scoring fallback |
Live-resolver paths (check_client_ptr, dnsbl::check, greylist::GreylistDb::is_allowed) aren't bench-able offline; production latency is dominated by DNS / Redis round-trips, not the pure helpers above.
Run with cargo bench -p mailrs-shield. See tests/perf_gate.rs for the regression budgets.
Feature flags
| Flag | Default | What it enables |
|---|---|---|
redis-store |
yes | greylist::GreylistDb (Redis + optional PG cold backup) |
Disable both default features (default-features = false) if you're plugging in your own backends.
License
Licensed under either of Apache License 2.0 or MIT license at your option.