Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/master' into AG-31863-dnsforward…
Browse files Browse the repository at this point in the history
…-tests
  • Loading branch information
Mizzick committed Apr 22, 2024
2 parents c610a6c + 762ef4a commit e7f7df2
Show file tree
Hide file tree
Showing 14 changed files with 115 additions and 280 deletions.
7 changes: 6 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,13 @@ NOTE: Add new changes BELOW THIS COMMENT.

- Support for comments in the ipset file ([#5345]).

### Changed

- Rewrite rules mechanics was changed due to improve resolving in safe search.

### Fixed

- YouTube restricted mode is not enforced by HTTPS queries on Firefox.
- Support for link-local subnets, i.e. `fe80::/16`, in the access settings
([#6192]).
- The ability to apply an invalid configuration for private RDNS, which led to
Expand Down Expand Up @@ -58,7 +63,7 @@ See also the [v0.107.48 GitHub milestone][ms-v0.107.48].

### Fixed

- Access settings not being applied to encrypted protocols ([#6890])
- Access settings not being applied to encrypted protocols ([#6890]).

[#6890]: https://github.com/AdguardTeam/AdGuardHome/issues/6890

Expand Down
7 changes: 4 additions & 3 deletions internal/client/persistent.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,9 +64,7 @@ type Persistent struct {
// upstream must be used.
UpstreamConfig *proxy.CustomUpstreamConfig

// TODO(d.kolyshev): Make SafeSearchConf a pointer.
SafeSearchConf filtering.SafeSearchConfig
SafeSearch filtering.SafeSearch
SafeSearch filtering.SafeSearch

// BlockedServices is the configuration of blocked services of a client.
BlockedServices *filtering.BlockedServices
Expand Down Expand Up @@ -95,6 +93,9 @@ type Persistent struct {
UseOwnBlockedServices bool
IgnoreQueryLog bool
IgnoreStatistics bool

// TODO(d.kolyshev): Make SafeSearchConf a pointer.
SafeSearchConf filtering.SafeSearchConfig
}

// SetTags sets the tags if they are known, otherwise logs an unknown tag.
Expand Down
34 changes: 13 additions & 21 deletions internal/dnsforward/dnsforward_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package dnsforward

import (
"cmp"
"context"
"crypto/ecdsa"
"crypto/rand"
"crypto/rsa"
Expand Down Expand Up @@ -491,19 +490,10 @@ func TestServerRace(t *testing.T) {
}

func TestSafeSearch(t *testing.T) {
resolver := &aghtest.Resolver{
OnLookupIP: func(_ context.Context, _, host string) (ips []net.IP, err error) {
ip4, ip6 := aghtest.HostToIPs(host)

return []net.IP{ip4.AsSlice(), ip6.AsSlice()}, nil
},
}

safeSearchConf := filtering.SafeSearchConfig{
Enabled: true,
Google: true,
Yandex: true,
CustomResolver: resolver,
Enabled: true,
Google: true,
Yandex: true,
}

filterConf := &filtering.Config{
Expand Down Expand Up @@ -540,7 +530,6 @@ func TestSafeSearch(t *testing.T) {
client := &dns.Client{}

yandexIP := netip.AddrFrom4([4]byte{213, 180, 193, 56})
googleIP, _ := aghtest.HostToIPs("forcesafesearch.google.com")

testCases := []struct {
host string
Expand All @@ -564,19 +553,19 @@ func TestSafeSearch(t *testing.T) {
wantCNAME: "",
}, {
host: "www.google.com.",
want: googleIP,
want: netip.Addr{},
wantCNAME: "forcesafesearch.google.com.",
}, {
host: "www.google.com.af.",
want: googleIP,
want: netip.Addr{},
wantCNAME: "forcesafesearch.google.com.",
}, {
host: "www.google.be.",
want: googleIP,
want: netip.Addr{},
wantCNAME: "forcesafesearch.google.com.",
}, {
host: "www.google.by.",
want: googleIP,
want: netip.Addr{},
wantCNAME: "forcesafesearch.google.com.",
}}

Expand All @@ -593,12 +582,15 @@ func TestSafeSearch(t *testing.T) {

cname := testutil.RequireTypeAssert[*dns.CNAME](t, reply.Answer[0])
assert.Equal(t, tc.wantCNAME, cname.Target)

a := testutil.RequireTypeAssert[*dns.A](t, reply.Answer[1])
assert.NotEmpty(t, a.A)
} else {
require.Len(t, reply.Answer, 1)
}

a := testutil.RequireTypeAssert[*dns.A](t, reply.Answer[len(reply.Answer)-1])
assert.Equal(t, net.IP(tc.want.AsSlice()), a.A)
a := testutil.RequireTypeAssert[*dns.A](t, reply.Answer[0])
assert.Equal(t, net.IP(tc.want.AsSlice()), a.A)
}
})
}
}
Expand Down
29 changes: 17 additions & 12 deletions internal/dnsforward/filter.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ func (s *Server) filterDNSRequest(dctx *dnsContext) (res *filtering.Result, err
req := pctx.Req
q := req.Question[0]
host := strings.TrimSuffix(q.Name, ".")

resVal, err := s.dnsFilter.CheckHost(host, q.Qtype, dctx.setts)
if err != nil {
return nil, fmt.Errorf("checking host %q: %w", host, err)
Expand All @@ -39,22 +40,15 @@ func (s *Server) filterDNSRequest(dctx *dnsContext) (res *filtering.Result, err
// TODO(a.garipov): Make CheckHost return a pointer.
res = &resVal
switch {
case res.IsFiltered:
log.Debug(
"dnsforward: host %q is filtered, reason: %q; rule: %q",
host,
res.Reason,
res.Rules[0].Text,
)
pctx.Res = s.genDNSFilterMessage(pctx, res)
case res.Reason.In(filtering.Rewritten, filtering.RewrittenRule) &&
res.CanonName != "" &&
len(res.IPList) == 0:
case isRewrittenCNAME(res):
// Resolve the new canonical name, not the original host name. The
// original question is readded in processFilteringAfterResponse.
dctx.origQuestion = q
req.Question[0].Name = dns.Fqdn(res.CanonName)
case res.Reason == filtering.Rewritten:
case res.IsFiltered:
log.Debug("dnsforward: host %q is filtered, reason: %q", host, res.Reason)
pctx.Res = s.genDNSFilterMessage(pctx, res)
case res.Reason.In(filtering.Rewritten, filtering.FilteredSafeSearch):
pctx.Res = s.getCNAMEWithIPs(req, res.IPList, res.CanonName)
case res.Reason.In(filtering.RewrittenRule, filtering.RewrittenAutoHosts):
if err = s.filterDNSRewrite(req, res, pctx); err != nil {
Expand All @@ -65,6 +59,17 @@ func (s *Server) filterDNSRequest(dctx *dnsContext) (res *filtering.Result, err
return res, err
}

// isRewrittenCNAME returns true if the request considered to be rewritten with
// CNAME and has no resolved IPs.
func isRewrittenCNAME(res *filtering.Result) (ok bool) {
return res.Reason.In(
filtering.Rewritten,
filtering.RewrittenRule,
filtering.FilteredSafeSearch) &&
res.CanonName != "" &&
len(res.IPList) == 0
}

// checkHostRules checks the host against filters. It is safe for concurrent
// use.
func (s *Server) checkHostRules(
Expand Down
2 changes: 1 addition & 1 deletion internal/dnsforward/msg.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ func (s *Server) genDNSFilterMessage(
) (resp *dns.Msg) {
req := dctx.Req
qt := req.Question[0].Qtype
if qt != dns.TypeA && qt != dns.TypeAAAA {
if qt != dns.TypeA && qt != dns.TypeAAAA && qt != dns.TypeHTTPS {
m, _, _ := s.dnsFilter.BlockingMode()
if m == filtering.BlockingModeNullIP {
return s.replyCompressed(req)
Expand Down
12 changes: 6 additions & 6 deletions internal/dnsforward/process.go
Original file line number Diff line number Diff line change
Expand Up @@ -598,7 +598,8 @@ func (s *Server) processFilteringAfterResponse(dctx *dnsContext) (rc resultCode)
return resultCodeSuccess
case
filtering.Rewritten,
filtering.RewrittenRule:
filtering.RewrittenRule,
filtering.FilteredSafeSearch:

if dctx.origQuestion.Name == "" {
// origQuestion is set in case we get only CNAME without IP from
Expand All @@ -608,11 +609,10 @@ func (s *Server) processFilteringAfterResponse(dctx *dnsContext) (rc resultCode)

pctx := dctx.proxyCtx
pctx.Req.Question[0], pctx.Res.Question[0] = dctx.origQuestion, dctx.origQuestion
if len(pctx.Res.Answer) > 0 {
rr := s.genAnswerCNAME(pctx.Req, res.CanonName)
answer := append([]dns.RR{rr}, pctx.Res.Answer...)
pctx.Res.Answer = answer
}

rr := s.genAnswerCNAME(pctx.Req, res.CanonName)
answer := append([]dns.RR{rr}, pctx.Res.Answer...)
pctx.Res.Answer = answer

return resultCodeSuccess
default:
Expand Down
2 changes: 2 additions & 0 deletions internal/filtering/filtering.go
Original file line number Diff line number Diff line change
Expand Up @@ -559,6 +559,8 @@ type Result struct {
Reason Reason `json:",omitempty"`

// IsFiltered is true if the request is filtered.
//
// TODO(d.kolyshev): Get rid of this flag.
IsFiltered bool `json:",omitempty"`
}

Expand Down
13 changes: 1 addition & 12 deletions internal/filtering/safesearch.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
package filtering

import "github.com/miekg/dns"

// SafeSearch interface describes a service for search engines hosts rewrites.
type SafeSearch interface {
// CheckHost checks host with safe search filter. CheckHost must be safe
Expand All @@ -16,9 +14,6 @@ type SafeSearch interface {

// SafeSearchConfig is a struct with safe search related settings.
type SafeSearchConfig struct {
// CustomResolver is the resolver used by safe search.
CustomResolver Resolver `yaml:"-" json:"-"`

// Enabled indicates if safe search is enabled entirely.
Enabled bool `yaml:"enabled" json:"enabled"`

Expand All @@ -40,13 +35,7 @@ func (d *DNSFilter) checkSafeSearch(
qtype uint16,
setts *Settings,
) (res Result, err error) {
if !setts.ProtectionEnabled ||
!setts.SafeSearchEnabled ||
(qtype != dns.TypeA && qtype != dns.TypeAAAA) {
return Result{}, nil
}

if d.safeSearch == nil {
if d.safeSearch == nil || !setts.ProtectionEnabled || !setts.SafeSearchEnabled {
return Result{}, nil
}

Expand Down
Loading

0 comments on commit e7f7df2

Please sign in to comment.