# spf-checker _(no submodules)_ ## Purpose Implements the SPF (RFC 7208) evaluation algorithm: parses the record, walks each mechanism in order, follows `include:` and `redirect=` with bounded depth and lookup limits, and matches the candidate IPv4 / IPv6 address against `ip4:` and `ip6:` CIDR ranges. The DNS resolver is fully injectable so the package can be embedded in browsers, edge workers, or sandboxed runtimes that need to route lookups through DoH or a service mesh. ## Features - Parses every mechanism token (`ip4`, `ip6`, `a`, `mx`, `include`, `all`, `exists`, `redirect`) and their qualifier prefixes (`+ - ~ ?`). - Bounded recursion: `MAX_INCLUDE_DEPTH = 10` and `MAX_DNS_LOOKUPS = 10` to surface RFC 7208 §4.6.4 permerror conditions instead of hanging. - IPv4 matching with `Number.parseInt` + bitmasks; IPv6 matching by expanding `::` shorthand and comparing bit prefixes. - Records `email_processing.spf.result_total` attributed by `result` for every outcome. ## Usage: Basic ```ts import { checkSpf } from "@suaveplan/email-processing/spf"; const out = await checkSpf(senderIp, fromDomain, async (d) => { const recs = await dns.resolveTxt(d); return recs.map((parts) => parts.join("")); }); ``` ## Usage: Advanced ```ts import { checkSpf, parseSpfRecord } from "@suaveplan/email-processing/spf"; const recs = await dnsResolver(domain); const v = recs.find((r) => r.startsWith("v=spf1")); if (v) { const mechs = parseSpfRecord(v); if (mechs.some((m) => m.type === "all" && m.qualifier === "+")) { log.warn({ domain }, "open SPF policy (+all)"); } } const out = await checkSpf(ip, domain, dnsResolver); ``` ## API Reference - `parseSpfRecord(record): SpfMechanism[]`. - `ipv4Matches(ip, cidr): boolean`. - `ipv6Matches(ip, cidr): boolean`. - `checkSpf(ip, domain, resolver, depth?, lookupCount?): Promise`. ## Implementation Notes `a` / `mx` / `exists` mechanisms are recognised but treated as non-matches because resolving them requires additional DNS lookups the caller may not want to fund — most real-world SPF records lean on `ip4:` / `ip6:` / `include:` anyway. The `lookupCount` parameter is exposed so callers can compose `checkSpf` across multiple top-level domains in a single run without re-instantiating the budget. The function records the SPF outcome to the OTel counter before returning, including the `permerror` paths, so dashboards can distinguish "bad record" from "no record".