1
0
mirror of https://github.com/StackExchange/dnscontrol.git synced 2024-05-11 05:55:12 +00:00

BIND Provider: Pick default TTL automatically for zonefiles (#109)

* BIND Provider: Pick default TTL automatically for zonefiles
This commit is contained in:
Tom Limoncelli
2017-04-30 09:28:35 -04:00
committed by GitHub
parent c13d83887c
commit d92fd5b461
4 changed files with 106 additions and 9 deletions

View File

@@ -238,7 +238,7 @@ func (c *Bind) GetDomainCorrections(dc *models.DomainConfig) ([]*models.Correcti
for _, r := range dc.Records {
zonefilerecords = append(zonefilerecords, r.RR())
}
err = WriteZoneFile(zf, zonefilerecords, dc.Name, models.DefaultTTL)
err = WriteZoneFile(zf, zonefilerecords, dc.Name)
if err != nil {
log.Fatalf("WriteZoneFile error: %v\n", err)

View File

@@ -57,8 +57,38 @@ func (z *zoneGenData) Less(i, j int) bool {
return a.String() < b.String()
}
// mostCommonTtl returns the most common TTL in a set of records. If there is
// a tie, the highest TTL is selected. This makes the results consistent.
// NS records are not included in the analysis because Tom said so.
func mostCommonTtl(records []dns.RR) uint32 {
// Index the TTLs in use:
d := make(map[uint32]int)
for _, r := range records {
if r.Header().Rrtype != dns.TypeNS {
d[r.Header().Ttl]++
}
}
// Find the largest count:
var mc int
for _, value := range d {
if value > mc {
mc = value
}
}
// Find the largest key with that count:
var mk uint32
for key, value := range d {
if value == mc {
if key > mk {
mk = key
}
}
}
return mk
}
// WriteZoneFile writes a beautifully formatted zone file.
func WriteZoneFile(w io.Writer, records []dns.RR, origin string, defaultTtl uint32) error {
func WriteZoneFile(w io.Writer, records []dns.RR, origin string) error {
// This function prioritizes beauty over efficiency.
// * The zone records are sorted by label, grouped by subzones to
// be easy to read and pleasant to the eye.
@@ -66,9 +96,11 @@ func WriteZoneFile(w io.Writer, records []dns.RR, origin string, defaultTtl uint
// * MX records are sorted numericly by preference value.
// * A records are sorted by IP address, not lexicographically.
// * Repeated labels are removed.
// * $TTL is used to eliminate clutter.
// * $TTL is used to eliminate clutter. The most common TTL value is used.
// * "@" is used instead of the apex domain name.
defaultTtl := mostCommonTtl(records)
z := &zoneGenData{
Origin: origin,
DefaultTtl: defaultTtl,

View File

@@ -27,7 +27,7 @@ func parseAndRegen(t *testing.T, buf *bytes.Buffer, expected string) {
}
// Generate it back:
buf2 := &bytes.Buffer{}
WriteZoneFile(buf2, parsed, "bosun.org.", 300)
WriteZoneFile(buf2, parsed, "bosun.org.")
// Compare:
if buf2.String() != expected {
@@ -35,6 +35,49 @@ func parseAndRegen(t *testing.T, buf *bytes.Buffer, expected string) {
}
}
func TestMostCommonTtl(t *testing.T) {
var records []dns.RR
var g, e uint32
r1, _ := dns.NewRR("bosun.org. 100 IN A 1.1.1.1")
r2, _ := dns.NewRR("bosun.org. 200 IN A 1.1.1.1")
r3, _ := dns.NewRR("bosun.org. 300 IN A 1.1.1.1")
r4, _ := dns.NewRR("bosun.org. 400 IN NS foo.bosun.org.")
r5, _ := dns.NewRR("bosun.org. 400 IN NS bar.bosun.org.")
// All records are TTL=100
records = nil
records, e = append(records, r1, r1, r1), 100
g = mostCommonTtl(records)
if e != g {
t.Fatalf("expected %d; got %d\n", e, g)
}
// Mixture of TTLs with an obvious winner.
records = nil
records, e = append(records, r1, r2, r2), 200
g = mostCommonTtl(records)
if e != g {
t.Fatalf("expected %d; got %d\n", e, g)
}
// 3-way tie. Largest TTL should be used.
records = nil
records, e = append(records, r1, r2, r3), 300
g = mostCommonTtl(records)
if e != g {
t.Fatalf("expected %d; got %d\n", e, g)
}
// NS records are ignored.
records = nil
records, e = append(records, r1, r4, r5), 100
g = mostCommonTtl(records)
if e != g {
t.Fatalf("expected %d; got %d\n", e, g)
}
}
// func WriteZoneFile
func TestWriteZoneFileSimple(t *testing.T) {
@@ -42,7 +85,7 @@ func TestWriteZoneFileSimple(t *testing.T) {
r2, _ := dns.NewRR("bosun.org. 300 IN A 192.30.252.154")
r3, _ := dns.NewRR("www.bosun.org. 300 IN CNAME bosun.org.")
buf := &bytes.Buffer{}
WriteZoneFile(buf, []dns.RR{r1, r2, r3}, "bosun.org.", 300)
WriteZoneFile(buf, []dns.RR{r1, r2, r3}, "bosun.org.")
expected := `$TTL 300
@ IN A 192.30.252.153
IN A 192.30.252.154
@@ -57,6 +100,28 @@ www IN CNAME bosun.org.
parseAndRegen(t, buf, expected)
}
func TestWriteZoneFileSimpleTtl(t *testing.T) {
r1, _ := dns.NewRR("bosun.org. 100 IN A 192.30.252.153")
r2, _ := dns.NewRR("bosun.org. 100 IN A 192.30.252.154")
r3, _ := dns.NewRR("bosun.org. 100 IN A 192.30.252.155")
r4, _ := dns.NewRR("www.bosun.org. 300 IN CNAME bosun.org.")
buf := &bytes.Buffer{}
WriteZoneFile(buf, []dns.RR{r1, r2, r3, r4}, "bosun.org.")
expected := `$TTL 100
@ IN A 192.30.252.153
IN A 192.30.252.154
IN A 192.30.252.155
www 300 IN CNAME bosun.org.
`
if buf.String() != expected {
t.Log(buf.String())
t.Log(expected)
t.Fatalf("Zone file does not match.")
}
parseAndRegen(t, buf, expected)
}
func TestWriteZoneFileMx(t *testing.T) {
//exhibits explicit ttls and long name
r1, _ := dns.NewRR(`bosun.org. 300 IN TXT "aaa"`)
@@ -70,7 +135,7 @@ func TestWriteZoneFileMx(t *testing.T) {
r8, _ := dns.NewRR(`_domainkey.bosun.org. 300 IN TXT "vvvv"`)
r9, _ := dns.NewRR(`google._domainkey.bosun.org. 300 IN TXT "\"foo\""`)
buf := &bytes.Buffer{}
WriteZoneFile(buf, []dns.RR{r1, r2, r3, r4, r5, r6, r7, r8, r9}, "bosun.org", 300)
WriteZoneFile(buf, []dns.RR{r1, r2, r3, r4, r5, r6, r7, r8, r9}, "bosun.org")
if buf.String() != testdataZFMX {
t.Log(buf.String())
t.Log(testdataZFMX)
@@ -117,7 +182,7 @@ func TestWriteZoneFileOrder(t *testing.T) {
}
buf := &bytes.Buffer{}
WriteZoneFile(buf, records, "stackoverflow.com.", 300)
WriteZoneFile(buf, records, "stackoverflow.com.")
// Compare
if buf.String() != testdataOrder {
t.Log("Found:")
@@ -138,7 +203,7 @@ func TestWriteZoneFileOrder(t *testing.T) {
}
// Generate
buf := &bytes.Buffer{}
WriteZoneFile(buf, records, "stackoverflow.com.", 300)
WriteZoneFile(buf, records, "stackoverflow.com.")
// Compare
if buf.String() != testdataOrder {
t.Log(buf.String())