dns/sanitize.go

105 lines
2.4 KiB
Go
Raw Normal View History

package dns
// Dedup removes identical RRs from rrs. It preserves the original ordering.
2015-08-25 05:52:56 +00:00
// The lowest TTL of any duplicates is used in the remaining one.
//
// TODO(miek): This function will be extended to also look for CNAMEs and DNAMEs.
// if found, it will prune rrs from the "other data" that can exist. Example:
// if it finds a: a.miek.nl. CNAME foo, all other RRs with the ownername a.miek.nl.
2015-08-25 08:30:43 +00:00
// will be removed. When a DNAME is found all RRs with an ownername below that of
// the DNAME will be removed.
func Dedup(rrs []RR) []RR {
m := make(map[string]RR)
2015-08-25 05:52:56 +00:00
keys := make([]string, 0, len(rrs))
2015-08-25 08:30:43 +00:00
var cname map[nameClass]bool
var dname map[nameClass]bool
for _, r := range rrs {
2015-08-25 08:30:43 +00:00
switch r.Header().Rrtype {
case TypeCNAME:
if cname == nil {
cname = make(map[nameClass]bool)
}
cname[nameClass{r.Header().Name, r.Header().Class}] = true
case TypeDNAME:
if dname == nil {
dname = make(map[nameClass]bool)
}
dname[nameClass{r.Header().Name, r.Header().Class}] = true
default:
if
}
key := normalizedString(r)
keys = append(keys, key)
if _, ok := m[key]; ok {
// Shortest TTL wins.
if m[key].Header().Ttl > r.Header().Ttl {
m[key].Header().Ttl = r.Header().Ttl
}
continue
}
m[key] = r
}
// If the length of the result map equals the amount of RRs we got,
2015-08-25 05:52:56 +00:00
// it means they were all different. We can then just return the original rrset.
if len(m) == len(rrs) {
return rrs
}
var i = 0
for i, _ = range rrs {
if len(m) == 0 {
break
}
// We saved the key for each RR.
delete(m, keys[i])
}
return rrs[:i]
}
2015-08-25 08:30:43 +00:00
// nameClass is used to index the CNAME and DNAME maps.
type nameClass struct {
name string
class uint16
}
// normalizedString returns a normalized string from r. The TTL
// is removed and the domain name is lowercased.
func normalizedString(r RR) string {
// A string Go DNS makes has: domainname<TAB>TTL<TAB>...
b := []byte(r.String())
// find the first non-escaped tab, then another, so we capture
// where the TTL lives.
esc := false
ttlStart, ttlEnd := 0, 0
for i, c := range b {
if c == '\\' {
esc = true
continue
}
if esc {
esc = false
continue
}
if c == '\t' {
if ttlStart == 0 {
ttlStart = i
continue
}
if ttlEnd == 0 {
ttlEnd = i
break
}
}
if c >= 'A' && c <= 'Z' {
b[i] = c + 32
}
}
// remove TTL.
copy(b[ttlStart:], b[ttlEnd:])
cut := ttlEnd - ttlStart
return string(b[:len(b)-cut])
}