From e685d81c92a0eb56d0799d08cb983a805112b065 Mon Sep 17 00:00:00 2001
From: Ainar Garipov <a.garipov@adguard.com>
Date: Tue, 24 Nov 2020 19:55:05 +0300
Subject: [PATCH] Pull request #847: dnsfilter: add $dnstype handling

Merge in DNS/adguard-home from 2337-dnstype to master

Updates #2102.
Updates #2337.

Squashed commit of the following:

commit ac4b7522c732c0bf8ee06539fd4c95b5dc1c87b8
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Tue Nov 24 17:50:33 2020 +0300

    dnsfilter: add $dnstype handling
---
 go.mod                               |  2 +-
 go.sum                               |  2 +
 internal/dnsfilter/dnsfilter.go      | 12 +++--
 internal/dnsfilter/dnsfilter_test.go | 74 +++++++++++++++-------------
 internal/dnsfilter/rewrites.go       |  3 --
 internal/dnsfilter/rewrites_test.go  | 60 +++++++++++-----------
 6 files changed, 81 insertions(+), 72 deletions(-)

diff --git a/go.mod b/go.mod
index fef5e380..c20a9da1 100644
--- a/go.mod
+++ b/go.mod
@@ -5,7 +5,7 @@ go 1.14
 require (
 	github.com/AdguardTeam/dnsproxy v0.33.2
 	github.com/AdguardTeam/golibs v0.4.3
-	github.com/AdguardTeam/urlfilter v0.12.3
+	github.com/AdguardTeam/urlfilter v0.13.0
 	github.com/NYTimes/gziphandler v1.1.1
 	github.com/beefsack/go-rate v0.0.0-20200827232406-6cde80facd47 // indirect
 	github.com/fsnotify/fsnotify v1.4.9
diff --git a/go.sum b/go.sum
index ccbe48fa..9a7f298f 100644
--- a/go.sum
+++ b/go.sum
@@ -28,6 +28,8 @@ github.com/AdguardTeam/golibs v0.4.3/go.mod h1:skKsDKIBB7kkFflLJBpfGX+G8QFTx0WKU
 github.com/AdguardTeam/gomitmproxy v0.2.0/go.mod h1:Qdv0Mktnzer5zpdpi5rAwixNJzW2FN91LjKJCkVbYGU=
 github.com/AdguardTeam/urlfilter v0.12.3 h1:FMjQG0eTgrr8xA3z2zaLVcCgGdpzoECPGWwgPjtwPNs=
 github.com/AdguardTeam/urlfilter v0.12.3/go.mod h1:1fcCQx5TGJANrQN6sHNNM9KPBl7qx7BJml45ko6vru0=
+github.com/AdguardTeam/urlfilter v0.13.0 h1:MfO46K81JVTkhgP6gRu/buKl5wAOSfusjiDwjT1JN1c=
+github.com/AdguardTeam/urlfilter v0.13.0/go.mod h1:klx4JbOfc4EaNb5lWLqOwfg+pVcyRukmoJRvO55lL5U=
 github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
 github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
 github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
diff --git a/internal/dnsfilter/dnsfilter.go b/internal/dnsfilter/dnsfilter.go
index b57d03fb..2b58b02e 100644
--- a/internal/dnsfilter/dnsfilter.go
+++ b/internal/dnsfilter/dnsfilter.go
@@ -596,11 +596,13 @@ func (d *Dnsfilter) matchHost(host string, qtype uint16, setts RequestFilteringS
 	//  but also while using the rules returned by it.
 	defer d.engineLock.RUnlock()
 
-	ureq := urlfilter.DNSRequest{}
-	ureq.Hostname = host
-	ureq.ClientIP = setts.ClientIP
-	ureq.ClientName = setts.ClientName
-	ureq.SortedClientTags = setts.ClientTags
+	ureq := urlfilter.DNSRequest{
+		Hostname:         host,
+		SortedClientTags: setts.ClientTags,
+		ClientIP:         setts.ClientIP,
+		ClientName:       setts.ClientName,
+		DNSType:          qtype,
+	}
 
 	if d.filteringEngineWhite != nil {
 		rr, ok := d.filteringEngineWhite.MatchRequest(ureq)
diff --git a/internal/dnsfilter/dnsfilter_test.go b/internal/dnsfilter/dnsfilter_test.go
index bfe06caa..1eb7a31c 100644
--- a/internal/dnsfilter/dnsfilter_test.go
+++ b/internal/dnsfilter/dnsfilter_test.go
@@ -368,6 +368,7 @@ const (
 	importantRules = `@@||example.org^` + nl + `||test.example.org^$important` + nl
 	regexRules     = `/example\.org/` + nl + `@@||test.example.org^` + nl
 	maskRules      = `test*.example.org^` + nl + `exam*.com` + nl
+	dnstypeRules   = `||example.org^$dnstype=AAAA` + nl + `@@||test.example.org^` + nl
 )
 
 var tests = []struct {
@@ -376,44 +377,51 @@ var tests = []struct {
 	hostname   string
 	isFiltered bool
 	reason     Reason
+	dnsType    uint16
 }{
-	{"sanity", "||doubleclick.net^", "www.doubleclick.net", true, FilteredBlackList},
-	{"sanity", "||doubleclick.net^", "nodoubleclick.net", false, NotFilteredNotFound},
-	{"sanity", "||doubleclick.net^", "doubleclick.net.ru", false, NotFilteredNotFound},
-	{"sanity", "||doubleclick.net^", "wmconvirus.narod.ru", false, NotFilteredNotFound},
+	{"sanity", "||doubleclick.net^", "www.doubleclick.net", true, FilteredBlackList, dns.TypeA},
+	{"sanity", "||doubleclick.net^", "nodoubleclick.net", false, NotFilteredNotFound, dns.TypeA},
+	{"sanity", "||doubleclick.net^", "doubleclick.net.ru", false, NotFilteredNotFound, dns.TypeA},
+	{"sanity", "||doubleclick.net^", "wmconvirus.narod.ru", false, NotFilteredNotFound, dns.TypeA},
 
-	{"blocking", blockingRules, "example.org", true, FilteredBlackList},
-	{"blocking", blockingRules, "test.example.org", true, FilteredBlackList},
-	{"blocking", blockingRules, "test.test.example.org", true, FilteredBlackList},
-	{"blocking", blockingRules, "testexample.org", false, NotFilteredNotFound},
-	{"blocking", blockingRules, "onemoreexample.org", false, NotFilteredNotFound},
+	{"blocking", blockingRules, "example.org", true, FilteredBlackList, dns.TypeA},
+	{"blocking", blockingRules, "test.example.org", true, FilteredBlackList, dns.TypeA},
+	{"blocking", blockingRules, "test.test.example.org", true, FilteredBlackList, dns.TypeA},
+	{"blocking", blockingRules, "testexample.org", false, NotFilteredNotFound, dns.TypeA},
+	{"blocking", blockingRules, "onemoreexample.org", false, NotFilteredNotFound, dns.TypeA},
 
-	{"whitelist", whitelistRules, "example.org", true, FilteredBlackList},
-	{"whitelist", whitelistRules, "test.example.org", false, NotFilteredWhiteList},
-	{"whitelist", whitelistRules, "test.test.example.org", false, NotFilteredWhiteList},
-	{"whitelist", whitelistRules, "testexample.org", false, NotFilteredNotFound},
-	{"whitelist", whitelistRules, "onemoreexample.org", false, NotFilteredNotFound},
+	{"whitelist", whitelistRules, "example.org", true, FilteredBlackList, dns.TypeA},
+	{"whitelist", whitelistRules, "test.example.org", false, NotFilteredWhiteList, dns.TypeA},
+	{"whitelist", whitelistRules, "test.test.example.org", false, NotFilteredWhiteList, dns.TypeA},
+	{"whitelist", whitelistRules, "testexample.org", false, NotFilteredNotFound, dns.TypeA},
+	{"whitelist", whitelistRules, "onemoreexample.org", false, NotFilteredNotFound, dns.TypeA},
 
-	{"important", importantRules, "example.org", false, NotFilteredWhiteList},
-	{"important", importantRules, "test.example.org", true, FilteredBlackList},
-	{"important", importantRules, "test.test.example.org", true, FilteredBlackList},
-	{"important", importantRules, "testexample.org", false, NotFilteredNotFound},
-	{"important", importantRules, "onemoreexample.org", false, NotFilteredNotFound},
+	{"important", importantRules, "example.org", false, NotFilteredWhiteList, dns.TypeA},
+	{"important", importantRules, "test.example.org", true, FilteredBlackList, dns.TypeA},
+	{"important", importantRules, "test.test.example.org", true, FilteredBlackList, dns.TypeA},
+	{"important", importantRules, "testexample.org", false, NotFilteredNotFound, dns.TypeA},
+	{"important", importantRules, "onemoreexample.org", false, NotFilteredNotFound, dns.TypeA},
 
-	{"regex", regexRules, "example.org", true, FilteredBlackList},
-	{"regex", regexRules, "test.example.org", false, NotFilteredWhiteList},
-	{"regex", regexRules, "test.test.example.org", false, NotFilteredWhiteList},
-	{"regex", regexRules, "testexample.org", true, FilteredBlackList},
-	{"regex", regexRules, "onemoreexample.org", true, FilteredBlackList},
+	{"regex", regexRules, "example.org", true, FilteredBlackList, dns.TypeA},
+	{"regex", regexRules, "test.example.org", false, NotFilteredWhiteList, dns.TypeA},
+	{"regex", regexRules, "test.test.example.org", false, NotFilteredWhiteList, dns.TypeA},
+	{"regex", regexRules, "testexample.org", true, FilteredBlackList, dns.TypeA},
+	{"regex", regexRules, "onemoreexample.org", true, FilteredBlackList, dns.TypeA},
 
-	{"mask", maskRules, "test.example.org", true, FilteredBlackList},
-	{"mask", maskRules, "test2.example.org", true, FilteredBlackList},
-	{"mask", maskRules, "example.com", true, FilteredBlackList},
-	{"mask", maskRules, "exampleeee.com", true, FilteredBlackList},
-	{"mask", maskRules, "onemoreexamsite.com", true, FilteredBlackList},
-	{"mask", maskRules, "example.org", false, NotFilteredNotFound},
-	{"mask", maskRules, "testexample.org", false, NotFilteredNotFound},
-	{"mask", maskRules, "example.co.uk", false, NotFilteredNotFound},
+	{"mask", maskRules, "test.example.org", true, FilteredBlackList, dns.TypeA},
+	{"mask", maskRules, "test2.example.org", true, FilteredBlackList, dns.TypeA},
+	{"mask", maskRules, "example.com", true, FilteredBlackList, dns.TypeA},
+	{"mask", maskRules, "exampleeee.com", true, FilteredBlackList, dns.TypeA},
+	{"mask", maskRules, "onemoreexamsite.com", true, FilteredBlackList, dns.TypeA},
+	{"mask", maskRules, "example.org", false, NotFilteredNotFound, dns.TypeA},
+	{"mask", maskRules, "testexample.org", false, NotFilteredNotFound, dns.TypeA},
+	{"mask", maskRules, "example.co.uk", false, NotFilteredNotFound, dns.TypeA},
+
+	{"dnstype", dnstypeRules, "onemoreexample.org", false, NotFilteredNotFound, dns.TypeA},
+	{"dnstype", dnstypeRules, "example.org", false, NotFilteredNotFound, dns.TypeA},
+	{"dnstype", dnstypeRules, "example.org", true, FilteredBlackList, dns.TypeAAAA},
+	{"dnstype", dnstypeRules, "test.example.org", false, NotFilteredWhiteList, dns.TypeA},
+	{"dnstype", dnstypeRules, "test.example.org", false, NotFilteredWhiteList, dns.TypeAAAA},
 }
 
 func TestMatching(t *testing.T) {
@@ -425,7 +433,7 @@ func TestMatching(t *testing.T) {
 			d := NewForTest(nil, filters)
 			defer d.Close()
 
-			ret, err := d.CheckHost(test.hostname, dns.TypeA, &setts)
+			ret, err := d.CheckHost(test.hostname, test.dnsType, &setts)
 			if err != nil {
 				t.Errorf("Error while matching host %s: %s", test.hostname, err)
 			}
diff --git a/internal/dnsfilter/rewrites.go b/internal/dnsfilter/rewrites.go
index 9c042228..0092344e 100644
--- a/internal/dnsfilter/rewrites.go
+++ b/internal/dnsfilter/rewrites.go
@@ -149,7 +149,6 @@ type rewriteEntryJSON struct {
 }
 
 func (d *Dnsfilter) handleRewriteList(w http.ResponseWriter, r *http.Request) {
-
 	arr := []*rewriteEntryJSON{}
 
 	d.confLock.Lock()
@@ -171,7 +170,6 @@ func (d *Dnsfilter) handleRewriteList(w http.ResponseWriter, r *http.Request) {
 }
 
 func (d *Dnsfilter) handleRewriteAdd(w http.ResponseWriter, r *http.Request) {
-
 	jsent := rewriteEntryJSON{}
 	err := json.NewDecoder(r.Body).Decode(&jsent)
 	if err != nil {
@@ -194,7 +192,6 @@ func (d *Dnsfilter) handleRewriteAdd(w http.ResponseWriter, r *http.Request) {
 }
 
 func (d *Dnsfilter) handleRewriteDelete(w http.ResponseWriter, r *http.Request) {
-
 	jsent := rewriteEntryJSON{}
 	err := json.NewDecoder(r.Body).Decode(&jsent)
 	if err != nil {
diff --git a/internal/dnsfilter/rewrites_test.go b/internal/dnsfilter/rewrites_test.go
index 2ed8210e..31b5cc0d 100644
--- a/internal/dnsfilter/rewrites_test.go
+++ b/internal/dnsfilter/rewrites_test.go
@@ -12,13 +12,13 @@ func TestRewrites(t *testing.T) {
 	d := Dnsfilter{}
 	// CNAME, A, AAAA
 	d.Rewrites = []RewriteEntry{
-		RewriteEntry{"somecname", "somehost.com", 0, nil},
-		RewriteEntry{"somehost.com", "0.0.0.0", 0, nil},
+		{"somecname", "somehost.com", 0, nil},
+		{"somehost.com", "0.0.0.0", 0, nil},
 
-		RewriteEntry{"host.com", "1.2.3.4", 0, nil},
-		RewriteEntry{"host.com", "1.2.3.5", 0, nil},
-		RewriteEntry{"host.com", "1:2:3::4", 0, nil},
-		RewriteEntry{"www.host.com", "host.com", 0, nil},
+		{"host.com", "1.2.3.4", 0, nil},
+		{"host.com", "1.2.3.5", 0, nil},
+		{"host.com", "1:2:3::4", 0, nil},
+		{"www.host.com", "host.com", 0, nil},
 	}
 	d.prepareRewrites()
 	r := d.processRewrites("host2.com", dns.TypeA)
@@ -39,8 +39,8 @@ func TestRewrites(t *testing.T) {
 
 	// wildcard
 	d.Rewrites = []RewriteEntry{
-		RewriteEntry{"host.com", "1.2.3.4", 0, nil},
-		RewriteEntry{"*.host.com", "1.2.3.5", 0, nil},
+		{"host.com", "1.2.3.4", 0, nil},
+		{"*.host.com", "1.2.3.5", 0, nil},
 	}
 	d.prepareRewrites()
 	r = d.processRewrites("host.com", dns.TypeA)
@@ -56,8 +56,8 @@ func TestRewrites(t *testing.T) {
 
 	// override a wildcard
 	d.Rewrites = []RewriteEntry{
-		RewriteEntry{"a.host.com", "1.2.3.4", 0, nil},
-		RewriteEntry{"*.host.com", "1.2.3.5", 0, nil},
+		{"a.host.com", "1.2.3.4", 0, nil},
+		{"*.host.com", "1.2.3.5", 0, nil},
 	}
 	d.prepareRewrites()
 	r = d.processRewrites("a.host.com", dns.TypeA)
@@ -67,8 +67,8 @@ func TestRewrites(t *testing.T) {
 
 	// wildcard + CNAME
 	d.Rewrites = []RewriteEntry{
-		RewriteEntry{"host.com", "1.2.3.4", 0, nil},
-		RewriteEntry{"*.host.com", "host.com", 0, nil},
+		{"host.com", "1.2.3.4", 0, nil},
+		{"*.host.com", "host.com", 0, nil},
 	}
 	d.prepareRewrites()
 	r = d.processRewrites("www.host.com", dns.TypeA)
@@ -78,9 +78,9 @@ func TestRewrites(t *testing.T) {
 
 	// 2 CNAMEs
 	d.Rewrites = []RewriteEntry{
-		RewriteEntry{"b.host.com", "a.host.com", 0, nil},
-		RewriteEntry{"a.host.com", "host.com", 0, nil},
-		RewriteEntry{"host.com", "1.2.3.4", 0, nil},
+		{"b.host.com", "a.host.com", 0, nil},
+		{"a.host.com", "host.com", 0, nil},
+		{"host.com", "1.2.3.4", 0, nil},
 	}
 	d.prepareRewrites()
 	r = d.processRewrites("b.host.com", dns.TypeA)
@@ -91,9 +91,9 @@ func TestRewrites(t *testing.T) {
 
 	// 2 CNAMEs + wildcard
 	d.Rewrites = []RewriteEntry{
-		RewriteEntry{"b.host.com", "a.host.com", 0, nil},
-		RewriteEntry{"a.host.com", "x.somehost.com", 0, nil},
-		RewriteEntry{"*.somehost.com", "1.2.3.4", 0, nil},
+		{"b.host.com", "a.host.com", 0, nil},
+		{"a.host.com", "x.somehost.com", 0, nil},
+		{"*.somehost.com", "1.2.3.4", 0, nil},
 	}
 	d.prepareRewrites()
 	r = d.processRewrites("b.host.com", dns.TypeA)
@@ -107,9 +107,9 @@ func TestRewritesLevels(t *testing.T) {
 	d := Dnsfilter{}
 	// exact host, wildcard L2, wildcard L3
 	d.Rewrites = []RewriteEntry{
-		RewriteEntry{"host.com", "1.1.1.1", 0, nil},
-		RewriteEntry{"*.host.com", "2.2.2.2", 0, nil},
-		RewriteEntry{"*.sub.host.com", "3.3.3.3", 0, nil},
+		{"host.com", "1.1.1.1", 0, nil},
+		{"*.host.com", "2.2.2.2", 0, nil},
+		{"*.sub.host.com", "3.3.3.3", 0, nil},
 	}
 	d.prepareRewrites()
 
@@ -136,8 +136,8 @@ func TestRewritesExceptionCNAME(t *testing.T) {
 	d := Dnsfilter{}
 	// wildcard; exception for a sub-domain
 	d.Rewrites = []RewriteEntry{
-		RewriteEntry{"*.host.com", "2.2.2.2", 0, nil},
-		RewriteEntry{"sub.host.com", "sub.host.com", 0, nil},
+		{"*.host.com", "2.2.2.2", 0, nil},
+		{"sub.host.com", "sub.host.com", 0, nil},
 	}
 	d.prepareRewrites()
 
@@ -156,8 +156,8 @@ func TestRewritesExceptionWC(t *testing.T) {
 	d := Dnsfilter{}
 	// wildcard; exception for a sub-wildcard
 	d.Rewrites = []RewriteEntry{
-		RewriteEntry{"*.host.com", "2.2.2.2", 0, nil},
-		RewriteEntry{"*.sub.host.com", "*.sub.host.com", 0, nil},
+		{"*.host.com", "2.2.2.2", 0, nil},
+		{"*.sub.host.com", "*.sub.host.com", 0, nil},
 	}
 	d.prepareRewrites()
 
@@ -176,11 +176,11 @@ func TestRewritesExceptionIP(t *testing.T) {
 	d := Dnsfilter{}
 	// exception for AAAA record
 	d.Rewrites = []RewriteEntry{
-		RewriteEntry{"host.com", "1.2.3.4", 0, nil},
-		RewriteEntry{"host.com", "AAAA", 0, nil},
-		RewriteEntry{"host2.com", "::1", 0, nil},
-		RewriteEntry{"host2.com", "A", 0, nil},
-		RewriteEntry{"host3.com", "A", 0, nil},
+		{"host.com", "1.2.3.4", 0, nil},
+		{"host.com", "AAAA", 0, nil},
+		{"host2.com", "::1", 0, nil},
+		{"host2.com", "A", 0, nil},
+		{"host3.com", "A", 0, nil},
 	}
 	d.prepareRewrites()