diff --git a/dns.go b/dns.go index a01fb9c5..e8c6f0ac 100644 --- a/dns.go +++ b/dns.go @@ -130,25 +130,28 @@ func (d *DNSServer) readQuery(m *dns.Msg) { } } -func (d *DNSServer) getRecord(q dns.Question) ([]dns.RR, error) { +func (d *DNSServer) getRecord(q dns.Question) ([]dns.RR, bool, error) { var rr []dns.RR var cnames []dns.RR + var hasOther bool = false domain, ok := d.Domains[strings.ToLower(q.Name)] if !ok { - return rr, fmt.Errorf("No records for domain %s", q.Name) + return rr, hasOther, fmt.Errorf("No records for domain %s", q.Name) } for _, ri := range domain.Records { if ri.Header().Rrtype == q.Qtype { rr = append(rr, ri) - } - if ri.Header().Rrtype == dns.TypeCNAME { - cnames = append(cnames, ri) + } else { + hasOther = true + if ri.Header().Rrtype == dns.TypeCNAME { + cnames = append(cnames, ri) + } } } if len(rr) == 0 { - return cnames, nil + return cnames, hasOther, nil } - return rr, nil + return rr, hasOther, nil } // answeringForDomain checks if we have any records for a domain @@ -198,7 +201,8 @@ func (d *DNSServer) answer(q dns.Question) ([]dns.RR, int, bool, error) { if !d.isOwnChallenge(q.Name) && !d.answeringForDomain(q.Name) { rcode = dns.RcodeNameError } - r, _ := d.getRecord(q) + r, hasOther, _ := d.getRecord(q) + if q.Qtype == dns.TypeTXT { if d.isOwnChallenge(q.Name) { txtRRs, err = d.answerOwnChallenge(q) @@ -208,6 +212,16 @@ func (d *DNSServer) answer(q dns.Question) ([]dns.RR, int, bool, error) { if err == nil { r = append(r, txtRRs...) } + } else { + // must not return NXDOMAIN if another type found for the name + // see https://datatracker.ietf.org/doc/html/rfc2308#section-5 + if len(r) == 0 && !hasOther { + // TXT not returned before, must check in DB + txtRRs, err = d.answerTXT(q) + if err == nil && len(txtRRs) > 0 { + rcode = dns.RcodeSuccess + } + } } if len(r) > 0 { // Make sure that we return NOERROR if there were dynamic records for the domain diff --git a/dns_test.go b/dns_test.go index ba42a5d5..6bb4ada9 100644 --- a/dns_test.go +++ b/dns_test.go @@ -109,7 +109,7 @@ func TestEDNS(t *testing.T) { resolv := resolver{server: "127.0.0.1:15353"} answer, _ := resolv.lookup("auth.example.org", dns.TypeOPT) if answer.Rcode != dns.RcodeSuccess { - t.Errorf("Was expecing NOERROR rcode for OPT query, but got [%s] instead.", dns.RcodeToString[answer.Rcode]) + t.Errorf("Was expecting NOERROR rcode for OPT query, but got [%s] instead.", dns.RcodeToString[answer.Rcode]) } } @@ -175,7 +175,7 @@ func TestAuthoritative(t *testing.T) { resolv := resolver{server: "127.0.0.1:15353"} answer, _ := resolv.lookup("nonexistent.auth.example.org", dns.TypeA) if answer.Rcode != dns.RcodeNameError { - t.Errorf("Was expecing NXDOMAIN rcode, but got [%s] instead.", dns.RcodeToString[answer.Rcode]) + t.Errorf("Was expecting NXDOMAIN rcode, but got [%s] instead.", dns.RcodeToString[answer.Rcode]) } if len(answer.Ns) != 1 { t.Errorf("Was expecting exactly one answer (SOA) for invalid subdomain, but got %d", len(answer.Ns)) @@ -256,6 +256,42 @@ func TestResolveTXT(t *testing.T) { } } +func TestResolveRcode(t *testing.T) { + resolv := resolver{server: "127.0.0.1:15353"} + validTXT := "______________valid_response_______________" + + atxt, err := DB.Register(cidrslice{}) + if err != nil { + t.Errorf("Could not initiate db record: [%v]", err) + return + } + atxt.Value = validTXT + err = DB.Update(atxt.ACMETxtPost) + if err != nil { + t.Errorf("Could not update db record: [%v]", err) + return + } + + for i, test := range []struct { + subDomain string + qType uint16 + expRcode int + expAnswers int + }{ + {atxt.Subdomain, dns.TypeA, dns.RcodeSuccess, 0}, + {atxt.Subdomain, dns.TypeNS, dns.RcodeSuccess, 0}, + {atxt.Subdomain, dns.TypeTXT, dns.RcodeSuccess, 1}, + {"nonexistent", dns.TypeA, dns.RcodeNameError, 0}, + } { + answer, _ := resolv.lookup(test.subDomain+".auth.example.org", test.qType) + if answer.Rcode != test.expRcode { + t.Errorf("Test %d: was expecting [%s] rcode, but got [%s] instead.", i, dns.RcodeToString[test.expRcode], dns.RcodeToString[answer.Rcode]) + } + if len(answer.Answer) != test.expAnswers { + t.Errorf("Test %d: was expecting %d answer, but got %d instead.", i, test.expAnswers, len(answer.Answer)) + } + } +} func TestCaseInsensitiveResolveA(t *testing.T) { resolv := resolver{server: "127.0.0.1:15353"} answer, err := resolv.lookup("aUtH.eXAmpLe.org", dns.TypeA) @@ -272,7 +308,7 @@ func TestCaseInsensitiveResolveSOA(t *testing.T) { resolv := resolver{server: "127.0.0.1:15353"} answer, _ := resolv.lookup("doesnotexist.aUtH.eXAmpLe.org", dns.TypeSOA) if answer.Rcode != dns.RcodeNameError { - t.Errorf("Was expecing NXDOMAIN rcode, but got [%s] instead.", dns.RcodeToString[answer.Rcode]) + t.Errorf("Was expecting NXDOMAIN rcode, but got [%s] instead.", dns.RcodeToString[answer.Rcode]) } if len(answer.Ns) == 0 {