package realm import ( "strings" "github.com/miekg/dns" ) // RecordsEntry is used to hold a mapping of DNS request types to DNS records type RecordsEntry map[uint16][]dns.RR // GetRecords will fetch the appropriate DNS records to the requested type func (entry RecordsEntry) GetRecords(rrType uint16) []dns.RR { var records []dns.RR records = make([]dns.RR, 0) if rrType == dns.TypeANY { for _, rrs := range entry { records = append(records, rrs...) } } else if rrs, ok := entry[rrType]; ok { records = append(records, rrs...) } return records } // DomainEntry is used to hold a mapping of DNS request classes to RecordEntrys type DomainEntry map[uint16]RecordsEntry // AddEntry is used to add a new DNS record to this mapping func (entry DomainEntry) AddEntry(record dns.RR) { var header *dns.RR_Header header = record.Header() if _, ok := entry[header.Class]; !ok { entry[header.Class] = make(RecordsEntry) } if _, ok := entry[header.Class][header.Rrtype]; !ok { entry[header.Class][header.Rrtype] = make([]dns.RR, 0) } entry[header.Class][header.Rrtype] = append(entry[header.Class][header.Rrtype], record) } // GetEntries is used to find the appropriate RecordEntrys for the requested DNS class func (entry DomainEntry) GetEntries(rrClass uint16) []RecordsEntry { var entries []RecordsEntry entries = make([]RecordsEntry, 0) if rrClass == dns.ClassANY { for _, entry := range entry { entries = append(entries, entry) } } else if entry, ok := entry[rrClass]; ok { entries = append(entries, entry) } return entries } // Registry is a container for looking up DNS records for any request type Registry struct { records map[string]DomainEntry } // NewRegistry will allocate and return a new *Registry func NewRegistry() *Registry { return &Registry{ records: make(map[string]DomainEntry), } } // addRecord is used to add a new DNS record to this registry func (r *Registry) addRecord(record dns.RR) { var header *dns.RR_Header header = record.Header() var name string name = dns.Fqdn(header.Name) name = strings.ToLower(name) if _, ok := r.records[name]; !ok { r.records[name] = make(DomainEntry) } r.records[name].AddEntry(record) // If this record is an SOA record then also store under the Mbox name if header.Rrtype == dns.TypeSOA { var soa *dns.SOA soa = record.(*dns.SOA) if _, ok := r.records[soa.Mbox]; !ok { r.records[soa.Mbox] = make(DomainEntry) } r.records[soa.Mbox].AddEntry(record) } } // AddZone is used to add the records from a *Zone into this *Registry func (r *Registry) AddZone(z *Zone) { for _, record := range z.Records() { r.addRecord(record) } } // Lookup will find all records which we should respond with for the given name, request type, and request class. func (r *Registry) Lookup(name string, reqType uint16, reqClass uint16) []dns.RR { name = dns.Fqdn(name) name = strings.ToLower(name) var records []dns.RR records = make([]dns.RR, 0) var domainEntry DomainEntry var ok bool domainEntry, ok = r.records[name] if !ok { return records } var recordEntries []RecordsEntry recordEntries = domainEntry.GetEntries(reqClass) for _, recordEntry := range recordEntries { var rrs []dns.RR rrs = recordEntry.GetRecords(reqType) records = append(records, rrs...) if len(rrs) == 0 && reqType == dns.TypeA { rrs = recordEntry.GetRecords(dns.TypeCNAME) for _, rr := range rrs { records = append(records, rr) var header *dns.RR_Header header = rr.Header() if header.Rrtype == dns.TypeCNAME && reqType != dns.TypeCNAME { // Attempt to resolve this CNAME record var cname *dns.CNAME cname = rr.(*dns.CNAME) var cnameRecords []dns.RR cnameRecords = r.Lookup(cname.Target, reqType, reqClass) records = append(records, cnameRecords...) } } } } return records }