AdGuardHome/internal/filtering/blocked.go
Dimitry Kolyshev f84ff2bd05 Pull request: AG-25263 dns config
Merge in DNS/adguard-home from AG-25263-dns-config to master

Squashed commit of the following:

commit 478b607526391af65de67d6d7f1d904198610cdf
Merge: b944d12fa 51340adb3
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Mon Sep 4 18:04:56 2023 +0400

    Merge remote-tracking branch 'origin/master' into AG-25263-dns-config

commit b944d12fa812b05b9d9f22d2287425ca36630329
Merge: b474f712f 0182b9ec1
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Fri Sep 1 09:13:36 2023 +0400

    Merge remote-tracking branch 'origin/master' into AG-25263-dns-config

    # Conflicts:
    #	internal/dnsforward/dnsforward.go

commit b474f712f64daa1a7d7e32d89edc901d2f273c9a
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Fri Sep 1 09:11:17 2023 +0400

    all: imp code

commit 635a316b8244f13d90a8fe2209f1673c0765aaa9
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Wed Aug 30 16:18:25 2023 +0300

    all: dnsfilter rm config embed

commit 5aa6212e89bc38e3d283b8d6b1a78726d10b3f3a
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Wed Aug 30 12:45:01 2023 +0300

    all: dnsfilter rm config embed
2023-09-04 17:18:43 +03:00

215 lines
5.2 KiB
Go

package filtering
import (
"encoding/json"
"fmt"
"net/http"
"time"
"github.com/AdguardTeam/AdGuardHome/internal/aghhttp"
"github.com/AdguardTeam/AdGuardHome/internal/schedule"
"github.com/AdguardTeam/golibs/log"
"github.com/AdguardTeam/urlfilter/rules"
"golang.org/x/exp/slices"
)
// serviceRules maps a service ID to its filtering rules.
var serviceRules map[string][]*rules.NetworkRule
// serviceIDs contains service IDs sorted alphabetically.
var serviceIDs []string
// initBlockedServices initializes package-level blocked service data.
func initBlockedServices() {
l := len(blockedServices)
serviceIDs = make([]string, l)
serviceRules = make(map[string][]*rules.NetworkRule, l)
for i, s := range blockedServices {
netRules := make([]*rules.NetworkRule, 0, len(s.Rules))
for _, text := range s.Rules {
rule, err := rules.NewNetworkRule(text, BlockedSvcsListID)
if err != nil {
log.Error("parsing blocked service %q rule %q: %s", s.ID, text, err)
continue
}
netRules = append(netRules, rule)
}
serviceIDs[i] = s.ID
serviceRules[s.ID] = netRules
}
slices.Sort(serviceIDs)
log.Debug("filtering: initialized %d services", l)
}
// BlockedServices is the configuration of blocked services.
type BlockedServices struct {
// Schedule is blocked services schedule for every day of the week.
Schedule *schedule.Weekly `json:"schedule" yaml:"schedule"`
// IDs is the names of blocked services.
IDs []string `json:"ids" yaml:"ids"`
}
// Clone returns a deep copy of blocked services.
func (s *BlockedServices) Clone() (c *BlockedServices) {
if s == nil {
return nil
}
return &BlockedServices{
Schedule: s.Schedule.Clone(),
IDs: slices.Clone(s.IDs),
}
}
// Validate returns an error if blocked services contain unknown service ID. s
// must not be nil.
func (s *BlockedServices) Validate() (err error) {
for _, id := range s.IDs {
_, ok := serviceRules[id]
if !ok {
return fmt.Errorf("unknown blocked-service %q", id)
}
}
return nil
}
// ApplyBlockedServices - set blocked services settings for this DNS request
func (d *DNSFilter) ApplyBlockedServices(setts *Settings) {
d.confMu.RLock()
defer d.confMu.RUnlock()
setts.ServicesRules = []ServiceEntry{}
bsvc := d.conf.BlockedServices
// TODO(s.chzhen): Use startTime from [dnsforward.dnsContext].
if !bsvc.Schedule.Contains(time.Now()) {
d.ApplyBlockedServicesList(setts, bsvc.IDs)
}
}
// ApplyBlockedServicesList appends filtering rules to the settings.
func (d *DNSFilter) ApplyBlockedServicesList(setts *Settings, list []string) {
for _, name := range list {
rules, ok := serviceRules[name]
if !ok {
log.Error("unknown service name: %s", name)
continue
}
setts.ServicesRules = append(setts.ServicesRules, ServiceEntry{
Name: name,
Rules: rules,
})
}
}
func (d *DNSFilter) handleBlockedServicesIDs(w http.ResponseWriter, r *http.Request) {
aghhttp.WriteJSONResponseOK(w, r, serviceIDs)
}
func (d *DNSFilter) handleBlockedServicesAll(w http.ResponseWriter, r *http.Request) {
aghhttp.WriteJSONResponseOK(w, r, struct {
BlockedServices []blockedService `json:"blocked_services"`
}{
BlockedServices: blockedServices,
})
}
// handleBlockedServicesList is the handler for the GET
// /control/blocked_services/list HTTP API.
//
// Deprecated: Use handleBlockedServicesGet.
func (d *DNSFilter) handleBlockedServicesList(w http.ResponseWriter, r *http.Request) {
var list []string
func() {
d.confMu.Lock()
defer d.confMu.Unlock()
list = d.conf.BlockedServices.IDs
}()
aghhttp.WriteJSONResponseOK(w, r, list)
}
// handleBlockedServicesSet is the handler for the POST
// /control/blocked_services/set HTTP API.
//
// Deprecated: Use handleBlockedServicesUpdate.
func (d *DNSFilter) handleBlockedServicesSet(w http.ResponseWriter, r *http.Request) {
list := []string{}
err := json.NewDecoder(r.Body).Decode(&list)
if err != nil {
aghhttp.Error(r, w, http.StatusBadRequest, "json.Decode: %s", err)
return
}
func() {
d.confMu.Lock()
defer d.confMu.Unlock()
d.conf.BlockedServices.IDs = list
log.Debug("Updated blocked services list: %d", len(list))
}()
d.conf.ConfigModified()
}
// handleBlockedServicesGet is the handler for the GET
// /control/blocked_services/get HTTP API.
func (d *DNSFilter) handleBlockedServicesGet(w http.ResponseWriter, r *http.Request) {
var bsvc *BlockedServices
func() {
d.confMu.RLock()
defer d.confMu.RUnlock()
bsvc = d.conf.BlockedServices.Clone()
}()
aghhttp.WriteJSONResponseOK(w, r, bsvc)
}
// handleBlockedServicesUpdate is the handler for the PUT
// /control/blocked_services/update HTTP API.
func (d *DNSFilter) handleBlockedServicesUpdate(w http.ResponseWriter, r *http.Request) {
bsvc := &BlockedServices{}
err := json.NewDecoder(r.Body).Decode(bsvc)
if err != nil {
aghhttp.Error(r, w, http.StatusBadRequest, "json.Decode: %s", err)
return
}
err = bsvc.Validate()
if err != nil {
aghhttp.Error(r, w, http.StatusUnprocessableEntity, "validating: %s", err)
return
}
if bsvc.Schedule == nil {
bsvc.Schedule = schedule.EmptyWeekly()
}
func() {
d.confMu.Lock()
defer d.confMu.Unlock()
d.conf.BlockedServices = bsvc
}()
log.Debug("updated blocked services schedule: %d", len(bsvc.IDs))
d.conf.ConfigModified()
}