Files
dns-autoresolver/internal/model/record.go
T

107 lines
2.3 KiB
Go

package model
import (
"sort"
"strings"
)
type RecordType string
const (
A RecordType = "A"
AAAA RecordType = "AAAA"
CNAME RecordType = "CNAME"
MX RecordType = "MX"
TXT RecordType = "TXT"
SRV RecordType = "SRV"
NS RecordType = "NS"
SOA RecordType = "SOA"
)
// Managed reports whether the type participates in diff+apply.
// NS and SOA are read-only.
func (t RecordType) Managed() bool {
switch t {
case A, AAAA, CNAME, MX, TXT, SRV:
return true
default:
return false
}
}
// Record is the provider-neutral representation of a DNS RRset.
// For MX the value is "<priority> <target>"; for SRV it is
// "<priority> <weight> <port> <target>". Values is an unordered set.
type Record struct {
Type RecordType
Name string
TTL int
Values []string
}
// Key uniquely identifies an RRset within a zone.
func (r Record) Key() string {
return string(r.Type) + " " + normalizeName(r.Name)
}
func normalizeName(name string) string {
n := strings.ToLower(strings.TrimSpace(name))
if n != "" && !strings.HasSuffix(n, ".") {
n += "."
}
return n
}
// normalizeValue canonicalizes a single RR value for comparison.
func normalizeValue(t RecordType, content string) string {
if t == TXT {
return content // byte-exact — case and whitespace are significant (DKIM/SPF/DMARC)
}
c := strings.Join(strings.Fields(content), " ") // collapse whitespace
switch t {
case MX:
parts := strings.SplitN(c, " ", 2)
if len(parts) == 2 {
return parts[0] + " " + normalizeName(parts[1])
}
return c
case SRV:
f := strings.Fields(c)
if len(f) == 4 {
return f[0] + " " + f[1] + " " + f[2] + " " + normalizeName(f[3])
}
return c
case CNAME, NS:
return normalizeName(c)
default: // A, AAAA, SOA
return strings.ToLower(c)
}
}
// NormalizedValues returns sorted, normalized values.
func (r Record) NormalizedValues() []string {
out := make([]string, len(r.Values))
for i, v := range r.Values {
out[i] = normalizeValue(r.Type, v)
}
sort.Strings(out)
return out
}
// Equal reports whether two records have the same TTL and value set.
func (r Record) Equal(o Record) bool {
if r.TTL != o.TTL {
return false
}
a, b := r.NormalizedValues(), o.NormalizedValues()
if len(a) != len(b) {
return false
}
for i := range a {
if a[i] != b[i] {
return false
}
}
return true
}