spf

package module
Version: v0.0.0-...-76747b8 Latest Latest
Warning

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

Go to latest
Published: Aug 17, 2017 License: MIT Imports: 9 Imported by: 9

README

Sender Policy Framework

A comprehensive RFC7208 implementation

Build Status Go Report Card GoDoc

About

The SPF Library implements Sender Policy Framework described in RFC 7208. It aims to cover all rough edge cases from RFC 7208. Hence, the library does not operate on strings only, rather "understands" SPF records and reacts properly to valid and invalid input. Wherever I found it useful, I added comments with RFC sections and quotes directly in the source code, so the readers can follow implemented logic.

Current status

The library is still under development. API may change, including function/methods names and signatures. I will consider it correct and stable once it passess all tests described in the most popular SPF implementation - pyspf.

Testing

Testing is an important part of this implementation. There are unit tests that will run locally in your environment, however there are also configuration files for named DNS server that would be able to respond implemented testcases. (In fact, for the long time I used a real DNS server with such configuration as a testing infrastructure for my code). There is a plan to implement simple DNS server that would be able to read .yaml files with comprehensive testsuite defined in pyspf package. Code coverage is also important part of the development and the aim is to keep it as high as 9x %

Dependencies

SPF library depends on another DNS library. Sadly, Go's builtin DNS library is not elastic enough and does not allow for controlling underlying DNS queries/responses.

Pull requests & code review

If you have any comments about code structure feel free to reach out or simply make a Pull Request

Documentation

Index

Constants

This section is empty.

Variables

View Source
var (
	ErrDNSTemperror     = errors.New("temporary DNS error")
	ErrDNSPermerror     = errors.New("permanent DNS error")
	ErrInvalidDomain    = errors.New("invalid domain name")
	ErrDNSLimitExceeded = errors.New("limit exceeded")
	ErrSPFNotFound      = errors.New("SPF record not found")
)

Errors could be used for root couse analysis

Functions

func NormalizeFQDN

func NormalizeFQDN(name string) string

NormalizeFQDN appends a root domain (a dot) to the FQDN.

Types

type DNSResolver

type DNSResolver struct{}

DNSResolver implements Resolver using local DNS

func (*DNSResolver) Exists

func (r *DNSResolver) Exists(name string) (bool, error)

Exists is used for a DNS A RR lookup (even when the connection type is IPv6). If any A record is returned, this mechanism matches.

func (*DNSResolver) LookupTXT

func (r *DNSResolver) LookupTXT(name string) ([]string, error)

LookupTXT returns the DNS TXT records for the given domain name.

func (*DNSResolver) LookupTXTStrict

func (r *DNSResolver) LookupTXTStrict(name string) ([]string, error)

LookupTXTStrict returns DNS TXT records for the given name, however it will return ErrDNSPermerror upon NXDOMAIN (RCODE 3)

func (*DNSResolver) MatchIP

func (r *DNSResolver) MatchIP(name string, matcher IPMatcherFunc) (bool, error)

MatchIP provides an address lookup, which should be done on the name using the type of lookup (A or AAAA). Then IPMatcherFunc used to compare checked IP to the returned address(es). If any address matches, the mechanism matches

func (*DNSResolver) MatchMX

func (r *DNSResolver) MatchMX(name string, matcher IPMatcherFunc) (bool, error)

MatchMX is similar to MatchIP but first performs an MX lookup on the name. Then it performs an address lookup on each MX name returned. Then IPMatcherFunc used to compare checked IP to the returned address(es). If any address matches, the mechanism matches

type IPMatcherFunc

type IPMatcherFunc func(ip net.IP) (bool, error)

IPMatcherFunc returns true if ip matches to implemented rules. If IPMatcherFunc returns any non nil error, the Resolver must stop any further processing and use the error as resulting error.

type LimitedResolver

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

LimitedResolver wraps a Resolver and limits number of lookups possible to do with it. All overlimited calls return ErrDNSLimitExceeded.

