spf

package module
v0.0.0-...-2c4ccd8 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 23, 2026 License: MIT Imports: 8 Imported by: 0

README

spf

A Go library for checking Sender Policy Framework (SPF) records, implementing RFC 7208.

Based on mileusna/spf.

Installation

go get github.com/Open-Email/go-spf

Usage

Basic check
import (
    "context"
    "net"
    "github.com/Open-Email/go-spf"
)

ctx := context.Background()
ip := net.ParseIP("203.0.113.1")
result := spf.CheckHost(ctx, ip, "example.com", "sender@example.com", "mail.example.com")

switch result {
case spf.Pass:
    // sender is authorized
case spf.Fail:
    // sender is not authorized
case spf.Softfail:
    // sender is not authorized, but domain is not explicitly rejecting
case spf.Neutral:
    // domain makes no assertion
case spf.None:
    // no SPF record found
case spf.TempError:
    // transient DNS error, try again later
case spf.PermError:
    // permanent error (e.g. malformed SPF record)
}
With explanation string

When the result is Fail, the domain's exp= modifier (if present) provides a human-readable explanation:

result, explanation := spf.CheckHostWithExplanation(ctx, ip, "example.com", "sender@example.com", "mail.example.com")
if result == spf.Fail && explanation != "" {
    log.Println("SPF fail reason:", explanation)
}
Custom DNS resolver

Change the DNS server used by the default resolver:

spf.DNSServer = "1.1.1.1:53" // default: 8.8.8.8:53

Configure the DNS query timeout:

spf.DNSTimeout = 5 * time.Second // default: 2s

Or implement the Resolver interface for full control (custom resolver, caching, mocking in tests):

type Resolver interface {
    LookupTXT(ctx context.Context, domain string) ([]string, error)
    LookupMX(ctx context.Context, domain string) ([]string, error)
    LookupA(ctx context.Context, domain string) ([]net.IP, error)
    LookupAAAA(ctx context.Context, domain string) ([]net.IP, error)
    LookupPTR(ctx context.Context, ip net.IP) ([]string, error)
}

result := spf.CheckHostWithResolver(ctx, ip, "example.com", "sender@example.com", "", myResolver)

The CheckHostWithExplanationAndResolver variant combines both:

result, explanation := spf.CheckHostWithExplanationAndResolver(ctx, ip, domain, sender, helo, myResolver)
Fetching the raw SPF record
record, result := spf.LookupSPF(ctx, "example.com")
// record: "v=spf1 ip4:203.0.113.0/24 ~all"
// result: None if no record, PermError if multiple records found

API

Function Description
CheckHost(ctx, ip, domain, sender, helo) Main SPF check, returns Result
CheckHostWithExplanation(ctx, ip, domain, sender, helo) Returns (Result, explanation)
CheckHostWithResolver(ctx, ip, domain, sender, helo, resolver) Check with custom Resolver
CheckHostWithExplanationAndResolver(ctx, ip, domain, sender, helo, resolver) Custom resolver and explanation
LookupSPF(ctx, domain) Fetch raw SPF TXT record

Parameters:

  • ctx — context for cancellation and timeouts
  • ip — IP address of the connecting SMTP client
  • domain — domain to check SPF for (from MAIL FROM or HELO)
  • sender — full MAIL FROM address; if empty, postmaster@<helo> is used
  • helo — domain from the SMTP HELO/EHLO command

RFC 7208 compliance

Supported mechanisms: all, include, a, mx, ptr, ip4, ip6, exists

Supported modifiers: redirect=, exp=

Supported macro letters: s, l, o, d, i, p, v, h with digit, r (reverse), and delimiter transformers

Enforced limits per RFC §4.6.4:

  • 10 DNS-querying mechanisms per evaluation
  • 2 void lookups (NXDOMAIN / empty responses)
  • 10 MX or PTR records per mechanism
  • 10 levels of include/redirect recursion
  • 10 CNAME hops per DNS lookup

Additional features:

  • Case-insensitive mechanism and modifier parsing
  • Duplicate redirect= or exp= modifier detection (→ PermError)
  • IPv6 PTR validation uses AAAA records (not A)
  • DNS errors on exists mechanism correctly return TempError
  • Void lookup counting on exists empty responses
  • TCP fallback on DNS truncation
  • Configurable DNS timeout via DNSTimeout
  • Context support for cancellation and deadline propagation

License

MIT

Documentation

Index

Constants

View Source
const (
	None      = Result("NONE")
	Neutral   = Result("NEUTRAL")
	Pass      = Result("PASS")
	Fail      = Result("FAIL")
	Softfail  = Result("SOFTFAIL")
	TempError = Result("TEMPERROR")
	PermError = Result("PERMERROR")
)

SPF results

Variables

View Source
var DNSServer = "8.8.8.8:53"

DNSServer is the DNS server address used by DefaultResolver, in <ip>:<port> format. Default is Google's 8.8.8.8:53. A misconfigured DNSServer will cause SPF checks to return TEMPERROR.

View Source
var DNSTimeout = 2 * time.Second

DNSTimeout is the timeout for individual DNS queries made by DefaultResolver. Default is 2 seconds. Adjust for your network conditions.

Functions

This section is empty.

Types

type Resolver

type Resolver interface {
	LookupTXT(ctx context.Context, domain string) ([]string, error)
	LookupMX(ctx context.Context, domain string) ([]string, error)
	LookupA(ctx context.Context, domain string) ([]net.IP, error)
	LookupAAAA(ctx context.Context, domain string) ([]net.IP, error)
	LookupPTR(ctx context.Context, ip net.IP) ([]string, error)
}

Resolver is the interface for DNS lookups used during SPF evaluation. Implement this interface to inject a custom or mock DNS resolver.

var DefaultResolver Resolver = &dnsResolver{}

DefaultResolver is the package-level Resolver that uses the miekg/dns implementation and the DNSServer global variable.

type Result

type Result string

Result of SPF check

func CheckHost

func CheckHost(ctx context.Context, ip net.IP, domain, sender, helo string) Result

CheckHost performs an SPF check. ip - the IP address of the SMTP client that is emitting the mail, either IPv4 or IPv6. domain - the domain that provides the sought-after authorization information; initially, the domain portion of the "MAIL FROM" or "HELO" identity. sender - the "MAIL FROM" or "HELO" identity. helo - domain from helo, used as sender domain if sender is not specified.

func CheckHostWithExplanation

func CheckHostWithExplanation(ctx context.Context, ip net.IP, domain, sender, helo string) (Result, string)

CheckHostWithExplanation is like CheckHost but also returns the explanation string if the result is Fail.

func CheckHostWithExplanationAndResolver

func CheckHostWithExplanationAndResolver(ctx context.Context, ip net.IP, domain, sender, helo string, r Resolver) (Result, string)

CheckHostWithExplanationAndResolver is like CheckHostWithResolver but also returns the explanation string.

func CheckHostWithResolver

func CheckHostWithResolver(ctx context.Context, ip net.IP, domain, sender, helo string, r Resolver) Result

CheckHostWithResolver is like CheckHost but uses the provided Resolver for all DNS lookups.

func LookupSPF

func LookupSPF(ctx context.Context, domain string) (spf string, r Result)

LookupSPF returns the SPF TXT record for domain. If no records or more than one record is found, r is set to None or PermError respectively. If the DNS lookup fails, r is TempError.

func (Result) IsSet

func (r Result) IsSet() bool

IsSet returns true if Result var is set to some value

func (Result) String

func (r Result) String() string

String representation of Result type

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL