spf

package module
v0.0.0-...-7f5e249 Latest Latest
Warning

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

Go to latest
Published: Jan 2, 2024 License: MIT Imports: 12 Imported by: 1

README

A library to evaluate SPF policy records

Complete, usable library to check whether a received email passes a published SPF (Sender Policy Framework) policy.

It implements all of the SPF checker protocol as described in RFC 7208, including macros and PTR checks, and passes 100% of the openspf and pyspf test suites.

A DNS stub resolver using miekg/dns is included, but can be replaced by anything that implements the spf.Resolver interface.

As well as providing an implementation of the SPF check_host() function it also provides hooks to instrument the checking process. The included example client uses these to show how an SPF record is evaluated.

Use as a CLI tool

spf is a commandline tool for evaluating spf records.

 spf -ip 8.8.8.8 -from steve@aol.com

 Result: softfail
 Error:  <nil>
 Explanation:

If run with the -trace flag it will show the steps take to check the spf
record, and if the -dns flag is added it will show all the DNS queries
involved.

 spf -help
 Usage of spf:
   -dns
     	show dns queries
   -from string
     	821.From address
   -helo string
     	domain used in 821.HELO
   -ip string
     	ip address from which the message is sent
   -mechanisms
    	show details about each mechanism
   -trace
     	show evaluation of record
./spf -trace -from n_e_i_bounces@insideapple.apple.com -ip 17.179.250.63
insideapple.apple.com.: v=spf1 include:_spf-txn.apple.com include:_spf-mkt.apple.com include:_spf.apple.com ~all
_spf-txn.apple.com.: v=spf1 ip4:17.151.1.0/24 ip4:17.171.37.0/24 ip4:17.111.110.0/23 ~all
_spf-txn.apple.com. returns softfail: v=spf1 ip4:17.151.1.0/24 ip4:17.171.37.0/24 ip4:17.111.110.0/23 ~all
insideapple.apple.com. included _spf-txn.apple.com which didn't match
_spf-mkt.apple.com.: v=spf1 ip4:17.171.23.0/24 ip4:17.179.250.0/24 ip4:17.32.227.0/24 ip4:17.240.6.0/24 ip4:17.240.49.0/24 ~all
_spf-mkt.apple.com. returns pass: v=spf1 ip4:17.171.23.0/24 ip4:17.179.250.0/24 ip4:17.32.227.0/24 ip4:17.240.6.0/24 ip4:17.240.49.0/24 ~all
insideapple.apple.com. included _spf-mkt.apple.com which matched, so the include returned pass
insideapple.apple.com. returns pass: v=spf1 include:_spf-txn.apple.com include:_spf-mkt.apple.com include:_spf.apple.com ~all
Result: pass
Error:  <nil>
Explanation:
Installing binaries

Binary releases of the commandline tool spf are available under Releases.

You'll need to unpack them with tar zxf spf-<stuff>.tar.gz or unzip the Windows packages.

These are built automatically and right now the workflow doesn't sign the binaries. You'll need to bypass the check for that, e.g. on macOS open it in finder, right click on it and select Open then give permission for it to run.

Use as a library

import "github.com/wttw/spf"

ip := net.ParseIP("8.8.8.8")
result, _ := spf.Check(context.Background(), ip, "steve@aol.com", "aol.com")
fmt.Println(result)

Documentation

Overview

Package spf implements an SPF checker to evaluate whether or not an email messages passes a published SPF (Sender Policy Framework) policy.

It implements all of the SPF checker protocol as described in RFC 7208, including macros and PTR checks, and passes 100% of the openspf and pyspf test suites.

A DNS stub resolver is included, but can be replaced by anything that implements the spf.Resolver interface.

The Hook interface can be used to hook into the check_host function to see more details about why a policy passes or fails.

Index

Examples

Constants

View Source
const DefaultDNSLimit = 10

DefaultDNSLimit is the maximum number of SPF terms that require DNS resolution to allow before returning a failure.

View Source
const DefaultMXAddressLimit = 10

DefaultMXAddressLimit is the maximum number of A or AAAA requests to allow while evaluating each "mx" mechanism before returning a failure.

View Source
const DefaultPtrAddressLimit = 10

DefaultPtrAddressLimit is the limit on how many PTR records will be used when evaluating a "ptr" mechanism or a "%{p}" macro.

View Source
const DefaultVoidQueryLimit = 2

DefaultVoidQueryLimit is the maximum number of DNS queries that return no records to allow before returning a failure.

Variables

View Source
var ResolvConf = "/etc/resolv.conf"

ResolvConf holds the path to a resolv.conf(5) format file used to configure DefaultResolver.

View Source
var ResultChar = map[ResultType]string{
	None:     "",
	Neutral:  "?",
	Pass:     "",
	Fail:     "-",
	Softfail: "~",
}

