be authoritative, but only when there's no external queries occuring
continuous-integration/drone/pr Build is passing Details

This commit is contained in:
Elizabeth Hunt 2024-04-07 19:02:42 -06:00
parent 5768f07ce5
commit e2ce6804a7
Signed by untrusted user who does not match committer: simponic
GPG Key ID: 2909B9A7FF6213EE
2 changed files with 89 additions and 80 deletions

View File

@ -70,11 +70,12 @@ func (h *DnsHandler) recursiveResolve(domain string, qtype uint16, maxDepth int)
answers = append(answers, cname) answers = append(answers, cname)
cnameRecursive, cnameAuth, err := h.resolveDNS(record.Content, qtype, maxDepth-1) cnameRecursive, cnameAuth, err := h.resolveDNS(record.Content, qtype, maxDepth-1)
authoritative = authoritative && cnameAuth
if err != nil { if err != nil {
log.Println(err) log.Println(err)
return nil, authoritative, err return nil, authoritative, err
} }
authoritative = authoritative && cnameAuth
answers = append(answers, cnameRecursive...) answers = append(answers, cnameRecursive...)
} }
@ -126,14 +127,16 @@ func (h *DnsHandler) resolveDNS(domain string, qtype uint16, maxDepth int) ([]dn
continue continue
} }
cnameAnswers, _, err := h.resolveDNS(cname.Target, qtype, maxDepth-1) cnameAnswers, cnameAuth, err := h.resolveDNS(cname.Target, qtype, maxDepth-1)
authoritative = authoritative && cnameAuth
if err != nil { if err != nil {
return nil, false, err return nil, false, err
} }
answers = append(answers, cnameAnswers...) answers = append(answers, cnameAnswers...)
} }
return answers, false, nil authoritative = authoritative && len(externalAnswers) == 0
return answers, authoritative, nil
} }
func (h *DnsHandler) ServeDNS(w dns.ResponseWriter, r *dns.Msg) { func (h *DnsHandler) ServeDNS(w dns.ResponseWriter, r *dns.Msg) {

View File

@ -58,83 +58,6 @@ func setup(arguments *args.Arguments) (*sql.DB, *dns.Server, string, func()) {
} }
} }
func TestWhenExternalDomain(t *testing.T) {
externalDb, _, externalAddr, externalCleanup := setup(nil)
internalDb, _, internalAddr, internalCleanup := setup(&args.Arguments{
DnsPort: randomPort(),
DnsResolvers: []string{externalAddr},
})
defer internalCleanup()
defer externalCleanup()
authoritativeRecords := []database.DNSRecord{
{
ID: "1",
UserID: "test",
Name: "external.example.com.",
Type: "CNAME",
Content: "external.internal.example.com.",
},
}
internalRecords := []database.DNSRecord{
{
ID: "1",
UserID: "test",
Name: "external.internal.example.com.",
Type: "A",
Content: "127.0.0.1",
},
{
ID: "2",
UserID: "test",
Name: "test.internal.example.com.",
Type: "CNAME",
Content: "external.example.com.",
},
}
for _, record := range authoritativeRecords {
database.SaveDNSRecord(externalDb, &record)
}
for _, record := range internalRecords {
database.SaveDNSRecord(internalDb, &record)
}
// ensure that if the record doesn't exist in the internal database, it will
// go and query the external dns resolvers, then loop back to the internal
qtype := dns.TypeA
domain := dns.Fqdn("test.internal.example.com.")
client := &dns.Client{}
message := &dns.Msg{}
message.SetQuestion(domain, qtype)
in, _, err := client.Exchange(message, internalAddr)
if err != nil {
t.Fatal(err)
}
if len(in.Answer) != 3 {
t.Fatalf("expected 3 answers, got %d", len(in.Answer))
}
aRecord := in.Answer[2]
if aRecord.Header().Name != internalRecords[0].Name {
t.Fatalf("expected %s, got %s", domain, aRecord.Header().Name)
}
if aRecord.Header().Rrtype != dns.TypeA {
t.Fatalf("expected %s, got %s", dns.TypeToString[aRecord.Header().Rrtype], internalRecords[1].Type)
}
if aRecord.(*dns.A).A.String() != internalRecords[0].Content {
t.Fatalf("expected %s, got %s", internalRecords[0].Content, aRecord.(*dns.A).A.String())
}
if in.Authoritative {
t.Fatalf("expected non-authoritative response")
}
}
func TestWhenCNAMEIsResolved(t *testing.T) { func TestWhenCNAMEIsResolved(t *testing.T) {
testDb, _, addr, cleanup := setup(nil) testDb, _, addr, cleanup := setup(nil)
defer cleanup() defer cleanup()
@ -322,3 +245,86 @@ func TestWhenUnresolvingCNAMEWithMaxDepth(t *testing.T) {
t.Fatalf("expected SERVFAIL, got %d", in.Rcode) t.Fatalf("expected SERVFAIL, got %d", in.Rcode)
} }
} }
func TestWhenExternalDomain(t *testing.T) {
externalDb, _, externalAddr, externalCleanup := setup(nil)
internalDb, _, internalAddr, internalCleanup := setup(&args.Arguments{
DnsPort: randomPort(),
DnsResolvers: []string{externalAddr},
})
defer internalCleanup()
defer externalCleanup()
authoritativeRecords := []database.DNSRecord{
{
ID: "1",
UserID: "test",
Name: "external.example.com.",
Type: "CNAME",
Content: "external.internal.example.com.",
},
{
ID: "2",
UserID: "test",
Name: "final.example.com.",
Type: "A",
Content: "127.0.0.1",
},
}
internalRecords := []database.DNSRecord{
{
ID: "1",
UserID: "test",
Name: "external.internal.example.com.",
Type: "CNAME",
Content: "final.example.com",
},
{
ID: "2",
UserID: "test",
Name: "test.internal.example.com.",
Type: "CNAME",
Content: "external.example.com.",
},
}
for _, record := range authoritativeRecords {
database.SaveDNSRecord(externalDb, &record)
}
for _, record := range internalRecords {
database.SaveDNSRecord(internalDb, &record)
}
// ensure that if the record doesn't exist in the internal database, it will
// go and query the external dns resolvers, then loop back to the internal
qtype := dns.TypeA
domain := dns.Fqdn("test.internal.example.com.")
client := &dns.Client{}
message := &dns.Msg{}
message.SetQuestion(domain, qtype)
in, _, err := client.Exchange(message, internalAddr)
if err != nil {
t.Fatal(err)
}
if len(in.Answer) != 4 {
t.Fatalf("expected 4 answers, got %d", len(in.Answer))
}
aRecord := in.Answer[3]
if aRecord.Header().Name != authoritativeRecords[1].Name {
t.Fatalf("expected %s, got %s", domain, aRecord.Header().Name)
}
if aRecord.Header().Rrtype != dns.TypeA {
t.Fatalf("expected %s, got %s", dns.TypeToString[aRecord.Header().Rrtype], internalRecords[1].Type)
}
if aRecord.(*dns.A).A.String() != authoritativeRecords[1].Content {
t.Fatalf("expected %s, got %s", authoritativeRecords[1].Content, aRecord.(*dns.A).A.String())
}
if in.Authoritative {
t.Fatalf("expected non-authoritative response")
}
}