274 lines
5.4 KiB
Go
274 lines
5.4 KiB
Go
package netboxdns
|
|
|
|
import (
|
|
"strings"
|
|
|
|
"github.com/doubleu-labs/coredns-netbox-plugin-dns/internal/netbox"
|
|
"github.com/miekg/dns"
|
|
)
|
|
|
|
type lookupResult int
|
|
|
|
const (
|
|
lookupSuccess lookupResult = iota
|
|
lookupNameError // NXDomain
|
|
lookupDelegation // Delegate, non-authoritative
|
|
)
|
|
|
|
type lookupResponse struct {
|
|
Answer []dns.RR
|
|
Ns []dns.RR
|
|
Extra []dns.RR
|
|
LookupResult lookupResult
|
|
}
|
|
|
|
func (netboxdns *NetboxDNS) lookup(
|
|
name string,
|
|
qtype uint16,
|
|
family int,
|
|
) (*lookupResponse, error) {
|
|
nameTrimmed := strings.TrimSuffix(name, ".")
|
|
// check if zone exists on Netbox
|
|
zone, err := netboxdns.matchZone(nameTrimmed)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if zone == nil {
|
|
return &lookupResponse{LookupResult: lookupNameError}, nil
|
|
}
|
|
|
|
// check if qname is for zone origin
|
|
if nameTrimmed == zone.Name {
|
|
originResponse, err := netboxdns.processOrigin(qtype, zone, family)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if originResponse != nil {
|
|
return originResponse, nil
|
|
}
|
|
}
|
|
|
|
// lookup exact request
|
|
direct, err := netboxdns.lookupDirect(nameTrimmed, qtype, zone, family)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if direct != nil {
|
|
return direct, nil
|
|
}
|
|
|
|
// if no exact records exist for the request, check if the qname is a
|
|
// delegate zone
|
|
delegate, err := netboxdns.lookupDelegate(nameTrimmed, zone, family)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if delegate != nil {
|
|
return delegate, nil
|
|
}
|
|
|
|
return &lookupResponse{LookupResult: lookupNameError}, nil
|
|
}
|
|
|
|
func (netboxdns *NetboxDNS) matchZone(qname string) (*netbox.Zone, error) {
|
|
managedZones, err := netbox.GetZones(netboxdns.requestClient)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
var out *netbox.Zone
|
|
for _, managedZone := range managedZones {
|
|
if dns.IsSubDomain(managedZone.Name, qname) {
|
|
if out == nil {
|
|
out = &managedZone
|
|
}
|
|
if len(managedZone.Name) > len(out.Name) {
|
|
out = &managedZone
|
|
}
|
|
}
|
|
}
|
|
return out, nil
|
|
}
|
|
|
|
func (netboxdns *NetboxDNS) processOrigin(
|
|
qtype uint16,
|
|
zone *netbox.Zone,
|
|
family int,
|
|
) (*lookupResponse, error) {
|
|
var queryType []string
|
|
switch qtype {
|
|
case dns.TypeSOA:
|
|
queryType = []string{"SOA", "NS"}
|
|
case dns.TypeNS:
|
|
queryType = []string{"NS"}
|
|
default:
|
|
return nil, nil
|
|
}
|
|
records, err := netbox.GetRecordsQuery(
|
|
netboxdns.requestClient,
|
|
&netbox.RecordQuery{
|
|
Name: "@",
|
|
Type: queryType,
|
|
Zone: zone,
|
|
},
|
|
)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
rrs, err := recordsToRR(records)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
answer := filterRRByType(rrs, dns.TypeSOA)
|
|
ns := filterRRByType(rrs, dns.TypeNS)
|
|
extraRecords, err := netboxdns.processExtra(ns, zone, family)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if len(extraRecords) == 0 {
|
|
// if no A/AAAA records exist for the NS in the specified zone, check if
|
|
// the server has records anywhere
|
|
extraRecords, err = netboxdns.processExtra(ns, nil, family)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
extra, err := recordsToRR(extraRecords)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if qtype == dns.TypeNS {
|
|
answer = ns
|
|
ns = nil
|
|
}
|
|
return &lookupResponse{
|
|
Answer: answer,
|
|
Ns: ns,
|
|
Extra: extra,
|
|
}, nil
|
|
}
|
|
|
|
func (netboxdns *NetboxDNS) processExtra(
|
|
answer []dns.RR,
|
|
zone *netbox.Zone,
|
|
family int,
|
|
) ([]netbox.Record, error) {
|
|
var out []netbox.Record
|
|
for _, rr := range answer {
|
|
name := ""
|
|
switch t := rr.(type) {
|
|
case *dns.SRV:
|
|
name = t.Target
|
|
case *dns.MX:
|
|
name = t.Mx
|
|
case *dns.NS:
|
|
name = t.Ns
|
|
case *dns.CNAME:
|
|
name = t.Target
|
|
}
|
|
if len(name) == 0 {
|
|
continue
|
|
}
|
|
var reqType []string
|
|
switch family {
|
|
case 1:
|
|
reqType = []string{"A"}
|
|
case 2:
|
|
reqType = []string{"AAAA"}
|
|
}
|
|
records, err := netbox.GetRecordsQuery(
|
|
netboxdns.requestClient,
|
|
&netbox.RecordQuery{
|
|
FQDN: strings.TrimSuffix(name, "."),
|
|
Type: reqType,
|
|
Zone: zone,
|
|
},
|
|
)
|
|
if err != nil {
|
|
return out, err
|
|
}
|
|
out = append(out, records...)
|
|
}
|
|
return out, nil
|
|
}
|
|
|
|
func (netboxdns *NetboxDNS) lookupDirect(
|
|
qname string,
|
|
qtype uint16,
|
|
zone *netbox.Zone,
|
|
family int,
|
|
) (*lookupResponse, error) {
|
|
records, err := netbox.GetRecordsQuery(
|
|
netboxdns.requestClient,
|
|
&netbox.RecordQuery{
|
|
FQDN: qname,
|
|
Type: []string{dns.TypeToString[qtype]},
|
|
Zone: zone,
|
|
},
|
|
)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if len(records) > 0 {
|
|
answer, err := recordsToRR(records)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
extraRecords, err := netboxdns.processExtra(answer, zone, family)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
extra, err := recordsToRR(extraRecords)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if qtype == dns.TypeCNAME {
|
|
answer = append(answer, extra...)
|
|
extra = nil
|
|
}
|
|
return &lookupResponse{
|
|
Answer: answer,
|
|
Extra: extra,
|
|
}, nil
|
|
}
|
|
return nil, nil
|
|
}
|
|
|
|
func (netboxdns *NetboxDNS) lookupDelegate(
|
|
qname string,
|
|
zone *netbox.Zone,
|
|
family int,
|
|
) (*lookupResponse, error) {
|
|
records, err := netbox.GetRecordsQuery(
|
|
netboxdns.requestClient,
|
|
&netbox.RecordQuery{
|
|
FQDN: qname,
|
|
Type: []string{"NS"},
|
|
Zone: zone,
|
|
},
|
|
)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if len(records) > 0 {
|
|
ns, err := recordsToRR(records)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
extraRecords, err := netboxdns.processExtra(ns, nil, family)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
extra, err := recordsToRR(extraRecords)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &lookupResponse{
|
|
Ns: ns,
|
|
Extra: extra,
|
|
LookupResult: lookupDelegation,
|
|
}, nil
|
|
}
|
|
return nil, nil
|
|
}
|