ResultChar maps between the spf.ResultType and the equivalent single character qualifier used in SPF text format.

Functions

func MacroIsValid

func MacroIsValid(macroString string) bool

MacroIsValid validates an SPF macro.

Types

type Checker

type Checker struct {
	Resolver        Resolver // used to resolve all DNS queries
	DNSLimit        int      // maximum number of DNS-using mechanisms
	MXAddressLimit  int      // maximum number of hostnames in an "mx" mechanism
	VoidQueryLimit  int      // maximum number of empty DNS responses
	PtrAddressLimit int      // use only this many PTR responses
	Hostname        string   // the hostname of the machine running the check
	Hook            Hook     // instrumentation hooks
}

Checker holds all the configuration and limits for checking SPF records.

var DefaultChecker *Checker

DefaultChecker is the Checker that will be used by the package level spf.Check function.

func NewChecker

func NewChecker() *Checker

NewChecker creates a new Checker with sensible defaults.

func (*Checker) CheckHost

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

CheckHost implements the SPF check_host() function for a given domain.

func (*Checker) ExpandDomainSpec

func (c *Checker) ExpandDomainSpec(ctx context.Context, domainSpec string, result *Result, domain string, exp bool) (string, error)

ExpandDomainSpec expands a domain-spec as an SPF macro, then checks that the result is a valid-appearing hostname.

func (*Checker) ExpandMacro

func (c *Checker) ExpandMacro(ctx context.Context, domainSpec string, result *Result, domain string, exp bool) (string, error)

ExpandMacro populates an SPF macro based on the current state of the check process.

func (*Checker) SPF

func (c *Checker) SPF(ctx context.Context, ip net.IP, mailFrom string, helo string) Result

SPF checks SPF policy for a message using both smtp.mailfrom and smtp.helo.

Example
package main

import (
	"context"
	"fmt"
	"net"

	"github.com/wttw/spf"
)

func main() {
	ip := net.ParseIP("8.8.8.8")
	c := spf.NewChecker()
	c.Hostname = "mail.example.com"
	result := c.SPF(context.Background(), ip, "steve@aol.com", "aol.com")
	fmt.Printf("Authentication-Results: %s\n", result.AuthenticationResults())
}
Output:

Authentication-Results: mail.example.com; spf=softfail smtp.helo=aol.com

type DefaultResolver

type DefaultResolver struct {
	// contains filtered or unexported fields
}

DefaultResolver is the Resolver that will be used in default constructed Checkers.

func (*DefaultResolver) Resolve

func (res *DefaultResolver) Resolve(ctx context.Context, r *dns.Msg) (*dns.Msg, error)

Resolve performs a low level DNS lookup using miekg/dns format packet representation.

type Hook

type Hook interface {
	Dns(r *dns.Msg, m *dns.Msg, err error)                                   // a dns record was looked up
	Record(record, domain string)                                            // an SPF record is about to be processed
	RecordResult(domain string, result *Result)                              // an SPF record has completed processing
	Macro(before, after string, err error)                                   // a macro has been expanded
	Mechanism(domain string, index int, mechanism Mechanism, result *Result) // an SPF mechanism has provided a result
	Redirect(target string)                                                  // an SPF redirect modifier is about to be executed
}

Hook allows a caller to intercept the SPF check process at various points through it's execution.

type Mechanism

type Mechanism interface {
	Evaluate(ctx context.Context, result *Result, domain string) (ResultType, error)
	String() string
}

Mechanism holds an SPF mechanism

func NewMechanism

func NewMechanism(raw string) (Mechanism, error)

NewMechanism creates a new Mechanism from it's text representation

type MechanismA

type MechanismA struct {
	Qualifier  ResultType
	DomainSpec string
	Mask4      net.IPMask
	Mask6      net.IPMask
}

MechanismA represents an SPF "a" mechanism. It matches based on DNS lookups of A and AAAA records for it's domain-spec.

func (MechanismA) Evaluate

func (m MechanismA) Evaluate(ctx context.Context, result *Result, domain string) (ResultType, error)

func (MechanismA) String

func (m MechanismA) String() string

type MechanismAll

type MechanismAll struct {
	Qualifier ResultType
}

MechanismAll represents an SPF "all" mechanism, it always matches.

func (MechanismAll) Evaluate

func (m MechanismAll) Evaluate(_ context.Context, _ *Result, _ string) (ResultType, error)

func (MechanismAll) String

func (m MechanismAll) String() string

type MechanismExists

type MechanismExists struct {
	Qualifier  ResultType
	DomainSpec string
}

MechanismExists represents an SPF "exists" mechanism. It matches based on the existence of a DNS A record for the - macro-expanded - domain-spec.

func (MechanismExists) Evaluate

func (m MechanismExists) Evaluate(ctx context.Context, result *Result, domain string) (ResultType, error)

