Skip to content

Commit

Permalink
all: allow multiple rules in dns filter results
Browse files Browse the repository at this point in the history
  • Loading branch information
ainar-g committed Dec 16, 2020
1 parent 2c56a68 commit bc59b76
Show file tree
Hide file tree
Showing 23 changed files with 500 additions and 289 deletions.
8 changes: 4 additions & 4 deletions internal/dnsfilter/blocked.go
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ func BlockedSvcKnown(s string) bool {
}

// ApplyBlockedServices - set blocked services settings for this DNS request
func (d *Dnsfilter) ApplyBlockedServices(setts *RequestFilteringSettings, list []string, global bool) {
func (d *DNSFilter) ApplyBlockedServices(setts *RequestFilteringSettings, list []string, global bool) {
setts.ServicesRules = []ServiceEntry{}
if global {
d.confLock.RLock()
Expand All @@ -210,7 +210,7 @@ func (d *Dnsfilter) ApplyBlockedServices(setts *RequestFilteringSettings, list [
}
}

func (d *Dnsfilter) handleBlockedServicesList(w http.ResponseWriter, r *http.Request) {
func (d *DNSFilter) handleBlockedServicesList(w http.ResponseWriter, r *http.Request) {
d.confLock.RLock()
list := d.Config.BlockedServices
d.confLock.RUnlock()
Expand All @@ -223,7 +223,7 @@ func (d *Dnsfilter) handleBlockedServicesList(w http.ResponseWriter, r *http.Req
}
}

func (d *Dnsfilter) handleBlockedServicesSet(w http.ResponseWriter, r *http.Request) {
func (d *DNSFilter) handleBlockedServicesSet(w http.ResponseWriter, r *http.Request) {
list := []string{}
err := json.NewDecoder(r.Body).Decode(&list)
if err != nil {
Expand All @@ -241,7 +241,7 @@ func (d *Dnsfilter) handleBlockedServicesSet(w http.ResponseWriter, r *http.Requ
}

// registerBlockedServicesHandlers - register HTTP handlers
func (d *Dnsfilter) registerBlockedServicesHandlers() {
func (d *DNSFilter) registerBlockedServicesHandlers() {
d.Config.HTTPRegister("GET", "/control/blocked_services/list", d.handleBlockedServicesList)
d.Config.HTTPRegister("POST", "/control/blocked_services/set", d.handleBlockedServicesSet)
}
130 changes: 84 additions & 46 deletions internal/dnsfilter/dnsfilter.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,8 +91,8 @@ type filtersInitializerParams struct {
blockFilters []Filter
}

// Dnsfilter holds added rules and performs hostname matches against the rules
type Dnsfilter struct {
// DNSFilter matches hostnames and DNS requests against filtering rules.
type DNSFilter struct {
rulesStorage *filterlist.RuleStorage
filteringEngine *urlfilter.DNSEngine
rulesStorageWhite *filterlist.RuleStorage
Expand Down Expand Up @@ -129,7 +129,7 @@ const (
NotFilteredNotFound Reason = iota
// NotFilteredWhiteList - the host is explicitly whitelisted
NotFilteredWhiteList
// NotFilteredError is return where there was an error during
// NotFilteredError is returned when there was an error during
// checking. Reserved, currently unused.
NotFilteredError

Expand All @@ -148,13 +148,17 @@ const (
// FilteredBlockedService - the host is blocked by "blocked services" settings
FilteredBlockedService

// ReasonRewrite - rewrite rule was applied
// ReasonRewrite is returned when there was a rewrite by
// a legacy DNS Rewrite rule.
ReasonRewrite

// RewriteEtcHosts - rewrite by /etc/hosts rule
RewriteEtcHosts
// RewriteAutoHosts is returned when there was a rewrite by
// autohosts rules (/etc/hosts and so on).
RewriteAutoHosts
)

// TODO(a.garipov): Resync with actual code names or replace completely
// in HTTP API v1.
var reasonNames = []string{
"NotFilteredNotFound",
"NotFilteredWhiteList",
Expand Down Expand Up @@ -189,7 +193,7 @@ func (r Reason) In(reasons ...Reason) bool {
}

// GetConfig - get configuration
func (d *Dnsfilter) GetConfig() RequestFilteringSettings {
func (d *DNSFilter) GetConfig() RequestFilteringSettings {
c := RequestFilteringSettings{}
// d.confLock.RLock()
c.SafeSearchEnabled = d.Config.SafeSearchEnabled
Expand All @@ -200,7 +204,7 @@ func (d *Dnsfilter) GetConfig() RequestFilteringSettings {
}

// WriteDiskConfig - write configuration
func (d *Dnsfilter) WriteDiskConfig(c *Config) {
func (d *DNSFilter) WriteDiskConfig(c *Config) {
d.confLock.Lock()
*c = d.Config
c.Rewrites = rewriteArrayDup(d.Config.Rewrites)
Expand All @@ -211,7 +215,7 @@ func (d *Dnsfilter) WriteDiskConfig(c *Config) {
// SetFilters - set new filters (synchronously or asynchronously)
// When filters are set asynchronously, the old filters continue working until the new filters are ready.
// In this case the caller must ensure that the old filter files are intact.
func (d *Dnsfilter) SetFilters(blockFilters, allowFilters []Filter, async bool) error {
func (d *DNSFilter) SetFilters(blockFilters, allowFilters []Filter, async bool) error {
if async {
params := filtersInitializerParams{
allowFilters: allowFilters,
Expand Down Expand Up @@ -245,7 +249,7 @@ func (d *Dnsfilter) SetFilters(blockFilters, allowFilters []Filter, async bool)
}

// Starts initializing new filters by signal from channel
func (d *Dnsfilter) filtersInitializer() {
func (d *DNSFilter) filtersInitializer() {
for {
params := <-d.filtersInitializerChan
err := d.initFiltering(params.allowFilters, params.blockFilters)
Expand All @@ -257,13 +261,13 @@ func (d *Dnsfilter) filtersInitializer() {
}

// Close - close the object
func (d *Dnsfilter) Close() {
func (d *DNSFilter) Close() {
d.engineLock.Lock()
defer d.engineLock.Unlock()
d.reset()
}

func (d *Dnsfilter) reset() {
func (d *DNSFilter) reset() {
var err error

if d.rulesStorage != nil {
Expand All @@ -290,25 +294,44 @@ type dnsFilterContext struct {

var gctx dnsFilterContext // global dnsfilter context

// ResultRule contains information about applied rules.
type ResultRule struct {
// FilterListID is the ID of the rule's filter list.
FilterListID int64 `json:",omitempty"`
// Text is the text of the rule.
Text string `json:",omitempty"`
// IP is the host IP. It is nil unless the rule uses the
// /etc/hosts syntax or the reason is FilteredSafeSearch.
IP net.IP `json:",omitempty"`
}

// Result holds state of hostname check
type Result struct {
IsFiltered bool `json:",omitempty"` // True if the host name is filtered
Reason Reason `json:",omitempty"` // Reason for blocking / unblocking
Rule string `json:",omitempty"` // Original rule text
IP net.IP `json:",omitempty"` // Not nil only in the case of a hosts file syntax
FilterID int64 `json:",omitempty"` // Filter ID the rule belongs to
// IsFiltered is true if the request is filtered.
IsFiltered bool `json:",omitempty"`

// for ReasonRewrite:
CanonName string `json:",omitempty"` // CNAME value
// Reason is the reason for blocking or unblocking the request.
Reason Reason `json:",omitempty"`

// for RewriteEtcHosts:
// Rules are applied rules. If Rules are not empty, each rule
// is not nil.
Rules []*ResultRule `json:",omitempty"`

// ReverseHosts is the reverse lookup rewrite result. It is
// empty unless Reason is set to RewriteAutoHosts.
ReverseHosts []string `json:",omitempty"`

// for ReasonRewrite & RewriteEtcHosts:
IPList []net.IP `json:",omitempty"` // list of IP addresses
// IPList is the lookup rewrite result. It is empty unless
// Reason is set to RewriteAutoHosts or ReasonRewrite.
IPList []net.IP `json:",omitempty"`

// CanonName is the CNAME value from the lookup rewrite result.
// It is empty unless Reason is set to ReasonRewrite.
CanonName string `json:",omitempty"`

// for FilteredBlockedService:
ServiceName string `json:",omitempty"` // Name of the blocked service
// ServiceName is the name of the blocked service. It is empty
// unless Reason is set to FilteredBlockedService.
ServiceName string `json:",omitempty"`
}

// Matched can be used to see if any match at all was found, no matter filtered or not
Expand All @@ -317,7 +340,7 @@ func (r Reason) Matched() bool {
}

// CheckHostRules tries to match the host against filtering rules only
func (d *Dnsfilter) CheckHostRules(host string, qtype uint16, setts *RequestFilteringSettings) (Result, error) {
func (d *DNSFilter) CheckHostRules(host string, qtype uint16, setts *RequestFilteringSettings) (Result, error) {
if !setts.FilteringEnabled {
return Result{}, nil
}
Expand All @@ -327,7 +350,7 @@ func (d *Dnsfilter) CheckHostRules(host string, qtype uint16, setts *RequestFilt

// CheckHost tries to match the host against filtering rules,
// then safebrowsing and parental if they are enabled
func (d *Dnsfilter) CheckHost(host string, qtype uint16, setts *RequestFilteringSettings) (Result, error) {
func (d *DNSFilter) CheckHost(host string, qtype uint16, setts *RequestFilteringSettings) (Result, error) {
// sometimes DNS clients will try to resolve ".", which is a request to get root servers
if host == "" {
return Result{Reason: NotFilteredNotFound}, nil
Expand Down Expand Up @@ -413,18 +436,18 @@ func (d *Dnsfilter) CheckHost(host string, qtype uint16, setts *RequestFiltering
return Result{}, nil
}

func (d *Dnsfilter) checkAutoHosts(host string, qtype uint16, result *Result) (matched bool) {
func (d *DNSFilter) checkAutoHosts(host string, qtype uint16, result *Result) (matched bool) {
ips := d.Config.AutoHosts.Process(host, qtype)
if ips != nil {
result.Reason = RewriteEtcHosts
result.Reason = RewriteAutoHosts
result.IPList = ips

return true
}

revHosts := d.Config.AutoHosts.ProcessReverse(host, qtype)
if len(revHosts) != 0 {
result.Reason = RewriteEtcHosts
result.Reason = RewriteAutoHosts

// TODO(a.garipov): Optimize this with a buffer.
result.ReverseHosts = make([]string, len(revHosts))
Expand All @@ -445,7 +468,7 @@ func (d *Dnsfilter) checkAutoHosts(host string, qtype uint16, result *Result) (m
// . repeat for the new domain name (Note: we return only the last CNAME)
// . Find A or AAAA record for a domain name (exact match or by wildcard)
// . if found, set IP addresses (IPv4 or IPv6 depending on qtype) in Result.IPList array
func (d *Dnsfilter) processRewrites(host string, qtype uint16) Result {
func (d *DNSFilter) processRewrites(host string, qtype uint16) Result {
var res Result

d.confLock.RLock()
Expand Down Expand Up @@ -504,9 +527,16 @@ func matchBlockedServicesRules(host string, svcs []ServiceEntry) Result {
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)

ruleText := rule.Text()
res.Rules = []*ResultRule{{
FilterListID: int64(rule.GetFilterListID()),
Text: ruleText,
}}

log.Debug("blocked services: matched rule: %s host: %s service: %s",
ruleText, host, s.Name)

return res
}
}
Expand Down Expand Up @@ -573,7 +603,7 @@ func createFilteringEngine(filters []Filter) (*filterlist.RuleStorage, *urlfilte
}

// Initialize urlfilter objects.
func (d *Dnsfilter) initFiltering(allowFilters, blockFilters []Filter) error {
func (d *DNSFilter) initFiltering(allowFilters, blockFilters []Filter) error {
rulesStorage, filteringEngine, err := createFilteringEngine(blockFilters)
if err != nil {
return err
Expand All @@ -600,7 +630,7 @@ func (d *Dnsfilter) initFiltering(allowFilters, blockFilters []Filter) error {

// matchHost is a low-level way to check only if hostname is filtered by rules,
// skipping expensive safebrowsing and parental lookups.
func (d *Dnsfilter) matchHost(host string, qtype uint16, setts RequestFilteringSettings) (Result, error) {
func (d *DNSFilter) matchHost(host string, qtype uint16, setts RequestFilteringSettings) (Result, error) {
d.engineLock.RLock()
// Keep in mind that this lock must be held no just when calling Match()
// but also while using the rules returned by it.
Expand Down Expand Up @@ -658,7 +688,8 @@ func (d *Dnsfilter) matchHost(host string, qtype uint16, setts RequestFilteringS
log.Debug("Filtering: found rule for host %q: %q list_id: %d",
host, rule.Text(), rule.GetFilterListID())
res := makeResult(rule, FilteredBlackList)
res.IP = rule.IP.To4()
res.Rules[0].IP = rule.IP.To4()

return res, nil
}

Expand All @@ -667,7 +698,8 @@ func (d *Dnsfilter) matchHost(host string, qtype uint16, setts RequestFilteringS
log.Debug("Filtering: found rule for host %q: %q list_id: %d",
host, rule.Text(), rule.GetFilterListID())
res := makeResult(rule, FilteredBlackList)
res.IP = rule.IP
res.Rules[0].IP = rule.IP

return res, nil
}

Expand All @@ -683,22 +715,28 @@ func (d *Dnsfilter) matchHost(host string, qtype uint16, setts RequestFilteringS
log.Debug("Filtering: found rule for host %q: %q list_id: %d",
host, rule.Text(), rule.GetFilterListID())
res := makeResult(rule, FilteredBlackList)
res.IP = net.IP{}
res.Rules[0].IP = net.IP{}

return res, nil
}

return Result{}, nil
}

// Construct Result object
// makeResult returns a properly constructed Result.
func makeResult(rule rules.Rule, reason Reason) Result {
res := Result{}
res.FilterID = int64(rule.GetFilterListID())
res.Rule = rule.Text()
res.Reason = reason
res := Result{
Reason: reason,
Rules: []*ResultRule{{
FilterListID: int64(rule.GetFilterListID()),
Text: rule.Text(),
}},
}

if reason == FilteredBlackList {
res.IsFiltered = true
}

return res
}

Expand All @@ -708,7 +746,7 @@ func InitModule() {
}

// New creates properly initialized DNS Filter that is ready to be used.
func New(c *Config, blockFilters []Filter) *Dnsfilter {
func New(c *Config, blockFilters []Filter) *DNSFilter {
if c != nil {
cacheConf := cache.Config{
EnableLRU: true,
Expand All @@ -730,7 +768,7 @@ func New(c *Config, blockFilters []Filter) *Dnsfilter {
}
}

d := new(Dnsfilter)
d := new(DNSFilter)

err := d.initSecurityServices()
if err != nil {
Expand Down Expand Up @@ -768,7 +806,7 @@ func New(c *Config, blockFilters []Filter) *Dnsfilter {
// Start - start the module:
// . start async filtering initializer goroutine
// . register web handlers
func (d *Dnsfilter) Start() {
func (d *DNSFilter) Start() {
d.filtersInitializerChan = make(chan filtersInitializerParams, 1)
go d.filtersInitializer()

Expand Down
Loading

0 comments on commit bc59b76

Please sign in to comment.