Pull request 1966: 6050 upd urlfilter

Merge in DNS/adguard-home from upd-urlfilter to master

Updates #6050.

Squashed commit of the following:

commit 80337ab02d616e25fa455e46c9535c088b5c5ea5
Merge: fb2cfd1a5 31f7aaecc
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Wed Aug 23 16:50:49 2023 +0300

    Merge branch 'master' into upd-urlfilter

commit fb2cfd1a5c94d92030fc8832615764f100d010e5
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Wed Aug 23 16:22:43 2023 +0300

    dnsforward: imp code, docs

commit 2900333bb85d4e064db9de27bd5bfe7c3ef00747
Merge: 977ed35e4 2bfc9fcb1
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Tue Aug 22 18:06:05 2023 +0300

    Merge branch 'master' into upd-urlfilter

commit 977ed35e4ed377f1031721d58e0fcb58de1e74ac
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Tue Aug 22 17:06:30 2023 +0300

    all: log changes

commit 1228a0770485799bf50bbe68005dbb0ba9a96a9c
Merge: 78305eb2e 4b4036fa6
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Tue Aug 22 16:51:42 2023 +0300

    Merge branch 'master' into upd-urlfilter

commit 78305eb2ebc3854dd11ce35d6b4c7eecccd7cc78
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Tue Aug 22 15:55:05 2023 +0300

    all: upd urlfilter

commit 63a29e18d5034e5f9433121ff7e7c45aebfa1f0f
Merge: 748c53430 762e5be97
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Mon Aug 21 20:12:49 2023 +0300

    Merge branch 'master' into upd-urlfilter

commit 748c5343020b0c6d4d4f16eb3d30b875c0a94e0f
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Mon Aug 21 20:07:44 2023 +0300

    all: imp code, docs

commit 91975140f3305a6793e07142f7c9a75120a4ce8c
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Thu Aug 17 16:16:19 2023 +0300

    all: upd urlfilter
This commit is contained in:
Eugene Burkov
2023-08-23 16:58:24 +03:00
parent 31f7aaecc3
commit 28cfde9212
28 changed files with 335 additions and 314 deletions

View File

