02e3e0315d
RFC2136 dynamic-DNS updater. Watches a records file (inotify) and new interface addresses and pushes TSIG-signed updates to BIND per zone, sending only the delta. Native miekg/dns (structured per-zone RCODEs), local status API + facter fact, systemd unit, nfpm RPM, Woodpecker CI. Replaces the puppet dns-update shell script; keeps the same records-file and TSIG-key contract.
78 lines
2.5 KiB
Go
78 lines
2.5 KiB
Go
package records
|
|
|
|
import (
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
func TestFQDN(t *testing.T) {
|
|
cases := []struct {
|
|
name, zone, want string
|
|
}{
|
|
{"ausyd1nxvm2070", "main.unkin.net", "ausyd1nxvm2070.main.unkin.net."},
|
|
{"@", "main.unkin.net", "main.unkin.net."},
|
|
{"", "main.unkin.net", "main.unkin.net."},
|
|
// The regression: an already-qualified name must NOT get the zone
|
|
// appended again (that produced the ".." empty label in the shell bug).
|
|
{"au-syd1-pve.main.unkin.net.", "main.unkin.net", "au-syd1-pve.main.unkin.net."},
|
|
{"cobbler.main.unkin.net.", "main.unkin.net", "cobbler.main.unkin.net."},
|
|
{"18", "24.18.198.in-addr.arpa", "18.24.18.198.in-addr.arpa."},
|
|
}
|
|
for _, c := range cases {
|
|
if got := FQDN(c.name, c.zone); got != c.want {
|
|
t.Errorf("FQDN(%q,%q)=%q want %q", c.name, c.zone, got, c.want)
|
|
}
|
|
if strings.Contains(FQDN(c.name, c.zone), "..") {
|
|
t.Errorf("FQDN(%q,%q) produced an empty label", c.name, c.zone)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestRRValidatesName(t *testing.T) {
|
|
// A well-formed FQDN record renders cleanly.
|
|
good := Record{Zone: "main.unkin.net", Name: "au-syd1-pve.main.unkin.net.", Type: "CNAME", TTL: 300, Value: "au-syd1-prod-halb.main.unkin.net."}
|
|
if _, err := good.RR(); err != nil {
|
|
t.Fatalf("good record: unexpected error %v", err)
|
|
}
|
|
// An empty label must be rejected, not silently emitted.
|
|
bad := Record{Zone: "main.unkin.net", Name: "broken..name.", Type: "A", TTL: 300, Value: "198.18.24.18"}
|
|
if _, err := bad.RR(); err == nil {
|
|
t.Fatalf("bad record: expected error for empty label, got nil")
|
|
}
|
|
}
|
|
|
|
func TestParseAndSet(t *testing.T) {
|
|
in := `# comment
|
|
main.unkin.net|ausyd1nxvm2070|A|300|198.18.24.18
|
|
|
|
24.18.198.in-addr.arpa|18|PTR|300|ausyd1nxvm2070.main.unkin.net.
|
|
main.unkin.net|au-syd1-pve.main.unkin.net.|CNAME|300|au-syd1-prod-halb.main.unkin.net.`
|
|
set, err := parse(strings.NewReader(in))
|
|
if err != nil {
|
|
t.Fatalf("parse: %v", err)
|
|
}
|
|
if set.Len() != 3 {
|
|
t.Fatalf("len=%d want 3", set.Len())
|
|
}
|
|
zones := set.Zones()
|
|
if len(zones) != 2 {
|
|
t.Fatalf("zones=%v want 2", zones)
|
|
}
|
|
// zones are fully-qualified and sorted
|
|
if zones[0] != "24.18.198.in-addr.arpa." || zones[1] != "main.unkin.net." {
|
|
t.Errorf("zones=%v", zones)
|
|
}
|
|
}
|
|
|
|
func TestParseReportsBadLine(t *testing.T) {
|
|
in := "main.unkin.net|host|A|notanumber|1.2.3.4\nmain.unkin.net|ok|A|300|1.2.3.4"
|
|
set, err := parse(strings.NewReader(in))
|
|
if err == nil {
|
|
t.Fatal("expected error for bad ttl")
|
|
}
|
|
// the good record is still collected
|
|
if set.Len() != 1 {
|
|
t.Errorf("len=%d want 1 (good record kept)", set.Len())
|
|
}
|
|
}
|