func (*LimitedResolver) Exists

func (r *LimitedResolver) Exists(name string) (bool, error)

Exists is used for a DNS A RR lookup (even when the connection type is IPv6). If any A record is returned, this mechanism matches. Returns false and ErrDNSLimitExceeded if total number of lookups made by underlying resolver exceed the limit.

func (*LimitedResolver) LookupTXT

func (r *LimitedResolver) LookupTXT(name string) ([]string, error)

LookupTXT returns the DNS TXT records for the given domain name. Returns nil and ErrDNSLimitExceeded if total number of lookups made by underlying resolver exceed the limit.

func (*LimitedResolver) LookupTXTStrict

func (r *LimitedResolver) LookupTXTStrict(name string) ([]string, error)

LookupTXTStrict returns the DNS TXT records for the given domain name. Returns nil and ErrDNSLimitExceeded if total number of lookups made by underlying resolver exceed the limit. It will also return ErrDNSPermerror upon DNS call return error NXDOMAIN (RCODE 3)

func (*LimitedResolver) MatchIP

func (r *LimitedResolver) MatchIP(name string, matcher IPMatcherFunc) (bool, error)

MatchIP provides an address lookup, which should be done on the name using the type of lookup (A or AAAA). Then IPMatcherFunc used to compare checked IP to the returned address(es). If any address matches, the mechanism matches Returns false and ErrDNSLimitExceeded if total number of lookups made by underlying resolver exceed the limit.

func (*LimitedResolver) MatchMX

func (r *LimitedResolver) MatchMX(name string, matcher IPMatcherFunc) (bool, error)

MatchMX is similar to MatchIP but first performs an MX lookup on the name. Then it performs an address lookup on each MX name returned. Then IPMatcherFunc used to compare checked IP to the returned address(es). If any address matches, the mechanism matches.

In addition to that limit, the evaluation of each "MX" record MUST NOT result in querying more than 10 address records -- either "A" or "AAAA" resource records. If this limit is exceeded, the "mx" mechanism MUST produce a "permerror" result.

Returns false and ErrDNSLimitExceeded if total number of lookups made by underlying resolver exceed the limit.

type MiekgDNSResolver

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

MiekgDNSResolver implements Resolver using github.com/miekg/dns

func (*MiekgDNSResolver) Exists

func (r *MiekgDNSResolver) Exists(name string) (bool, error)

Exists is used for a DNS A RR lookup (even when the connection type is IPv6). If any A record is returned, this mechanism matches.

func (*MiekgDNSResolver) LookupTXT

func (r *MiekgDNSResolver) LookupTXT(name string) ([]string, error)

LookupTXT returns the DNS TXT records for the given domain name.

func (*MiekgDNSResolver) LookupTXTStrict

func (r *MiekgDNSResolver) LookupTXTStrict(name string) ([]string, error)

LookupTXTStrict returns DNS TXT records for the given name, however it will return ErrDNSPermerror upon NXDOMAIN (RCODE 3)

func (*MiekgDNSResolver) MatchIP

func (r *MiekgDNSResolver) MatchIP(name string, matcher IPMatcherFunc) (bool, error)

MatchIP provides an address lookup, which should be done on the name using the type of lookup (A or AAAA). Then IPMatcherFunc used to compare checked IP to the returned address(es). If any address matches, the mechanism matches

func (*MiekgDNSResolver) MatchMX

func (r *MiekgDNSResolver) MatchMX(name string, matcher IPMatcherFunc) (bool, error)

MatchMX is similar to MatchIP but first performs an MX lookup on the name. Then it performs an address lookup on each MX name returned. Then IPMatcherFunc used to compare checked IP to the returned address(es). If any address matches, the mechanism matches

type Resolver