func (MechanismExists) String

func (m MechanismExists) String() string

type MechanismInclude

type MechanismInclude struct {
	Qualifier  ResultType
	DomainSpec string
}

MechanismInclude represents an SPF "include" mechanism, it matches based on the result of an SPF check on another host name.

func (MechanismInclude) Evaluate

func (m MechanismInclude) Evaluate(ctx context.Context, result *Result, domain string) (ResultType, error)

func (MechanismInclude) String

func (m MechanismInclude) String() string

type MechanismIp4

type MechanismIp4 struct {
	Qualifier ResultType
	Net       *net.IPNet
}

MechanismIp4 represents an SPF "ip4" mechanism. It matches based on the connecting IP being within the provided address range.

func (MechanismIp4) Evaluate

func (m MechanismIp4) Evaluate(_ context.Context, result *Result, _ string) (ResultType, error)

func (MechanismIp4) String

func (m MechanismIp4) String() string

type MechanismIp6

type MechanismIp6 struct {
	Qualifier ResultType
	Net       *net.IPNet
}

MechanismIp6 represents an SPF "ip6" mechanism. It matches based on the connecting IP being within the provided address range.

func (MechanismIp6) Evaluate

func (m MechanismIp6) Evaluate(_ context.Context, result *Result, _ string) (ResultType, error)

func (MechanismIp6) String

func (m MechanismIp6) String() string

type MechanismMX

type MechanismMX struct {
	Qualifier  ResultType
	DomainSpec string
	Mask4      net.IPMask
	Mask6      net.IPMask
}

MechanismMX represents an SPF "mx" mechanism. It matches based on DNS lookups of MX records for it's domain-spec, and DNS lookups for A and AAAA records for the results of those.

func (MechanismMX) Evaluate

func (m MechanismMX) Evaluate(ctx context.Context, result *Result, domain string) (ResultType, error)

func (MechanismMX) String

func (m MechanismMX) String() string

type MechanismPTR

type MechanismPTR struct {
	Qualifier  ResultType
	DomainSpec string
}

MechanismPTR represents an SPF "ptr" mechanism.

func (MechanismPTR) Evaluate

func (m MechanismPTR) Evaluate(ctx context.Context, result *Result, domain string) (ResultType, error)

MechanismPTR represents the SPF "ptr" mechanism.

func (MechanismPTR) String

func (m MechanismPTR) String() string

type Resolver

type Resolver interface {
	Resolve(ctx context.Context, r *dns.Msg) (*dns.Msg, error)
}

Resolver is used for all DNS lookups during an SPF check

type Result

type Result struct {
	Type        ResultType
	Error       error
	DNSQueries  int
	VoidLookups int
	Explanation string
	UsedHelo    bool
	// contains filtered or unexported fields
}

Result is all the information gathered during checking SPF for a message.

func (*Result) AuthenticationResults

func (r *Result) AuthenticationResults() string

AuthenticationResults displays a Result as an RFC 8601 Authentication-Results: header

func (*Result) String

func (r *Result) String() string

type ResultType

type ResultType int

ResultType is the overall SPF result from checking a message.

const (
	None ResultType = iota
	Neutral
	Pass
	Fail
	Softfail
	Temperror
	Permerror
)

func Check

func Check(ctx context.Context, ip net.IP, mailFrom string, helo string) (ResultType, string)

Check checks SPF policy for a message using both smtp.mailfrom and smtp.helo.

Example
package main

import (
	"context"
	"fmt"
	"net"

	"github.com/wttw/spf"
)

func main() {
	ip := net.ParseIP("8.8.8.8")
	result, _ := spf.Check(context.Background(), ip, "steve@aol.com", "aol.com")
	fmt.Println(result)
}
Output:

softfail

func ResultTypeString

func ResultTypeString(s string) (ResultType, error)

ResultTypeString retrieves an enum value from the enum constants string name. Throws an error if the param is not part of the enum.

func ResultTypeValues

func ResultTypeValues() []ResultType

ResultTypeValues returns all values of the enum

func (ResultType) IsAResultType

func (i ResultType) IsAResultType() bool

IsAResultType returns "true" if the value is listed in the enum definition. "false" otherwise

func (ResultType) String

func (i ResultType) String() string

type SPFRecord

type SPFRecord struct {
	Mechanisms     []Mechanism
	Exp            string
	Redirect       string
	OtherModifiers []string
}

SPFRecord holds an SPF record parsed from a single DNS TXT record.

func ParseSPF

func ParseSPF(s string) (*SPFRecord, error)

ParseSPF parses the text of an SPF record.

Directories

Path Synopsis
cmd
spf
spf is a commandline tool for evaluating spf records.
spf is a commandline tool for evaluating spf records.
+build tools
+build tools

Jump to

Keyboard shortcuts

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