dns/zone.go

184 lines
4.9 KiB
Go
Raw Normal View History

2012-07-14 20:01:52 +00:00
package dns
// A structure for handling zone data
import (
2012-08-08 09:08:25 +00:00
"github.com/miekg/radix"
2012-08-01 14:30:45 +00:00
"strings"
2012-08-25 17:57:25 +00:00
"sync"
2012-07-14 20:01:52 +00:00
)
2012-08-27 07:12:41 +00:00
// Zone represents a DNS zone. It's safe for concurrent use by
// multilpe goroutines.
2012-07-14 20:01:52 +00:00
type Zone struct {
2012-07-16 17:46:16 +00:00
Origin string // Origin of the zone
2012-08-17 06:29:45 +00:00
Wildcard int // Whenever we see a wildcard name, this is incremented
2012-07-15 16:11:17 +00:00
*radix.Radix // Zone data
2012-08-25 18:57:26 +00:00
mutex *sync.RWMutex
2012-07-14 20:01:52 +00:00
}
2012-08-27 07:12:41 +00:00
// NewZone creates an initialized zone with Origin set to origin.
func NewZone(origin string) *Zone {
if origin == "" {
origin = "."
}
if _, _, ok := IsDomainName(origin); !ok {
return nil
}
z := new(Zone)
z.mutex = new(sync.RWMutex)
z.Origin = Fqdn(origin)
z.Radix = radix.New()
return z
}
2012-08-25 17:38:52 +00:00
// ZoneData holds all the RRs having their owner name equal to Name.
2012-07-14 20:01:52 +00:00
type ZoneData struct {
2012-08-05 06:13:09 +00:00
Name string // Domain name for this node
RR map[uint16][]RR // Map of the RR type to the RR
Signatures map[uint16][]*RR_RRSIG // DNSSEC signatures for the RRs, stored under type covered
2012-08-25 12:44:51 +00:00
NonAuth bool // Always false, except for NSsets that differ from z.Origin
2012-08-25 17:57:25 +00:00
mutex *sync.RWMutex
2012-07-14 20:54:49 +00:00
}
2012-08-27 07:12:41 +00:00
// newZoneData creates a new zone data element
func newZoneData(s string) *ZoneData {
zd := new(ZoneData)
zd.Name = s
zd.RR = make(map[uint16][]RR)
zd.Signatures = make(map[uint16][]*RR_RRSIG)
zd.mutex = new(sync.RWMutex)
return zd
}
2012-08-25 17:38:52 +00:00
// toRadixName reverses a domain name so that when we store it in the radix tree
2012-08-01 14:30:45 +00:00
// we preserve the nsec ordering of the zone (this idea was stolen from NSD).
// each label is also lowercased.
func toRadixName(d string) string {
2012-08-05 06:13:09 +00:00
if d == "." {
return "."
}
2012-08-01 14:30:45 +00:00
s := ""
for _, l := range SplitLabels(d) {
2012-08-03 16:28:00 +00:00
s = strings.ToLower(l) + "." + s
2012-08-01 14:30:45 +00:00
}
2012-08-05 06:13:09 +00:00
return "." + s
2012-08-01 14:30:45 +00:00
}
2012-07-14 20:54:49 +00:00
2012-08-25 17:38:52 +00:00
// Insert inserts an RR into the zone. There is no check for duplicate data, although
// Remove will remove all duplicates.
2012-07-16 19:16:42 +00:00
func (z *Zone) Insert(r RR) error {
2012-08-09 19:05:20 +00:00
if !IsSubDomain(z.Origin, r.Header().Name) {
2012-07-16 19:16:42 +00:00
return &Error{Err: "out of zone data", Name: r.Header().Name}
2012-07-16 17:46:16 +00:00
}
2012-08-01 14:30:45 +00:00
key := toRadixName(r.Header().Name)
2012-08-25 17:57:25 +00:00
z.mutex.Lock()
2012-08-01 14:30:45 +00:00
zd := z.Radix.Find(key)
2012-07-15 16:11:17 +00:00
if zd == nil {
2012-08-25 17:57:25 +00:00
defer z.mutex.Unlock()
2012-08-09 21:00:51 +00:00
// Check if its a wildcard name
if len(r.Header().Name) > 1 && r.Header().Name[0] == '*' && r.Header().Name[1] == '.' {
z.Wildcard++
}
2012-08-27 07:12:41 +00:00
zd := newZoneData(r.Header().Name)
2012-07-15 16:11:17 +00:00
switch t := r.Header().Rrtype; t {
case TypeRRSIG:
sigtype := r.(*RR_RRSIG).TypeCovered
zd.Signatures[sigtype] = append(zd.Signatures[sigtype], r.(*RR_RRSIG))
2012-07-16 19:20:58 +00:00
case TypeNS:
// NS records with other names than z.Origin are non-auth
2012-07-16 19:24:05 +00:00
if r.Header().Name != z.Origin {
2012-07-16 19:20:58 +00:00
zd.NonAuth = true
}
fallthrough
2012-07-15 16:11:17 +00:00
default:
zd.RR[t] = append(zd.RR[t], r)
}
2012-08-01 14:30:45 +00:00
z.Radix.Insert(key, zd)
2012-07-16 19:24:05 +00:00
return nil
2012-07-15 16:11:17 +00:00
}
2012-08-25 17:57:25 +00:00
z.mutex.Unlock()
zd.Value.(*ZoneData).mutex.Lock()
defer zd.Value.(*ZoneData).mutex.Unlock()
2012-07-16 19:24:05 +00:00
// Name already there
2012-07-15 16:11:17 +00:00
switch t := r.Header().Rrtype; t {
case TypeRRSIG:
sigtype := r.(*RR_RRSIG).TypeCovered
zd.Value.(*ZoneData).Signatures[sigtype] = append(zd.Value.(*ZoneData).Signatures[sigtype], r.(*RR_RRSIG))
2012-07-16 19:20:58 +00:00
case TypeNS:
2012-07-16 19:24:05 +00:00
if r.Header().Name != z.Origin {
zd.Value.(*ZoneData).NonAuth = true
2012-07-16 19:20:58 +00:00
}
fallthrough
2012-07-15 16:11:17 +00:00
default:
zd.Value.(*ZoneData).RR[t] = append(zd.Value.(*ZoneData).RR[t], r)
2012-07-15 16:11:17 +00:00
}
2012-07-16 19:24:05 +00:00
return nil
2012-07-14 20:54:49 +00:00
}
2012-09-01 15:06:24 +00:00
// Remove removes the RR r from the zone. If the RR can not be found,
// this is a no-op.
2012-07-16 19:24:05 +00:00
func (z *Zone) Remove(r RR) error {
key := toRadixName(r.Header().Name)
2012-08-25 17:57:25 +00:00
z.mutex.Lock()
zd := z.Radix.Find(key)
if zd == nil {
2012-08-25 17:57:25 +00:00
defer z.mutex.Unlock()
return nil
}
2012-08-25 17:57:25 +00:00
z.mutex.Unlock()
zd.Value.(*ZoneData).mutex.Lock()
defer zd.Value.(*ZoneData).mutex.Unlock()
2012-08-25 12:44:51 +00:00
remove := false
switch t := r.Header().Rrtype; t {
case TypeRRSIG:
sigtype := r.(*RR_RRSIG).TypeCovered
2012-08-25 12:44:51 +00:00
for i, zr := range zd.Value.(*ZoneData).RR[sigtype] {
if r == zr {
zd.Value.(*ZoneData).RR[sigtype] = append(zd.Value.(*ZoneData).RR[sigtype][:i], zd.Value.(*ZoneData).RR[sigtype][i+1:]...)
remove = true
}
}
default:
2012-08-25 12:44:51 +00:00
for i, zr := range zd.Value.(*ZoneData).RR[t] {
if r == zr {
2012-08-25 12:44:51 +00:00
zd.Value.(*ZoneData).RR[t] = append(zd.Value.(*ZoneData).RR[t][:i], zd.Value.(*ZoneData).RR[t][i+1:]...)
remove = true
}
}
}
2012-08-25 12:44:51 +00:00
if remove && len(r.Header().Name) > 1 && r.Header().Name[0] == '*' && r.Header().Name[1] == '.' {
z.Wildcard--
if z.Wildcard < 0 {
z.Wildcard = 0
}
}
2012-08-25 12:44:51 +00:00
// TODO(mg): what to do if the whole structure is empty? Set it to nil?
2012-07-16 19:24:05 +00:00
return nil
2012-07-14 20:01:52 +00:00
}
2012-08-03 16:28:00 +00:00
2012-08-21 15:21:47 +00:00
// Find looks up the ownername s in the zone and returns the
// data when found or nil when nothing is found.
2012-08-03 16:28:00 +00:00
func (z *Zone) Find(s string) *ZoneData {
2012-08-25 20:12:29 +00:00
z.mutex.RLock()
defer z.mutex.RUnlock()
2012-08-04 18:56:57 +00:00
zd := z.Radix.Find(toRadixName(s))
if zd == nil {
2012-08-03 16:28:00 +00:00
return nil
}
2012-08-04 18:56:57 +00:00
return zd.Value.(*ZoneData)
}
2012-08-21 15:21:47 +00:00
// Predecessor searches the zone for a name shorter than s.
2012-08-04 18:56:57 +00:00
func (z *Zone) Predecessor(s string) *ZoneData {
2012-08-25 20:12:29 +00:00
z.mutex.RLock()
defer z.mutex.RUnlock()
2012-08-04 18:56:57 +00:00
zd := z.Radix.Predecessor(toRadixName(s))
2012-08-03 16:28:00 +00:00
if zd == nil {
return nil
}
return zd.Value.(*ZoneData)
}