anotherspf is a lightweight and standards-compliant SPF (Sender Policy Framework) record evaluator written in Go. It performs DNS-based SPF validation for a given IP, sender, and domain, supporting full SPF rule parsing, macro expansion, DNS lookup limits, and recursive evaluation of modifiers like include and redirect.
go get github.com/kisshan13/anotherspf-
RFC 7208-compliant SPF evaluation
-
Supports IPv4 and IPv6
-
Enforces 10-DNS-lookup limit per SPF spec
-
Pluggable DNS resolver interface
-
Evaluates SPF mechanisms and modifiers:
a,mx,ip4,ip6,include,exists,allredirect,exp
-
Expands macros like
%{s},%{i},%{h},%{d}etc. -
Thread-safe and embeddable in other applications
info, err := anotherspf.Check(
"192.0.2.1",
"example.com",
"user@example.com",
nil, // uses default DNS resolver
)
if err != nil {
log.Fatal(err)
}
fmt.Println("SPF result:", info.Status)resolver := &MyResolver{} // implements DNSResolver
info, err := anotherspf.Check(
"192.0.2.1",
"example.com",
"user@example.com",
resolver,
)Evaluates the SPF record for the given sender IP, domain, and envelope sender address.
Behavior:
- Uses the provided
DNSResolverfor all DNS lookups - Falls back to an internal default resolver if
resolverisnil - Retrieves TXT records for the domain
- Parses the SPF record
- Recursively evaluates SPF mechanisms and modifiers
- Tracks DNS lookups and enforces RFC limits
- Returns a structured
SPFInfoobject and an error (if any)
Allows full control over DNS resolution, useful for testing, caching, custom DNS stacks, or sandboxed environments.
type DNSResolver interface {
LookupTXT(host string) ([]string, error)
LookupIP(host string) ([]net.IP, error)
LookupMX(name string) ([]*net.MX, error)
}If nil is passed to Check, a default resolver using Goβs standard net package is used internally.
Tracks state and result of an SPF evaluation.
type SPFInfo struct {
LookupCount int
Lookups map[string]*Lookup
lookedDns map[string]bool
Status Result
PassedRule *Rule
Rule []*Rule
Record string
mu sync.Mutex
}Represents a parsed SPF rule (mechanism or modifier).
type Rule struct {
Modifier Modifier
Mechanism Mechanism
Qualifier Qualifier
ContainsMacro bool
Key string
Value string
}Carries contextual variables for SPF macro expansion.
type MacroContext struct {
Sender string
IP string
Helo string
Domain string
Authoritative string
}Possible SPF evaluation results:
const (
Pass Result = "pass"
Fail Result = "fail"
SoftFail Result = "softfail"
Neutral Result = "neutral"
TempError Result = "temperror"
PermError Result = "permerror"
None Result = "none"
)ip4,ip6a,mx,existsinclude,all
redirect=<domain>exp=<explanation>- Unknown modifiers are parsed but ignored.
const (
QPass = "+"
QFail = "-"
QSoftFail = "~"
QNeutral = "?"
)- Enforces a strict maximum of 10 DNS lookups
- Deduplicates A, MX, and TXT queries per host
- Uses resolver-level caching if provided
- Fails with
PermErrorwhen lookup limits are exceeded
Recursively evaluates parsed SPF rules with DNS checks.
Expands macros in SPF strings using runtime context.
Parses SPF TXT records into a structured rule list.
MIT β See the GitHub repository for details.