Ready for CoreDNS testing
This commit is contained in:
parent
772469d21b
commit
b79df4bf1d
9 changed files with 290 additions and 124 deletions
121
yamlzone.go
121
yamlzone.go
|
|
@ -18,11 +18,24 @@ type Record struct {
|
|||
Value string
|
||||
}
|
||||
|
||||
type NamedRecord struct {
|
||||
Name string
|
||||
Record Record
|
||||
}
|
||||
|
||||
type Zone struct {
|
||||
Subzones map[string]*Zone
|
||||
Records []Record
|
||||
GlueRecords []Record
|
||||
SOA NamedRecord
|
||||
IsDelegationPoint bool
|
||||
GlueRecords []NamedRecord // Only set (optionally) for delegation points
|
||||
}
|
||||
|
||||
type LookupResult struct {
|
||||
Answer []NamedRecord
|
||||
Ns []NamedRecord
|
||||
Extra []NamedRecord
|
||||
IsReferral bool
|
||||
}
|
||||
|
||||
type contextKey string
|
||||
|
|
@ -57,31 +70,40 @@ func LoadZoneBytes(data []byte, filename string) (*Zone, error) {
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := z.Validate(".", false); err != nil {
|
||||
if err := z.Validate(".", nil, nil); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return z, nil
|
||||
}
|
||||
|
||||
func (z *Zone) Validate(name string, zoneApexPresent bool) error {
|
||||
nameservers := map[string]bool{}
|
||||
otherRecords := []Record{}
|
||||
func (z *Zone) Validate(name string, soa *Record, ns *[]Record) error {
|
||||
nameservers, otherRecords := []Record{}, []Record{}
|
||||
nameserversMap := map[string]bool{}
|
||||
isZoneApex := false
|
||||
cnameCount := 0
|
||||
for _, record := range z.Records {
|
||||
switch record.Type {
|
||||
case "SOA":
|
||||
isZoneApex = true
|
||||
soa = &record
|
||||
z.SOA = NamedRecord{Name: name, Record: record}
|
||||
case "NS":
|
||||
nameservers[record.Value] = true
|
||||
nameservers = append(nameservers, record)
|
||||
nameserversMap[record.Value] = true
|
||||
case "CNAME":
|
||||
cnameCount++
|
||||
default:
|
||||
otherRecords = append(otherRecords, record)
|
||||
}
|
||||
}
|
||||
|
||||
zoneApexPresent = zoneApexPresent || isZoneApex
|
||||
if cnameCount > 1 || (cnameCount > 0 && len(otherRecords) > 0) {
|
||||
return fmt.Errorf("%s: extraneous records found next to CNAME", name)
|
||||
}
|
||||
|
||||
z.IsDelegationPoint = !isZoneApex && len(nameservers) > 0
|
||||
|
||||
if !zoneApexPresent {
|
||||
if soa == nil {
|
||||
// Outside zone
|
||||
if z.IsDelegationPoint {
|
||||
return fmt.Errorf("%s: delegation point found outside zone", name)
|
||||
|
|
@ -93,51 +115,51 @@ func (z *Zone) Validate(name string, zoneApexPresent bool) error {
|
|||
if len(nameservers) == 0 {
|
||||
return fmt.Errorf("%s: zone apex missing NS records", name)
|
||||
}
|
||||
ns = &nameservers
|
||||
} else if len(nameservers) > 0 {
|
||||
// Delegation point (does not fall through to subzone validation)
|
||||
if len(otherRecords) > 0 {
|
||||
return fmt.Errorf("%s: non-glue, non-NS records found at delegation point: %v", name, otherRecords)
|
||||
}
|
||||
for subname, subzone := range z.Subzones {
|
||||
glueRecords, err := subzone.GetGlueRecords(concatName(name, subname), nameservers)
|
||||
// This populates z.GlueRecords directly
|
||||
err := subzone.GetGlueRecords(concatName(name, subname), nameserversMap)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
z.GlueRecords = append(z.GlueRecords, glueRecords...)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
// Subzone validation
|
||||
// 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), zoneApexPresent); err != nil {
|
||||
if err := subzone.Validate(concatName(name, subname), soa, ns); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (z *Zone) GetGlueRecords(name string, nameservers map[string]bool) ([]Record, error) {
|
||||
func (z *Zone) GetGlueRecords(name string, nameserversMap map[string]bool) error {
|
||||
// If the domain is not a nameserver, it must have no records
|
||||
if _, ok := nameservers[name]; !ok {
|
||||
if _, ok := nameserversMap[strings.TrimSuffix(name, ".")]; !ok {
|
||||
if len(z.Records) > 0 {
|
||||
return nil, fmt.Errorf("%s: non-glue records found under delegation point: %v", name, z.Records)
|
||||
return fmt.Errorf("%s: non-glue records found under delegation point: %v", name, z.Records)
|
||||
}
|
||||
}
|
||||
// Any records under a delegation point must be glue records
|
||||
for _, record := range z.Records {
|
||||
if !(record.Type == "A" || record.Type == "AAAA") {
|
||||
return nil, fmt.Errorf("%s: non-glue record found under delegation point: %v", name, record)
|
||||
return fmt.Errorf("%s: non-glue record found under delegation point: %v", name, record)
|
||||
}
|
||||
z.GlueRecords = append(z.GlueRecords, NamedRecord{Name: name, Record: record})
|
||||
}
|
||||
for subname, subzone := range z.Subzones {
|
||||
if glueRecords, err := subzone.GetGlueRecords(concatName(name, subname), nameservers); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
z.GlueRecords = append(z.GlueRecords, glueRecords...)
|
||||
if err := subzone.GetGlueRecords(concatName(name, subname), nameserversMap); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return z.GlueRecords, nil
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *Record) UnmarshalYAML(ctx context.Context, data []byte) error {
|
||||
|
|
@ -252,38 +274,67 @@ func nameToPath(name string) []string {
|
|||
return path
|
||||
}
|
||||
|
||||
func (z *Zone) Lookup(name string) ([]Record, bool) {
|
||||
func (z *Zone) Lookup(name string) (LookupResult, bool) {
|
||||
res := LookupResult{}
|
||||
path := nameToPath(name)
|
||||
for _, label := range path {
|
||||
// Support empty name and trailing dot
|
||||
if label == "" {
|
||||
continue
|
||||
}
|
||||
if sz, ok := z.Subzones[label]; ok {
|
||||
z = sz
|
||||
if z.IsDelegationPoint {
|
||||
res.IsReferral = true
|
||||
// Capture NS records
|
||||
for _, record := range z.Records {
|
||||
if record.Type == "NS" {
|
||||
res.Ns = append(res.Ns, NamedRecord{Name: name, Record: record})
|
||||
}
|
||||
}
|
||||
// Retrieve glue records from cache
|
||||
res.Extra = append(res.Extra, z.GlueRecords...)
|
||||
return res, true
|
||||
}
|
||||
if sz, ok := z.Subzones[label]; !ok {
|
||||
// NXDOMAIN
|
||||
res.Ns = []NamedRecord{z.SOA}
|
||||
return res, false
|
||||
} else {
|
||||
return nil, false
|
||||
z = sz
|
||||
}
|
||||
}
|
||||
return z.Records, true
|
||||
res.Answer = []NamedRecord{}
|
||||
for _, record := range z.Records {
|
||||
res.Answer = append(res.Answer, NamedRecord{Name: name, Record: record})
|
||||
}
|
||||
if len(res.Answer) == 0 {
|
||||
// NODATA
|
||||
res.Ns = []NamedRecord{z.SOA}
|
||||
return res, true
|
||||
}
|
||||
return res, true
|
||||
}
|
||||
|
||||
func (z *Zone) FilterRecords(records []Record, recordType string) []Record {
|
||||
filtered := []Record{}
|
||||
for _, record := range records {
|
||||
if record.Type == recordType {
|
||||
filtered = append(filtered, record)
|
||||
func (z *Zone) FilterRecords(res LookupResult, recordType string) LookupResult {
|
||||
filtered := []NamedRecord{}
|
||||
for _, nr := range res.Answer {
|
||||
if nr.Record.Type == recordType {
|
||||
filtered = append(filtered, nr)
|
||||
}
|
||||
}
|
||||
return filtered
|
||||
res.Answer = filtered
|
||||
if len(res.Answer) == 0 && !res.IsReferral {
|
||||
// This ends up as NODATA even if it had data before filtering
|
||||
res.Ns = []NamedRecord{z.SOA}
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
func (z *Zone) LookupType(name string, recordType string) ([]Record, bool) {
|
||||
records, ok := z.Lookup(name)
|
||||
func (z *Zone) LookupType(name string, recordType string) (LookupResult, bool) {
|
||||
res, ok := z.Lookup(name)
|
||||
if !ok {
|
||||
return nil, false
|
||||
return LookupResult{}, false
|
||||
}
|
||||
return z.FilterRecords(records, recordType), true
|
||||
return z.FilterRecords(res, recordType), true
|
||||
}
|
||||
|
||||
func concatName(name string, subname string) string {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue