From fa4f5ba7ab0df0682c8e2930cd6186aee71c0d6e Mon Sep 17 00:00:00 2001 From: Dessa Simpson Date: Tue, 2 Dec 2025 01:32:34 -0700 Subject: [PATCH] Fix CNAME and glue record behavior --- TODO.txt | 1 + yamlzone.go | 27 ++++++++++++++++++--------- yamlzone_test.go | 11 +++++++++-- 3 files changed, 28 insertions(+), 11 deletions(-) diff --git a/TODO.txt b/TODO.txt index ecb8774..e7c9751 100644 --- a/TODO.txt +++ b/TODO.txt @@ -1,3 +1,4 @@ +- Refactor bad testdata into bad/ dir - CNAME resolution - Ensure NS records don't point inward (will require changes to unrelated tests) - Alias values via `!alias [index, default 0]` diff --git a/yamlzone.go b/yamlzone.go index de8f788..38b8350 100644 --- a/yamlzone.go +++ b/yamlzone.go @@ -123,10 +123,11 @@ func (z *Zone) Validate(name string, soa *NamedRecord, ns *[]Record) error { } for subname, subzone := range z.Subzones { // This populates z.GlueRecords directly - err := subzone.GetGlueRecords(concatName(name, subname), nameserversMap) + glueRecords, err := subzone.GetGlueRecords(concatName(name, subname), nameserversMap) if err != nil { return err } + z.GlueRecords = append(z.GlueRecords, glueRecords...) } return nil } else { @@ -136,7 +137,6 @@ func (z *Zone) Validate(name string, soa *NamedRecord, ns *[]Record) error { } // Either we're outside a zone, at a zone apex, or at a non-delegated subzone - for subname, subzone := range z.Subzones { if err := subzone.Validate(concatName(name, subname), soa, ns); err != nil { return err @@ -145,26 +145,30 @@ func (z *Zone) Validate(name string, soa *NamedRecord, ns *[]Record) error { return nil } -func (z *Zone) GetGlueRecords(name string, nameserversMap map[string]bool) error { +func (z *Zone) GetGlueRecords(name string, nameserversMap map[string]bool) ([]NamedRecord, error) { // If the domain is not a nameserver, it must have no records if _, ok := nameserversMap[strings.TrimSuffix(name, ".")]; !ok { if len(z.Records) > 0 { - return fmt.Errorf("%s: non-glue records found under delegation point: %v", name, z.Records) + return nil, fmt.Errorf("%s: non-glue records found under delegation point: %v", name, z.Records) } } // Any records under a delegation point must be glue records + glueRecords := []NamedRecord{} for _, record := range z.Records { if !(record.Type == "A" || record.Type == "AAAA") { - return fmt.Errorf("%s: non-glue record found under delegation point: %v", name, record) + return nil, fmt.Errorf("%s: non-glue record found under delegation point: %v", name, record) } - z.GlueRecords = append(z.GlueRecords, NamedRecord{Name: name, Record: record}) + glueRecords = append(glueRecords, NamedRecord{Name: name, Record: record}) } for subname, subzone := range z.Subzones { - if err := subzone.GetGlueRecords(concatName(name, subname), nameserversMap); err != nil { - return err + var subGlueRecords []NamedRecord + var err error + if subGlueRecords, err = subzone.GetGlueRecords(concatName(name, subname), nameserversMap); err != nil { + return nil, err } + glueRecords = append(glueRecords, subGlueRecords...) } - return nil + return glueRecords, nil } func (r *Record) UnmarshalYAML(ctx context.Context, data []byte) error { @@ -299,6 +303,11 @@ func (z *Zone) LookupType(name string, recordType string) (LookupResult, bool) { } else { z = sz } + if len(z.Records) > 0 && z.Records[0].Type == "CNAME" { + // Return CNAME + res.Answer = []NamedRecord{{Name: name, Record: z.Records[0]}} + return res, true + } if z.IsDelegationPoint { // Retrieve glue records from cache, whether referral or NS query res.Extra = append(res.Extra, z.GlueRecords...) diff --git a/yamlzone_test.go b/yamlzone_test.go index 19594ea..c34d6e5 100644 --- a/yamlzone_test.go +++ b/yamlzone_test.go @@ -181,8 +181,15 @@ func TestFullZone(t *testing.T) { assertRecord(t, res.Answer[0].Record, "CNAME", 3600, "example.com") }) - t.Run("Lookup www.example.com TXT", func(t *testing.T) { - res, ok := zFull.LookupType("www.example.com", "TXT") + t.Run("LookupType www.example.com A", func(t *testing.T) { + res, ok := zFull.LookupType("www.example.com", "CNAME") + assertOk(t, ok) + assertRecordCount(t, res, 1) + assertRecord(t, res.Answer[0].Record, "CNAME", 3600, "example.com") + }) + + t.Run("LookupType mail.example.com TXT", func(t *testing.T) { + res, ok := zFull.LookupType("mail.example.com", "TXT") assertOk(t, ok) assertRecordCount(t, res, 0) })