Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

BED-564/add-cname-chain-to-spf-tree #25

Merged
merged 1 commit into from
Mar 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions listener.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ type Listener interface {
NonMatch(qualifier, mechanism, value string, result Result, err error)
Match(qualifier, mechanism, value string, result Result, explanation string, extras *ResponseExtras, err error)
FirstMatch(r Result, err error)
MatchingIP(qualifier, mechanism, value string, fqdn string, ipn net.IPNet, host string, ip net.IP)
// VoidLookup Should only be called after a Directive or CheckHost call, to ensure count is updated to correct
// directive and state is correct
VoidLookup(qualifier, mechanism, value string, fqdn string)
MatchingIP(qualifier, mechanism, value, fqdn string, ipn net.IPNet, host string, ip net.IP)
// LookupExtras should only be called after a Directive or CheckHost call,
// to ensure updates on correct directive and state stay consistent.
LookupExtras(qualifier, mechanism, value, fqdn string, extras *ResponseExtras)
}
33 changes: 15 additions & 18 deletions parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -187,9 +187,8 @@ func (p *parser) checkHost(ip net.IP, domain, sender string) (r Result, expl str

var txts []string
txts, extras, err = p.resolver.LookupTXTStrict(NormalizeFQDN(domain))
if extras.Void() {
p.fireVoidLookup(nil, domain)
}

p.fireLookupExtras(nil, domain, extras)

switch err {
case nil:
Expand Down Expand Up @@ -377,17 +376,17 @@ func (p *parser) fireMatch(t *token, r Result, explanation string, extras *Respo
p.listener.Match(t.qualifier.String(), t.mechanism.String(), t.value, r, explanation, extras, e)
}

func (p *parser) fireVoidLookup(t *token, fqdn string) {
func (p *parser) fireLookupExtras(t *token, fqdn string, extras *ResponseExtras) {
if p.listener == nil {
return
}

if t == nil {
p.listener.VoidLookup("", "", "", fqdn)
p.listener.LookupExtras("", "", "", fqdn, extras)
return
}

p.listener.VoidLookup(t.qualifier.String(), t.mechanism.String(), t.value, fqdn)
p.listener.LookupExtras(t.qualifier.String(), t.mechanism.String(), t.value, fqdn, extras)
}

func (p *parser) fireFirstMatch(r Result, e error) {
Expand Down Expand Up @@ -536,9 +535,9 @@ func (p *parser) parseA(t *token) (bool, Result, *ResponseExtras, error) {
p.fireMatchingIP(t, fqdn, n, host, p.ip)
return n.Contains(p.ip), nil
})
if extras.Void() {
p.fireVoidLookup(t, fqdn)
}

p.fireLookupExtras(t, fqdn, extras)

if err != nil {
return found, result, nil, NewSpfError(spferr.KindDNS, err, nil)
}
Expand Down Expand Up @@ -576,9 +575,9 @@ func (p *parser) parseMX(t *token) (bool, Result, *ResponseExtras, error) {
p.fireMatchingIP(t, fqdn, n, host, p.ip)
return n.Contains(p.ip), nil
})
if extras.Void() {
p.fireVoidLookup(t, fqdn)
}

p.fireLookupExtras(t, fqdn, extras)

if err != nil {
return true, Permerror, nil, NewSpfError(spferr.KindDNS, err, t)
}
Expand Down Expand Up @@ -673,9 +672,8 @@ func (p *parser) parseExists(t *token) (bool, Result, *ResponseExtras, error) {
result, _ := matchingResult(t.qualifier)

found, extras, err := p.resolver.Exists(resolvedDomain)
if extras.Void() {
p.fireVoidLookup(t, resolvedDomain)
}

p.fireLookupExtras(t, resolvedDomain, extras)

switch err {
case nil:
Expand Down Expand Up @@ -704,9 +702,8 @@ func (p *parser) parsePtr(t *token) (bool, Result, *ResponseExtras, error) {
}

ptrs, extras, err := p.resolver.LookupPTR(p.ip.String())
if extras.Void() {
p.fireVoidLookup(t, fqdn)
}

p.fireLookupExtras(t, fqdn, extras)

switch err {
case nil:
Expand Down
11 changes: 6 additions & 5 deletions printer/printer.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,11 +83,6 @@ func (p *Printer) Match(qualifier, mechanism, value string, result spf.Result, e
// fmt.Fprintf(p.w, "%sMATCH: %s, %q, %v\n", strings.Repeat(" ", p.c), result, explanation, err)
}

func (p *Printer) VoidLookup(qualifier, mechanism, value string, fqdn string) {
// do nothing
fmt.Fprintf(p.w, "%sVOID: %s\n", strings.Repeat(" ", p.c), fqdn)
}

func (p *Printer) FirstMatch(r spf.Result, err error) {
fmt.Fprintf(p.w, "%sFIRST-MATCH: %s, %v\n", strings.Repeat(" ", p.c), r, err)
}
Expand Down Expand Up @@ -136,3 +131,9 @@ func (p *Printer) MatchIP(name string, matcher spf.IPMatcherFunc) (bool, *spf.Re
func (p *Printer) MatchMX(name string, matcher spf.IPMatcherFunc) (bool, *spf.ResponseExtras, error) {
return p.r.MatchMX(name, matcher)
}

func (p *Printer) LookupExtras(qualifier, mechanism, value, fqdn string, extras *spf.ResponseExtras) {
if extras.Void() {
fmt.Fprintf(p.w, "%sVOID: %s\n", strings.Repeat(" ", p.c), fqdn)
}
}
34 changes: 17 additions & 17 deletions printer/printer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ func ExamplePrinter() {
// ip4:87.253.232.0/21 (87.253.232.0/21)
// ip4:185.189.236.0/22 (185.189.236.0/22)
// ?all
// = neutral, &{1491000000000 false}, , <nil>
// = neutral, &{1491000000000 false []}, , <nil>
// include:servers.mcsv.net (servers.mcsv.net.)
// CHECK_HOST("0.0.0.0", "servers.mcsv.net.", "aspmx.l.google.com")
// lookup(TXT:strict) servers.mcsv.net.
Expand All @@ -114,7 +114,7 @@ func ExamplePrinter() {
// ip4:198.2.128.0/18 (198.2.128.0/18)
// ip4:148.105.8.0/21 (148.105.8.0/21)
// ?all
// = neutral, &{152000000000 false}, , <nil>
// = neutral, &{152000000000 false []}, , <nil>
// ip4:109.168.127.160/27 (109.168.127.160/27)
// ip4:212.31.252.64/27 (212.31.252.64/27)
// ip4:212.77.68.6 (212.77.68.6)
Expand All @@ -133,7 +133,7 @@ func ExamplePrinter() {
// ip4:109.168.121.57/32 (109.168.121.57/32)
// ip4:109.168.121.58/32 (109.168.121.58/32)
// -all
// = fail, &{269000000000 false}, , <nil>
// = fail, &{269000000000 false []}, , <nil>
// CHECK_HOST("0.0.0.0", "ptr.test.redsift.io.", "aspmx.l.google.com")
// lookup(TXT:strict) ptr.test.redsift.io.
// SPF: v=spf1 ptr ~all
Expand All @@ -142,7 +142,7 @@ func ExamplePrinter() {
// lookup(PTR) 0.0.0.0
// VOID: ptr.test.redsift.io.
// ~all
// = softfail, &{299000000000 false}, , <nil>
// = softfail, &{299000000000 false []}, , <nil>
// ## of lookups: 15
}

Expand Down Expand Up @@ -210,9 +210,9 @@ func ExamplePrinter_ipv6nil() {
// lookup(a:web.q4press.com.) web.q4press.com. -> (52.23.113.139/32 has? 0.0.0.0) = false
// lookup(a:web.q4press.com.) web.q4press.com. -> (54.177.118.13/32 has? 0.0.0.0) = false
// -all
// = fail, &{3303000000000 false}, , <nil>
// = fail, &{3303000000000 false []}, , <nil>
// ~all
// = softfail, &{59000000000 false}, , <nil>
// = softfail, &{59000000000 false []}, , <nil>
//
}

Expand Down Expand Up @@ -254,12 +254,12 @@ func ExamplePrinter_voids() {
// ip4:80.194.146.205 (80.194.146.205)
// -all
// FIRST-MATCH: fail, <nil>
// = 8, &{60000000000 false}, , result is unreliable with IgnoreMatches option enabled
// = 8, &{60000000000 false []}, , result is unreliable with IgnoreMatches option enabled
// include:err008.3.spf.qa.redsift.tech (err008.3.spf.qa.redsift.tech.)
// CHECK_HOST("0.0.0.0", "err008.3.spf.qa.redsift.tech.", "redsift.io")
// lookup(TXT:strict) err008.3.spf.qa.redsift.tech.
// VOID: err008.3.spf.qa.redsift.tech.
// = none, &{0 true}, , permanent DNS error
// = none, &{0 true []}, , permanent DNS error
// include:err008.4.spf.qa.redsift.tech (err008.4.spf.qa.redsift.tech.)
// CHECK_HOST("0.0.0.0", "err008.4.spf.qa.redsift.tech.", "redsift.io")
// lookup(TXT:strict) err008.4.spf.qa.redsift.tech.
Expand All @@ -269,18 +269,18 @@ func ExamplePrinter_voids() {
// CHECK_HOST("0.0.0.0", "err008.5.spf.qa.redsift.tech.", "redsift.io")
// lookup(TXT:strict) err008.5.spf.qa.redsift.tech.
// VOID: err008.5.spf.qa.redsift.tech.
// = none, &{0 true}, , SPF record not found
// = none, &{0 true []}, , SPF record not found
// include:err008.6.spf.qa.redsift.tech (err008.6.spf.qa.redsift.tech.)
// CHECK_HOST("0.0.0.0", "err008.6.spf.qa.redsift.tech.", "redsift.io")
// lookup(TXT:strict) err008.6.spf.qa.redsift.tech.
// VOID: err008.6.spf.qa.redsift.tech.
// = none, &{0 true}, , permanent DNS error
// = none, &{0 true []}, , permanent DNS error
// -all
// = 8, &{60000000000 false}, , result is unreliable with IgnoreMatches option enabled
// = 8, &{60000000000 false []}, , result is unreliable with IgnoreMatches option enabled
// -all
// = 8, &{60000000000 false}, , result is unreliable with IgnoreMatches option enabled
// = 8, &{60000000000 false []}, , result is unreliable with IgnoreMatches option enabled
// -all
// = 8, &{60000000000 false}, , result is unreliable with IgnoreMatches option enabled
// = 8, &{60000000000 false []}, , result is unreliable with IgnoreMatches option enabled
// ## of lookups: 6

}
Expand Down Expand Up @@ -374,7 +374,7 @@ func ExamplePrinter_ignoreMatches() {
// ip4:185.189.236.0/22 (185.189.236.0/22)
// ?all
// FIRST-MATCH: neutral, <nil>
// = 8, &{1491000000000 false}, , result is unreliable with IgnoreMatches option enabled
// = 8, &{1491000000000 false []}, , result is unreliable with IgnoreMatches option enabled
// include:servers.mcsv.net (servers.mcsv.net.)
// CHECK_HOST("0.0.0.0", "servers.mcsv.net.", "aspmx.l.google.com")
// lookup(TXT:strict) servers.mcsv.net.
Expand All @@ -384,7 +384,7 @@ func ExamplePrinter_ignoreMatches() {
// ip4:198.2.128.0/18 (198.2.128.0/18)
// ip4:148.105.8.0/21 (148.105.8.0/21)
// ?all
// = 8, &{152000000000 false}, , result is unreliable with IgnoreMatches option enabled
// = 8, &{152000000000 false []}, , result is unreliable with IgnoreMatches option enabled
// ip4:109.168.127.160/27 (109.168.127.160/27)
// ip4:212.31.252.64/27 (212.31.252.64/27)
// ip4:212.77.68.6 (212.77.68.6)
Expand All @@ -403,7 +403,7 @@ func ExamplePrinter_ignoreMatches() {
// ip4:109.168.121.57/32 (109.168.121.57/32)
// ip4:109.168.121.58/32 (109.168.121.58/32)
// -all
// = 8, &{269000000000 false}, , result is unreliable with IgnoreMatches option enabled
// = 8, &{269000000000 false []}, , result is unreliable with IgnoreMatches option enabled
// CHECK_HOST("0.0.0.0", "ptr.test.redsift.io.", "aspmx.l.google.com")
// lookup(TXT:strict) ptr.test.redsift.io.
// SPF: v=spf1 ptr ~all
Expand All @@ -413,6 +413,6 @@ func ExamplePrinter_ignoreMatches() {
// VOID: ptr.test.redsift.io.
// ~all
// FIRST-MATCH: softfail, <nil>
// = 8, &{299000000000 false}, , result is unreliable with IgnoreMatches option enabled
// = 8, &{299000000000 false []}, , result is unreliable with IgnoreMatches option enabled
// ## of lookups: 15
}
24 changes: 6 additions & 18 deletions resolver_miekg.go
Original file line number Diff line number Diff line change
Expand Up @@ -194,10 +194,7 @@ func (r *miekgDNSResolver) LookupTXT(name string) ([]string, *ResponseExtras, er
minTTL = 0
}

extras := NewResponseExtras(
time.Duration(minTTL)*time.Second,
(len(res.Answer) == 0 && res.Rcode == dns.RcodeSuccess) || res.Rcode == dns.RcodeNameError,
)
extras := NewResponseExtras(time.Duration(minTTL)*time.Second, (len(res.Answer) == 0 && res.Rcode == dns.RcodeSuccess) || res.Rcode == dns.RcodeNameError, nil)

return txts, extras, nil
}
Expand All @@ -216,7 +213,7 @@ func (r *miekgDNSResolver) LookupTXTStrict(name string) ([]string, *ResponseExtr

if res.Rcode == dns.RcodeNameError {
// Mark it as a void lookup as we got NXDomain
return nil, NewResponseExtras(0, true), ErrDNSPermerror
return nil, NewResponseExtras(0, true, nil), ErrDNSPermerror
}

var minTTL uint32 = math.MaxUint32
Expand All @@ -235,10 +232,7 @@ func (r *miekgDNSResolver) LookupTXTStrict(name string) ([]string, *ResponseExtr
minTTL = 0
}

extras := NewResponseExtras(
time.Duration(minTTL)*time.Second,
len(res.Answer) == 0 && res.Rcode == dns.RcodeSuccess,
)
extras := NewResponseExtras(time.Duration(minTTL)*time.Second, len(res.Answer) == 0 && res.Rcode == dns.RcodeSuccess, nil)

return txts, extras, nil
}
Expand Down Expand Up @@ -271,10 +265,7 @@ func (r *miekgDNSResolver) Exists(name string) (bool, *ResponseExtras, error) {
minTTL = 0
}

extras := NewResponseExtras(
time.Duration(minTTL)*time.Second,
(len(res.Answer) == 0 && res.Rcode == dns.RcodeSuccess) || res.Rcode == dns.RcodeNameError,
)
extras := NewResponseExtras(time.Duration(minTTL)*time.Second, (len(res.Answer) == 0 && res.Rcode == dns.RcodeSuccess) || res.Rcode == dns.RcodeNameError, nil)

return len(res.Answer) > 0, extras, nil
}
Expand All @@ -298,7 +289,7 @@ func matchIP(rrs []dns.RR, matcher IPMatcherFunc, name string) (bool, *ResponseE
}

if m, e := matcher(ip, name); m || e != nil {
return m, NewResponseExtras(time.Duration(ttl)*time.Second, false), e
return m, NewResponseExtras(time.Duration(ttl)*time.Second, false, nil), e
}
}
return false, nil, nil
Expand Down Expand Up @@ -440,10 +431,7 @@ func (r *miekgDNSResolver) LookupPTR(name string) ([]string, *ResponseExtras, er
minTTL = 0
}

extras := NewResponseExtras(
time.Duration(minTTL)*time.Second,
(len(res.Answer) == 0 && res.Rcode == dns.RcodeSuccess) || res.Rcode == dns.RcodeNameError,
)
extras := NewResponseExtras(time.Duration(minTTL)*time.Second, (len(res.Answer) == 0 && res.Rcode == dns.RcodeSuccess) || res.Rcode == dns.RcodeNameError, nil)

return ptrs, extras, nil
}
Expand Down
22 changes: 17 additions & 5 deletions spf.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,18 +68,23 @@ type IPMatcherFunc func(ip net.IP, name string) (bool, error)

// ResponseExtras contains additional information returned alongside DNS query results.
type ResponseExtras struct {
ttl time.Duration // Minimum TTL of the DNS response
void bool // Indicates if the response is a result of a DNS void lookup.

ttl time.Duration // Minimum TTL of the DNS response
// Indicates if the response is a result of a DNS void lookup.
// A DNS void lookup, as defined in Section 4.6.4 of RFC 7208 (https://datatracker.ietf.org/doc/html/rfc7208#section-4.6.4),
// is a query for a domain that is intentionally configured to have no associated DNS records,
// such as an explicit configuration for a "blackhole" or an intentionally nonexistent domain.
// This type of query typically returns a response with no relevant DNS records (e.g., NXDOMAIN),
// and the 'Void' field in this struct is set to 'true' to indicate that the response resulted from such a lookup.
void bool
cnameChain []string
}

func NewResponseExtras(ttl time.Duration, void bool) *ResponseExtras {
return &ResponseExtras{ttl, void}
func NewResponseExtras(ttl time.Duration, void bool, cnameChain []string) *ResponseExtras {
return &ResponseExtras{
ttl: ttl,
void: void,
cnameChain: cnameChain,
}
}

func (x *ResponseExtras) TTL() time.Duration {
Expand All @@ -93,6 +98,13 @@ func (x *ResponseExtras) Void() bool {
return x != nil && x.void
}

func (x *ResponseExtras) CNAMEChain() []string {
if x == nil {
return nil
}
return x.cnameChain
}

// Resolver provides an abstraction for DNS layer operations.
type Resolver interface {
// LookupTXT returns the DNS TXT records for the given domain name,
Expand Down
Loading