diff --git a/plugin/normalize.go b/plugin/normalize.go index b46966b74..7543e42a0 100644 --- a/plugin/normalize.go +++ b/plugin/normalize.go @@ -163,8 +163,12 @@ func SplitHostPort(s string) (hosts []string, port string, err error) { return []string{s}, port, nil } + if s[0] == ':' || (s[0] == '0' && strings.Contains(s, ":")) { + return nil, "", fmt.Errorf("invalid CIDR %s", s) + } + // now check if multiple hosts must be returned. - nets := cidr.Class(n) + nets := cidr.Split(n) hosts = cidr.Reverse(nets) return hosts, port, nil } diff --git a/plugin/normalize_test.go b/plugin/normalize_test.go index 434bfb6f2..7f1fb4773 100644 --- a/plugin/normalize_test.go +++ b/plugin/normalize_test.go @@ -81,6 +81,50 @@ func TestHostNormalizeExact(t *testing.T) { {"example.org.:53", []string{"example.org."}}, {"10.0.0.0/8:53", []string{"10.in-addr.arpa."}}, {"10.0.0.0/15", []string{"0.10.in-addr.arpa.", "1.10.in-addr.arpa."}}, + {"10.9.3.0/18", []string{"0.9.10.in-addr.arpa.", "1.9.10.in-addr.arpa.", "2.9.10.in-addr.arpa."}}, + {"2001:db8::/29", []string{ + "8.b.d.0.1.0.0.2.ip6.arpa.", + "9.b.d.0.1.0.0.2.ip6.arpa.", + "a.b.d.0.1.0.0.2.ip6.arpa.", + "b.b.d.0.1.0.0.2.ip6.arpa.", + "c.b.d.0.1.0.0.2.ip6.arpa.", + "d.b.d.0.1.0.0.2.ip6.arpa.", + "e.b.d.0.1.0.0.2.ip6.arpa.", + "f.b.d.0.1.0.0.2.ip6.arpa.", + }}, + {"2001:db8::/30", []string{ + "8.b.d.0.1.0.0.2.ip6.arpa.", + "9.b.d.0.1.0.0.2.ip6.arpa.", + "a.b.d.0.1.0.0.2.ip6.arpa.", + "b.b.d.0.1.0.0.2.ip6.arpa.", + }}, + {"2001:db8::/115", []string{ + "0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa.", + "1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa.", + }}, + {"2001:db8::/114", []string{ + "0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa.", + "1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa.", + "2.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa.", + "3.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa.", + }}, + {"2001:db8::/113", []string{ + "0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa.", + "1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa.", + "2.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa.", + "3.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa.", + "4.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa.", + "5.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa.", + "6.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa.", + "7.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa.", + }}, + {"2001:db8::/112", []string{ + "0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa.", + }}, + {"2001:db8::/108", []string{ + "0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa.", + }}, + {"::fFFF:B:F/115", nil}, {"dns://example.org", []string{"example.org."}}, } @@ -90,7 +134,7 @@ func TestHostNormalizeExact(t *testing.T) { sort.Strings(expected) for j := range expected { if expected[j] != actual[j] { - t.Errorf("Test %d, expected %v, got %v", i, expected, actual) + t.Errorf("Test %d, expected %v, got %v", i, expected[j], actual[j]) } } } diff --git a/plugin/pkg/cidr/cidr.go b/plugin/pkg/cidr/cidr.go index c1b02d25e..d15648933 100644 --- a/plugin/pkg/cidr/cidr.go +++ b/plugin/pkg/cidr/cidr.go @@ -4,19 +4,28 @@ package cidr import ( "math" "net" + "strings" "github.com/apparentlymart/go-cidr/cidr" "github.com/miekg/dns" ) -// Class return slice of "classful" (/8, /16, /24 or /32 only) CIDR's from the CIDR in net. -func Class(n *net.IPNet) []string { +// Split returns a slice of non-overlapping subnets that in union equal the subnet n, +// and where each subnet falls on a reverse name segment boundary. +// for ipv4 this is any multiple of 8 bits (/8, /16, /24 or /32) +// for ipv6 this is any multiple of 4 bits +func Split(n *net.IPNet) []string { + boundary := 8 + nstr := n.String() + if strings.Contains(nstr,":") { + boundary = 4 + } ones, _ := n.Mask.Size() - if ones%8 == 0 { + if ones%boundary == 0 { return []string{n.String()} } - mask := int(math.Ceil(float64(ones)/8)) * 8 + mask := int(math.Ceil(float64(ones)/float64(boundary))) * boundary networks := nets(n, mask) cidrs := make([]string, len(networks)) for i := range networks { diff --git a/plugin/pkg/cidr/cidr_test.go b/plugin/pkg/cidr/cidr_test.go index 86ea28bee..055a82efd 100644 --- a/plugin/pkg/cidr/cidr_test.go +++ b/plugin/pkg/cidr/cidr_test.go @@ -14,12 +14,13 @@ var tests = []struct { {"10.0.0.0/16", []string{"10.0.0.0/16"}, []string{"0.10.in-addr.arpa."}}, {"192.168.1.1/23", []string{"192.168.0.0/24", "192.168.1.0/24"}, []string{"0.168.192.in-addr.arpa.", "1.168.192.in-addr.arpa."}}, {"10.129.60.0/22", []string{"10.129.60.0/24", "10.129.61.0/24", "10.129.62.0/24", "10.129.63.0/24"}, []string{"60.129.10.in-addr.arpa.", "61.129.10.in-addr.arpa.", "62.129.10.in-addr.arpa.", "63.129.10.in-addr.arpa."}}, + {"2001:db8::/31", []string{"2001:db8::/32", "2001:db9::/32"}, []string{"8.b.d.0.1.0.0.2.ip6.arpa.", "9.b.d.0.1.0.0.2.ip6.arpa."}}, } -func TestClass(t *testing.T) { +func TestSplit(t *testing.T) { for i, tc := range tests { _, n, _ := net.ParseCIDR(tc.in) - nets := Class(n) + nets := Split(n) if len(nets) != len(tc.expected) { t.Errorf("Test %d, expected %d subnets, got %d", i, len(tc.expected), len(nets)) continue @@ -35,7 +36,7 @@ func TestClass(t *testing.T) { func TestReverse(t *testing.T) { for i, tc := range tests { _, n, _ := net.ParseCIDR(tc.in) - nets := Class(n) + nets := Split(n) reverse := Reverse(nets) for j := range reverse { if reverse[j] != tc.zones[j] {