package blocking import ( "net/netip" "slices" "strings" "testing" ) func pfx(s string) netip.Prefix { return netip.MustParsePrefix(s) } // rankOf is the rank PassBelow's model gives an address. func rankOf(assigns []Assignment, a netip.Addr) int { best, bits := 0, -1 for _, x := range assigns { if x.Prefix.Contains(a) && x.Prefix.Bits() >= bits { if x.Prefix.Bits() > bits || x.Rank > best { best = x.Rank } bits = x.Prefix.Bits() } } return best } func covered(ps []netip.Prefix, a netip.Addr) bool { for _, p := range ps { if p.Contains(a) { return true } } return false } func TestPassBelow(t *testing.T) { assigns := []Assignment{ {pfx("10.10.0.20/32"), -1}, // work laptop: off {pfx("10.10.0.0/24"), 2}, // the kids' network: very strict {pfx("10.10.0.50/32"), 1}, // except one tablet: strict {pfx("2a02:db8::/64"), 1}, // an IPv6 network: strict {pfx("2a02:db8::99/128"), 0}, // one device in it: normal } probe := []string{"10.10.0.20", "10.10.0.21", "10.10.0.50", "10.10.0.51", "10.10.1.1", "192.168.1.1", "0.0.0.1", "255.255.255.255", "2a02:db8::1", "2a02:db8::99", "2a02:db8:0:1::1", "::1", "ffff::1"} for k := 0; k <= 3; k++ { gate := PassBelow(assigns, k) for _, s := range probe { a := netip.MustParseAddr(s) if want := rankOf(assigns, a) < k; covered(gate, a) != want { t.Errorf("k=%d %s: passes=%v, want %v (rank %d)", k, s, !want, want, rankOf(assigns, a)) } } for _, p := range gate { if p.Bits() == 0 { t.Errorf("k=%d: a /0 trigger", k) } // named matches IPv4 clients as ::ffff:a.b.c.d: no IPv6 // prefix may reach them. if p.Addr().Is6() && p.Overlaps(v4Mapped) { t.Errorf("k=%d: %s covers IPv4-mapped addresses", k, p) } } } // Off clients only, before the normal lists. if got := PassBelow(assigns, 0); !slices.Equal(got, []netip.Prefix{pfx("10.10.0.20/32")}) { t.Errorf("off gate = %v", got) } // Nobody above normal: everyone passes the gate of step 1: two IPv4 // halves, and IPv6 around the IPv4-mapped /96 (96 prefixes). if got := PassBelow(nil, 1); len(got) != 2+96 { t.Errorf("empty step-1 gate = %v", got) } // The complement of one IPv4 address is 32 prefixes (plus IPv6's 96). if got := PassBelow([]Assignment{{pfx("10.10.0.50/32"), 1}}, 1); len(got) != 32+96 { t.Errorf("complement of one address: %d prefixes", len(got)) } } func TestClientIPOwner(t *testing.T) { for p, want := range map[string]string{ "10.10.0.37/32": "32.37.0.10.10.rpz-client-ip", "10.10.0.0/24": "24.0.0.10.10.rpz-client-ip", "2a02:db8::/64": "64.zz.db8.2a02.rpz-client-ip", "2a02:db8::1/128": "128.1.zz.db8.2a02.rpz-client-ip", "::/1": "1.zz.rpz-client-ip", "2001:db8:1:2:3:4:5:6/128": "128.6.5.4.3.2.1.db8.2001.rpz-client-ip", } { if got := ClientIPOwner(pfx(p)); got != want { t.Errorf("%s: %s, want %s", p, got, want) } } z := GateZoneFile("t", []netip.Prefix{pfx("10.10.0.20/32")}) if !strings.Contains(z, "32.20.0.10.10.rpz-client-ip\tCNAME\trpz-passthru.\n") { t.Errorf("gate zone:\n%s", z) } }