mirror of
https://github.com/joohoi/acme-dns.git
synced 2024-12-22 21:43:50 +07:00
de7fe3cb1d
* Handle OPT questions (EDNS) * Handle authoritative bit, and append SOA for authoritative NXDOMAIN responses * Changelog entry * Fix linter errors
237 lines
6.8 KiB
Go
237 lines
6.8 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 TestOpcodeUpdate(t *testing.T) {
|
|
msg := new(dns.Msg)
|
|
msg.Id = dns.Id()
|
|
msg.Question = make([]dns.Question, 1)
|
|
msg.Question[0] = dns.Question{Name: dns.Fqdn("auth.example.org"), Qtype: dns.TypeANY, Qclass: dns.ClassINET}
|
|
msg.MsgHdr.Opcode = dns.OpcodeUpdate
|
|
in, err := dns.Exchange(msg, "127.0.0.1:15353")
|
|
if err != nil || in == nil {
|
|
t.Errorf("Encountered an error with UPDATE request")
|
|
} else if err == nil {
|
|
if in.Rcode != dns.RcodeRefused {
|
|
t.Errorf("Expected RCODE Refused from UPDATE request, but got [%s] instead", dns.RcodeToString[in.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.Answer) != 1 {
|
|
t.Errorf("Was expecting exactly one answer (SOA) for invalid subdomain, but got %d", len(answer.Answer))
|
|
}
|
|
if answer.Answer[0].Header().Rrtype != dns.TypeSOA {
|
|
t.Errorf("Was expecting SOA record as answer for NXDOMAIN but got [%s]", dns.TypeToString[answer.Answer[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)
|
|
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)
|
|
}
|
|
}
|
|
}
|
|
}
|