cherry-pick: filtering: restore rewrite behavior with other question types

Updates #4008.

Squashed commit of the following:

commit babbc29331cfc2603c0c3b0987f5ba926690ec3e
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Fri Dec 24 18:46:20 2021 +0300

    filtering: restore rewrite behavior with other question types
This commit is contained in:
Ainar Garipov
2021-12-24 20:14:36 +03:00
committed by Ainar Garipov
parent 9f36e57c1e
commit 84c9085516
5 changed files with 172 additions and 131 deletions

View File

@@ -507,61 +507,76 @@ func (d *DNSFilter) matchSysHostsIntl(
return res, nil
}
// Process rewrites table
// . Find CNAME for a domain name (exact match or by wildcard)
// . if found and CNAME equals to domain name - this is an exception; exit
// . if found, set domain name to canonical name
// . repeat for the new domain name (Note: we return only the last CNAME)
// . Find A or AAAA record for a domain name (exact match or by wildcard)
// . if found, set IP addresses (IPv4 or IPv6 depending on qtype) in Result.IPList array
// processRewrites performs filtering based on the legacy rewrite records.
//
// Firstly, it finds CNAME rewrites for host. If the CNAME is the same as host,
// this query isn't filtered. If it's different, repeat the process for the new
// CNAME, breaking loops in the process.
//
// Secondly, it finds A or AAAA rewrites for host and, if found, sets res.IPList
// accordingly. If the found rewrite has a special value of "A" or "AAAA", the
// result is an exception.
func (d *DNSFilter) processRewrites(host string, qtype uint16) (res Result) {
d.confLock.RLock()
defer d.confLock.RUnlock()
rr := findRewrites(d.Rewrites, host, qtype)
if len(rr) != 0 {
res.Reason = Rewritten
rewrites, matched := findRewrites(d.Rewrites, host, qtype)
if !matched {
return Result{}
}
res.Reason = Rewritten
cnames := stringutil.NewSet()
origHost := host
for len(rr) != 0 && rr[0].Type == dns.TypeCNAME {
log.Debug("rewrite: CNAME for %s is %s", host, rr[0].Answer)
for matched && len(rewrites) > 0 && rewrites[0].Type == dns.TypeCNAME {
rwAns := rewrites[0].Answer
if host == rr[0].Answer { // "host == CNAME" is an exception
log.Debug("rewrite: cname for %s is %s", host, rwAns)
if host == rwAns {
// Rewrite of a domain onto itself is an exception rule.
res.Reason = NotFilteredNotFound
return res
}
host = rr[0].Answer
host = rwAns
if cnames.Has(host) {
log.Info("rewrite: breaking CNAME redirection loop: %s. Question: %s", host, origHost)
log.Info("rewrite: cname loop for %q on %q", origHost, host)
return res
}
cnames.Add(host)
res.CanonName = rr[0].Answer
rr = findRewrites(d.Rewrites, host, qtype)
res.CanonName = host
rewrites, matched = findRewrites(d.Rewrites, host, qtype)
}
for _, r := range rr {
if r.Type == qtype && (qtype == dns.TypeA || qtype == dns.TypeAAAA) {
if r.IP == nil { // IP exception
res.Reason = NotFilteredNotFound
return res
}
res.IPList = append(res.IPList, r.IP)
log.Debug("rewrite: A/AAAA for %s is %s", host, r.IP)
}
}
setRewriteResult(&res, host, rewrites, qtype)
return res
}
// setRewriteResult sets the Reason or IPList of res if necessary. res must not
// be nil.
func setRewriteResult(res *Result, host string, rewrites []RewriteEntry, 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)
}
}
}
// matchBlockedServicesRules checks the host against the blocked services rules
// in settings, if any. The err is always nil, it is only there to make this
// a valid hostChecker function.