Skip to content

Commit

Permalink
all: safesearch package
Browse files Browse the repository at this point in the history
  • Loading branch information
Mizzick committed Feb 28, 2023
1 parent bb22643 commit 4eee323
Show file tree
Hide file tree
Showing 6 changed files with 638 additions and 0 deletions.
29 changes: 29 additions & 0 deletions internal/filtering/safesearch.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,37 @@ import (
"github.com/AdguardTeam/AdGuardHome/internal/aghhttp"
"github.com/AdguardTeam/golibs/cache"
"github.com/AdguardTeam/golibs/log"
"github.com/AdguardTeam/urlfilter/rules"
)

// SafeSearch interface.
type SafeSearch interface {
// SearchHost returns a replacement address for the search engine host.
SearchHost(host string, qtype uint16) (res *rules.DNSRewrite)

// CheckHost checks host with safe search engine.
CheckHost(host string, qtype uint16) (res Result, err error)
}

// SafeSearchConf is a struct with safe search related settings.
type SafeSearchConf struct {
// Enabled indicates if safe search is enabled entirely.
Enabled bool `yaml:"enabled" json:"enabled"`

// Services flags. Each flag indicates if the corresponding service is
// enabled or disabled.

Bing bool `yaml:"bing" json:"bing"`
DuckDuckGo bool `yaml:"duckduckgo" json:"duckduckgo"`
Google bool `yaml:"google" json:"google"`
Pixabay bool `yaml:"pixabay" json:"pixabay"`
Yandex bool `yaml:"yandex" json:"yandex"`
YouTube bool `yaml:"youtube" json:"youtube"`

// CustomResolver is the resolver used by safe search.
CustomResolver Resolver `yaml:"-"`
}

/*
expire byte[4]
res Result
Expand Down
74 changes: 74 additions & 0 deletions internal/filtering/safesearch/matcher.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package safesearch

import (
"fmt"
"strings"

"github.com/AdguardTeam/AdGuardHome/internal/filtering"
"github.com/AdguardTeam/golibs/log"
"github.com/AdguardTeam/urlfilter"
"github.com/AdguardTeam/urlfilter/filterlist"
"github.com/AdguardTeam/urlfilter/rules"
)

// Matcher interface.
type Matcher interface {
// MatchRequest returns matching safesearch rewrite rules for request.
MatchRequest(dReq *urlfilter.DNSRequest) (rules []*rules.NetworkRule)
}

// DefaultMatcher is the default safesearch matcher.
type DefaultMatcher struct {
// engine is the DNS filtering engine.
engine *urlfilter.DNSEngine
}

// NewDefaultMatcher returns new safesearch matcher. listID is used as an
// identifier of the underlying rules list.
func NewDefaultMatcher(listID int, settings filtering.SafeSearchConf) (m *DefaultMatcher, err error) {
m = &DefaultMatcher{}

err = m.resetRules(listID, settings)
if err != nil {
return nil, err
}

return m, nil
}

// type check
var _ Matcher = (*DefaultMatcher)(nil)

// MatchRequest implements the [Matcher] interface for *DefaultMatcher.
func (m *DefaultMatcher) MatchRequest(dReq *urlfilter.DNSRequest) (rules []*rules.NetworkRule) {
res, _ := m.engine.MatchRequest(dReq)

return res.DNSRewrites()
}

// resetRules resets the filtering rules.
func (m *DefaultMatcher) resetRules(listID int, settings filtering.SafeSearchConf) (err error) {
var sb strings.Builder
for service, serviceRules := range safeSearchRules {
if isServiceProtected(settings, service) {
sb.WriteString(serviceRules)
}
}

strList := &filterlist.StringRuleList{
ID: listID,
RulesText: sb.String(),
IgnoreCosmetic: true,
}

rs, err := filterlist.NewRuleStorage([]filterlist.RuleList{strList})
if err != nil {
return fmt.Errorf("creating rule storage: %w", err)
}

m.engine = urlfilter.NewDNSEngine(rs)

log.Info("safesearch: filter %d: reset %d rules", listID, m.engine.RulesCount)

return nil
}
75 changes: 75 additions & 0 deletions internal/filtering/safesearch/matcher_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package safesearch

import (
"testing"

"github.com/AdguardTeam/AdGuardHome/internal/filtering"
"github.com/AdguardTeam/urlfilter"
"github.com/miekg/dns"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

var defaultSafeSearchConf = filtering.SafeSearchConf{
Enabled: true,
Bing: true,
DuckDuckGo: true,
Google: true,
Pixabay: true,
Yandex: true,
YouTube: true,
}

func TestNewDefaultMatcher(t *testing.T) {
m, err := NewDefaultMatcher(-1, defaultSafeSearchConf)
require.NoError(t, err)
require.NotNil(t, m)
}

func TestDefaultMatcher_MatchRequest(t *testing.T) {
m, err := NewDefaultMatcher(-1, defaultSafeSearchConf)
require.NoError(t, err)

testCases := []struct {
name string
host string
want string
dtyp uint16
}{{
name: "not_filtered",
host: "test-not-filtered.com",
want: "",
dtyp: dns.TypeA,
}, {
name: "yandex",
host: "yandex.by",
want: "|yandex.by^$dnsrewrite=NOERROR;A;213.180.193.56",
dtyp: dns.TypeA,
}, {
name: "yandex_ru",
host: "yandex.ru",
want: "|yandex.ru^$dnsrewrite=NOERROR;A;213.180.193.56",
dtyp: dns.TypeA,
}, {
name: "google",
host: "www.google.com",
want: "|www.google.com^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com",
dtyp: dns.TypeA,
}}

for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
rws := m.MatchRequest(&urlfilter.DNSRequest{
Hostname: tc.host,
DNSType: tc.dtyp,
})

if tc.want != "" {
require.NotEmpty(t, rws)
assert.Equal(t, tc.want, rws[0].RuleText)
} else {
assert.Empty(t, rws)
}
})
}
}
Loading

0 comments on commit 4eee323

Please sign in to comment.