+ dnsfilter: use global and per-client BlockedServices array

This commit is contained in:
Simon Zolin 2019-07-23 12:21:37 +03:00
parent 04a477c14a
commit e81a9c7d56
7 changed files with 156 additions and 4 deletions

View File

@ -39,12 +39,19 @@ const defaultParentalURL = "%s://%s/check-parental-control-hash?prefixes=%s&sens
const defaultParentalSensitivity = 13 // use "TEEN" by default const defaultParentalSensitivity = 13 // use "TEEN" by default
const maxDialCacheSize = 2 // the number of host names for safebrowsing and parental control 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 // RequestFilteringSettings is custom filtering settings
type RequestFilteringSettings struct { type RequestFilteringSettings struct {
FilteringEnabled bool FilteringEnabled bool
SafeSearchEnabled bool SafeSearchEnabled bool
SafeBrowsingEnabled bool SafeBrowsingEnabled bool
ParentalEnabled bool ParentalEnabled bool
ServicesRules []ServiceEntry
} }
// RewriteEntry is a rewrite array element // RewriteEntry is a rewrite array element
@ -139,6 +146,8 @@ const (
FilteredInvalid FilteredInvalid
// FilteredSafeSearch - the host was replaced with safesearch variant // FilteredSafeSearch - the host was replaced with safesearch variant
FilteredSafeSearch FilteredSafeSearch
// FilteredBlockedService - the host is blocked by "blocked services" settings
FilteredBlockedService
// ReasonRewrite - rewrite rule was applied // ReasonRewrite - rewrite rule was applied
ReasonRewrite ReasonRewrite
@ -155,6 +164,7 @@ func (i Reason) String() string {
"FilteredParental", "FilteredParental",
"FilteredInvalid", "FilteredInvalid",
"FilteredSafeSearch", "FilteredSafeSearch",
"FilteredBlockedService",
"Rewrite", "Rewrite",
} }
@ -185,6 +195,9 @@ type Result struct {
// for ReasonRewrite: // for ReasonRewrite:
CanonName string `json:",omitempty"` // CNAME value CanonName string `json:",omitempty"` // CNAME value
IPList []net.IP `json:",omitempty"` // list of IP addresses 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 // 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.SafeSearchEnabled = d.SafeSearchEnabled
setts.SafeBrowsingEnabled = d.SafeBrowsingEnabled setts.SafeBrowsingEnabled = d.SafeBrowsingEnabled
setts.ParentalEnabled = d.ParentalEnabled setts.ParentalEnabled = d.ParentalEnabled
if len(clientAddr) != 0 && d.FilterHandler != nil { if d.FilterHandler != nil {
d.FilterHandler(clientAddr, &setts) 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 // check safeSearch if no match
if setts.SafeSearchEnabled { if setts.SafeSearchEnabled {
result, err = d.checkSafeSearch(host) result, err = d.checkSafeSearch(host)
@ -326,6 +346,26 @@ func (d *Dnsfilter) processRewrites(host string, qtype uint16) Result {
return res 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) { func setCacheResult(cache *fastcache.Cache, host string, res Result) {
var buf bytes.Buffer var buf bytes.Buffer
enc := gob.NewEncoder(&buf) enc := gob.NewEncoder(&buf)

View File

@ -10,8 +10,10 @@ import (
"testing" "testing"
"time" "time"
"github.com/AdguardTeam/urlfilter"
"github.com/bluele/gcache" "github.com/bluele/gcache"
"github.com/miekg/dns" "github.com/miekg/dns"
"github.com/stretchr/testify/assert"
) )
// HELPERS // HELPERS
@ -453,6 +455,12 @@ func applyClientSettings(clientAddr string, setts *RequestFilteringSettings) {
setts.FilteringEnabled = false setts.FilteringEnabled = false
setts.ParentalEnabled = false setts.ParentalEnabled = false
setts.SafeBrowsingEnabled = true 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, // Check behaviour without any per-client settings,
@ -485,6 +493,10 @@ func TestClientSettings(t *testing.T) {
t.Fatalf("CheckHost safesearch") 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: // override client settings:
d.FilterHandler = applyClientSettings d.FilterHandler = applyClientSettings
@ -505,6 +517,10 @@ func TestClientSettings(t *testing.T) {
if !r.IsFiltered || r.Reason != FilteredSafeBrowsing { if !r.IsFiltered || r.Reason != FilteredSafeBrowsing {
t.Fatalf("CheckHost 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 // BENCHMARKS

View File

@ -96,6 +96,10 @@ type FilteringConfig struct {
ParentalBlockHost string `yaml:"parental_block_host"` ParentalBlockHost string `yaml:"parental_block_host"`
SafeBrowsingBlockHost string `yaml:"safebrowsing_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"` dnsfilter.Config `yaml:",inline"`
} }

View File

@ -186,6 +186,10 @@ func (l *queryLog) getQueryLog() []map[string]interface{} {
jsonEntry["filterId"] = entry.Result.FilterID jsonEntry["filterId"] = entry.Result.FilterID
} }
if len(entry.Result.ServiceName) != 0 {
jsonEntry["service_name"] = entry.Result.ServiceName
}
answers := answerToMap(a) answers := answerToMap(a)
if answers != nil { if answers != nil {
jsonEntry["answer"] = answers jsonEntry["answer"] = answers

70
home/blocked_services.go Normal file
View File

@ -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)
}
}

View File

@ -54,6 +54,7 @@ func initDNSServer(baseDir string) {
log.Error("upstream.AddressToUpstream: %s", err) log.Error("upstream.AddressToUpstream: %s", err)
return return
} }
config.dnsctx.rdnsIP = make(map[string]bool) config.dnsctx.rdnsIP = make(map[string]bool)
config.dnsctx.rdnsChannel = make(chan string, 256) config.dnsctx.rdnsChannel = make(chan string, 256)
go asyncRDNSLoop() go asyncRDNSLoop()
@ -210,19 +211,35 @@ func generateServerConfig() (dnsforward.ServerConfig, error) {
newconfig.Upstreams = upstreamConfig.Upstreams newconfig.Upstreams = upstreamConfig.Upstreams
newconfig.DomainsReservedUpstreams = upstreamConfig.DomainReservedUpstreams newconfig.DomainsReservedUpstreams = upstreamConfig.DomainReservedUpstreams
newconfig.AllServers = config.DNS.AllServers newconfig.AllServers = config.DNS.AllServers
newconfig.FilterHandler = applyClientSettings newconfig.FilterHandler = applyAdditionalFiltering
newconfig.OnDNSRequest = onDNSRequest newconfig.OnDNSRequest = onDNSRequest
return newconfig, nil return newconfig, nil
} }
// If a client has his own settings, apply them // 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) c, ok := config.clients.Find(clientAddr)
if !ok || !c.UseOwnSettings { if !ok {
return return
} }
log.Debug("Using settings for client with IP %s", clientAddr) 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.FilteringEnabled = c.FilteringEnabled
setts.SafeSearchEnabled = c.SafeSearchEnabled setts.SafeSearchEnabled = c.SafeSearchEnabled
setts.SafeBrowsingEnabled = c.SafeBrowsingEnabled setts.SafeBrowsingEnabled = c.SafeBrowsingEnabled

View File

@ -101,6 +101,7 @@ func run(args options) {
initConfig() initConfig()
config.clients.Init() config.clients.Init()
initServices()
if !config.firstRun { if !config.firstRun {
// Do the upgrade if necessary // Do the upgrade if necessary