github.com/pmoroney/dnscontrol@v0.2.4-0.20171024134423-fad98f73f44a/providers/bind/prettyzone_test.go (about)

     1  package bind
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"log"
     7  	"math/rand"
     8  	"testing"
     9  
    10  	"github.com/miekg/dns"
    11  	"github.com/miekg/dns/dnsutil"
    12  )
    13  
    14  func parseAndRegen(t *testing.T, buf *bytes.Buffer, expected string) {
    15  	// Take a zonefile, parse it, then generate a zone. We should
    16  	// get back the same string.
    17  	// This is used after any WriteZoneFile test as an extra verification step.
    18  
    19  	// Parse the output:
    20  	var parsed []dns.RR
    21  	for x := range dns.ParseZone(buf, "bosun.org", "bosun.org.zone") {
    22  		if x.Error != nil {
    23  			log.Fatalf("Error in zonefile: %v", x.Error)
    24  		} else {
    25  			parsed = append(parsed, x.RR)
    26  		}
    27  	}
    28  	// Generate it back:
    29  	buf2 := &bytes.Buffer{}
    30  	WriteZoneFile(buf2, parsed, "bosun.org.")
    31  
    32  	// Compare:
    33  	if buf2.String() != expected {
    34  		t.Fatalf("Regenerated zonefile does not match: got=(\n%v\n)\nexpected=(\n%v\n)\n", buf2.String(), expected)
    35  	}
    36  }
    37  
    38  func TestMostCommonTtl(t *testing.T) {
    39  	var records []dns.RR
    40  	var g, e uint32
    41  	r1, _ := dns.NewRR("bosun.org. 100 IN A 1.1.1.1")
    42  	r2, _ := dns.NewRR("bosun.org. 200 IN A 1.1.1.1")
    43  	r3, _ := dns.NewRR("bosun.org. 300 IN A 1.1.1.1")
    44  	r4, _ := dns.NewRR("bosun.org. 400 IN NS foo.bosun.org.")
    45  	r5, _ := dns.NewRR("bosun.org. 400 IN NS bar.bosun.org.")
    46  
    47  	// All records are TTL=100
    48  	records = nil
    49  	records, e = append(records, r1, r1, r1), 100
    50  	g = mostCommonTtl(records)
    51  	if e != g {
    52  		t.Fatalf("expected %d; got %d\n", e, g)
    53  	}
    54  
    55  	// Mixture of TTLs with an obvious winner.
    56  	records = nil
    57  	records, e = append(records, r1, r2, r2), 200
    58  	g = mostCommonTtl(records)
    59  	if e != g {
    60  		t.Fatalf("expected %d; got %d\n", e, g)
    61  	}
    62  
    63  	// 3-way tie. Largest TTL should be used.
    64  	records = nil
    65  	records, e = append(records, r1, r2, r3), 300
    66  	g = mostCommonTtl(records)
    67  	if e != g {
    68  		t.Fatalf("expected %d; got %d\n", e, g)
    69  	}
    70  
    71  	// NS records are ignored.
    72  	records = nil
    73  	records, e = append(records, r1, r4, r5), 100
    74  	g = mostCommonTtl(records)
    75  	if e != g {
    76  		t.Fatalf("expected %d; got %d\n", e, g)
    77  	}
    78  
    79  }
    80  
    81  // func WriteZoneFile
    82  
    83  func TestWriteZoneFileSimple(t *testing.T) {
    84  	r1, _ := dns.NewRR("bosun.org. 300 IN A 192.30.252.153")
    85  	r2, _ := dns.NewRR("bosun.org. 300 IN A 192.30.252.154")
    86  	r3, _ := dns.NewRR("www.bosun.org. 300 IN CNAME bosun.org.")
    87  	buf := &bytes.Buffer{}
    88  	WriteZoneFile(buf, []dns.RR{r1, r2, r3}, "bosun.org.")
    89  	expected := `$TTL 300
    90  @                IN A     192.30.252.153
    91                   IN A     192.30.252.154
    92  www              IN CNAME bosun.org.
    93  `
    94  	if buf.String() != expected {
    95  		t.Log(buf.String())
    96  		t.Log(expected)
    97  		t.Fatalf("Zone file does not match.")
    98  	}
    99  
   100  	parseAndRegen(t, buf, expected)
   101  }
   102  
   103  func TestWriteZoneFileSimpleTtl(t *testing.T) {
   104  	r1, _ := dns.NewRR("bosun.org. 100 IN A 192.30.252.153")
   105  	r2, _ := dns.NewRR("bosun.org. 100 IN A 192.30.252.154")
   106  	r3, _ := dns.NewRR("bosun.org. 100 IN A 192.30.252.155")
   107  	r4, _ := dns.NewRR("www.bosun.org. 300 IN CNAME bosun.org.")
   108  	buf := &bytes.Buffer{}
   109  	WriteZoneFile(buf, []dns.RR{r1, r2, r3, r4}, "bosun.org.")
   110  	expected := `$TTL 100
   111  @                IN A     192.30.252.153
   112                   IN A     192.30.252.154
   113                   IN A     192.30.252.155
   114  www        300   IN CNAME bosun.org.
   115  `
   116  	if buf.String() != expected {
   117  		t.Log(buf.String())
   118  		t.Log(expected)
   119  		t.Fatalf("Zone file does not match.")
   120  	}
   121  
   122  	parseAndRegen(t, buf, expected)
   123  }
   124  
   125  func TestWriteZoneFileMx(t *testing.T) {
   126  	//exhibits explicit ttls and long name
   127  	r1, _ := dns.NewRR(`bosun.org. 300 IN TXT "aaa"`)
   128  	r2, _ := dns.NewRR(`bosun.org. 300 IN TXT "bbb"`)
   129  	r2.(*dns.TXT).Txt[0] = `b"bb`
   130  	r3, _ := dns.NewRR("bosun.org. 300 IN MX 1 ASPMX.L.GOOGLE.COM.")
   131  	r4, _ := dns.NewRR("bosun.org. 300 IN MX 5 ALT1.ASPMX.L.GOOGLE.COM.")
   132  	r5, _ := dns.NewRR("bosun.org. 300 IN MX 10 ASPMX3.GOOGLEMAIL.COM.")
   133  	r6, _ := dns.NewRR("bosun.org. 300 IN A 198.252.206.16")
   134  	r7, _ := dns.NewRR("*.bosun.org. 600 IN A 198.252.206.16")
   135  	r8, _ := dns.NewRR(`_domainkey.bosun.org. 300 IN TXT "vvvv"`)
   136  	r9, _ := dns.NewRR(`google._domainkey.bosun.org. 300 IN TXT "\"foo\""`)
   137  	buf := &bytes.Buffer{}
   138  	WriteZoneFile(buf, []dns.RR{r1, r2, r3, r4, r5, r6, r7, r8, r9}, "bosun.org")
   139  	if buf.String() != testdataZFMX {
   140  		t.Log(buf.String())
   141  		t.Log(testdataZFMX)
   142  		t.Fatalf("Zone file does not match.")
   143  	}
   144  	parseAndRegen(t, buf, testdataZFMX)
   145  }
   146  
   147  var testdataZFMX = `$TTL 300
   148  @                IN A     198.252.206.16
   149                   IN MX    1 ASPMX.L.GOOGLE.COM.
   150                   IN MX    5 ALT1.ASPMX.L.GOOGLE.COM.
   151                   IN MX    10 ASPMX3.GOOGLEMAIL.COM.
   152                   IN TXT   "aaa"
   153                   IN TXT   "b\"bb"
   154  *          600   IN A     198.252.206.16
   155  _domainkey       IN TXT   "vvvv"
   156  google._domainkey IN TXT  "\"foo\""
   157  `
   158  
   159  func TestWriteZoneFileSrv(t *testing.T) {
   160  	//exhibits explicit ttls and long name
   161  	r1, _ := dns.NewRR(`bosun.org. 300 IN SRV 10 10 9999 foo.com.`)
   162  	r2, _ := dns.NewRR(`bosun.org. 300 IN SRV 10 20 5050 foo.com.`)
   163  	r3, _ := dns.NewRR(`bosun.org. 300 IN SRV 10 10 5050 foo.com.`)
   164  	r4, _ := dns.NewRR(`bosun.org. 300 IN SRV 20 10 5050 foo.com.`)
   165  	r5, _ := dns.NewRR(`bosun.org. 300 IN SRV 10 10 5050 foo.com.`)
   166  	buf := &bytes.Buffer{}
   167  	WriteZoneFile(buf, []dns.RR{r1, r2, r3, r4, r5}, "bosun.org")
   168  	if buf.String() != testdataZFSRV {
   169  		t.Log(buf.String())
   170  		t.Log(testdataZFSRV)
   171  		t.Fatalf("Zone file does not match.")
   172  	}
   173  	parseAndRegen(t, buf, testdataZFSRV)
   174  }
   175  
   176  var testdataZFSRV = `$TTL 300
   177  @                IN SRV   10 10 5050 foo.com.
   178                   IN SRV   10 10 5050 foo.com.
   179                   IN SRV   10 20 5050 foo.com.
   180                   IN SRV   20 10 5050 foo.com.
   181                   IN SRV   10 10 9999 foo.com.
   182  `
   183  
   184  func TestWriteZoneFilePtr(t *testing.T) {
   185  	//exhibits explicit ttls and long name
   186  	r1, _ := dns.NewRR(`bosun.org. 300 IN PTR chell.bosun.org`)
   187  	r2, _ := dns.NewRR(`bosun.org. 300 IN PTR barney.bosun.org.`)
   188  	r3, _ := dns.NewRR(`bosun.org. 300 IN PTR alex.bosun.org.`)
   189  	buf := &bytes.Buffer{}
   190  	WriteZoneFile(buf, []dns.RR{r1, r2, r3}, "bosun.org")
   191  	if buf.String() != testdataZFPTR {
   192  		t.Log(buf.String())
   193  		t.Log(testdataZFPTR)
   194  		t.Fatalf("Zone file does not match.")
   195  	}
   196  	parseAndRegen(t, buf, testdataZFPTR)
   197  }
   198  
   199  var testdataZFPTR = `$TTL 300
   200  @                IN PTR   alex.bosun.org.
   201                   IN PTR   barney.bosun.org.
   202                   IN PTR   chell.bosun.org.
   203  `
   204  
   205  func TestWriteZoneFileCaa(t *testing.T) {
   206  	//exhibits explicit ttls and long name
   207  	r1, _ := dns.NewRR(`bosun.org. 300 IN CAA 0 issuewild ";"`)
   208  	r2, _ := dns.NewRR(`bosun.org. 300 IN CAA 0 issue "letsencrypt.org"`)
   209  	r3, _ := dns.NewRR(`bosun.org. 300 IN CAA 1 iodef "http://example.com"`)
   210  	r4, _ := dns.NewRR(`bosun.org. 300 IN CAA 0 iodef "https://example.com"`)
   211  	r5, _ := dns.NewRR(`bosun.org. 300 IN CAA 0 iodef "https://example.net"`)
   212  	r6, _ := dns.NewRR(`bosun.org. 300 IN CAA 1 iodef "mailto:example.com"`)
   213  	buf := &bytes.Buffer{}
   214  	WriteZoneFile(buf, []dns.RR{r1, r2, r3, r4, r5, r6}, "bosun.org")
   215  	if buf.String() != testdataZFCAA {
   216  		t.Log(buf.String())
   217  		t.Log(testdataZFCAA)
   218  		t.Fatalf("Zone file does not match.")
   219  	}
   220  	parseAndRegen(t, buf, testdataZFCAA)
   221  }
   222  
   223  var testdataZFCAA = `$TTL 300
   224  @                IN CAA   1 iodef "http://example.com"
   225                   IN CAA   1 iodef "mailto:example.com"
   226                   IN CAA   0 iodef "https://example.com"
   227                   IN CAA   0 iodef "https://example.net"
   228                   IN CAA   0 issue "letsencrypt.org"
   229                   IN CAA   0 issuewild ";"
   230  `
   231  
   232  func TestWriteZoneFileOrder(t *testing.T) {
   233  	var records []dns.RR
   234  	for i, td := range []string{
   235  		"@",
   236  		"@",
   237  		"@",
   238  		"stackoverflow.com.",
   239  		"*",
   240  		"foo",
   241  		"bar.foo",
   242  		"hip.foo",
   243  		"mup",
   244  		"a.mup",
   245  		"bzt.mup",
   246  		"aaa.bzt.mup",
   247  		"zzz.bzt.mup",
   248  		"nnn.mup",
   249  		"zt.mup",
   250  		"zap",
   251  	} {
   252  		name := dnsutil.AddOrigin(td, "stackoverflow.com.")
   253  		r, _ := dns.NewRR(fmt.Sprintf("%s 300 IN A 1.2.3.%d", name, i))
   254  		records = append(records, r)
   255  	}
   256  
   257  	buf := &bytes.Buffer{}
   258  	WriteZoneFile(buf, records, "stackoverflow.com.")
   259  	// Compare
   260  	if buf.String() != testdataOrder {
   261  		t.Log("Found:")
   262  		t.Log(buf.String())
   263  		t.Log("Expected:")
   264  		t.Log(testdataOrder)
   265  		t.Fatalf("Zone file does not match.")
   266  	}
   267  	parseAndRegen(t, buf, testdataOrder)
   268  
   269  	// Now shuffle the list many times and make sure it still works:
   270  	for iteration := 5; iteration > 0; iteration-- {
   271  		// Randomize the list:
   272  		perm := rand.Perm(len(records))
   273  		for i, v := range perm {
   274  			records[i], records[v] = records[v], records[i]
   275  			//fmt.Println(i, v)
   276  		}
   277  		// Generate
   278  		buf := &bytes.Buffer{}
   279  		WriteZoneFile(buf, records, "stackoverflow.com.")
   280  		// Compare
   281  		if buf.String() != testdataOrder {
   282  			t.Log(buf.String())
   283  			t.Log(testdataOrder)
   284  			t.Fatalf("Zone file does not match.")
   285  		}
   286  		parseAndRegen(t, buf, testdataOrder)
   287  	}
   288  }
   289  
   290  var testdataOrder = `$TTL 300
   291  @                IN A     1.2.3.0
   292                   IN A     1.2.3.1
   293                   IN A     1.2.3.2
   294                   IN A     1.2.3.3
   295  *                IN A     1.2.3.4
   296  foo              IN A     1.2.3.5
   297  bar.foo          IN A     1.2.3.6
   298  hip.foo          IN A     1.2.3.7
   299  mup              IN A     1.2.3.8
   300  a.mup            IN A     1.2.3.9
   301  bzt.mup          IN A     1.2.3.10
   302  aaa.bzt.mup      IN A     1.2.3.11
   303  zzz.bzt.mup      IN A     1.2.3.12
   304  nnn.mup          IN A     1.2.3.13
   305  zt.mup           IN A     1.2.3.14
   306  zap              IN A     1.2.3.15
   307  `
   308  
   309  // func formatLine
   310  
   311  func TestFormatLine(t *testing.T) {
   312  	tests := []struct {
   313  		lengths  []int
   314  		fields   []string
   315  		expected string
   316  	}{
   317  		{[]int{2, 2, 0}, []string{"a", "b", "c"}, "a  b  c"},
   318  		{[]int{2, 2, 0}, []string{"aaaaa", "b", "c"}, "aaaaa b c"},
   319  	}
   320  	for _, ts := range tests {
   321  		actual := formatLine(ts.lengths, ts.fields)
   322  		if actual != ts.expected {
   323  			t.Errorf("\"%s\" != \"%s\"", actual, ts.expected)
   324  		}
   325  	}
   326  }
   327  
   328  // func zoneLabelLess
   329  
   330  func TestZoneLabelLess(t *testing.T) {
   331  	/*
   332  			The zone should sort in prefix traversal order:
   333  
   334  		  @
   335  		  *
   336  		  foo
   337  		  bar.foo
   338  		  hip.foo
   339  		  mup
   340  		  a.mup
   341  		  bzt.mup
   342  		  *.bzt.mup
   343  		  1.bzt.mup
   344  		  2.bzt.mup
   345  		  10.bzt.mup
   346  		  aaa.bzt.mup
   347  		  zzz.bzt.mup
   348  		  nnn.mup
   349  		  zt.mup
   350  		  zap
   351  	*/
   352  
   353  	var tests = []struct {
   354  		e1, e2   string
   355  		expected bool
   356  	}{
   357  		{"@", "@", false},
   358  		{"@", "*", true},
   359  		{"@", "b", true},
   360  		{"*", "@", false},
   361  		{"*", "*", false},
   362  		{"*", "b", true},
   363  		{"foo", "foo", false},
   364  		{"foo", "bar", false},
   365  		{"bar", "foo", true},
   366  		{"a.mup", "mup", false},
   367  		{"mup", "a.mup", true},
   368  		{"a.mup", "a.mup", false},
   369  		{"a.mup", "bzt.mup", true},
   370  		{"a.mup", "aa.mup", true},
   371  		{"zt.mup", "aaa.bzt.mup", false},
   372  		{"aaa.bzt.mup", "mup", false},
   373  		{"*.bzt.mup", "aaa.bzt.mup", true},
   374  		{"1.bzt.mup", "aaa.bzt.mup", true},
   375  		{"1.bzt.mup", "2.bzt.mup", true},
   376  		{"10.bzt.mup", "2.bzt.mup", false},
   377  		{"nnn.mup", "aaa.bzt.mup", false},
   378  		{`www\.miek.nl`, `www.miek.nl`, false},
   379  	}
   380  
   381  	for _, test := range tests {
   382  		actual := zoneLabelLess(test.e1, test.e2)
   383  		if test.expected != actual {
   384  			t.Errorf("%v: expected (%v) got (%v)\n", test.e1, test.e2, actual)
   385  		}
   386  		actual = zoneLabelLess(test.e2, test.e1)
   387  		// The reverse should work too:
   388  		var expected bool
   389  		if test.e1 == test.e2 {
   390  			expected = false
   391  		} else {
   392  			expected = !test.expected
   393  		}
   394  		if expected != actual {
   395  			t.Errorf("%v: expected (%v) got (%v)\n", test.e1, test.e2, actual)
   396  		}
   397  	}
   398  }
   399  
   400  func TestZoneRrtypeLess(t *testing.T) {
   401  	/*
   402  		In zonefiles we want to list SOAs, then NSs, then all others.
   403  	*/
   404  
   405  	var tests = []struct {
   406  		e1, e2   uint16
   407  		expected bool
   408  	}{
   409  		{dns.TypeSOA, dns.TypeSOA, false},
   410  		{dns.TypeSOA, dns.TypeA, true},
   411  		{dns.TypeSOA, dns.TypeTXT, true},
   412  		{dns.TypeSOA, dns.TypeNS, true},
   413  		{dns.TypeNS, dns.TypeSOA, false},
   414  		{dns.TypeNS, dns.TypeA, true},
   415  		{dns.TypeNS, dns.TypeTXT, true},
   416  		{dns.TypeNS, dns.TypeNS, false},
   417  		{dns.TypeA, dns.TypeSOA, false},
   418  		{dns.TypeA, dns.TypeA, false},
   419  		{dns.TypeA, dns.TypeTXT, true},
   420  		{dns.TypeA, dns.TypeNS, false},
   421  		{dns.TypeMX, dns.TypeSOA, false},
   422  		{dns.TypeMX, dns.TypeA, false},
   423  		{dns.TypeMX, dns.TypeTXT, true},
   424  		{dns.TypeMX, dns.TypeNS, false},
   425  	}
   426  
   427  	for _, test := range tests {
   428  		actual := zoneRrtypeLess(test.e1, test.e2)
   429  		if test.expected != actual {
   430  			t.Errorf("%v: expected (%v) got (%v)\n", test.e1, test.e2, actual)
   431  		}
   432  		actual = zoneRrtypeLess(test.e2, test.e1)
   433  		// The reverse should work too:
   434  		var expected bool
   435  		if test.e1 == test.e2 {
   436  			expected = false
   437  		} else {
   438  			expected = !test.expected
   439  		}
   440  		if expected != actual {
   441  			t.Errorf("%v: expected (%v) got (%v)\n", test.e1, test.e2, actual)
   442  		}
   443  	}
   444  }