@@ -10,6 +10,7 @@ import (
"github.com/AdguardTeam/AdGuardHome/internal/aghnet"
"github.com/AdguardTeam/AdGuardHome/internal/aghtest"
"github.com/AdguardTeam/golibs/netutil"
"github.com/AdguardTeam/golibs/testutil"
"github.com/AdguardTeam/urlfilter/rules"
"github.com/miekg/dns"
@@ -22,13 +23,13 @@ func TestDNSFilter_CheckHostRules_dnsrewrite(t *testing.T) {
|cname^$dnsrewrite=new-cname
|a-record^$dnsrewrite=127.0.0.1
|aaaa-record^$dnsrewrite=::1
|txt-record^$dnsrewrite=NOERROR;TXT;hello-world
|refused^$dnsrewrite=REFUSED
|mapped^$dnsrewrite=NOERROR;AAAA;::ffff:127.0.0.1
|a-records^$dnsrewrite=127.0.0.1
|a-records^$dnsrewrite=127.0.0.2
@@ -61,10 +62,11 @@ func TestDNSFilter_CheckHostRules_dnsrewrite(t *testing.T) {
FilteringEnabled: true,
}
ipv4p1 := net.IPv4(127, 0, 0, 1)
ipv4p2 := net.IPv4(127, 0, 0, 2)
ipv6p1 := net.IP{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}
ipv6p2 := net.IP{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2}
ipv4p1 := netutil.IPv4Localhost()
ipv4p2 := ipv4p1.Next()
ipv6p1 := netutil.IPv6Localhost()
ipv6p2 := ipv6p1.Next()
mapped := netip.AddrFrom16(ipv4p1.As16())
testCasesA := []struct {
name string
@@ -111,6 +113,11 @@ func TestDNSFilter_CheckHostRules_dnsrewrite(t *testing.T) {
want: []any{ipv4p1},
rcode: dns.RcodeSuccess,
dtyp: dns.TypeA,
}, {
name: "mapped",
want: []any{mapped},
rcode: dns.RcodeSuccess,
dtyp: dns.TypeAAAA,
}}
for _, tc := range testCasesA {

View File

@@ -54,7 +54,7 @@ type ServiceEntry struct {
// Settings are custom filtering settings for a client.
type Settings struct {
ClientName string
ClientIP net.IP
ClientIP netip.Addr
ClientTags []string
ServicesRules []ServiceEntry
@@ -404,7 +404,7 @@ type ResultRule struct {
Text string `json:",omitempty"`
// IP is the host IP. It is nil unless the rule uses the
// /etc/hosts syntax or the reason is FilteredSafeSearch.
IP net.IP `json:",omitempty"`
IP netip.Addr `json:",omitempty"`
// FilterListID is the ID of the rule's filter list.
FilterListID int64 `json:",omitempty"`
}
@@ -430,7 +430,7 @@ type Result struct {
// IPList is the lookup rewrite result. It is empty unless Reason is set to
// Rewritten.
IPList []net.IP `json:",omitempty"`
IPList []netip.Addr `json:",omitempty"`
// Rules are applied rules. If Rules are not empty, each rule is not nil.
Rules []*ResultRule `json:",omitempty"`
@@ -787,22 +787,27 @@ func (d *DNSFilter) matchHostProcessDNSResult(
return makeResult([]rules.Rule{dnsres.NetworkRule}, reason)
}
if qtype == dns.TypeA && dnsres.HostRulesV4 != nil {
res = makeResult(hostRulesToRules(dnsres.HostRulesV4), FilteredBlockList)
for i, hr := range dnsres.HostRulesV4 {
res.Rules[i].IP = hr.IP.To4()
switch qtype {
case dns.TypeA:
if dnsres.HostRulesV4 != nil {
res = makeResult(hostRulesToRules(dnsres.HostRulesV4), FilteredBlockList)
for i, hr := range dnsres.HostRulesV4 {
res.Rules[i].IP = hr.IP
}
return res
}
case dns.TypeAAAA:
if dnsres.HostRulesV6 != nil {
res = makeResult(hostRulesToRules(dnsres.HostRulesV6), FilteredBlockList)
for i, hr := range dnsres.HostRulesV6 {
res.Rules[i].IP = hr.IP
}
return res
}
if qtype == dns.TypeAAAA && dnsres.HostRulesV6 != nil {
res = makeResult(hostRulesToRules(dnsres.HostRulesV6), FilteredBlockList)
for i, hr := range dnsres.HostRulesV6 {
res.Rules[i].IP = hr.IP.To16()
return res
}
return res
default:
// Go on.
}
return hostResultForOtherQType(dnsres)
@@ -837,7 +842,7 @@ func (d *DNSFilter) matchHost(
Hostname: host,
SortedClientTags: setts.ClientTags,
// TODO(e.burkov): Wait for urlfilter update to pass net.IP.
ClientIP: setts.ClientIP.String(),
ClientIP: setts.ClientIP,
ClientName: setts.ClientName,
DNSType: rrtype,
}

View File

@@ -3,12 +3,13 @@ package filtering
import (
"bytes"
"fmt"
"net"
"net/netip"
"testing"
"github.com/AdguardTeam/AdGuardHome/internal/aghtest"
"github.com/AdguardTeam/AdGuardHome/internal/filtering/hashprefix"
"github.com/AdguardTeam/golibs/log"
"github.com/AdguardTeam/golibs/netutil"
"github.com/AdguardTeam/golibs/testutil"
"github.com/AdguardTeam/urlfilter/rules"
"github.com/miekg/dns"
@@ -149,8 +150,8 @@ func TestDNSFilter_CheckHost_hostRules(t *testing.T) {
require.Len(t, res.Rules, 2)
assert.Equal(t, res.Rules[0].IP, net.IP{0, 0, 0, 1})
assert.Equal(t, res.Rules[1].IP, net.IP{0, 0, 0, 2})
assert.Equal(t, res.Rules[0].IP, netip.AddrFrom4([4]byte{0, 0, 0, 1}))
assert.Equal(t, res.Rules[1].IP, netip.AddrFrom4([4]byte{0, 0, 0, 2}))
// One IPv6 address.
res, err = d.CheckHost("host2", dns.TypeAAAA, setts)
@@ -160,7 +161,7 @@ func TestDNSFilter_CheckHost_hostRules(t *testing.T) {
require.Len(t, res.Rules, 1)
assert.Equal(t, res.Rules[0].IP, net.IPv6loopback)
assert.Equal(t, res.Rules[0].IP, netutil.IPv6Localhost())
}
// Safe Browsing.

View File

@@ -3,8 +3,8 @@ package filtering
import (
"encoding/json"
"fmt"
"net"
"net/http"
"net/netip"
"net/url"
"os"
"path/filepath"
@@ -404,8 +404,8 @@ type checkHostResp struct {
SvcName string `json:"service_name"`
// for Rewrite:
CanonName string `json:"cname"` // CNAME value
IPList []net.IP `json:"ip_addrs"` // list of IP addresses
CanonName string `json:"cname"` // CNAME value
IPList []netip.Addr `json:"ip_addrs"` // list of IP addresses
// FilterID is the ID of the rule's filter list.
//

View File

@@ -1,9 +1,10 @@
package rewrite
import (
"net"
"net/netip"
"testing"
"github.com/AdguardTeam/golibs/netutil"
"github.com/AdguardTeam/urlfilter"
"github.com/AdguardTeam/urlfilter/rules"
"github.com/miekg/dns"
@@ -45,33 +46,43 @@ func TestDefaultStorage_CRUD(t *testing.T) {
}
func TestDefaultStorage_MatchRequest(t *testing.T) {
var (
addr1v4 = netip.AddrFrom4([4]byte{1, 2, 3, 4})
addr2v4 = netip.AddrFrom4([4]byte{1, 2, 3, 5})
addr3v4 = netip.AddrFrom4([4]byte{1, 2, 3, 6})
addr4v4 = netip.AddrFrom4([4]byte{1, 2, 3, 7})
addr1v6 = netip.MustParseAddr("1:2:3::4")
addr2v6 = netip.MustParseAddr("1234::5678")
)
items := []*Item{{
// This one and below are about CNAME, A and AAAA.
Domain: "somecname",
Answer: "somehost.com",
}, {
Domain: "somehost.com",
Answer: "0.0.0.0",
Answer: netip.IPv4Unspecified().String(),
}, {
Domain: "host.com",
Answer: "1.2.3.4",
Answer: addr1v4.String(),
}, {
Domain: "host.com",
Answer: "1.2.3.5",
Answer: addr2v4.String(),
}, {
Domain: "host.com",
Answer: "1:2:3::4",
Answer: addr1v6.String(),
}, {
Domain: "www.host.com",
Answer: "host.com",
}, {
// This one is a wildcard.
Domain: "*.host.com",
Answer: "1.2.3.5",
Answer: addr2v4.String(),
}, {
// This one and below are about wildcard overriding.
Domain: "a.host.com",
Answer: "1.2.3.4",
Answer: addr1v4.String(),
}, {
// This one is about CNAME and wildcard interacting.
Domain: "*.host2.com",
@@ -89,13 +100,13 @@ func TestDefaultStorage_MatchRequest(t *testing.T) {
Answer: "x.host.com",
}, {
Domain: "*.hostboth.com",
Answer: "1.2.3.6",
Answer: addr3v4.String(),
}, {
Domain: "*.hostboth.com",
Answer: "1234::5678",
Answer: addr2v6.String(),
}, {
Domain: "BIGHOST.COM",
Answer: "1.2.3.7",
Answer: addr4v4.String(),
}, {
Domain: "*.issue4016.com",
Answer: "sub.issue4016.com",
@@ -123,12 +134,12 @@ func TestDefaultStorage_MatchRequest(t *testing.T) {
name: "rewritten_a",
host: "www.host.com",
wantDNSRewrites: []*rules.DNSRewrite{{
Value: net.IP{1, 2, 3, 4}.To16(),
Value: addr1v4,
NewCNAME: "",
RCode: dns.RcodeSuccess,
RRType: dns.TypeA,
}, {
Value: net.IP{1, 2, 3, 5}.To16(),
Value: addr2v4,
NewCNAME: "",
RCode: dns.RcodeSuccess,
RRType: dns.TypeA,
@@ -138,7 +149,7 @@ func TestDefaultStorage_MatchRequest(t *testing.T) {
name: "rewritten_aaaa",
host: "www.host.com",
wantDNSRewrites: []*rules.DNSRewrite{{
Value: net.ParseIP("1:2:3::4"),
Value: addr1v6,
NewCNAME: "",
RCode: dns.RcodeSuccess,
RRType: dns.TypeAAAA,
@@ -148,7 +159,7 @@ func TestDefaultStorage_MatchRequest(t *testing.T) {
name: "wildcard_match",
host: "abc.host.com",
wantDNSRewrites: []*rules.DNSRewrite{{
Value: net.IP{1, 2, 3, 5}.To16(),
Value: addr2v4,
NewCNAME: "",
RCode: dns.RcodeSuccess,
RRType: dns.TypeA,
@@ -169,12 +180,12 @@ func TestDefaultStorage_MatchRequest(t *testing.T) {
name: "wildcard_cname_interaction",
host: "www.host2.com",
wantDNSRewrites: []*rules.DNSRewrite{{
Value: net.IP{1, 2, 3, 4}.To16(),
Value: addr1v4,
NewCNAME: "",
RCode: dns.RcodeSuccess,
RRType: dns.TypeA,
}, {
Value: net.IP{1, 2, 3, 5}.To16(),
Value: addr2v4,
NewCNAME: "",
RCode: dns.RcodeSuccess,
RRType: dns.TypeA,
@@ -184,7 +195,7 @@ func TestDefaultStorage_MatchRequest(t *testing.T) {
name: "two_cnames",
host: "b.host.com",
wantDNSRewrites: []*rules.DNSRewrite{{
Value: net.IP{0, 0, 0, 0}.To16(),
Value: netip.IPv4Unspecified(),
NewCNAME: "",
RCode: dns.RcodeSuccess,
RRType: dns.TypeA,
@@ -194,7 +205,7 @@ func TestDefaultStorage_MatchRequest(t *testing.T) {
name: "two_cnames_and_wildcard",
host: "b.host3.com",
wantDNSRewrites: []*rules.DNSRewrite{{
Value: net.IP{1, 2, 3, 5}.To16(),
Value: addr2v4,
NewCNAME: "",
RCode: dns.RcodeSuccess,
RRType: dns.TypeA,
@@ -204,7 +215,7 @@ func TestDefaultStorage_MatchRequest(t *testing.T) {
name: "issue3343",
host: "www.hostboth.com",
wantDNSRewrites: []*rules.DNSRewrite{{
Value: net.ParseIP("1234::5678"),
Value: addr2v6,
NewCNAME: "",
RCode: dns.RcodeSuccess,
RRType: dns.TypeAAAA,
@@ -214,7 +225,7 @@ func TestDefaultStorage_MatchRequest(t *testing.T) {
name: "issue3351",
host: "bighost.com",
wantDNSRewrites: []*rules.DNSRewrite{{
Value: net.IP{1, 2, 3, 7}.To16(),
Value: addr4v4,
NewCNAME: "",
RCode: dns.RcodeSuccess,
RRType: dns.TypeA,
@@ -255,16 +266,22 @@ func TestDefaultStorage_MatchRequest(t *testing.T) {
}
func TestDefaultStorage_MatchRequest_Levels(t *testing.T) {
var (
addr1 = netip.AddrFrom4([4]byte{1, 1, 1, 1})
addr2 = netip.AddrFrom4([4]byte{2, 2, 2, 2})
addr3 = netip.AddrFrom4([4]byte{3, 3, 3, 3})
)
// Exact host, wildcard L2, wildcard L3.
items := []*Item{{
Domain: "host.com",
Answer: "1.1.1.1",
Answer: addr1.String(),
}, {
Domain: "*.host.com",
Answer: "2.2.2.2",
Answer: addr2.String(),
}, {
Domain: "*.sub.host.com",
Answer: "3.3.3.3",
Answer: addr3.String(),
}}
s, err := NewDefaultStorage(-1, items)
@@ -279,7 +296,7 @@ func TestDefaultStorage_MatchRequest_Levels(t *testing.T) {
name: "exact_match",
host: "host.com",
wantDNSRewrites: []*rules.DNSRewrite{{
Value: net.IP{1, 1, 1, 1}.To16(),
Value: addr1,
NewCNAME: "",
RCode: dns.RcodeSuccess,
RRType: dns.TypeA,
@@ -289,7 +306,7 @@ func TestDefaultStorage_MatchRequest_Levels(t *testing.T) {
name: "l2_match",
host: "sub.host.com",
wantDNSRewrites: []*rules.DNSRewrite{{
Value: net.IP{2, 2, 2, 2}.To16(),
Value: addr2,
NewCNAME: "",
RCode: dns.RcodeSuccess,
RRType: dns.TypeA,
@@ -300,7 +317,7 @@ func TestDefaultStorage_MatchRequest_Levels(t *testing.T) {
// name: "l3_match",
// host: "my.sub.host.com",
// wantDNSRewrites: []*rules.DNSRewrite{{
// Value: net.IP{3, 3, 3, 3}.To16(),
// Value: addr3,
// NewCNAME: "",
// RCode: dns.RcodeSuccess,
// RRType: dns.TypeA,
@@ -321,10 +338,12 @@ func TestDefaultStorage_MatchRequest_Levels(t *testing.T) {
}
func TestDefaultStorage_MatchRequest_ExceptionCNAME(t *testing.T) {
addr := netip.AddrFrom4([4]byte{2, 2, 2, 2})
// Wildcard and exception for a sub-domain.
items := []*Item{{
Domain: "*.host.com",
Answer: "2.2.2.2",
Answer: addr.String(),
}, {
Domain: "sub.host.com",
Answer: "sub.host.com",
@@ -345,7 +364,7 @@ func TestDefaultStorage_MatchRequest_ExceptionCNAME(t *testing.T) {
name: "match_subdomain",
host: "my.host.com",
wantDNSRewrites: []*rules.DNSRewrite{{
Value: net.IP{2, 2, 2, 2}.To16(),
Value: addr,
NewCNAME: "",
RCode: dns.RcodeSuccess,
RRType: dns.TypeA,
@@ -377,16 +396,18 @@ func TestDefaultStorage_MatchRequest_ExceptionCNAME(t *testing.T) {
}
func TestDefaultStorage_MatchRequest_ExceptionIP(t *testing.T) {
addr := netip.AddrFrom4([4]byte{1, 2, 3, 4})
// Exception for AAAA record.
items := []*Item{{
Domain: "host.com",
Answer: "1.2.3.4",
Answer: addr.String(),
}, {
Domain: "host.com",
Answer: "AAAA",
}, {
Domain: "host2.com",
Answer: "::1",
Answer: netutil.IPv6Localhost().String(),
}, {
Domain: "host2.com",
Answer: "A",
@@ -407,7 +428,7 @@ func TestDefaultStorage_MatchRequest_ExceptionIP(t *testing.T) {
name: "match_A",
host: "host.com",
wantDNSRewrites: []*rules.DNSRewrite{{
Value: net.IP{1, 2, 3, 4}.To16(),
Value: addr,
NewCNAME: "",
RCode: dns.RcodeSuccess,
RRType: dns.TypeA,
@@ -427,7 +448,7 @@ func TestDefaultStorage_MatchRequest_ExceptionIP(t *testing.T) {
name: "match_AAAA_host2.com",
host: "host2.com",
wantDNSRewrites: []*rules.DNSRewrite{{
Value: net.ParseIP("::1"),
Value: netutil.IPv6Localhost(),
NewCNAME: "",
RCode: dns.RcodeSuccess,
RRType: dns.TypeAAAA,

View File

@@ -2,7 +2,7 @@ package filtering
import (
"fmt"
"net"
"net/netip"
"strings"
"github.com/AdguardTeam/golibs/errors"
@@ -27,22 +27,12 @@ type LegacyRewrite struct {
// IP is the IP address that should be used in the response if Type is
// dns.TypeA or dns.TypeAAAA.
IP net.IP `yaml:"-"`
IP netip.Addr `yaml:"-"`
// Type is the DNS record type: A, AAAA, or CNAME.
Type uint16 `yaml:"-"`
}
// clone returns a deep clone of rw.
func (rw *LegacyRewrite) clone() (cloneRW *LegacyRewrite) {
return &LegacyRewrite{
Domain: rw.Domain,
Answer: rw.Answer,
IP: slices.Clone(rw.IP),
Type: rw.Type,
}
}
// equal returns true if the rw is equal to the other.
func (rw *LegacyRewrite) equal(other *LegacyRewrite) (ok bool) {
return rw.Domain == other.Domain && rw.Answer == other.Answer
@@ -62,11 +52,11 @@ func (rw *LegacyRewrite) matchesQType(qt uint16) (ok bool) {
// If the types match or the entry is set to allow only the other type,
// include them.
return rw.Type == qt || rw.IP == nil
return rw.Type == qt || rw.IP == netip.Addr{}
}
// normalize makes sure that the a new or decoded entry is normalized with
// regards to domain name case, IP length, and so on.
// normalize makes sure that the new or decoded entry is normalized with regards
// to domain name case, IP length, and so on.
//
// If rw is nil, it returns an errors.
func (rw *LegacyRewrite) normalize() (err error) {
@@ -81,12 +71,12 @@ func (rw *LegacyRewrite) normalize() (err error) {
switch rw.Answer {
case "AAAA":
rw.IP = nil
rw.IP = netip.Addr{}
rw.Type = dns.TypeAAAA
return nil
case "A":
rw.IP = nil
rw.IP = netip.Addr{}
rw.Type = dns.TypeA
return nil
@@ -94,19 +84,18 @@ func (rw *LegacyRewrite) normalize() (err error) {
// Go on.
}
ip := net.ParseIP(rw.Answer)
if ip == nil {
ip, err := netip.ParseAddr(rw.Answer)
if err != nil {
log.Debug("normalizing legacy rewrite: %s", err)
rw.Type = dns.TypeCNAME
return nil
}
ip4 := ip.To4()
if ip4 != nil {
rw.IP = ip4
rw.IP = ip
if ip.Is4() {
rw.Type = dns.TypeA
} else {
rw.IP = ip
rw.Type = dns.TypeAAAA
}
@@ -207,7 +196,7 @@ func findRewrites(
func setRewriteResult(res *Result, host string, rewrites []*LegacyRewrite, qtype uint16) {
for _, rw := range rewrites {
if rw.Type == qtype && (qtype == dns.TypeA || qtype == dns.TypeAAAA) {
if rw.IP == nil {
if rw.IP == (netip.Addr{}) {
// "A"/"AAAA" exception: allow getting from upstream.
res.Reason = NotFilteredNotFound
@@ -225,7 +214,12 @@ func setRewriteResult(res *Result, host string, rewrites []*LegacyRewrite, qtype
func cloneRewrites(entries []*LegacyRewrite) (clone []*LegacyRewrite) {
clone = make([]*LegacyRewrite, len(entries))
for i, rw := range entries {
clone[i] = rw.clone()
clone[i] = &LegacyRewrite{
Domain: rw.Domain,
Answer: rw.Answer,
IP: rw.IP,
Type: rw.Type,
}
}
return clone

View File

@@ -2,6 +2,7 @@ package filtering
import (
"net"
"net/netip"
"testing"
"github.com/miekg/dns"
@@ -15,33 +16,43 @@ func TestRewrites(t *testing.T) {
d, _ := newForTest(t, nil, nil)
t.Cleanup(d.Close)
var (
addr1v4 = netip.AddrFrom4([4]byte{1, 2, 3, 4})
addr2v4 = netip.AddrFrom4([4]byte{1, 2, 3, 5})
addr3v4 = netip.AddrFrom4([4]byte{1, 2, 3, 6})
addr4v4 = netip.AddrFrom4([4]byte{1, 2, 3, 7})
addr1v6 = netip.MustParseAddr("1:2:3::4")
addr2v6 = netip.MustParseAddr("1234::5678")
)
d.Rewrites = []*LegacyRewrite{{
// This one and below are about CNAME, A and AAAA.
Domain: "somecname",
Answer: "somehost.com",
}, {
Domain: "somehost.com",
Answer: "0.0.0.0",
Answer: netip.IPv4Unspecified().String(),
}, {
Domain: "host.com",
Answer: "1.2.3.4",
Answer: addr1v4.String(),
}, {
Domain: "host.com",
Answer: "1.2.3.5",
Answer: addr2v4.String(),
}, {
Domain: "host.com",
Answer: "1:2:3::4",
Answer: addr1v6.String(),
}, {
Domain: "www.host.com",
Answer: "host.com",
}, {
// This one is a wildcard.
Domain: "*.host.com",
Answer: "1.2.3.5",
Answer: addr2v4.String(),
}, {
// This one and below are about wildcard overriding.
Domain: "a.host.com",
Answer: "1.2.3.4",
Answer: addr1v4.String(),
}, {
// This one is about CNAME and wildcard interacting.
Domain: "*.host2.com",
@@ -59,13 +70,13 @@ func TestRewrites(t *testing.T) {
Answer: "x.host.com",
}, {
Domain: "*.hostboth.com",
Answer: "1.2.3.6",
Answer: addr3v4.String(),
}, {
Domain: "*.hostboth.com",
Answer: "1234::5678",
Answer: addr2v6.String(),
}, {
Domain: "BIGHOST.COM",
Answer: "1.2.3.7",
Answer: addr4v4.String(),
}, {
Domain: "*.issue4016.com",
Answer: "sub.issue4016.com",
@@ -77,7 +88,7 @@ func TestRewrites(t *testing.T) {
name string
host string
wantCName string
wantIPs []net.IP
wantIPs []netip.Addr
wantReason Reason
dtyp uint16
}{{
@@ -91,63 +102,63 @@ func TestRewrites(t *testing.T) {
name: "rewritten_a",
host: "www.host.com",
wantCName: "host.com",
wantIPs: []net.IP{{1, 2, 3, 4}, {1, 2, 3, 5}},
wantIPs: []netip.Addr{addr1v4, addr2v4},
wantReason: Rewritten,
dtyp: dns.TypeA,
}, {
name: "rewritten_aaaa",
host: "www.host.com",
wantCName: "host.com",
wantIPs: []net.IP{net.ParseIP("1:2:3::4")},
wantIPs: []netip.Addr{addr1v6},
wantReason: Rewritten,
dtyp: dns.TypeAAAA,
}, {
name: "wildcard_match",
host: "abc.host.com",
wantCName: "",
wantIPs: []net.IP{{1, 2, 3, 5}},
wantIPs: []netip.Addr{addr2v4},
wantReason: Rewritten,
dtyp: dns.TypeA,
}, {
name: "wildcard_override",
host: "a.host.com",
wantCName: "",
wantIPs: []net.IP{{1, 2, 3, 4}},
wantIPs: []netip.Addr{addr1v4},
wantReason: Rewritten,
dtyp: dns.TypeA,
}, {
name: "wildcard_cname_interaction",
host: "www.host2.com",
wantCName: "host.com",
wantIPs: []net.IP{{1, 2, 3, 4}, {1, 2, 3, 5}},
wantIPs: []netip.Addr{addr1v4, addr2v4},
wantReason: Rewritten,
dtyp: dns.TypeA,
}, {
name: "two_cnames",
host: "b.host.com",
wantCName: "somehost.com",
wantIPs: []net.IP{{0, 0, 0, 0}},
wantIPs: []netip.Addr{netip.IPv4Unspecified()},
wantReason: Rewritten,
dtyp: dns.TypeA,
}, {
name: "two_cnames_and_wildcard",
host: "b.host3.com",
wantCName: "x.host.com",
wantIPs: []net.IP{{1, 2, 3, 5}},
wantIPs: []netip.Addr{addr2v4},
wantReason: Rewritten,
dtyp: dns.TypeA,
}, {
name: "issue3343",
host: "www.hostboth.com",
wantCName: "",
wantIPs: []net.IP{net.ParseIP("1234::5678")},
wantIPs: []netip.Addr{addr2v6},
wantReason: Rewritten,
dtyp: dns.TypeAAAA,
}, {
name: "issue3351",
host: "bighost.com",
wantCName: "",
wantIPs: []net.IP{{1, 2, 3, 7}},
wantIPs: []netip.Addr{addr4v4},
wantReason: Rewritten,
dtyp: dns.TypeA,
}, {
@@ -254,25 +265,25 @@ func TestRewritesExceptionCNAME(t *testing.T) {
testCases := []struct {
name string
host string
want net.IP
want netip.Addr
}{{
name: "match_subdomain",
host: "my.host.com",
want: net.IP{2, 2, 2, 2},
want: netip.AddrFrom4([4]byte{2, 2, 2, 2}),
}, {
name: "exception_cname",
host: "sub.host.com",
want: nil,
want: netip.Addr{},
}, {
name: "exception_wildcard",
host: "my.sub.host.com",
want: nil,
want: netip.Addr{},
}}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
r := d.processRewrites(tc.host, dns.TypeA)
if tc.want == nil {
if tc.want == (netip.Addr{}) {
assert.Equal(t, NotFilteredNotFound, r.Reason, "got %s", r.Reason)
return
@@ -280,7 +291,7 @@ func TestRewritesExceptionCNAME(t *testing.T) {
assert.Equal(t, Rewritten, r.Reason)
require.Len(t, r.IPList, 1)
assert.True(t, tc.want.Equal(r.IPList[0]))
assert.Equal(t, tc.want, r.IPList[0])
})
}
}
@@ -314,58 +325,58 @@ func TestRewritesExceptionIP(t *testing.T) {
require.NoError(t, d.prepareRewrites())
testCases := []struct {
name string
host string
want []net.IP
dtyp uint16
name string
host string
want []netip.Addr
dtyp uint16
wantReason Reason
}{{
name: "match_A",
host: "host.com",
want: []net.IP{{1, 2, 3, 4}},
dtyp: dns.TypeA,
name: "match_A",
host: "host.com",
want: []netip.Addr{netip.AddrFrom4([4]byte{1, 2, 3, 4})},
dtyp: dns.TypeA,
wantReason: Rewritten,
}, {
name: "exception_AAAA_host.com",
host: "host.com",
want: nil,
dtyp: dns.TypeAAAA,
name: "exception_AAAA_host.com",
host: "host.com",
want: nil,
dtyp: dns.TypeAAAA,
wantReason: NotFilteredNotFound,
}, {
name: "exception_A_host2.com",
host: "host2.com",
want: nil,
dtyp: dns.TypeA,
name: "exception_A_host2.com",
host: "host2.com",
want: nil,
dtyp: dns.TypeA,
wantReason: NotFilteredNotFound,
}, {
name: "match_AAAA_host2.com",
host: "host2.com",
want: []net.IP{net.ParseIP("::1")},
dtyp: dns.TypeAAAA,
name: "match_AAAA_host2.com",
host: "host2.com",
want: []netip.Addr{netip.MustParseAddr("::1")},
dtyp: dns.TypeAAAA,
wantReason: Rewritten,
}, {
name: "exception_A_host3.com",
host: "host3.com",
want: nil,
dtyp: dns.TypeA,
name: "exception_A_host3.com",
host: "host3.com",
want: nil,
dtyp: dns.TypeA,
wantReason: NotFilteredNotFound,
}, {
name: "match_AAAA_host3.com",
host: "host3.com",
want: []net.IP{},
dtyp: dns.TypeAAAA,
name: "match_AAAA_host3.com",
host: "host3.com",
want: nil,
dtyp: dns.TypeAAAA,
wantReason: Rewritten,
}}
for _, tc := range testCases {
t.Run(tc.name+"_"+tc.host, func(t *testing.T) {
t.Run(tc.name, func(t *testing.T) {
if tc.name != "match_AAAA_host3.com" {
t.SkipNow()
}
r := d.processRewrites(tc.host, tc.dtyp)
if tc.want == nil {
assert.Equal(t, NotFilteredNotFound, r.Reason)
return
}
assert.Equalf(t, Rewritten, r.Reason, "got %s", r.Reason)
require.Len(t, r.IPList, len(tc.want))
for _, ip := range tc.want {
assert.True(t, ip.Equal(r.IPList[0]))
}
assert.Equal(t, tc.want, r.IPList)
assert.Equal(t, tc.wantReason, r.Reason)
})
}
}

View File

@@ -8,6 +8,7 @@ import (
"encoding/gob"
"fmt"
"net"
"net/netip"
"strings"
"sync"
"time"
@@ -239,10 +240,9 @@ func (ss *Default) newResult(
}
if rewrite.RRType == qtype {
v := rewrite.Value
ip, ok := v.(net.IP)
if !ok || ip == nil {
return nil, fmt.Errorf("expected ip rewrite value, got %T(%[1]v)", v)
ip, ok := rewrite.Value.(netip.Addr)
if !ok || ip == (netip.Addr{}) {
return nil, fmt.Errorf("expected ip rewrite value, got %T(%[1]v)", rewrite.Value)
}
res.Rules[0].IP = ip
@@ -267,12 +267,13 @@ func (ss *Default) newResult(
for _, ip := range ips {
// TODO(a.garipov): Remove this filtering once the resolver we use
// actually learns about network.
ip = fitToProto(ip, qtype)
if ip == nil {
addr := fitToProto(ip, qtype)
if addr == (netip.Addr{}) {
continue
}
res.Rules[0].IP = ip
// TODO(e.burkov): Rules[0]?
res.Rules[0].IP = addr
}
return res, nil
@@ -293,17 +294,16 @@ func qtypeToProto(qtype rules.RRType) (proto string) {
// fitToProto returns a non-nil IP address if ip is the correct protocol version
// for qtype. qtype is expected to be either [dns.TypeA] or [dns.TypeAAAA].
func fitToProto(ip net.IP, qtype rules.RRType) (res net.IP) {
ip4 := ip.To4()
if qtype == dns.TypeA {
return ip4
func fitToProto(ip net.IP, qtype rules.RRType) (res netip.Addr) {
if ip4 := ip.To4(); qtype == dns.TypeA {
if ip4 != nil {
return netip.AddrFrom4([4]byte(ip4))
}
} else if ip = ip.To16(); ip != nil && qtype == dns.TypeAAAA {
return netip.AddrFrom16([16]byte(ip))
}
if ip4 == nil {
return ip
}
return nil
return netip.Addr{}
}
// setCacheResult stores data in cache for host. qtype is expected to be either

View File

@@ -3,6 +3,7 @@ package safesearch
import (
"context"
"net"
"net/netip"
"testing"
"time"
@@ -33,7 +34,7 @@ var defaultSafeSearchConf = filtering.SafeSearchConfig{
YouTube: true,
}
var yandexIP = net.IPv4(213, 180, 193, 56)
var yandexIP = netip.AddrFrom4([4]byte{213, 180, 193, 56})
func newForTest(t testing.TB, ssConf filtering.SafeSearchConfig) (ss *Default) {
ss, err := NewDefault(ssConf, "", testCacheSize, testCacheTTL)
@@ -93,7 +94,7 @@ func TestSafeSearchCacheGoogle(t *testing.T) {
OnLookupIP: func(_ context.Context, _, host string) (ips []net.IP, err error) {
ip4, ip6 := aghtest.HostToIPs(host)
return []net.IP{ip4, ip6}, nil
return []net.IP{ip4.AsSlice(), ip6.AsSlice()}, nil
},
}
@@ -109,14 +110,14 @@ func TestSafeSearchCacheGoogle(t *testing.T) {
require.NoError(t, err)
require.Len(t, res.Rules, 1)
assert.True(t, res.Rules[0].IP.Equal(wantIP))
assert.Equal(t, wantIP, res.Rules[0].IP)
// Check cache.
cachedValue, isFound := ss.getCachedResult(domain, testQType)
require.True(t, isFound)
require.Len(t, cachedValue.Rules, 1)
assert.True(t, cachedValue.Rules[0].IP.Equal(wantIP))
assert.Equal(t, wantIP, cachedValue.Rules[0].IP)
}
const googleHost = "www.google.com"

View File

@@ -3,6 +3,7 @@ package safesearch_test
import (
"context"
"net"
"net/netip"
"testing"
"time"
@@ -43,7 +44,7 @@ var testConf = filtering.SafeSearchConfig{
// yandexIP is the expected IP address of Yandex safe search results. Keep in
// sync with the rules data.
var yandexIP = net.IPv4(213, 180, 193, 56)
var yandexIP = netip.AddrFrom4([4]byte{213, 180, 193, 56})
func TestDefault_CheckHost_yandex(t *testing.T) {
conf := testConf
@@ -87,7 +88,7 @@ func TestDefault_CheckHost_yandexAAAA(t *testing.T) {
// once the TODO in [safesearch.Default.newResult] is resolved.
require.Len(t, res.Rules, 1)
assert.Nil(t, res.Rules[0].IP)
assert.Empty(t, res.Rules[0].IP)
assert.EqualValues(t, filtering.SafeSearchListID, res.Rules[0].FilterListID)
}
@@ -96,7 +97,7 @@ func TestDefault_CheckHost_google(t *testing.T) {
OnLookupIP: func(_ context.Context, _, host string) (ips []net.IP, err error) {
ip4, ip6 := aghtest.HostToIPs(host)
return []net.IP{ip4, ip6}, nil
return []net.IP{ip4.AsSlice(), ip6.AsSlice()}, nil
},
}
@@ -178,7 +179,7 @@ func TestDefault_CheckHost_duckduckgoAAAA(t *testing.T) {
// once the TODO in [safesearch.Default.newResult] is resolved.
require.Len(t, res.Rules, 1)
assert.Nil(t, res.Rules[0].IP)
assert.Empty(t, res.Rules[0].IP)
assert.EqualValues(t, filtering.SafeSearchListID, res.Rules[0].FilterListID)
}