Pull request: AG-31778-fix-safesearch-https
Squashed commit of the following: commit 85ea3d985e83209e3b49119959aedd330df24d23 Author: Dimitry Kolyshev <dkolyshev@adguard.com> Date: Thu Apr 18 15:19:38 2024 +0200 all: imp docs commit b0695daddbcf191454c5e829ca4d19def8ddacbf Merge: a79f98f2f48c6242a7
Author: Dimitry Kolyshev <dkolyshev@adguard.com> Date: Wed Apr 17 11:06:49 2024 +0200 Merge remote-tracking branch 'origin/master' into AG-31778-fix-safesearch-https # Conflicts: # CHANGELOG.md commit a79f98f2f215a4a79ca4d186c0da33db936429dc Author: Dimitry Kolyshev <dkolyshev@adguard.com> Date: Wed Apr 17 11:05:34 2024 +0200 dnsforward: imp code commit b901a1169cc78313298d70cce770cd1523ccbf9f Author: Dimitry Kolyshev <dkolyshev@adguard.com> Date: Tue Apr 16 11:03:52 2024 +0200 dnsforward: imp code commit fb6e66971b1b984147ec400ceaff856e7b5710c7 Author: Dimitry Kolyshev <dkolyshev@adguard.com> Date: Tue Apr 16 10:08:51 2024 +0200 all: safesearch rewrites commit 88add21831fff7e04539f5dd299832883a6f3995 Merge: b78ad8f74201ac73cf
Author: Dimitry Kolyshev <dkolyshev@adguard.com> Date: Tue Apr 16 09:43:20 2024 +0200 Merge remote-tracking branch 'origin/master' into AG-31778-fix-safesearch-https # Conflicts: # CHANGELOG.md commit b78ad8f748c7fa52533e0541cae16bd51c201370 Author: Dimitry Kolyshev <dkolyshev@adguard.com> Date: Fri Apr 12 13:34:39 2024 +0200 all: safesearch rewrites commit fb3efbb053242c537ca872542006917b8e8ac1ff Author: Dimitry Kolyshev <dkolyshev@adguard.com> Date: Thu Apr 11 13:15:37 2024 +0200 safesearch: imp code commit 1193c704f4d30be4a2cc66e84a31c9a6020ab269 Merge: 14e823d7cff7c715c5
Author: Dimitry Kolyshev <dkolyshev@adguard.com> Date: Thu Apr 11 13:13:44 2024 +0200 Merge remote-tracking branch 'origin/master' into AG-31778-fix-safesearch-https # Conflicts: # CHANGELOG.md commit 14e823d7cc13c275c2ed04704883a94b95e29963 Author: Dimitry Kolyshev <dkolyshev@adguard.com> Date: Thu Apr 11 13:11:43 2024 +0200 all: safesearch https commit cd403a2897ae56a9059a78f24b104af5805d84ab Author: Dimitry Kolyshev <dkolyshev@adguard.com> Date: Thu Apr 11 12:09:27 2024 +0200 Revert "all: safesearch https" This reverts commit 1c9564b9b4db70f85b2f827cc06b65d2b67b08b1. commit 1c9564b9b4db70f85b2f827cc06b65d2b67b08b1 Author: Dimitry Kolyshev <dkolyshev@adguard.com> Date: Wed Apr 10 12:41:47 2024 +0200 all: safesearch https commit 5f42688fbab566973acc8dc414a992819492a9ac Author: Dimitry Kolyshev <dkolyshev@adguard.com> Date: Wed Apr 10 09:22:30 2024 +0200 filtering: imp code commit eb9bd9f47cd71cafe8eee4698a8a0d5d25dea3d3 Author: Dimitry Kolyshev <dkolyshev@adguard.com> Date: Wed Apr 10 09:19:22 2024 +0200 all: changelog commit 0c77c705a942fe83d3809a7efbc8a6baf5886762 Author: Dimitry Kolyshev <dkolyshev@adguard.com> Date: Wed Apr 10 08:55:22 2024 +0200 safesearch: imp tests commit 492a93fbb5ff54678e22a15577f509b2327c2ebe Author: Dimitry Kolyshev <dkolyshev@adguard.com> Date: Tue Apr 9 14:45:16 2024 +0200 all: changelog commit a665e7246d11503c47d48ccc714e6862f764e930 Author: Dimitry Kolyshev <dkolyshev@adguard.com> Date: Tue Apr 9 14:41:24 2024 +0200 safesearch: https req
This commit is contained in:
parent
48c6242a7b
commit
762ef4a6db
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
@ -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.
|
||||
|
|
|
@ -2,7 +2,6 @@ package dnsforward
|
|||
|
||||
import (
|
||||
"cmp"
|
||||
"context"
|
||||
"crypto/ecdsa"
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
|
@ -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{
|
||||
|
@ -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
|
||||
|
@ -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.",
|
||||
}}
|
||||
|
||||
|
@ -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)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
@ -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 {
|
||||
|
@ -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(
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
@ -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:
|
||||
|
|
|
@ -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"`
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
@ -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"`
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -3,11 +3,9 @@ package safesearch
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/binary"
|
||||
"encoding/gob"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/netip"
|
||||
"strings"
|
||||
"sync"
|
||||
|
@ -67,7 +65,6 @@ type Default struct {
|
|||
engine *urlfilter.DNSEngine
|
||||
|
||||
cache cache.Cache
|
||||
resolver filtering.Resolver
|
||||
logPrefix string
|
||||
cacheTTL time.Duration
|
||||
}
|
||||
|
@ -80,11 +77,6 @@ func NewDefault(
|
|||
cacheSize uint,
|
||||
cacheTTL time.Duration,
|
||||
) (ss *Default, err error) {
|
||||
var resolver filtering.Resolver = net.DefaultResolver
|
||||
if conf.CustomResolver != nil {
|
||||
resolver = conf.CustomResolver
|
||||
}
|
||||
|
||||
ss = &Default{
|
||||
mu: &sync.RWMutex{},
|
||||
|
||||
|
@ -92,7 +84,6 @@ func NewDefault(
|
|||
EnableLRU: true,
|
||||
MaxSize: cacheSize,
|
||||
}),
|
||||
resolver: resolver,
|
||||
// Use %s, because the client safe-search names already contain double
|
||||
// quotes.
|
||||
logPrefix: fmt.Sprintf("safesearch %s: ", name),
|
||||
|
@ -170,8 +161,11 @@ func (ss *Default) CheckHost(host string, qtype rules.RRType) (res filtering.Res
|
|||
ss.log(log.DEBUG, "lookup for %q finished in %s", host, time.Since(start))
|
||||
}()
|
||||
|
||||
if qtype != dns.TypeA && qtype != dns.TypeAAAA {
|
||||
return filtering.Result{}, fmt.Errorf("unsupported question type %s", dns.Type(qtype))
|
||||
switch qtype {
|
||||
case dns.TypeA, dns.TypeAAAA, dns.TypeHTTPS:
|
||||
// Go on.
|
||||
default:
|
||||
return filtering.Result{}, nil
|
||||
}
|
||||
|
||||
// Check cache. Return cached result if it was found
|
||||
|
@ -195,6 +189,9 @@ func (ss *Default) CheckHost(host string, qtype rules.RRType) (res filtering.Res
|
|||
}
|
||||
|
||||
res = *fltRes
|
||||
|
||||
// TODO(a.garipov): Consider switch back to resolving CNAME records IPs and
|
||||
// saving results to cache.
|
||||
ss.setCacheResult(host, qtype, res)
|
||||
|
||||
return res, nil
|
||||
|
@ -223,20 +220,13 @@ func (ss *Default) searchHost(host string, qtype rules.RRType) (res *rules.DNSRe
|
|||
}
|
||||
|
||||
// newResult creates Result object from rewrite rule. qtype must be either
|
||||
// [dns.TypeA] or [dns.TypeAAAA]. If err is nil, res is never nil, so that the
|
||||
// empty result is converted into a NODATA response.
|
||||
//
|
||||
// TODO(a.garipov): Use the main rewrite result mechanism used in
|
||||
// [dnsforward.Server.filterDNSRequest]. Now we resolve IPs for CNAME to save
|
||||
// them in the safe search cache.
|
||||
// [dns.TypeA] or [dns.TypeAAAA], or [dns.TypeHTTPS]. If err is nil, res is
|
||||
// never nil, so that the empty result is converted into a NODATA response.
|
||||
func (ss *Default) newResult(
|
||||
rewrite *rules.DNSRewrite,
|
||||
qtype rules.RRType,
|
||||
) (res *filtering.Result, err error) {
|
||||
res = &filtering.Result{
|
||||
Rules: []*filtering.ResultRule{{
|
||||
FilterListID: rulelist.URLFilterIDSafeSearch,
|
||||
}},
|
||||
Reason: filtering.FilteredSafeSearch,
|
||||
IsFiltered: true,
|
||||
}
|
||||
|
@ -247,69 +237,19 @@ func (ss *Default) newResult(
|
|||
return nil, fmt.Errorf("expected ip rewrite value, got %T(%[1]v)", rewrite.Value)
|
||||
}
|
||||
|
||||
res.Rules[0].IP = ip
|
||||
res.Rules = []*filtering.ResultRule{{
|
||||
FilterListID: rulelist.URLFilterIDSafeSearch,
|
||||
IP: ip,
|
||||
}}
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
host := rewrite.NewCNAME
|
||||
if host == "" {
|
||||
return res, nil
|
||||
}
|
||||
|
||||
res.CanonName = host
|
||||
|
||||
ss.log(log.DEBUG, "resolving %q", host)
|
||||
|
||||
ips, err := ss.resolver.LookupIP(context.Background(), qtypeToProto(qtype), host)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("resolving cname: %w", err)
|
||||
}
|
||||
|
||||
ss.log(log.DEBUG, "resolved %s", ips)
|
||||
|
||||
for _, ip := range ips {
|
||||
// TODO(a.garipov): Remove this filtering once the resolver we use
|
||||
// actually learns about network.
|
||||
addr := fitToProto(ip, qtype)
|
||||
if addr == (netip.Addr{}) {
|
||||
continue
|
||||
}
|
||||
|
||||
// TODO(e.burkov): Rules[0]?
|
||||
res.Rules[0].IP = addr
|
||||
}
|
||||
res.CanonName = rewrite.NewCNAME
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
// qtypeToProto returns "ip4" for [dns.TypeA] and "ip6" for [dns.TypeAAAA].
|
||||
// It panics for other types.
|
||||
func qtypeToProto(qtype rules.RRType) (proto string) {
|
||||
switch qtype {
|
||||
case dns.TypeA:
|
||||
return "ip4"
|
||||
case dns.TypeAAAA:
|
||||
return "ip6"
|
||||
default:
|
||||
panic(fmt.Errorf("safesearch: unsupported question type %s", dns.Type(qtype)))
|
||||
}
|
||||
}
|
||||
|
||||
// fitToProto returns a non-nil IP address if ip is the correct protocol version
|
||||
// for qtype. qtype is expected to be either [dns.TypeA] or [dns.TypeAAAA].
|
||||
func fitToProto(ip net.IP, qtype rules.RRType) (res netip.Addr) {
|
||||
if ip4 := ip.To4(); qtype == dns.TypeA {
|
||||
if ip4 != nil {
|
||||
return netip.AddrFrom4([4]byte(ip4))
|
||||
}
|
||||
} else if ip = ip.To16(); ip != nil && qtype == dns.TypeAAAA {
|
||||
return netip.AddrFrom16([16]byte(ip))
|
||||
}
|
||||
|
||||
return netip.Addr{}
|
||||
}
|
||||
|
||||
// setCacheResult stores data in cache for host. qtype is expected to be either
|
||||
// [dns.TypeA] or [dns.TypeAAAA].
|
||||
func (ss *Default) setCacheResult(host string, qtype rules.RRType, res filtering.Result) {
|
||||
|
|
|
@ -1,13 +1,10 @@
|
|||
package safesearch
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
"net/netip"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/aghtest"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/filtering"
|
||||
"github.com/AdguardTeam/urlfilter/rules"
|
||||
"github.com/miekg/dns"
|
||||
|
@ -79,47 +76,6 @@ func TestSafeSearchCacheYandex(t *testing.T) {
|
|||
assert.Equal(t, cachedValue.Rules[0].IP, yandexIP)
|
||||
}
|
||||
|
||||
func TestSafeSearchCacheGoogle(t *testing.T) {
|
||||
const domain = "www.google.ru"
|
||||
|
||||
ss := newForTest(t, filtering.SafeSearchConfig{Enabled: false})
|
||||
|
||||
res, err := ss.CheckHost(domain, testQType)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.False(t, res.IsFiltered)
|
||||
assert.Empty(t, res.Rules)
|
||||
|
||||
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
|
||||
},
|
||||
}
|
||||
|
||||
ss = newForTest(t, defaultSafeSearchConf)
|
||||
ss.resolver = resolver
|
||||
|
||||
// Lookup for safesearch domain.
|
||||
rewrite := ss.searchHost(domain, testQType)
|
||||
|
||||
wantIP, _ := aghtest.HostToIPs(rewrite.NewCNAME)
|
||||
|
||||
res, err = ss.CheckHost(domain, testQType)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, res.Rules, 1)
|
||||
|
||||
assert.Equal(t, wantIP, res.Rules[0].IP)
|
||||
|
||||
// Check cache.
|
||||
cachedValue, isFound := ss.getCachedResult(domain, testQType)
|
||||
require.True(t, isFound)
|
||||
require.Len(t, cachedValue.Rules, 1)
|
||||
|
||||
assert.Equal(t, wantIP, cachedValue.Rules[0].IP)
|
||||
}
|
||||
|
||||
const googleHost = "www.google.com"
|
||||
|
||||
var dnsRewriteSink *rules.DNSRewrite
|
||||
|
|
|
@ -7,7 +7,6 @@ import (
|
|||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/aghtest"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/filtering"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/filtering/rulelist"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/filtering/safesearch"
|
||||
|
@ -31,8 +30,6 @@ const (
|
|||
|
||||
// testConf is the default safe search configuration for tests.
|
||||
var testConf = filtering.SafeSearchConfig{
|
||||
CustomResolver: nil,
|
||||
|
||||
Enabled: true,
|
||||
|
||||
Bing: true,
|
||||
|
@ -52,61 +49,60 @@ func TestDefault_CheckHost_yandex(t *testing.T) {
|
|||
ss, err := safesearch.NewDefault(conf, "", testCacheSize, testCacheTTL)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Check host for each domain.
|
||||
for _, host := range []string{
|
||||
hosts := []string{
|
||||
"yandex.ru",
|
||||
"yAndeX.ru",
|
||||
"YANdex.COM",
|
||||
"yandex.by",
|
||||
"yandex.kz",
|
||||
"www.yandex.com",
|
||||
} {
|
||||
var res filtering.Result
|
||||
res, err = ss.CheckHost(host, testQType)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.True(t, res.IsFiltered)
|
||||
|
||||
require.Len(t, res.Rules, 1)
|
||||
|
||||
assert.Equal(t, yandexIP, res.Rules[0].IP)
|
||||
assert.Equal(t, rulelist.URLFilterIDSafeSearch, res.Rules[0].FilterListID)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDefault_CheckHost_yandexAAAA(t *testing.T) {
|
||||
conf := testConf
|
||||
ss, err := safesearch.NewDefault(conf, "", testCacheSize, testCacheTTL)
|
||||
require.NoError(t, err)
|
||||
testCases := []struct {
|
||||
want netip.Addr
|
||||
name string
|
||||
qt uint16
|
||||
}{{
|
||||
want: yandexIP,
|
||||
name: "a",
|
||||
qt: dns.TypeA,
|
||||
}, {
|
||||
want: netip.Addr{},
|
||||
name: "aaaa",
|
||||
qt: dns.TypeAAAA,
|
||||
}, {
|
||||
want: netip.Addr{},
|
||||
name: "https",
|
||||
qt: dns.TypeHTTPS,
|
||||
}}
|
||||
|
||||
res, err := ss.CheckHost("www.yandex.ru", dns.TypeAAAA)
|
||||
require.NoError(t, err)
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
for _, host := range hosts {
|
||||
// Check host for each domain.
|
||||
var res filtering.Result
|
||||
res, err = ss.CheckHost(host, tc.qt)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.True(t, res.IsFiltered)
|
||||
assert.True(t, res.IsFiltered)
|
||||
assert.Equal(t, filtering.FilteredSafeSearch, res.Reason)
|
||||
|
||||
// TODO(a.garipov): Currently, the safe-search filter returns a single rule
|
||||
// with a nil IP address. This isn't really necessary and should be changed
|
||||
// once the TODO in [safesearch.Default.newResult] is resolved.
|
||||
require.Len(t, res.Rules, 1)
|
||||
if tc.want == (netip.Addr{}) {
|
||||
assert.Empty(t, res.Rules)
|
||||
} else {
|
||||
require.Len(t, res.Rules, 1)
|
||||
|
||||
assert.Empty(t, res.Rules[0].IP)
|
||||
assert.Equal(t, rulelist.URLFilterIDSafeSearch, res.Rules[0].FilterListID)
|
||||
rule := res.Rules[0]
|
||||
assert.Equal(t, tc.want, rule.IP)
|
||||
assert.Equal(t, rulelist.URLFilterIDSafeSearch, rule.FilterListID)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDefault_CheckHost_google(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
|
||||
},
|
||||
}
|
||||
|
||||
wantIP, _ := aghtest.HostToIPs("forcesafesearch.google.com")
|
||||
|
||||
conf := testConf
|
||||
conf.CustomResolver = resolver
|
||||
ss, err := safesearch.NewDefault(conf, "", testCacheSize, testCacheTTL)
|
||||
ss, err := safesearch.NewDefault(testConf, "", testCacheSize, testCacheTTL)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Check host for each domain.
|
||||
|
@ -125,11 +121,9 @@ func TestDefault_CheckHost_google(t *testing.T) {
|
|||
require.NoError(t, err)
|
||||
|
||||
assert.True(t, res.IsFiltered)
|
||||
|
||||
require.Len(t, res.Rules, 1)
|
||||
|
||||
assert.Equal(t, wantIP, res.Rules[0].IP)
|
||||
assert.Equal(t, rulelist.URLFilterIDSafeSearch, res.Rules[0].FilterListID)
|
||||
assert.Equal(t, filtering.FilteredSafeSearch, res.Reason)
|
||||
assert.Equal(t, "forcesafesearch.google.com", res.CanonName)
|
||||
assert.Empty(t, res.Rules)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -154,17 +148,7 @@ func (r *testResolver) LookupIP(
|
|||
}
|
||||
|
||||
func TestDefault_CheckHost_duckduckgoAAAA(t *testing.T) {
|
||||
conf := testConf
|
||||
conf.CustomResolver = &testResolver{
|
||||
OnLookupIP: func(_ context.Context, network, host string) (ips []net.IP, err error) {
|
||||
assert.Equal(t, "ip6", network)
|
||||
assert.Equal(t, "safe.duckduckgo.com", host)
|
||||
|
||||
return nil, nil
|
||||
},
|
||||
}
|
||||
|
||||
ss, err := safesearch.NewDefault(conf, "", testCacheSize, testCacheTTL)
|
||||
ss, err := safesearch.NewDefault(testConf, "", testCacheSize, testCacheTTL)
|
||||
require.NoError(t, err)
|
||||
|
||||
// The DuckDuckGo safe-search addresses are resolved through CNAMEs, but
|
||||
|
@ -174,14 +158,9 @@ func TestDefault_CheckHost_duckduckgoAAAA(t *testing.T) {
|
|||
require.NoError(t, err)
|
||||
|
||||
assert.True(t, res.IsFiltered)
|
||||
|
||||
// TODO(a.garipov): Currently, the safe-search filter returns a single rule
|
||||
// with a nil IP address. This isn't really necessary and should be changed
|
||||
// once the TODO in [safesearch.Default.newResult] is resolved.
|
||||
require.Len(t, res.Rules, 1)
|
||||
|
||||
assert.Empty(t, res.Rules[0].IP)
|
||||
assert.Equal(t, rulelist.URLFilterIDSafeSearch, res.Rules[0].FilterListID)
|
||||
assert.Equal(t, filtering.FilteredSafeSearch, res.Reason)
|
||||
assert.Equal(t, "safe.duckduckgo.com", res.CanonName)
|
||||
assert.Empty(t, res.Rules)
|
||||
}
|
||||
|
||||
func TestDefault_Update(t *testing.T) {
|
||||
|
|
|
@ -249,8 +249,6 @@ func (o *clientObject) toPersistent(
|
|||
}
|
||||
|
||||
if o.SafeSearchConf.Enabled {
|
||||
o.SafeSearchConf.CustomResolver = safeSearchResolver{}
|
||||
|
||||
err = cli.SetSafeSearch(
|
||||
o.SafeSearchConf,
|
||||
filteringConf.SafeSearchCacheSize,
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package home
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/netip"
|
||||
|
@ -528,36 +527,6 @@ func closeDNSServer() {
|
|||
log.Debug("all dns modules are closed")
|
||||
}
|
||||
|
||||
// safeSearchResolver is a [filtering.Resolver] implementation used for safe
|
||||
// search.
|
||||
type safeSearchResolver struct{}
|
||||
|
||||
// type check
|
||||
var _ filtering.Resolver = safeSearchResolver{}
|
||||
|
||||
// LookupIP implements [filtering.Resolver] interface for safeSearchResolver.
|
||||
// It returns the slice of net.Addr with IPv4 and IPv6 instances.
|
||||
func (r safeSearchResolver) LookupIP(
|
||||
ctx context.Context,
|
||||
network string,
|
||||
host string,
|
||||
) (ips []net.IP, err error) {
|
||||
addrs, err := Context.dnsServer.Resolve(ctx, network, host)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(addrs) == 0 {
|
||||
return nil, fmt.Errorf("couldn't lookup host: %s", host)
|
||||
}
|
||||
|
||||
for _, a := range addrs {
|
||||
ips = append(ips, a.AsSlice())
|
||||
}
|
||||
|
||||
return ips, nil
|
||||
}
|
||||
|
||||
// checkStatsAndQuerylogDirs checks and returns directory paths to store
|
||||
// statistics and query log.
|
||||
func checkStatsAndQuerylogDirs(
|
||||
|
|
|
@ -439,7 +439,6 @@ func setupDNSFilteringConf(conf *filtering.Config) (err error) {
|
|||
conf.ParentalBlockHost = host
|
||||
}
|
||||
|
||||
conf.SafeSearchConf.CustomResolver = safeSearchResolver{}
|
||||
conf.SafeSearch, err = safesearch.NewDefault(
|
||||
conf.SafeSearchConf,
|
||||
"default",
|
||||
|
|
Loading…
Reference in New Issue