AdGuardHome/internal/filtering/rewrites.go

233 lines
5.5 KiB
Go
Raw Normal View History

package filtering
import (
"fmt"
"net"
"strings"
"github.com/AdguardTeam/golibs/errors"
Pull request 1964: AG-23599 use hostsfile Merge in DNS/adguard-home from AG-23599-use-hostsfile to master Squashed commit of the following: commit 4766e67a9d5faa4bc89a2a935d187ce4829f7214 Merge: 38369360b 762e5be97 Author: Eugene Burkov <E.Burkov@AdGuard.COM> Date: Tue Aug 22 16:33:54 2023 +0300 Merge branch 'master' into AG-23599-use-hostsfile commit 38369360b7d0e5c9ec373c5a06bac8792ca9cd69 Author: Eugene Burkov <E.Burkov@AdGuard.COM> Date: Mon Aug 21 18:09:15 2023 +0300 filtering: imp tests commit 1c4d4a9f9639f048173e1c949f39f9ecb6ed0347 Author: Eugene Burkov <E.Burkov@AdGuard.COM> Date: Mon Aug 21 14:00:10 2023 +0300 filtering: imp cognit, cyclo commit c50c33d7240c2812a715759fabf140e02184b729 Author: Eugene Burkov <E.Burkov@AdGuard.COM> Date: Mon Aug 21 12:57:31 2023 +0300 filtering: imp code commit 92203b16719a717a2946c0401e166b1b38ddb7bc Author: Eugene Burkov <E.Burkov@AdGuard.COM> Date: Fri Aug 18 17:39:11 2023 +0300 all: imp code, docs commit 523e8cd50f9136feede657385b7274fa6ba64131 Author: Eugene Burkov <E.Burkov@AdGuard.COM> Date: Thu Aug 17 15:14:02 2023 +0300 all: fix ipv6 commit 6ce4537132615cbdc34a0b1f326fedd2b63c355d Author: Eugene Burkov <E.Burkov@AdGuard.COM> Date: Thu Aug 17 14:17:27 2023 +0300 all: rm urlfilter from hosts commit d6666e851680c7e586325ea5970e0356ab919074 Author: Eugene Burkov <E.Burkov@AdGuard.COM> Date: Wed Aug 16 15:09:52 2023 +0300 WIP commit 4a2732960558bef6636d3c428bad4c7c830016ca Author: Eugene Burkov <E.Burkov@AdGuard.COM> Date: Wed Aug 16 14:47:13 2023 +0300 all: use hostsfile
2023-08-22 16:45:11 +03:00
"github.com/AdguardTeam/golibs/log"
"github.com/AdguardTeam/golibs/mathutil"
"github.com/miekg/dns"
"golang.org/x/exp/slices"
)
// Legacy DNS rewrites
// LegacyRewrite is a single legacy DNS rewrite record.
//
// Instances of *LegacyRewrite must never be nil.
type LegacyRewrite struct {
// Domain is the domain pattern for which this rewrite should work.
Domain string `yaml:"domain"`
// Answer is the IP address, canonical name, or one of the special
// values: "A" or "AAAA".
Answer string `yaml:"answer"`
// IP is the IP address that should be used in the response if Type is
// dns.TypeA or dns.TypeAAAA.
IP net.IP `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
}
// matchesQType returns true if the entry matches the question type qt.
func (rw *LegacyRewrite) matchesQType(qt uint16) (ok bool) {
// Add CNAMEs, since they match for all types requests.
if rw.Type == dns.TypeCNAME {
return true
}
// Reject types other than A and AAAA.
if qt != dns.TypeA && qt != dns.TypeAAAA {
return false
}
// If the types match or the entry is set to allow only the other type,
// include them.
return rw.Type == qt || rw.IP == nil
}
// normalize makes sure that the a 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) {
if rw == nil {
return errors.Error("nil rewrite entry")
}
// TODO(a.garipov): Write a case-agnostic version of strings.HasSuffix and
// use it in matchDomainWildcard instead of using strings.ToLower
// everywhere.
rw.Domain = strings.ToLower(rw.Domain)
switch rw.Answer {
case "AAAA":
rw.IP = nil
rw.Type = dns.TypeAAAA
return nil
case "A":
rw.IP = nil
rw.Type = dns.TypeA
return nil
default:
// Go on.
}
ip := net.ParseIP(rw.Answer)
if ip == nil {
rw.Type = dns.TypeCNAME
return nil
}
ip4 := ip.To4()
if ip4 != nil {
rw.IP = ip4
rw.Type = dns.TypeA
} else {
rw.IP = ip
rw.Type = dns.TypeAAAA
}
return nil
}
// isWildcard returns true if pat is a wildcard domain pattern.
func isWildcard(pat string) bool {
return len(pat) > 1 && pat[0] == '*' && pat[1] == '.'
}
// matchDomainWildcard returns true if host matches the wildcard pattern.
func matchDomainWildcard(host, wildcard string) (ok bool) {
return isWildcard(wildcard) && strings.HasSuffix(host, wildcard[1:])
}
// Compare is used to sort rewrites according to the following priority:
Pull request: 4871 imp filtering Merge in DNS/adguard-home from 4871-imp-filtering to master Closes #4871. Squashed commit of the following: commit 618e7c558447703c114332708c94ef1b34362cf9 Merge: 41ff8ab7 11e4f091 Author: Eugene Burkov <E.Burkov@AdGuard.COM> Date: Thu Sep 22 19:27:08 2022 +0300 Merge branch 'master' into 4871-imp-filtering commit 41ff8ab755a87170e7334dedcae00f01dcca238a Author: Eugene Burkov <E.Burkov@AdGuard.COM> Date: Thu Sep 22 19:26:11 2022 +0300 filtering: imp code, log commit e4ae1d1788406ffd7ef0fcc6df896a22b0c2db37 Author: Eugene Burkov <E.Burkov@AdGuard.COM> Date: Thu Sep 22 14:11:07 2022 +0300 filtering: move handlers into single func commit f7a340b4c10980f512ae935a156f02b0133a1627 Author: Eugene Burkov <E.Burkov@AdGuard.COM> Date: Wed Sep 21 19:21:09 2022 +0300 all: imp code commit e064bf4d3de0283e4bda2aaf5b9822bb8a08f4a6 Author: Eugene Burkov <E.Burkov@AdGuard.COM> Date: Tue Sep 20 20:12:16 2022 +0300 all: imp name commit e7eda3905762f0821e1be1ac3cf77e0ecbedeff4 Author: Eugene Burkov <E.Burkov@AdGuard.COM> Date: Tue Sep 20 17:51:23 2022 +0300 all: finally get rid of filtering commit 188550d873e625cc2951583bb3a2eaad036745f5 Author: Eugene Burkov <E.Burkov@AdGuard.COM> Date: Tue Sep 20 17:36:03 2022 +0300 filtering: merge refresh commit e54ed9c7952b17e66b790c835269b28fbc26f9ca Author: Eugene Burkov <E.Burkov@AdGuard.COM> Date: Tue Sep 20 17:16:23 2022 +0300 filtering: merge filters commit 32da31b754a319487d5f9d5e81e607d349b90180 Author: Eugene Burkov <E.Burkov@AdGuard.COM> Date: Tue Sep 20 14:48:13 2022 +0300 filtering: imp docs commit 43b0cafa7a27bb9b620c2ba50ccdddcf32cfcecc Author: Eugene Burkov <E.Burkov@AdGuard.COM> Date: Tue Sep 20 14:38:04 2022 +0300 all: imp code commit 253a2ea6c92815d364546e34d631e406dd604644 Author: Eugene Burkov <E.Burkov@AdGuard.COM> Date: Mon Sep 19 20:43:15 2022 +0300 filtering: rm important flag commit 1b87f08f946389d410f13412c7e486290d5e752d Author: Eugene Burkov <E.Burkov@AdGuard.COM> Date: Mon Sep 19 17:05:40 2022 +0300 all: move filtering to the package commit daa13499f1dd4fe475c4b75769e34f1eb0915bdf Author: Eugene Burkov <E.Burkov@AdGuard.COM> Date: Mon Sep 19 15:13:55 2022 +0300 all: finish merging commit d6db75eb2e1f23528e9200ea51507eb793eefa3c Author: Eugene Burkov <E.Burkov@AdGuard.COM> Date: Fri Sep 16 18:18:14 2022 +0300 all: continue merging commit 45b4c484deb7198a469aa18d719bb9dbe81e5d22 Author: Eugene Burkov <E.Burkov@AdGuard.COM> Date: Wed Sep 14 15:44:22 2022 +0300 all: merge filtering types
2022-09-23 13:23:35 +03:00
//
// 1. A and AAAA > CNAME;
// 2. wildcard > exact;
// 3. lower level wildcard > higher level wildcard;
func (rw *LegacyRewrite) Compare(b *LegacyRewrite) (res int) {
if rw.Type == dns.TypeCNAME && b.Type != dns.TypeCNAME {
return -1
} else if rw.Type != dns.TypeCNAME && b.Type == dns.TypeCNAME {
return 1
}
aIsWld, bIsWld := isWildcard(rw.Domain), isWildcard(b.Domain)
if aIsWld == bIsWld {
// Both are either wildcards or both aren't.
return len(rw.Domain) - len(b.Domain)
}
if aIsWld {
return 1
}
return -1
}
// prepareRewrites normalizes and validates all legacy DNS rewrites.
func (d *DNSFilter) prepareRewrites() (err error) {
for i, r := range d.Rewrites {
err = r.normalize()
if err != nil {
return fmt.Errorf("at index %d: %w", i, err)
}
}
return nil
}
// findRewrites returns the list of matched rewrite entries. If rewrites are
// empty, but matched is true, the domain is found among the rewrite rules but
// not for this question type.
//
// The result priority is: CNAME, then A and AAAA; exact, then wildcard. If the
// host is matched exactly, wildcard entries aren't returned. If the host
// matched by wildcards, return the most specific for the question type.
func findRewrites(
entries []*LegacyRewrite,
host string,
qtype uint16,
) (rewrites []*LegacyRewrite, matched bool) {
for _, e := range entries {
if e.Domain != host && !matchDomainWildcard(host, e.Domain) {
continue
}
matched = true
if e.matchesQType(qtype) {
rewrites = append(rewrites, e)
}
}
if len(rewrites) == 0 {
return nil, matched
}
slices.SortFunc(rewrites, (*LegacyRewrite).Compare)
for i, r := range rewrites {
if isWildcard(r.Domain) {
// Don't use rewrites[:0], because we need to return at least one
// item here.
rewrites = rewrites[:mathutil.Max(1, i)]
break
}
}
return rewrites, matched
}
Pull request 1964: AG-23599 use hostsfile Merge in DNS/adguard-home from AG-23599-use-hostsfile to master Squashed commit of the following: commit 4766e67a9d5faa4bc89a2a935d187ce4829f7214 Merge: 38369360b 762e5be97 Author: Eugene Burkov <E.Burkov@AdGuard.COM> Date: Tue Aug 22 16:33:54 2023 +0300 Merge branch 'master' into AG-23599-use-hostsfile commit 38369360b7d0e5c9ec373c5a06bac8792ca9cd69 Author: Eugene Burkov <E.Burkov@AdGuard.COM> Date: Mon Aug 21 18:09:15 2023 +0300 filtering: imp tests commit 1c4d4a9f9639f048173e1c949f39f9ecb6ed0347 Author: Eugene Burkov <E.Burkov@AdGuard.COM> Date: Mon Aug 21 14:00:10 2023 +0300 filtering: imp cognit, cyclo commit c50c33d7240c2812a715759fabf140e02184b729 Author: Eugene Burkov <E.Burkov@AdGuard.COM> Date: Mon Aug 21 12:57:31 2023 +0300 filtering: imp code commit 92203b16719a717a2946c0401e166b1b38ddb7bc Author: Eugene Burkov <E.Burkov@AdGuard.COM> Date: Fri Aug 18 17:39:11 2023 +0300 all: imp code, docs commit 523e8cd50f9136feede657385b7274fa6ba64131 Author: Eugene Burkov <E.Burkov@AdGuard.COM> Date: Thu Aug 17 15:14:02 2023 +0300 all: fix ipv6 commit 6ce4537132615cbdc34a0b1f326fedd2b63c355d Author: Eugene Burkov <E.Burkov@AdGuard.COM> Date: Thu Aug 17 14:17:27 2023 +0300 all: rm urlfilter from hosts commit d6666e851680c7e586325ea5970e0356ab919074 Author: Eugene Burkov <E.Burkov@AdGuard.COM> Date: Wed Aug 16 15:09:52 2023 +0300 WIP commit 4a2732960558bef6636d3c428bad4c7c830016ca Author: Eugene Burkov <E.Burkov@AdGuard.COM> Date: Wed Aug 16 14:47:13 2023 +0300 all: use hostsfile
2023-08-22 16:45:11 +03:00
// setRewriteResult sets the Reason or IPList of res if necessary. res must not
// be nil.
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 {
// "A"/"AAAA" exception: allow getting from upstream.
res.Reason = NotFilteredNotFound
return
}
res.IPList = append(res.IPList, rw.IP)
log.Debug("rewrite: a/aaaa for %s is %s", host, rw.IP)
}
}
}
// cloneRewrites returns a deep copy of entries.
func cloneRewrites(entries []*LegacyRewrite) (clone []*LegacyRewrite) {
clone = make([]*LegacyRewrite, len(entries))
for i, rw := range entries {
clone[i] = rw.clone()
}
return clone
}