Pull request: 2499 rewrite: storage vol.2
Merge in DNS/adguard-home from 2499-rewrites-1 to master
Squashed commit of the following:
commit 6303107d6ca7dd88175e4e123128189e5958f060
Merge: e2040b95 09f88cf2
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date: Mon Dec 5 13:38:01 2022 +0200
Merge remote-tracking branch 'origin/master' into 2499-rewrites-1
commit e2040b95dd3157d033d929bb45fc7662b9918a78
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date: Mon Dec 5 12:00:21 2022 +0200
rewrite: item
commit c7278e8adeec1ba3a090cc93db30c80699617c52
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date: Sun Dec 4 12:57:59 2022 +0200
rewrite: imp code
commit d23a740262a4fbdd9b25f7449ced707c0d2be634
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date: Fri Dec 2 13:08:25 2022 +0200
rewrite: imp code
commit 773a5211b6662afd03a34219e7114c6f1e2bb579
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date: Fri Dec 2 13:05:20 2022 +0200
rewrite: imp code
commit 48b54e19da9844d9b868d0be7e428ad6bacae6a5
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date: Fri Dec 2 13:02:47 2022 +0200
rewrite: tests item
commit 62af2bd91f5559840e7948ac4bf7c36b1bee1dc2
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date: Thu Dec 1 17:11:21 2022 +0200
rewrite: tests
commit f040b609391cb2275b11d4732bbac0380c01de07
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date: Thu Dec 1 17:04:59 2022 +0200
rewrite: imp code
commit 4592b8c4e6107e5a746261d3335282827ce36b74
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date: Thu Dec 1 17:02:31 2022 +0200
rewrite: imp code
commit cc1660695341c558dbac6acaa31ac160a45f6105
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date: Thu Dec 1 16:19:27 2022 +0200
rewrite: imp code
commit cf3840b76d45bf319630256c01586159dd1e85fe
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date: Thu Dec 1 13:16:40 2022 +0200
rewrite: tests
commit 6fd6f03ca4320d4345032139b43cb457b3ae4278
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date: Thu Dec 1 11:14:44 2022 +0200
rewrite: imp code
commit 2ebd2a1e79afc8f486cf6533968c51ca61bc03ab
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date: Wed Nov 30 11:43:24 2022 +0200
rewrite: tests
commit 7da987994303a3e7b16eb6b0baaa4b59a52b97be
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date: Wed Nov 30 10:54:57 2022 +0200
filtering: imp code
commit ab98efc6710fac7cba28dab5bca9b60e9ec34ef7
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date: Mon Nov 28 13:31:12 2022 +0200
filtering: rewritehttp
This commit is contained in:
parent
09f88cf21d
commit
01652e6ab2
|
@ -2,7 +2,7 @@ package rewrite
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net/netip"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/miekg/dns"
|
"github.com/miekg/dns"
|
||||||
|
@ -26,11 +26,15 @@ func (rw *Item) equal(other *Item) (ok bool) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
return rw.Domain == other.Domain && rw.Answer == other.Answer
|
return *rw == *other
|
||||||
}
|
}
|
||||||
|
|
||||||
// toRule converts rw to a filter rule.
|
// toRule converts rw to a filter rule.
|
||||||
func (rw *Item) toRule() (res string) {
|
func (rw *Item) toRule() (res string) {
|
||||||
|
if rw == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
domain := strings.ToLower(rw.Domain)
|
domain := strings.ToLower(rw.Domain)
|
||||||
|
|
||||||
dType, exception := rw.rewriteParams()
|
dType, exception := rw.rewriteParams()
|
||||||
|
@ -53,13 +57,13 @@ func (rw *Item) rewriteParams() (dType uint16, exception bool) {
|
||||||
// Go on.
|
// Go on.
|
||||||
}
|
}
|
||||||
|
|
||||||
ip := net.ParseIP(rw.Answer)
|
addr, err := netip.ParseAddr(rw.Answer)
|
||||||
if ip == nil {
|
if err != nil {
|
||||||
|
// TODO(d.kolyshev): Validate rw.Answer as a domain name.
|
||||||
return dns.TypeCNAME, false
|
return dns.TypeCNAME, false
|
||||||
}
|
}
|
||||||
|
|
||||||
ip4 := ip.To4()
|
if addr.Is4() {
|
||||||
if ip4 != nil {
|
|
||||||
dType = dns.TypeA
|
dType = dns.TypeA
|
||||||
} else {
|
} else {
|
||||||
dType = dns.TypeAAAA
|
dType = dns.TypeAAAA
|
||||||
|
|
|
@ -0,0 +1,124 @@
|
||||||
|
package rewrite
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestItem_equal(t *testing.T) {
|
||||||
|
const (
|
||||||
|
testDomain = "example.org"
|
||||||
|
testAnswer = "1.1.1.1"
|
||||||
|
)
|
||||||
|
|
||||||
|
testItem := &Item{
|
||||||
|
Domain: testDomain,
|
||||||
|
Answer: testAnswer,
|
||||||
|
}
|
||||||
|
|
||||||
|
testCases := []struct {
|
||||||
|
name string
|
||||||
|
left *Item
|
||||||
|
right *Item
|
||||||
|
want bool
|
||||||
|
}{{
|
||||||
|
name: "nil_left",
|
||||||
|
left: nil,
|
||||||
|
right: testItem,
|
||||||
|
want: false,
|
||||||
|
}, {
|
||||||
|
name: "nil_right",
|
||||||
|
left: testItem,
|
||||||
|
right: nil,
|
||||||
|
want: false,
|
||||||
|
}, {
|
||||||
|
name: "nils",
|
||||||
|
left: nil,
|
||||||
|
right: nil,
|
||||||
|
want: true,
|
||||||
|
}, {
|
||||||
|
name: "equal",
|
||||||
|
left: testItem,
|
||||||
|
right: testItem,
|
||||||
|
want: true,
|
||||||
|
}, {
|
||||||
|
name: "distinct",
|
||||||
|
left: testItem,
|
||||||
|
right: &Item{
|
||||||
|
Domain: "other",
|
||||||
|
Answer: "other",
|
||||||
|
},
|
||||||
|
want: false,
|
||||||
|
}}
|
||||||
|
|
||||||
|
for _, tc := range testCases {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
res := tc.left.equal(tc.right)
|
||||||
|
assert.Equal(t, tc.want, res)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestItem_toRule(t *testing.T) {
|
||||||
|
const testDomain = "example.org"
|
||||||
|
|
||||||
|
testCases := []struct {
|
||||||
|
name string
|
||||||
|
item *Item
|
||||||
|
want string
|
||||||
|
}{{
|
||||||
|
name: "nil",
|
||||||
|
item: nil,
|
||||||
|
want: "",
|
||||||
|
}, {
|
||||||
|
name: "a_rule",
|
||||||
|
item: &Item{
|
||||||
|
Domain: testDomain,
|
||||||
|
Answer: "1.1.1.1",
|
||||||
|
},
|
||||||
|
want: "|example.org^$dnsrewrite=NOERROR;A;1.1.1.1",
|
||||||
|
}, {
|
||||||
|
name: "aaaa_rule",
|
||||||
|
item: &Item{
|
||||||
|
Domain: testDomain,
|
||||||
|
Answer: "1:2:3::4",
|
||||||
|
},
|
||||||
|
want: "|example.org^$dnsrewrite=NOERROR;AAAA;1:2:3::4",
|
||||||
|
}, {
|
||||||
|
name: "cname_rule",
|
||||||
|
item: &Item{
|
||||||
|
Domain: testDomain,
|
||||||
|
Answer: "other.org",
|
||||||
|
},
|
||||||
|
want: "|example.org^$dnsrewrite=NOERROR;CNAME;other.org",
|
||||||
|
}, {
|
||||||
|
name: "wildcard_rule",
|
||||||
|
item: &Item{
|
||||||
|
Domain: "*.example.org",
|
||||||
|
Answer: "other.org",
|
||||||
|
},
|
||||||
|
want: "|*.example.org^$dnsrewrite=NOERROR;CNAME;other.org",
|
||||||
|
}, {
|
||||||
|
name: "aaaa_exception",
|
||||||
|
item: &Item{
|
||||||
|
Domain: testDomain,
|
||||||
|
Answer: "A",
|
||||||
|
},
|
||||||
|
want: "@@||example.org^$dnstype=A,dnsrewrite",
|
||||||
|
}, {
|
||||||
|
name: "aaaa_exception",
|
||||||
|
item: &Item{
|
||||||
|
Domain: testDomain,
|
||||||
|
Answer: "AAAA",
|
||||||
|
},
|
||||||
|
want: "@@||example.org^$dnstype=AAAA,dnsrewrite",
|
||||||
|
}}
|
||||||
|
|
||||||
|
for _, tc := range testCases {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
res := tc.item.toRule()
|
||||||
|
assert.Equal(t, tc.want, res)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -7,15 +7,18 @@ import (
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/AdguardTeam/golibs/log"
|
"github.com/AdguardTeam/golibs/log"
|
||||||
|
"github.com/AdguardTeam/golibs/stringutil"
|
||||||
"github.com/AdguardTeam/urlfilter"
|
"github.com/AdguardTeam/urlfilter"
|
||||||
"github.com/AdguardTeam/urlfilter/filterlist"
|
"github.com/AdguardTeam/urlfilter/filterlist"
|
||||||
|
"github.com/AdguardTeam/urlfilter/rules"
|
||||||
|
"github.com/miekg/dns"
|
||||||
"golang.org/x/exp/slices"
|
"golang.org/x/exp/slices"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Storage is a storage for rewrite rules.
|
// Storage is a storage for rewrite rules.
|
||||||
type Storage interface {
|
type Storage interface {
|
||||||
// MatchRequest finds a matching rule for the specified request.
|
// MatchRequest returns matching dnsrewrites for the specified request.
|
||||||
MatchRequest(dReq *urlfilter.DNSRequest) (res *urlfilter.DNSResult, matched bool)
|
MatchRequest(dReq *urlfilter.DNSRequest) (rws []*rules.DNSRewrite)
|
||||||
|
|
||||||
// Add adds item to the storage.
|
// Add adds item to the storage.
|
||||||
Add(item *Item) (err error)
|
Add(item *Item) (err error)
|
||||||
|
@ -38,14 +41,14 @@ type DefaultStorage struct {
|
||||||
// ruleList is the filtering rule ruleList used by the engine.
|
// ruleList is the filtering rule ruleList used by the engine.
|
||||||
ruleList filterlist.RuleList
|
ruleList filterlist.RuleList
|
||||||
|
|
||||||
|
// rewrites stores the rewrite entries from configuration.
|
||||||
|
rewrites []*Item
|
||||||
|
|
||||||
// urlFilterID is the synthetic integer identifier for the urlfilter engine.
|
// urlFilterID is the synthetic integer identifier for the urlfilter engine.
|
||||||
//
|
//
|
||||||
// TODO(a.garipov): Change the type to a string in module urlfilter and
|
// TODO(a.garipov): Change the type to a string in module urlfilter and
|
||||||
// remove this crutch.
|
// remove this crutch.
|
||||||
urlFilterID int
|
urlFilterID int
|
||||||
|
|
||||||
// rewrites stores the rewrite entries from configuration.
|
|
||||||
rewrites []*Item
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewDefaultStorage returns new rewrites storage. listID is used as an
|
// NewDefaultStorage returns new rewrites storage. listID is used as an
|
||||||
|
@ -72,11 +75,81 @@ func NewDefaultStorage(listID int, rewrites []*Item) (s *DefaultStorage, err err
|
||||||
var _ Storage = (*DefaultStorage)(nil)
|
var _ Storage = (*DefaultStorage)(nil)
|
||||||
|
|
||||||
// MatchRequest implements the [Storage] interface for *DefaultStorage.
|
// MatchRequest implements the [Storage] interface for *DefaultStorage.
|
||||||
func (s *DefaultStorage) MatchRequest(dReq *urlfilter.DNSRequest) (res *urlfilter.DNSResult, matched bool) {
|
func (s *DefaultStorage) MatchRequest(dReq *urlfilter.DNSRequest) (rws []*rules.DNSRewrite) {
|
||||||
s.mu.RLock()
|
s.mu.RLock()
|
||||||
defer s.mu.RUnlock()
|
defer s.mu.RUnlock()
|
||||||
|
|
||||||
return s.engine.MatchRequest(dReq)
|
rrules := s.rewriteRulesForReq(dReq)
|
||||||
|
if len(rrules) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(a.garipov): Check cnames for cycles on initialisation.
|
||||||
|
cnames := stringutil.NewSet()
|
||||||
|
host := dReq.Hostname
|
||||||
|
for len(rrules) > 0 && rrules[0].DNSRewrite != nil && rrules[0].DNSRewrite.NewCNAME != "" {
|
||||||
|
rule := rrules[0]
|
||||||
|
rwAns := rule.DNSRewrite.NewCNAME
|
||||||
|
|
||||||
|
log.Debug("rewrite: cname for %s is %s", host, rwAns)
|
||||||
|
|
||||||
|
if dReq.Hostname == rwAns {
|
||||||
|
// A request for the hostname itself is an exception rule.
|
||||||
|
// TODO(d.kolyshev): Check rewrite of a pattern onto itself.
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if host == rwAns && isWildcard(rule.RuleText) {
|
||||||
|
// An "*.example.com → sub.example.com" rewrite matching in a loop.
|
||||||
|
//
|
||||||
|
// See https://github.com/AdguardTeam/AdGuardHome/issues/4016.
|
||||||
|
|
||||||
|
return []*rules.DNSRewrite{rule.DNSRewrite}
|
||||||
|
}
|
||||||
|
|
||||||
|
if cnames.Has(rwAns) {
|
||||||
|
log.Info("rewrite: cname loop for %q on %q", dReq.Hostname, rwAns)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
cnames.Add(rwAns)
|
||||||
|
|
||||||
|
drules := s.rewriteRulesForReq(&urlfilter.DNSRequest{
|
||||||
|
Hostname: rwAns,
|
||||||
|
DNSType: dReq.DNSType,
|
||||||
|
})
|
||||||
|
if drules != nil {
|
||||||
|
rrules = drules
|
||||||
|
}
|
||||||
|
|
||||||
|
host = rwAns
|
||||||
|
}
|
||||||
|
|
||||||
|
return s.collectDNSRewrites(rrules, dReq.DNSType)
|
||||||
|
}
|
||||||
|
|
||||||
|
// collectDNSRewrites filters DNSRewrite by question type.
|
||||||
|
func (s *DefaultStorage) collectDNSRewrites(
|
||||||
|
rewrites []*rules.NetworkRule,
|
||||||
|
qtyp uint16,
|
||||||
|
) (rws []*rules.DNSRewrite) {
|
||||||
|
for _, rewrite := range rewrites {
|
||||||
|
dnsRewrite := rewrite.DNSRewrite
|
||||||
|
if matchesQType(dnsRewrite, qtyp) {
|
||||||
|
rws = append(rws, dnsRewrite)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return rws
|
||||||
|
}
|
||||||
|
|
||||||
|
// rewriteRulesForReq returns matching dnsrewrite rules.
|
||||||
|
func (s *DefaultStorage) rewriteRulesForReq(dReq *urlfilter.DNSRequest) (rules []*rules.NetworkRule) {
|
||||||
|
res, _ := s.engine.MatchRequest(dReq)
|
||||||
|
|
||||||
|
return res.DNSRewrites()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add implements the [Storage] interface for *DefaultStorage.
|
// Add implements the [Storage] interface for *DefaultStorage.
|
||||||
|
@ -122,6 +195,7 @@ func (s *DefaultStorage) List() (items []*Item) {
|
||||||
|
|
||||||
// resetRules resets the filtering rules.
|
// resetRules resets the filtering rules.
|
||||||
func (s *DefaultStorage) resetRules() (err error) {
|
func (s *DefaultStorage) resetRules() (err error) {
|
||||||
|
// TODO(a.garipov): Use strings.Builder.
|
||||||
var rulesText []string
|
var rulesText []string
|
||||||
for _, rewrite := range s.rewrites {
|
for _, rewrite := range s.rewrites {
|
||||||
rulesText = append(rulesText, rewrite.toRule())
|
rulesText = append(rulesText, rewrite.toRule())
|
||||||
|
@ -141,7 +215,27 @@ func (s *DefaultStorage) resetRules() (err error) {
|
||||||
s.ruleList = strList
|
s.ruleList = strList
|
||||||
s.engine = urlfilter.NewDNSEngine(rs)
|
s.engine = urlfilter.NewDNSEngine(rs)
|
||||||
|
|
||||||
log.Info("filter %d: reset %d rules", s.urlFilterID, s.engine.RulesCount)
|
log.Info("rewrite: filter %d: reset %d rules", s.urlFilterID, s.engine.RulesCount)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// matchesQType returns true if dnsrewrite matches the question type qt.
|
||||||
|
func matchesQType(dnsrr *rules.DNSRewrite, qt uint16) (ok bool) {
|
||||||
|
// Add CNAMEs, since they match for all types requests.
|
||||||
|
if dnsrr.RRType == dns.TypeCNAME {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reject types other than A and AAAA.
|
||||||
|
if qt != dns.TypeA && qt != dns.TypeAAAA {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return dnsrr.RRType == qt
|
||||||
|
}
|
||||||
|
|
||||||
|
// isWildcard returns true if pat is a wildcard domain pattern.
|
||||||
|
func isWildcard(pat string) (res bool) {
|
||||||
|
return strings.HasPrefix(pat, "|*.")
|
||||||
|
}
|
||||||
|
|
|
@ -1,8 +1,13 @@
|
||||||
package rewrite
|
package rewrite
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"net"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/AdguardTeam/urlfilter"
|
||||||
|
"github.com/AdguardTeam/urlfilter/rules"
|
||||||
|
"github.com/miekg/dns"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -38,3 +43,416 @@ func TestDefaultStorage_CRUD(t *testing.T) {
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Len(t, s.List(), 0)
|
require.Len(t, s.List(), 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestDefaultStorage_MatchRequest(t *testing.T) {
|
||||||
|
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",
|
||||||
|
}, {
|
||||||
|
Domain: "host.com",
|
||||||
|
Answer: "1.2.3.4",
|
||||||
|
}, {
|
||||||
|
Domain: "host.com",
|
||||||
|
Answer: "1.2.3.5",
|
||||||
|
}, {
|
||||||
|
Domain: "host.com",
|
||||||
|
Answer: "1:2:3::4",
|
||||||
|
}, {
|
||||||
|
Domain: "www.host.com",
|
||||||
|
Answer: "host.com",
|
||||||
|
}, {
|
||||||
|
// This one is a wildcard.
|
||||||
|
Domain: "*.host.com",
|
||||||
|
Answer: "1.2.3.5",
|
||||||
|
}, {
|
||||||
|
// This one and below are about wildcard overriding.
|
||||||
|
Domain: "a.host.com",
|
||||||
|
Answer: "1.2.3.4",
|
||||||
|
}, {
|
||||||
|
// This one is about CNAME and wildcard interacting.
|
||||||
|
Domain: "*.host2.com",
|
||||||
|
Answer: "host.com",
|
||||||
|
}, {
|
||||||
|
// This one and below are about 2 level CNAME.
|
||||||
|
Domain: "b.host.com",
|
||||||
|
Answer: "somecname",
|
||||||
|
}, {
|
||||||
|
// This one and below are about 2 level CNAME and wildcard.
|
||||||
|
Domain: "b.host3.com",
|
||||||
|
Answer: "a.host3.com",
|
||||||
|
}, {
|
||||||
|
Domain: "a.host3.com",
|
||||||
|
Answer: "x.host.com",
|
||||||
|
}, {
|
||||||
|
Domain: "*.hostboth.com",
|
||||||
|
Answer: "1.2.3.6",
|
||||||
|
}, {
|
||||||
|
Domain: "*.hostboth.com",
|
||||||
|
Answer: "1234::5678",
|
||||||
|
}, {
|
||||||
|
Domain: "BIGHOST.COM",
|
||||||
|
Answer: "1.2.3.7",
|
||||||
|
}, {
|
||||||
|
Domain: "*.issue4016.com",
|
||||||
|
Answer: "sub.issue4016.com",
|
||||||
|
}}
|
||||||
|
|
||||||
|
s, err := NewDefaultStorage(-1, items)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
testCases := []struct {
|
||||||
|
name string
|
||||||
|
host string
|
||||||
|
wantDNSRewrites []*rules.DNSRewrite
|
||||||
|
dtyp uint16
|
||||||
|
}{{
|
||||||
|
name: "not_filtered_not_found",
|
||||||
|
host: "hoost.com",
|
||||||
|
wantDNSRewrites: nil,
|
||||||
|
dtyp: dns.TypeA,
|
||||||
|
}, {
|
||||||
|
name: "not_filtered_qtype",
|
||||||
|
host: "www.host.com",
|
||||||
|
wantDNSRewrites: nil,
|
||||||
|
dtyp: dns.TypeMX,
|
||||||
|
}, {
|
||||||
|
name: "rewritten_a",
|
||||||
|
host: "www.host.com",
|
||||||
|
wantDNSRewrites: []*rules.DNSRewrite{{
|
||||||
|
Value: net.IP{1, 2, 3, 4}.To16(),
|
||||||
|
NewCNAME: "",
|
||||||
|
RCode: dns.RcodeSuccess,
|
||||||
|
RRType: dns.TypeA,
|
||||||
|
}, {
|
||||||
|
Value: net.IP{1, 2, 3, 5}.To16(),
|
||||||
|
NewCNAME: "",
|
||||||
|
RCode: dns.RcodeSuccess,
|
||||||
|
RRType: dns.TypeA,
|
||||||
|
}},
|
||||||
|
dtyp: dns.TypeA,
|
||||||
|
}, {
|
||||||
|
name: "rewritten_aaaa",
|
||||||
|
host: "www.host.com",
|
||||||
|
wantDNSRewrites: []*rules.DNSRewrite{{
|
||||||
|
Value: net.ParseIP("1:2:3::4"),
|
||||||
|
NewCNAME: "",
|
||||||
|
RCode: dns.RcodeSuccess,
|
||||||
|
RRType: dns.TypeAAAA,
|
||||||
|
}},
|
||||||
|
dtyp: dns.TypeAAAA,
|
||||||
|
}, {
|
||||||
|
name: "wildcard_match",
|
||||||
|
host: "abc.host.com",
|
||||||
|
wantDNSRewrites: []*rules.DNSRewrite{{
|
||||||
|
Value: net.IP{1, 2, 3, 5}.To16(),
|
||||||
|
NewCNAME: "",
|
||||||
|
RCode: dns.RcodeSuccess,
|
||||||
|
RRType: dns.TypeA,
|
||||||
|
}},
|
||||||
|
dtyp: dns.TypeA,
|
||||||
|
//}, {
|
||||||
|
// TODO(d.kolyshev): This is about matching in urlfilter.
|
||||||
|
// name: "wildcard_override",
|
||||||
|
// host: "a.host.com",
|
||||||
|
// wantDNSRewrites: []*rules.DNSRewrite{{
|
||||||
|
// Value: net.IP{1, 2, 3, 4}.To16(),
|
||||||
|
// NewCNAME: "",
|
||||||
|
// RCode: dns.RcodeSuccess,
|
||||||
|
// RRType: dns.TypeA,
|
||||||
|
// }},
|
||||||
|
// dtyp: dns.TypeA,
|
||||||
|
}, {
|
||||||
|
name: "wildcard_cname_interaction",
|
||||||
|
host: "www.host2.com",
|
||||||
|
wantDNSRewrites: []*rules.DNSRewrite{{
|
||||||
|
Value: net.IP{1, 2, 3, 4}.To16(),
|
||||||
|
NewCNAME: "",
|
||||||
|
RCode: dns.RcodeSuccess,
|
||||||
|
RRType: dns.TypeA,
|
||||||
|
}, {
|
||||||
|
Value: net.IP{1, 2, 3, 5}.To16(),
|
||||||
|
NewCNAME: "",
|
||||||
|
RCode: dns.RcodeSuccess,
|
||||||
|
RRType: dns.TypeA,
|
||||||
|
}},
|
||||||
|
dtyp: dns.TypeA,
|
||||||
|
}, {
|
||||||
|
name: "two_cnames",
|
||||||
|
host: "b.host.com",
|
||||||
|
wantDNSRewrites: []*rules.DNSRewrite{{
|
||||||
|
Value: net.IP{0, 0, 0, 0}.To16(),
|
||||||
|
NewCNAME: "",
|
||||||
|
RCode: dns.RcodeSuccess,
|
||||||
|
RRType: dns.TypeA,
|
||||||
|
}},
|
||||||
|
dtyp: dns.TypeA,
|
||||||
|
}, {
|
||||||
|
name: "two_cnames_and_wildcard",
|
||||||
|
host: "b.host3.com",
|
||||||
|
wantDNSRewrites: []*rules.DNSRewrite{{
|
||||||
|
Value: net.IP{1, 2, 3, 5}.To16(),
|
||||||
|
NewCNAME: "",
|
||||||
|
RCode: dns.RcodeSuccess,
|
||||||
|
RRType: dns.TypeA,
|
||||||
|
}},
|
||||||
|
dtyp: dns.TypeA,
|
||||||
|
}, {
|
||||||
|
name: "issue3343",
|
||||||
|
host: "www.hostboth.com",
|
||||||
|
wantDNSRewrites: []*rules.DNSRewrite{{
|
||||||
|
Value: net.ParseIP("1234::5678"),
|
||||||
|
NewCNAME: "",
|
||||||
|
RCode: dns.RcodeSuccess,
|
||||||
|
RRType: dns.TypeAAAA,
|
||||||
|
}},
|
||||||
|
dtyp: dns.TypeAAAA,
|
||||||
|
}, {
|
||||||
|
name: "issue3351",
|
||||||
|
host: "bighost.com",
|
||||||
|
wantDNSRewrites: []*rules.DNSRewrite{{
|
||||||
|
Value: net.IP{1, 2, 3, 7}.To16(),
|
||||||
|
NewCNAME: "",
|
||||||
|
RCode: dns.RcodeSuccess,
|
||||||
|
RRType: dns.TypeA,
|
||||||
|
}},
|
||||||
|
dtyp: dns.TypeA,
|
||||||
|
}, {
|
||||||
|
name: "issue4008",
|
||||||
|
host: "somehost.com",
|
||||||
|
wantDNSRewrites: nil,
|
||||||
|
dtyp: dns.TypeHTTPS,
|
||||||
|
}, {
|
||||||
|
name: "issue4016",
|
||||||
|
host: "www.issue4016.com",
|
||||||
|
wantDNSRewrites: []*rules.DNSRewrite{{
|
||||||
|
Value: nil,
|
||||||
|
NewCNAME: "sub.issue4016.com",
|
||||||
|
RCode: dns.RcodeSuccess,
|
||||||
|
RRType: dns.TypeNone,
|
||||||
|
}},
|
||||||
|
dtyp: dns.TypeA,
|
||||||
|
}, {
|
||||||
|
name: "issue4016_self",
|
||||||
|
host: "sub.issue4016.com",
|
||||||
|
wantDNSRewrites: nil,
|
||||||
|
dtyp: dns.TypeA,
|
||||||
|
}}
|
||||||
|
|
||||||
|
for _, tc := range testCases {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
dnsRewrites := s.MatchRequest(&urlfilter.DNSRequest{
|
||||||
|
Hostname: tc.host,
|
||||||
|
DNSType: tc.dtyp,
|
||||||
|
})
|
||||||
|
|
||||||
|
assert.Equal(t, tc.wantDNSRewrites, dnsRewrites)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDefaultStorage_MatchRequest_Levels(t *testing.T) {
|
||||||
|
// Exact host, wildcard L2, wildcard L3.
|
||||||
|
items := []*Item{{
|
||||||
|
Domain: "host.com",
|
||||||
|
Answer: "1.1.1.1",
|
||||||
|
}, {
|
||||||
|
Domain: "*.host.com",
|
||||||
|
Answer: "2.2.2.2",
|
||||||
|
}, {
|
||||||
|
Domain: "*.sub.host.com",
|
||||||
|
Answer: "3.3.3.3",
|
||||||
|
}}
|
||||||
|
|
||||||
|
s, err := NewDefaultStorage(-1, items)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
testCases := []struct {
|
||||||
|
name string
|
||||||
|
host string
|
||||||
|
wantDNSRewrites []*rules.DNSRewrite
|
||||||
|
dtyp uint16
|
||||||
|
}{{
|
||||||
|
name: "exact_match",
|
||||||
|
host: "host.com",
|
||||||
|
wantDNSRewrites: []*rules.DNSRewrite{{
|
||||||
|
Value: net.IP{1, 1, 1, 1}.To16(),
|
||||||
|
NewCNAME: "",
|
||||||
|
RCode: dns.RcodeSuccess,
|
||||||
|
RRType: dns.TypeA,
|
||||||
|
}},
|
||||||
|
dtyp: dns.TypeA,
|
||||||
|
}, {
|
||||||
|
name: "l2_match",
|
||||||
|
host: "sub.host.com",
|
||||||
|
wantDNSRewrites: []*rules.DNSRewrite{{
|
||||||
|
Value: net.IP{2, 2, 2, 2}.To16(),
|
||||||
|
NewCNAME: "",
|
||||||
|
RCode: dns.RcodeSuccess,
|
||||||
|
RRType: dns.TypeA,
|
||||||
|
}},
|
||||||
|
dtyp: dns.TypeA,
|
||||||
|
//}, {
|
||||||
|
// TODO(d.kolyshev): This is about matching in urlfilter.
|
||||||
|
// name: "l3_match",
|
||||||
|
// host: "my.sub.host.com",
|
||||||
|
// wantDNSRewrites: []*rules.DNSRewrite{{
|
||||||
|
// Value: net.IP{3, 3, 3, 3}.To16(),
|
||||||
|
// NewCNAME: "",
|
||||||
|
// RCode: dns.RcodeSuccess,
|
||||||
|
// RRType: dns.TypeA,
|
||||||
|
// }},
|
||||||
|
// dtyp: dns.TypeA,
|
||||||
|
}}
|
||||||
|
|
||||||
|
for _, tc := range testCases {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
dnsRewrites := s.MatchRequest(&urlfilter.DNSRequest{
|
||||||
|
Hostname: tc.host,
|
||||||
|
DNSType: tc.dtyp,
|
||||||
|
})
|
||||||
|
|
||||||
|
assert.Equal(t, tc.wantDNSRewrites, dnsRewrites)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDefaultStorage_MatchRequest_ExceptionCNAME(t *testing.T) {
|
||||||
|
// Wildcard and exception for a sub-domain.
|
||||||
|
items := []*Item{{
|
||||||
|
Domain: "*.host.com",
|
||||||
|
Answer: "2.2.2.2",
|
||||||
|
}, {
|
||||||
|
Domain: "sub.host.com",
|
||||||
|
Answer: "sub.host.com",
|
||||||
|
}, {
|
||||||
|
Domain: "*.sub.host.com",
|
||||||
|
Answer: "*.sub.host.com",
|
||||||
|
}}
|
||||||
|
|
||||||
|
s, err := NewDefaultStorage(-1, items)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
testCases := []struct {
|
||||||
|
name string
|
||||||
|
host string
|
||||||
|
wantDNSRewrites []*rules.DNSRewrite
|
||||||
|
dtyp uint16
|
||||||
|
}{{
|
||||||
|
name: "match_subdomain",
|
||||||
|
host: "my.host.com",
|
||||||
|
wantDNSRewrites: []*rules.DNSRewrite{{
|
||||||
|
Value: net.IP{2, 2, 2, 2}.To16(),
|
||||||
|
NewCNAME: "",
|
||||||
|
RCode: dns.RcodeSuccess,
|
||||||
|
RRType: dns.TypeA,
|
||||||
|
}},
|
||||||
|
dtyp: dns.TypeA,
|
||||||
|
}, {
|
||||||
|
name: "exception_cname",
|
||||||
|
host: "sub.host.com",
|
||||||
|
wantDNSRewrites: nil,
|
||||||
|
dtyp: dns.TypeA,
|
||||||
|
//}, {
|
||||||
|
// TODO(d.kolyshev): This is about matching in urlfilter.
|
||||||
|
// name: "exception_wildcard",
|
||||||
|
// host: "my.sub.host.com",
|
||||||
|
// wantDNSRewrites: nil,
|
||||||
|
// dtyp: dns.TypeA,
|
||||||
|
}}
|
||||||
|
|
||||||
|
for _, tc := range testCases {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
dnsRewrites := s.MatchRequest(&urlfilter.DNSRequest{
|
||||||
|
Hostname: tc.host,
|
||||||
|
DNSType: tc.dtyp,
|
||||||
|
})
|
||||||
|
|
||||||
|
assert.Equal(t, tc.wantDNSRewrites, dnsRewrites)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDefaultStorage_MatchRequest_ExceptionIP(t *testing.T) {
|
||||||
|
// Exception for AAAA record.
|
||||||
|
items := []*Item{{
|
||||||
|
Domain: "host.com",
|
||||||
|
Answer: "1.2.3.4",
|
||||||
|
}, {
|
||||||
|
Domain: "host.com",
|
||||||
|
Answer: "AAAA",
|
||||||
|
}, {
|
||||||
|
Domain: "host2.com",
|
||||||
|
Answer: "::1",
|
||||||
|
}, {
|
||||||
|
Domain: "host2.com",
|
||||||
|
Answer: "A",
|
||||||
|
}, {
|
||||||
|
Domain: "host3.com",
|
||||||
|
Answer: "A",
|
||||||
|
}}
|
||||||
|
|
||||||
|
s, err := NewDefaultStorage(-1, items)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
testCases := []struct {
|
||||||
|
name string
|
||||||
|
host string
|
||||||
|
wantDNSRewrites []*rules.DNSRewrite
|
||||||
|
dtyp uint16
|
||||||
|
}{{
|
||||||
|
name: "match_A",
|
||||||
|
host: "host.com",
|
||||||
|
wantDNSRewrites: []*rules.DNSRewrite{{
|
||||||
|
Value: net.IP{1, 2, 3, 4}.To16(),
|
||||||
|
NewCNAME: "",
|
||||||
|
RCode: dns.RcodeSuccess,
|
||||||
|
RRType: dns.TypeA,
|
||||||
|
}},
|
||||||
|
dtyp: dns.TypeA,
|
||||||
|
}, {
|
||||||
|
name: "exception_AAAA_host.com",
|
||||||
|
host: "host.com",
|
||||||
|
wantDNSRewrites: nil,
|
||||||
|
dtyp: dns.TypeAAAA,
|
||||||
|
}, {
|
||||||
|
name: "exception_A_host2.com",
|
||||||
|
host: "host2.com",
|
||||||
|
wantDNSRewrites: nil,
|
||||||
|
dtyp: dns.TypeA,
|
||||||
|
}, {
|
||||||
|
name: "match_AAAA_host2.com",
|
||||||
|
host: "host2.com",
|
||||||
|
wantDNSRewrites: []*rules.DNSRewrite{{
|
||||||
|
Value: net.ParseIP("::1"),
|
||||||
|
NewCNAME: "",
|
||||||
|
RCode: dns.RcodeSuccess,
|
||||||
|
RRType: dns.TypeAAAA,
|
||||||
|
}},
|
||||||
|
dtyp: dns.TypeAAAA,
|
||||||
|
}, {
|
||||||
|
name: "exception_A_host3.com",
|
||||||
|
host: "host3.com",
|
||||||
|
wantDNSRewrites: nil,
|
||||||
|
dtyp: dns.TypeA,
|
||||||
|
}, {
|
||||||
|
name: "match_AAAA_host3.com",
|
||||||
|
host: "host3.com",
|
||||||
|
wantDNSRewrites: nil,
|
||||||
|
dtyp: dns.TypeAAAA,
|
||||||
|
}}
|
||||||
|
|
||||||
|
for _, tc := range testCases {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
dnsRewrites := s.MatchRequest(&urlfilter.DNSRequest{
|
||||||
|
Hostname: tc.host,
|
||||||
|
DNSType: tc.dtyp,
|
||||||
|
})
|
||||||
|
|
||||||
|
assert.Equal(t, tc.wantDNSRewrites, dnsRewrites)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,93 @@
|
||||||
|
package filtering
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/AdguardTeam/AdGuardHome/internal/aghhttp"
|
||||||
|
"github.com/AdguardTeam/golibs/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TODO(d.kolyshev): Use [rewrite.Item] instead.
|
||||||
|
type rewriteEntryJSON struct {
|
||||||
|
Domain string `json:"domain"`
|
||||||
|
Answer string `json:"answer"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DNSFilter) handleRewriteList(w http.ResponseWriter, r *http.Request) {
|
||||||
|
arr := []*rewriteEntryJSON{}
|
||||||
|
|
||||||
|
d.confLock.Lock()
|
||||||
|
for _, ent := range d.Config.Rewrites {
|
||||||
|
jsent := rewriteEntryJSON{
|
||||||
|
Domain: ent.Domain,
|
||||||
|
Answer: ent.Answer,
|
||||||
|
}
|
||||||
|
arr = append(arr, &jsent)
|
||||||
|
}
|
||||||
|
d.confLock.Unlock()
|
||||||
|
|
||||||
|
_ = aghhttp.WriteJSONResponse(w, r, arr)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DNSFilter) handleRewriteAdd(w http.ResponseWriter, r *http.Request) {
|
||||||
|
rwJSON := rewriteEntryJSON{}
|
||||||
|
err := json.NewDecoder(r.Body).Decode(&rwJSON)
|
||||||
|
if err != nil {
|
||||||
|
aghhttp.Error(r, w, http.StatusBadRequest, "json.Decode: %s", err)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
rw := &LegacyRewrite{
|
||||||
|
Domain: rwJSON.Domain,
|
||||||
|
Answer: rwJSON.Answer,
|
||||||
|
}
|
||||||
|
|
||||||
|
err = rw.normalize()
|
||||||
|
if err != nil {
|
||||||
|
// Shouldn't happen currently, since normalize only returns a non-nil
|
||||||
|
// error when a rewrite is nil, but be change-proof.
|
||||||
|
aghhttp.Error(r, w, http.StatusBadRequest, "normalizing: %s", err)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
d.confLock.Lock()
|
||||||
|
d.Config.Rewrites = append(d.Config.Rewrites, rw)
|
||||||
|
d.confLock.Unlock()
|
||||||
|
log.Debug("rewrite: added element: %s -> %s [%d]", rw.Domain, rw.Answer, len(d.Config.Rewrites))
|
||||||
|
|
||||||
|
d.Config.ConfigModified()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DNSFilter) handleRewriteDelete(w http.ResponseWriter, r *http.Request) {
|
||||||
|
jsent := rewriteEntryJSON{}
|
||||||
|
err := json.NewDecoder(r.Body).Decode(&jsent)
|
||||||
|
if err != nil {
|
||||||
|
aghhttp.Error(r, w, http.StatusBadRequest, "json.Decode: %s", err)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
entDel := &LegacyRewrite{
|
||||||
|
Domain: jsent.Domain,
|
||||||
|
Answer: jsent.Answer,
|
||||||
|
}
|
||||||
|
arr := []*LegacyRewrite{}
|
||||||
|
|
||||||
|
d.confLock.Lock()
|
||||||
|
for _, ent := range d.Config.Rewrites {
|
||||||
|
if ent.equal(entDel) {
|
||||||
|
log.Debug("rewrite: removed element: %s -> %s", ent.Domain, ent.Answer)
|
||||||
|
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
arr = append(arr, ent)
|
||||||
|
}
|
||||||
|
d.Config.Rewrites = arr
|
||||||
|
d.confLock.Unlock()
|
||||||
|
|
||||||
|
d.Config.ConfigModified()
|
||||||
|
}
|
|
@ -3,22 +3,16 @@
|
||||||
package filtering
|
package filtering
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/AdguardTeam/AdGuardHome/internal/aghhttp"
|
|
||||||
"github.com/AdguardTeam/golibs/errors"
|
"github.com/AdguardTeam/golibs/errors"
|
||||||
"github.com/AdguardTeam/golibs/log"
|
|
||||||
"github.com/miekg/dns"
|
"github.com/miekg/dns"
|
||||||
"golang.org/x/exp/slices"
|
"golang.org/x/exp/slices"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TODO(d.kolyshev): Rename this file to rewritehttp.go.
|
|
||||||
|
|
||||||
// LegacyRewrite is a single legacy DNS rewrite record.
|
// LegacyRewrite is a single legacy DNS rewrite record.
|
||||||
//
|
//
|
||||||
// Instances of *LegacyRewrite must never be nil.
|
// Instances of *LegacyRewrite must never be nil.
|
||||||
|
@ -223,86 +217,3 @@ func max(a, b int) int {
|
||||||
|
|
||||||
return b
|
return b
|
||||||
}
|
}
|
||||||
|
|
||||||
type rewriteEntryJSON struct {
|
|
||||||
Domain string `json:"domain"`
|
|
||||||
Answer string `json:"answer"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *DNSFilter) handleRewriteList(w http.ResponseWriter, r *http.Request) {
|
|
||||||
arr := []*rewriteEntryJSON{}
|
|
||||||
|
|
||||||
d.confLock.Lock()
|
|
||||||
for _, ent := range d.Config.Rewrites {
|
|
||||||
jsent := rewriteEntryJSON{
|
|
||||||
Domain: ent.Domain,
|
|
||||||
Answer: ent.Answer,
|
|
||||||
}
|
|
||||||
arr = append(arr, &jsent)
|
|
||||||
}
|
|
||||||
d.confLock.Unlock()
|
|
||||||
|
|
||||||
_ = aghhttp.WriteJSONResponse(w, r, arr)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *DNSFilter) handleRewriteAdd(w http.ResponseWriter, r *http.Request) {
|
|
||||||
rwJSON := rewriteEntryJSON{}
|
|
||||||
err := json.NewDecoder(r.Body).Decode(&rwJSON)
|
|
||||||
if err != nil {
|
|
||||||
aghhttp.Error(r, w, http.StatusBadRequest, "json.Decode: %s", err)
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
rw := &LegacyRewrite{
|
|
||||||
Domain: rwJSON.Domain,
|
|
||||||
Answer: rwJSON.Answer,
|
|
||||||
}
|
|
||||||
|
|
||||||
err = rw.normalize()
|
|
||||||
if err != nil {
|
|
||||||
// Shouldn't happen currently, since normalize only returns a non-nil
|
|
||||||
// error when a rewrite is nil, but be change-proof.
|
|
||||||
aghhttp.Error(r, w, http.StatusBadRequest, "normalizing: %s", err)
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
d.confLock.Lock()
|
|
||||||
d.Config.Rewrites = append(d.Config.Rewrites, rw)
|
|
||||||
d.confLock.Unlock()
|
|
||||||
log.Debug("rewrite: added element: %s -> %s [%d]", rw.Domain, rw.Answer, len(d.Config.Rewrites))
|
|
||||||
|
|
||||||
d.Config.ConfigModified()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *DNSFilter) handleRewriteDelete(w http.ResponseWriter, r *http.Request) {
|
|
||||||
jsent := rewriteEntryJSON{}
|
|
||||||
err := json.NewDecoder(r.Body).Decode(&jsent)
|
|
||||||
if err != nil {
|
|
||||||
aghhttp.Error(r, w, http.StatusBadRequest, "json.Decode: %s", err)
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
entDel := &LegacyRewrite{
|
|
||||||
Domain: jsent.Domain,
|
|
||||||
Answer: jsent.Answer,
|
|
||||||
}
|
|
||||||
arr := []*LegacyRewrite{}
|
|
||||||
|
|
||||||
d.confLock.Lock()
|
|
||||||
for _, ent := range d.Config.Rewrites {
|
|
||||||
if ent.equal(entDel) {
|
|
||||||
log.Debug("rewrite: removed element: %s -> %s", ent.Domain, ent.Answer)
|
|
||||||
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
arr = append(arr, ent)
|
|
||||||
}
|
|
||||||
d.Config.Rewrites = arr
|
|
||||||
d.confLock.Unlock()
|
|
||||||
|
|
||||||
d.Config.ConfigModified()
|
|
||||||
}
|
|
||||||
|
|
|
@ -10,7 +10,6 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// TODO(e.burkov): All the tests in this file may and should me merged together.
|
// TODO(e.burkov): All the tests in this file may and should me merged together.
|
||||||
// TODO(d.kolyshev): Move these tests to rewrite package.
|
|
||||||
|
|
||||||
func TestRewrites(t *testing.T) {
|
func TestRewrites(t *testing.T) {
|
||||||
d, _ := newForTest(t, nil, nil)
|
d, _ := newForTest(t, nil, nil)
|
||||||
|
|
|
@ -227,9 +227,9 @@ gocyclo --over 13 ./internal/dhcpd ./internal/filtering/ ./internal/home/
|
||||||
|
|
||||||
# Apply stricter standards to new or somewhat refactored code.
|
# Apply stricter standards to new or somewhat refactored code.
|
||||||
gocyclo --over 10 ./internal/aghio/ ./internal/aghnet/ ./internal/aghos/\
|
gocyclo --over 10 ./internal/aghio/ ./internal/aghnet/ ./internal/aghos/\
|
||||||
./internal/aghtest/ ./internal/dnsforward/ ./internal/stats/\
|
./internal/aghtest/ ./internal/dnsforward/ ./internal/filtering/rewrite/\
|
||||||
./internal/tools/ ./internal/updater/ ./internal/next/ ./internal/version/\
|
./internal/stats/ ./internal/tools/ ./internal/updater/ ./internal/next/\
|
||||||
./scripts/vetted-filters/ ./main.go
|
./internal/version/ ./scripts/vetted-filters/ ./main.go
|
||||||
|
|
||||||
ineffassign ./...
|
ineffassign ./...
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue