diff --git a/.travis.yml b/.travis.yml index 320fe40..bbed3a3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,5 @@ language: go go: - - 1.6 - 1.7 - 1.8 - master diff --git a/resolver_miekg.go b/resolver_miekg.go index 1ca0f7f..62a27fa 100644 --- a/resolver_miekg.go +++ b/resolver_miekg.go @@ -5,6 +5,7 @@ import ( "sync" "github.com/miekg/dns" + "strings" ) // NewMiekgDNSResolver returns new instance of Resolver @@ -66,7 +67,7 @@ func (r *MiekgDNSResolver) LookupTXT(name string) ([]string, error) { txts := make([]string, 0, len(res.Answer)) for _, a := range res.Answer { if r, ok := a.(*dns.TXT); ok { - txts = append(txts, r.Txt...) + txts = append(txts, strings.Join(r.Txt,"")) } } return txts, nil @@ -91,7 +92,7 @@ func (r *MiekgDNSResolver) LookupTXTStrict(name string) ([]string, error) { txts := make([]string, 0, len(res.Answer)) for _, a := range res.Answer { if r, ok := a.(*dns.TXT); ok { - txts = append(txts, r.Txt...) + txts = append(txts, strings.Join(r.Txt,"")) } } return txts, nil @@ -134,9 +135,10 @@ func matchIP(rrs []dns.RR, matcher IPMatcherFunc) (bool, error) { // If any address matches, the mechanism matches func (r *MiekgDNSResolver) MatchIP(name string, matcher IPMatcherFunc) (bool, error) { var wg sync.WaitGroup - hits := make(chan hit) + qTypes := []uint16{dns.TypeA, dns.TypeAAAA} + hits := make(chan hit, len(qTypes)) - for _, qType := range []uint16{dns.TypeA, dns.TypeAAAA} { + for _, qType := range qTypes { wg.Add(1) go func(qType uint16) { defer wg.Done() @@ -184,7 +186,7 @@ func (r *MiekgDNSResolver) MatchMX(name string, matcher IPMatcherFunc) (bool, er } var wg sync.WaitGroup - hits := make(chan hit) + hits := make(chan hit, len(res.Answer)) for _, rr := range res.Answer { mx, ok := rr.(*dns.MX) diff --git a/resolver_miekg_test.go b/resolver_miekg_test.go index ea86d70..aba259e 100644 --- a/resolver_miekg_test.go +++ b/resolver_miekg_test.go @@ -1,6 +1,10 @@ package spf -import "testing" +import ( + "testing" + + "github.com/miekg/dns" +) func TestMiekgDNSResolver(t *testing.T) { _, e := NewMiekgDNSResolver("8.8.8.8") // invalid TCP address, no port specified @@ -8,3 +12,41 @@ func TestMiekgDNSResolver(t *testing.T) { t.Errorf(`want "address 8.8.8.8: missing port in address"`) } } + +func TestMiekgDNSResolver_LookupTXTStrict_Multiline(t *testing.T) { + dns.HandleFunc("multiline.test.", zone(map[uint16][]string{ + dns.TypeTXT: { + `multiline.test. 0 IN TXT "v=spf1 ip4:10.0.0.1 ip4:10.0.0" ".2 -all"`, + }, + })) + defer dns.HandleRemove("multiline.test.") + + r, e := testResolver.LookupTXTStrict("multiline.test.") + + if e != nil { + t.Fatal(e) + } + + if len(r) != 1 { + t.Errorf("want 1 got %d", len(r)) + } +} + +func TestMiekgDNSResolver_LookupTXT_Multiline(t *testing.T) { + dns.HandleFunc("multiline.test.", zone(map[uint16][]string{ + dns.TypeTXT: { + `multiline.test. 0 IN TXT "v=spf1 ip4:10.0.0.1 ip4:10.0.0" ".2 -all"`, + }, + })) + defer dns.HandleRemove("multiline.test.") + + r, e := testResolver.LookupTXT("multiline.test.") + + if e != nil { + t.Fatal(e) + } + + if len(r) != 1 { + t.Errorf("want 1 got %d", len(r)) + } +} diff --git a/resolver_std.go b/resolver_std.go index 000a33c..55b3f83 100644 --- a/resolver_std.go +++ b/resolver_std.go @@ -8,10 +8,7 @@ import ( // DNSResolver implements Resolver using local DNS type DNSResolver struct{} -func errDNS(e error) error { - if e == nil { - return nil - } +func isNoHost(e error) bool { if dnsErr, ok := e.(*net.DNSError); ok { // That is the most reliable way I found to detect Permerror // https://github.com/golang/go/blob/master/src/net/dnsclient.go#L43 @@ -25,8 +22,27 @@ func errDNS(e error) error { // the mechanism continues as if the server returned no error (RCODE // 0) and zero answer records. if dnsErr.Err == "no such host" { - return nil + return true } + if dnsErr.Err == "dnsquery: DNS name does not exist." { + return true + } + if dnsErr.Err == "dnsquery: DNS record does not exist." { + return true + } + if dnsErr.Err == "dnsquery: No records found for given DNS query." { + return true + } + } + return false +} + +func errDNS(e error) error { + if e == nil { + return nil + } + if isNoHost(e) { + return nil } return ErrDNSTemperror } @@ -36,23 +52,9 @@ func errDNS(e error) error { func (r *DNSResolver) LookupTXTStrict(name string) ([]string, error) { txts, err := net.LookupTXT(name) - if dnsErr, ok := err.(*net.DNSError); ok { - // That is the most reliable way I found to detect Permerror - // https://github.com/golang/go/blob/master/src/net/dnsclient.go#L43 - // Upon RCODE 3 return code we should return None result and pretend no - // From RFC7208: - // Several mechanisms rely on information fetched from the DNS. For - // these DNS queries, except where noted, if the DNS server returns an - // error (RCODE other than 0 or 3) or the query times out, the - // mechanism stops and the topmost check_host() returns "temperror". - // If the server returns "Name Error" (RCODE 3), then evaluation of - // the mechanism continues as if the server returned no error (RCODE - // 0) and zero answer records. - if dnsErr.Err == "no such host" { - return nil, ErrDNSPermerror - } + if isNoHost(err) { + return nil, ErrDNSPermerror } - err = errDNS(err) if err != nil { return nil, err @@ -117,7 +119,7 @@ func (r *DNSResolver) MatchMX(name string, matcher IPMatcherFunc) (bool, error) } var wg sync.WaitGroup - hits := make(chan hit) + hits := make(chan hit, len(mxs)) for _, mx := range mxs { wg.Add(1)