type Resolver interface {
	// LookupTXT returns the DNS TXT records for the given domain name.
	LookupTXT(string) ([]string, error)
	// LookupTXTStrict returns DNS TXT records for the given name, however it
	// will return ErrDNSPermerror upon returned NXDOMAIN (RCODE 3)
	LookupTXTStrict(string) ([]string, error)
	// Exists is used for a DNS A RR lookup (even when the
	// connection type is IPv6).  If any A record is returned, this
	// mechanism matches.
	Exists(string) (bool, error)
	// MatchIP provides an address lookup, which should be done on the name
	// using the type of lookup (A or AAAA).
	// Then IPMatcherFunc used to compare checked IP to the returned address(es).
	// If any address matches, the mechanism matches
	MatchIP(string, IPMatcherFunc) (bool, error)
	// MatchMX is similar to MatchIP but first performs an MX lookup on the
	// name.  Then it performs an address lookup on each MX name returned.
	// Then IPMatcherFunc used to compare checked IP to the returned address(es).
	// If any address matches, the mechanism matches
	MatchMX(string, IPMatcherFunc) (bool, error)
}

Resolver provides abstraction for DNS layer

func NewLimitedResolver

func NewLimitedResolver(r Resolver, lookupLimit, mxQueriesLimit uint16) Resolver

NewLimitedResolver returns a resolver which will pass up to lookupLimit calls to r. In addition to that limit, the evaluation of each "MX" record will be limited to mxQueryLimit. All calls over the limit will return ErrDNSLimitExceeded.

func NewMiekgDNSResolver

func NewMiekgDNSResolver(addr string) (Resolver, error)

NewMiekgDNSResolver returns new instance of Resolver

type Result

type Result int

Result represents result of SPF evaluation as it defined by RFC7208 https://tools.ietf.org/html/rfc7208#section-2.6

const (

	// None means either (a) no syntactically valid DNS
	// domain name was extracted from the SMTP session that could be used
	// as the one to be authorized, or (b) no SPF records were retrieved
	// from the DNS.
	None Result
	// Neutral result means the ADMD has explicitly stated that it
	// is not asserting whether the IP address is authorized.
	Neutral
	// Pass result is an explicit statement that the client
	// is authorized to inject mail with the given identity.
	Pass
	// Fail result is an explicit statement that the client
	// is not authorized to use the domain in the given identity.
	Fail
	// Softfail result is a weak statement by the publishing ADMD
	// that the host is probably not authorized.  It has not published
	// a stronger, more definitive policy that results in a "fail".
	Softfail
	// Temperror result means the SPF verifier encountered a transient
	// (generally DNS) error while performing the check.
	// A later retry may succeed without further DNS operator action.
	Temperror
	// Permerror result means the domain's published records could
	// not be correctly interpreted.
	// This signals an error condition that definitely requires
	// DNS operator intervention to be resolved.
	Permerror
)

func CheckHost

func CheckHost(ip net.IP, domain, sender string) (Result, string, error)

CheckHost is a main entrypoint function evaluating e-mail with regard to SPF and it utilizes DNSResolver as a resolver. As per RFC 7208 it will accept 3 parameters: <ip> - IP{4,6} address of the connected client <domain> - domain portion of the MAIL FROM or HELO identity <sender> - MAIL FROM or HELO identity All the parameters should be parsed and dereferenced from real email fields. This means domain should already be extracted from MAIL FROM field so this function can focus on the core part.

CheckHost returns result of verification, explanations as result of "exp=", and error as the reason for the encountered problem.

func CheckHostWithResolver

func CheckHostWithResolver(ip net.IP, domain, sender string, resolver Resolver) (Result, string, error)

CheckHostWithResolver allows using custom Resolver. Note, that DNS lookup limits need to be enforced by provided Resolver.

The function returns result of verification, explanations as result of "exp=", and error as the reason for the encountered problem.

func (Result) String

func (r Result) String() string

String returns string form of the result as defined by RFC7208 https://tools.ietf.org/html/rfc7208#section-2.6

type SyntaxError

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

SyntaxError represents parsing error, it holds reference to faulty token as well as error describing fault

func (SyntaxError) Error

func (e SyntaxError) Error() string

Jump to

Keyboard shortcuts

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