Files
coredns/plugin/file/tree/elem.go
Ville Vesilehto 70fb03f711 fix(file): fix data race in tree Elem.Name (#7574)
Eagerly set name in newElem and make Name() read-only to avoid
racy lazy writes under concurrent lookups. Add tests for empty-name
comparisons and concurrent access to Less/Name(). In addition,
regression tests to CloudDNS plugin.

Signed-off-by: Ville Vesilehto <ville@vesilehto.fi>
2025-09-28 19:49:47 -07:00

104 lines
2.3 KiB
Go

package tree
import "github.com/miekg/dns"
// Elem is an element in the tree.
type Elem struct {
m map[uint16][]dns.RR
name string // owner name
}
// newElem returns a new elem.
func newElem(rr dns.RR) *Elem {
e := Elem{m: make(map[uint16][]dns.RR)}
e.m[rr.Header().Rrtype] = []dns.RR{rr}
// Eagerly set the cached owner name to avoid racy lazy writes later.
e.name = rr.Header().Name
return &e
}
// Types returns the types of the records in e. The returned list is not sorted.
func (e *Elem) Types() []uint16 {
t := make([]uint16, len(e.m))
i := 0
for ty := range e.m {
t[i] = ty
i++
}
return t
}
// Type returns the RRs with type qtype from e.
func (e *Elem) Type(qtype uint16) []dns.RR { return e.m[qtype] }
// TypeForWildcard returns the RRs with type qtype from e. The ownername returned is set to qname.
func (e *Elem) TypeForWildcard(qtype uint16, qname string) []dns.RR {
rrs := e.m[qtype]
if rrs == nil {
return nil
}
copied := make([]dns.RR, len(rrs))
for i := range rrs {
copied[i] = dns.Copy(rrs[i])
copied[i].Header().Name = qname
}
return copied
}
// All returns all RRs from e, regardless of type.
func (e *Elem) All() []dns.RR {
list := []dns.RR{}
for _, rrs := range e.m {
list = append(list, rrs...)
}
return list
}
// Name returns the name for this node.
func (e *Elem) Name() string {
// Read-only: name is eagerly set in newElem and should not be mutated here.
if e.name != "" {
return e.name
}
for _, rrs := range e.m {
return rrs[0].Header().Name
}
return ""
}
// Empty returns true is e does not contain any RRs, i.e. is an empty-non-terminal.
func (e *Elem) Empty() bool { return len(e.m) == 0 }
// Insert inserts rr into e. If rr is equal to existing RRs, the RR will be added anyway.
func (e *Elem) Insert(rr dns.RR) {
t := rr.Header().Rrtype
if e.m == nil {
e.m = make(map[uint16][]dns.RR)
e.m[t] = []dns.RR{rr}
return
}
rrs, ok := e.m[t]
if !ok {
e.m[t] = []dns.RR{rr}
return
}
rrs = append(rrs, rr)
e.m[t] = rrs
}
// Delete removes all RRs of type rr.Header().Rrtype from e.
func (e *Elem) Delete(rr dns.RR) {
if e.m == nil {
return
}
t := rr.Header().Rrtype
delete(e.m, t)
}
// Less is a tree helper function that calls less.
func Less(a *Elem, name string) int { return less(name, a.Name()) }