acme-dns/dns_test.go
znerol c13035a344 Refactor: Use more specific type in argument of DB.Update (#162)
The DB.Update function takes a type of ACMETxt. However, the function
only requires the Value and Subdomain fields.

Refactor the function such that it takes ACMETxtPost instead of the full
ACMETxt record. This will simplify extraction of txt-record related
logic from the db code.
2019-06-12 15:41:02 +03:00

245 lines
6.9 KiB
Go

package main
import (
"database/sql"
"database/sql/driver"
"errors"
"fmt"
"testing"
"github.com/erikstmartin/go-testdb"
"github.com/miekg/dns"
)
var resolv resolver
var server *dns.Server
type resolver struct {
server string
}
func (r *resolver) lookup(host string, qtype uint16) (*dns.Msg, error) {
msg := new(dns.Msg)
msg.Id = dns.Id()
msg.Question = make([]dns.Question, 1)
msg.Question[0] = dns.Question{Name: dns.Fqdn(host), Qtype: qtype, Qclass: dns.ClassINET}
in, err := dns.Exchange(msg, r.server)
if err != nil {
return in, fmt.Errorf("Error querying the server [%v]", err)
}
if in != nil && in.Rcode != dns.RcodeSuccess {
return in, fmt.Errorf("Received error from the server [%s]", dns.RcodeToString[in.Rcode])
}
return in, nil
}
func hasExpectedTXTAnswer(answer []dns.RR, cmpTXT string) error {
for _, record := range answer {
// We expect only one answer, so no need to loop through the answer slice
if rec, ok := record.(*dns.TXT); ok {
for _, txtValue := range rec.Txt {
if txtValue == cmpTXT {
return nil
}
}
} else {
errmsg := fmt.Sprintf("Got answer of unexpected type [%q]", answer[0])
return errors.New(errmsg)
}
}
return errors.New("Expected answer not found")
}
func TestQuestionDBError(t *testing.T) {
testdb.SetQueryWithArgsFunc(func(query string, args []driver.Value) (result driver.Rows, err error) {
columns := []string{"Username", "Password", "Subdomain", "Value", "LastActive"}
return testdb.RowsFromSlice(columns, [][]driver.Value{}), errors.New("Prepared query error")
})
defer testdb.Reset()
tdb, err := sql.Open("testdb", "")
if err != nil {
t.Errorf("Got error: %v", err)
}
oldDb := DB.GetBackend()
DB.SetBackend(tdb)
defer DB.SetBackend(oldDb)
q := dns.Question{Name: dns.Fqdn("whatever.tld"), Qtype: dns.TypeTXT, Qclass: dns.ClassINET}
_, err = dnsserver.answerTXT(q)
if err == nil {
t.Errorf("Expected error but got none")
}
}
func TestParse(t *testing.T) {
var testcfg = DNSConfig{
General: general{
Domain: ")",
Nsname: "ns1.auth.example.org",
Nsadmin: "admin.example.org",
StaticRecords: []string{},
Debug: false,
},
}
dnsserver.ParseRecords(testcfg)
if !loggerHasEntryWithMessage("Error while adding SOA record") {
t.Errorf("Expected SOA parsing to return error, but did not find one")
}
}
func TestResolveA(t *testing.T) {
resolv := resolver{server: "127.0.0.1:15353"}
answer, err := resolv.lookup("auth.example.org", dns.TypeA)
if err != nil {
t.Errorf("%v", err)
}
if len(answer.Answer) == 0 {
t.Error("No answer for DNS query")
}
_, err = resolv.lookup("nonexistent.domain.tld", dns.TypeA)
if err == nil {
t.Errorf("Was expecting error because of NXDOMAIN but got none")
}
}
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.RcodeFormatError {
t.Errorf("Was expecing FORMERR rcode for OPT query, but got [%s] instead.", dns.RcodeToString[answer.Rcode])
}
}
func TestResolveCNAME(t *testing.T) {
resolv := resolver{server: "127.0.0.1:15353"}
expected := "cn.example.org. 3600 IN CNAME something.example.org."
answer, err := resolv.lookup("cn.example.org", dns.TypeCNAME)
if err != nil {
t.Errorf("Got unexpected error: %s", err)
}
if len(answer.Answer) != 1 {
t.Errorf("Expected exactly 1 RR in answer, but got %d instead.", len(answer.Answer))
}
if answer.Answer[0].Header().Rrtype != dns.TypeCNAME {
t.Errorf("Expected a CNAME answer, but got [%s] instead.", dns.TypeToString[answer.Answer[0].Header().Rrtype])
}
if answer.Answer[0].String() != expected {
t.Errorf("Expected CNAME answer [%s] but got [%s] instead.", expected, answer.Answer[0].String())
}
}
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])
}
if len(answer.Ns) != 1 {
t.Errorf("Was expecting exactly one answer (SOA) for invalid subdomain, but got %d", len(answer.Ns))
}
if answer.Ns[0].Header().Rrtype != dns.TypeSOA {
t.Errorf("Was expecting SOA record as answer for NXDOMAIN but got [%s]", dns.TypeToString[answer.Ns[0].Header().Rrtype])
}
if !answer.MsgHdr.Authoritative {
t.Errorf("Was expecting authoritative bit to be set")
}
nanswer, _ := resolv.lookup("nonexsitent.nonauth.tld", dns.TypeA)
if len(nanswer.Answer) > 0 {
t.Errorf("Didn't expect answers for non authotitative domain query")
}
if nanswer.MsgHdr.Authoritative {
t.Errorf("Authoritative bit should not be set for non-authoritative domain.")
}
}
func TestResolveTXT(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
expTXT string
getAnswer bool
validAnswer bool
}{
{atxt.Subdomain, validTXT, true, true},
{atxt.Subdomain, "invalid", true, false},
{"a097455b-52cc-4569-90c8-7a4b97c6eba8", validTXT, false, false},
} {
answer, err := resolv.lookup(test.subDomain+".auth.example.org", dns.TypeTXT)
if err != nil {
if test.getAnswer {
t.Fatalf("Test %d: Expected answer but got: %v", i, err)
}
} else {
if !test.getAnswer {
t.Errorf("Test %d: Expected no answer, but got one.", i)
}
}
if len(answer.Answer) > 0 {
if !test.getAnswer && answer.Answer[0].Header().Rrtype != dns.TypeSOA {
t.Errorf("Test %d: Expected no answer, but got: [%q]", i, answer)
}
if test.getAnswer {
err = hasExpectedTXTAnswer(answer.Answer, test.expTXT)
if err != nil {
if test.validAnswer {
t.Errorf("Test %d: %v", i, err)
}
} else {
if !test.validAnswer {
t.Errorf("Test %d: Answer was not expected to be valid, answer [%q], compared to [%s]", i, answer, test.expTXT)
}
}
}
} else {
if test.getAnswer {
t.Errorf("Test %d: Expected answer, but didn't get one", i)
}
}
}
}
func TestCaseInsensitiveResolveA(t *testing.T) {
resolv := resolver{server: "127.0.0.1:15353"}
answer, err := resolv.lookup("aUtH.eXAmpLe.org", dns.TypeA)
if err != nil {
t.Errorf("%v", err)
}
if len(answer.Answer) == 0 {
t.Error("No answer for DNS query")
}
}
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])
}
if len(answer.Ns) == 0 {
t.Error("No SOA answer for DNS query")
}
}