+ dnsfilter: use global and per-client BlockedServices array
This commit is contained in:
parent
04a477c14a
commit
e81a9c7d56
|
@ -39,12 +39,19 @@ const defaultParentalURL = "%s://%s/check-parental-control-hash?prefixes=%s&sens
|
|||
const defaultParentalSensitivity = 13 // use "TEEN" by default
|
||||
const maxDialCacheSize = 2 // the number of host names for safebrowsing and parental control
|
||||
|
||||
// ServiceEntry - blocked service array element
|
||||
type ServiceEntry struct {
|
||||
Name string
|
||||
Rules []*urlfilter.NetworkRule
|
||||
}
|
||||
|
||||
// RequestFilteringSettings is custom filtering settings
|
||||
type RequestFilteringSettings struct {
|
||||
FilteringEnabled bool
|
||||
SafeSearchEnabled bool
|
||||
SafeBrowsingEnabled bool
|
||||
ParentalEnabled bool
|
||||
ServicesRules []ServiceEntry
|
||||
}
|
||||
|
||||
// RewriteEntry is a rewrite array element
|
||||
|
@ -139,6 +146,8 @@ const (
|
|||
FilteredInvalid
|
||||
// FilteredSafeSearch - the host was replaced with safesearch variant
|
||||
FilteredSafeSearch
|
||||
// FilteredBlockedService - the host is blocked by "blocked services" settings
|
||||
FilteredBlockedService
|
||||
|
||||
// ReasonRewrite - rewrite rule was applied
|
||||
ReasonRewrite
|
||||
|
@ -155,6 +164,7 @@ func (i Reason) String() string {
|
|||
"FilteredParental",
|
||||
"FilteredInvalid",
|
||||
"FilteredSafeSearch",
|
||||
"FilteredBlockedService",
|
||||
|
||||
"Rewrite",
|
||||
}
|
||||
|
@ -185,6 +195,9 @@ type Result struct {
|
|||
// for ReasonRewrite:
|
||||
CanonName string `json:",omitempty"` // CNAME value
|
||||
IPList []net.IP `json:",omitempty"` // list of IP addresses
|
||||
|
||||
// for FilteredBlockedService:
|
||||
ServiceName string `json:",omitempty"` // Name of the blocked service
|
||||
}
|
||||
|
||||
// Matched can be used to see if any match at all was found, no matter filtered or not
|
||||
|
@ -209,7 +222,7 @@ func (d *Dnsfilter) CheckHost(host string, qtype uint16, clientAddr string) (Res
|
|||
setts.SafeSearchEnabled = d.SafeSearchEnabled
|
||||
setts.SafeBrowsingEnabled = d.SafeBrowsingEnabled
|
||||
setts.ParentalEnabled = d.ParentalEnabled
|
||||
if len(clientAddr) != 0 && d.FilterHandler != nil {
|
||||
if d.FilterHandler != nil {
|
||||
d.FilterHandler(clientAddr, &setts)
|
||||
}
|
||||
|
||||
|
@ -232,6 +245,13 @@ func (d *Dnsfilter) CheckHost(host string, qtype uint16, clientAddr string) (Res
|
|||
}
|
||||
}
|
||||
|
||||
if len(setts.ServicesRules) != 0 {
|
||||
result = matchBlockedServicesRules(host, setts.ServicesRules)
|
||||
if result.Reason.Matched() {
|
||||
return result, nil
|
||||
}
|
||||
}
|
||||
|
||||
// check safeSearch if no match
|
||||
if setts.SafeSearchEnabled {
|
||||
result, err = d.checkSafeSearch(host)
|
||||
|
@ -326,6 +346,26 @@ func (d *Dnsfilter) processRewrites(host string, qtype uint16) Result {
|
|||
return res
|
||||
}
|
||||
|
||||
func matchBlockedServicesRules(host string, svcs []ServiceEntry) Result {
|
||||
req := urlfilter.NewRequestForHostname(host)
|
||||
res := Result{}
|
||||
|
||||
for _, s := range svcs {
|
||||
for _, rule := range s.Rules {
|
||||
if rule.Match(req) {
|
||||
res.Reason = FilteredBlockedService
|
||||
res.IsFiltered = true
|
||||
res.ServiceName = s.Name
|
||||
res.Rule = rule.Text()
|
||||
log.Debug("Blocked Services: matched rule: %s host: %s service: %s",
|
||||
res.Rule, host, s.Name)
|
||||
return res
|
||||
}
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
func setCacheResult(cache *fastcache.Cache, host string, res Result) {
|
||||
var buf bytes.Buffer
|
||||
enc := gob.NewEncoder(&buf)
|
||||
|
|
|
@ -10,8 +10,10 @@ import (
|
|||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/AdguardTeam/urlfilter"
|
||||
"github.com/bluele/gcache"
|
||||
"github.com/miekg/dns"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
// HELPERS
|
||||
|
@ -453,6 +455,12 @@ func applyClientSettings(clientAddr string, setts *RequestFilteringSettings) {
|
|||
setts.FilteringEnabled = false
|
||||
setts.ParentalEnabled = false
|
||||
setts.SafeBrowsingEnabled = true
|
||||
|
||||
rule, _ := urlfilter.NewNetworkRule("||facebook.com^", 0)
|
||||
s := ServiceEntry{}
|
||||
s.Name = "facebook"
|
||||
s.Rules = []*urlfilter.NetworkRule{rule}
|
||||
setts.ServicesRules = append(setts.ServicesRules, s)
|
||||
}
|
||||
|
||||
// Check behaviour without any per-client settings,
|
||||
|
@ -485,6 +493,10 @@ func TestClientSettings(t *testing.T) {
|
|||
t.Fatalf("CheckHost safesearch")
|
||||
}
|
||||
|
||||
// not blocked
|
||||
r, _ = d.CheckHost("facebook.com", dns.TypeA, "1.1.1.1")
|
||||
assert.True(t, !r.IsFiltered)
|
||||
|
||||
// override client settings:
|
||||
d.FilterHandler = applyClientSettings
|
||||
|
||||
|
@ -505,6 +517,10 @@ func TestClientSettings(t *testing.T) {
|
|||
if !r.IsFiltered || r.Reason != FilteredSafeBrowsing {
|
||||
t.Fatalf("CheckHost FilteredSafeBrowsing")
|
||||
}
|
||||
|
||||
// blocked by additional rules
|
||||
r, _ = d.CheckHost("facebook.com", dns.TypeA, "1.1.1.1")
|
||||
assert.True(t, r.IsFiltered && r.Reason == FilteredBlockedService)
|
||||
}
|
||||
|
||||
// BENCHMARKS
|
||||
|
|
|
@ -96,6 +96,10 @@ type FilteringConfig struct {
|
|||
ParentalBlockHost string `yaml:"parental_block_host"`
|
||||
SafeBrowsingBlockHost string `yaml:"safebrowsing_block_host"`
|
||||
|
||||
// Names of services to block (globally).
|
||||
// Per-client settings can override this configuration.
|
||||
BlockedServices []string `json:"blocked_services"`
|
||||
|
||||
dnsfilter.Config `yaml:",inline"`
|
||||
}
|
||||
|
||||
|
|
|
@ -186,6 +186,10 @@ func (l *queryLog) getQueryLog() []map[string]interface{} {
|
|||
jsonEntry["filterId"] = entry.Result.FilterID
|
||||
}
|
||||
|
||||
if len(entry.Result.ServiceName) != 0 {
|
||||
jsonEntry["service_name"] = entry.Result.ServiceName
|
||||
}
|
||||
|
||||
answers := answerToMap(a)
|
||||
if answers != nil {
|
||||
jsonEntry["answer"] = answers
|
||||
|
|
|
@ -0,0 +1,70 @@
|
|||
package home
|
||||
|
||||
import (
|
||||
"github.com/AdguardTeam/AdGuardHome/dnsfilter"
|
||||
"github.com/AdguardTeam/golibs/log"
|
||||
"github.com/AdguardTeam/urlfilter"
|
||||
)
|
||||
|
||||
var serviceRules map[string][]*urlfilter.NetworkRule // service name -> filtering rules
|
||||
|
||||
type svc struct {
|
||||
name string
|
||||
rules []string
|
||||
}
|
||||
|
||||
// Keep in sync with:
|
||||
// client/src/helpers/constants.js
|
||||
// client/src/components/ui/Icons.js
|
||||
var serviceRulesArray = []svc{
|
||||
{"whatsapp", []string{"||whatsapp.net^"}},
|
||||
{"facebook", []string{"||facebook.com^"}},
|
||||
{"twitter", []string{"||twitter.com^", "||t.co^", "||twimg.com^"}},
|
||||
{"youtube", []string{"||youtube.com^", "||ytimg.com^"}},
|
||||
{"messenger", []string{"||fb.com^", "||facebook.com^"}},
|
||||
{"twitch", []string{"||twitch.tv^", "||ttvnw.net^"}},
|
||||
{"netflix", []string{"||nflxext.com^", "||netflix.com^"}},
|
||||
{"instagram", []string{"||instagram.com^"}},
|
||||
{"snapchat", []string{"||snapchat.com^"}},
|
||||
{"discord", []string{"||discord.gg^", "||discordapp.net^", "||discordapp.com^"}},
|
||||
{"ok", []string{"||ok.ru^"}},
|
||||
{"skype", []string{"||skype.com^"}},
|
||||
{"vk", []string{"||vk.com^"}},
|
||||
{"steam", []string{"||steam.com^"}},
|
||||
{"mail_ru", []string{"||mail.ru^"}},
|
||||
}
|
||||
|
||||
// convert array to map
|
||||
func initServices() {
|
||||
serviceRules = make(map[string][]*urlfilter.NetworkRule)
|
||||
for _, s := range serviceRulesArray {
|
||||
rules := []*urlfilter.NetworkRule{}
|
||||
for _, text := range s.rules {
|
||||
rule, err := urlfilter.NewNetworkRule(text, 0)
|
||||
if err != nil {
|
||||
log.Error("urlfilter.NewNetworkRule: %s rule: %s", err, text)
|
||||
continue
|
||||
}
|
||||
rules = append(rules, rule)
|
||||
}
|
||||
serviceRules[s.name] = rules
|
||||
}
|
||||
}
|
||||
|
||||
// ApplyBlockedServices - set blocked services settings for this DNS request
|
||||
func ApplyBlockedServices(setts *dnsfilter.RequestFilteringSettings, list []string) {
|
||||
setts.ServicesRules = []dnsfilter.ServiceEntry{}
|
||||
for _, name := range list {
|
||||
rules, ok := serviceRules[name]
|
||||
|
||||
if !ok {
|
||||
log.Error("unknown service name: %s", name)
|
||||
continue
|
||||
}
|
||||
|
||||
s := dnsfilter.ServiceEntry{}
|
||||
s.Name = name
|
||||
s.Rules = rules
|
||||
setts.ServicesRules = append(setts.ServicesRules, s)
|
||||
}
|
||||
}
|
23
home/dns.go
23
home/dns.go
|
@ -54,6 +54,7 @@ func initDNSServer(baseDir string) {
|
|||
log.Error("upstream.AddressToUpstream: %s", err)
|
||||
return
|
||||
}
|
||||
|
||||
config.dnsctx.rdnsIP = make(map[string]bool)
|
||||
config.dnsctx.rdnsChannel = make(chan string, 256)
|
||||
go asyncRDNSLoop()
|
||||
|
@ -210,19 +211,35 @@ func generateServerConfig() (dnsforward.ServerConfig, error) {
|
|||
newconfig.Upstreams = upstreamConfig.Upstreams
|
||||
newconfig.DomainsReservedUpstreams = upstreamConfig.DomainReservedUpstreams
|
||||
newconfig.AllServers = config.DNS.AllServers
|
||||
newconfig.FilterHandler = applyClientSettings
|
||||
newconfig.FilterHandler = applyAdditionalFiltering
|
||||
newconfig.OnDNSRequest = onDNSRequest
|
||||
return newconfig, nil
|
||||
}
|
||||
|
||||
// If a client has his own settings, apply them
|
||||
func applyClientSettings(clientAddr string, setts *dnsfilter.RequestFilteringSettings) {
|
||||
func applyAdditionalFiltering(clientAddr string, setts *dnsfilter.RequestFilteringSettings) {
|
||||
|
||||
ApplyBlockedServices(setts, config.DNS.BlockedServices)
|
||||
|
||||
if len(clientAddr) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
c, ok := config.clients.Find(clientAddr)
|
||||
if !ok || !c.UseOwnSettings {
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
log.Debug("Using settings for client with IP %s", clientAddr)
|
||||
|
||||
if c.UseOwnBlockedServices {
|
||||
ApplyBlockedServices(setts, c.BlockedServices)
|
||||
}
|
||||
|
||||
if !c.UseOwnSettings {
|
||||
return
|
||||
}
|
||||
|
||||
setts.FilteringEnabled = c.FilteringEnabled
|
||||
setts.SafeSearchEnabled = c.SafeSearchEnabled
|
||||
setts.SafeBrowsingEnabled = c.SafeBrowsingEnabled
|
||||
|
|
|
@ -101,6 +101,7 @@ func run(args options) {
|
|||
|
||||
initConfig()
|
||||
config.clients.Init()
|
||||
initServices()
|
||||
|
||||
if !config.firstRun {
|
||||
// Do the upgrade if necessary
|
||||
|
|
Loading…
Reference in New Issue