435c0d9fc8
This PR switches the Nomad repository from using govendor to Go modules for managing dependencies. Aspects of the Nomad workflow remain pretty much the same. The usual Makefile targets should continue to work as they always did. The API submodule simply defers to the parent Nomad version on the repository, keeping the semantics of API versioning that currently exists.
87 lines
2 KiB
Go
87 lines
2 KiB
Go
package dns
|
|
|
|
// Dedup removes identical RRs from rrs. It preserves the original ordering.
|
|
// The lowest TTL of any duplicates is used in the remaining one. Dedup modifies
|
|
// rrs.
|
|
// m is used to store the RRs temporary. If it is nil a new map will be allocated.
|
|
func Dedup(rrs []RR, m map[string]RR) []RR {
|
|
|
|
if m == nil {
|
|
m = make(map[string]RR)
|
|
}
|
|
// Save the keys, so we don't have to call normalizedString twice.
|
|
keys := make([]*string, 0, len(rrs))
|
|
|
|
for _, r := range rrs {
|
|
key := normalizedString(r)
|
|
keys = append(keys, &key)
|
|
if mr, ok := m[key]; ok {
|
|
// Shortest TTL wins.
|
|
rh, mrh := r.Header(), mr.Header()
|
|
if mrh.Ttl > rh.Ttl {
|
|
mrh.Ttl = rh.Ttl
|
|
}
|
|
continue
|
|
}
|
|
|
|
m[key] = r
|
|
}
|
|
// If the length of the result map equals the amount of RRs we got,
|
|
// it means they were all different. We can then just return the original rrset.
|
|
if len(m) == len(rrs) {
|
|
return rrs
|
|
}
|
|
|
|
j := 0
|
|
for i, r := range rrs {
|
|
// If keys[i] lives in the map, we should copy and remove it.
|
|
if _, ok := m[*keys[i]]; ok {
|
|
delete(m, *keys[i])
|
|
rrs[j] = r
|
|
j++
|
|
}
|
|
|
|
if len(m) == 0 {
|
|
break
|
|
}
|
|
}
|
|
|
|
return rrs[:j]
|
|
}
|
|
|
|
// normalizedString returns a normalized string from r. The TTL
|
|
// is removed and the domain name is lowercased. We go from this:
|
|
// DomainName<TAB>TTL<TAB>CLASS<TAB>TYPE<TAB>RDATA to:
|
|
// lowercasename<TAB>CLASS<TAB>TYPE...
|
|
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 := 0; i < len(b) && ttlEnd == 0; i++ {
|
|
switch {
|
|
case b[i] == '\\':
|
|
esc = !esc
|
|
case b[i] == '\t' && !esc:
|
|
if ttlStart == 0 {
|
|
ttlStart = i
|
|
continue
|
|
}
|
|
if ttlEnd == 0 {
|
|
ttlEnd = i
|
|
}
|
|
case b[i] >= 'A' && b[i] <= 'Z' && !esc:
|
|
b[i] += 32
|
|
default:
|
|
esc = false
|
|
}
|
|
}
|
|
|
|
// remove TTL.
|
|
copy(b[ttlStart:], b[ttlEnd:])
|
|
cut := ttlEnd - ttlStart
|
|
return string(b[:len(b)-cut])
|
|
}
|