+ 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 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)
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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)
|
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
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue