Merge in DNS/adguard-home from 951-blocked-services-schedule to master Squashed commit of the following: commit 0f0770292f8aa9dc0dddc05edefacf6655f329b8 Merge: 2705fbcfb7309a5335Author: Stanislav Chzhen <s.chzhen@adguard.com> Date: Wed Jun 14 15:00:56 2023 +0300 Merge branch 'master' into 951-blocked-services-schedule commit 2705fbcfba57392bcc0fa9b6e1f1dfdce9796963 Author: Stanislav Chzhen <s.chzhen@adguard.com> Date: Wed Jun 14 14:56:01 2023 +0300 schedule: imp field alignment commit ff8fa040558e97192a5a2c91c1d5722c67d88b3d Merge: f99936f369fda7bfd3Author: Stanislav Chzhen <s.chzhen@adguard.com> Date: Tue Jun 13 11:49:15 2023 +0300 Merge branch 'master' into 951-blocked-services-schedule commit f99936f36be3453b50d772ce10bfdc1ad14879c5 Author: Stanislav Chzhen <s.chzhen@adguard.com> Date: Fri Jun 9 19:14:06 2023 +0300 schedule: add tests commit 8436c2c5675d5cc22a3554ded2ce4a49eeeefc91 Author: Stanislav Chzhen <s.chzhen@adguard.com> Date: Thu Jun 8 20:10:03 2023 +0300 schedule: imp code commit 850a3d93e659e0584bd449cf5af3f13b36be9e62 Author: Stanislav Chzhen <s.chzhen@adguard.com> Date: Thu Jun 8 18:57:23 2023 +0300 schedule: imp tests commit d54c42b782a4a806d2f35abbea1826ba6394a64c Merge: 21dfa51ea156c199bbAuthor: Stanislav Chzhen <s.chzhen@adguard.com> Date: Thu Jun 8 18:12:03 2023 +0300 Merge branch 'master' into 951-blocked-services-schedule commit 21dfa51ea7441c655ac39f2cbceeaa598d364bff Author: Stanislav Chzhen <s.chzhen@adguard.com> Date: Thu Jun 8 18:02:47 2023 +0300 all: add todo commit d0d4532c8911fb6365f6af66ceb72a70b158b77d Author: Stanislav Chzhen <s.chzhen@adguard.com> Date: Wed Jun 7 18:54:44 2023 +0300 all: upd chlog commit dc1d55fa23c0ec8e7f7b7cd25d3d0cbf3e51e236 Author: Stanislav Chzhen <s.chzhen@adguard.com> Date: Wed Jun 7 17:34:28 2023 +0300 all: add tests commit 601775307c2b23fa9d6cf2aa271793e8fd5f1797 Author: Stanislav Chzhen <s.chzhen@adguard.com> Date: Wed Jun 7 13:20:00 2023 +0300 all: add schedule pkg commit e0bcad0193a559a89f5f5052f5a985fa730d23be Author: Stanislav Chzhen <s.chzhen@adguard.com> Date: Fri Jun 2 13:03:04 2023 +0300 filtering: add test case commit da7a7c20c84151ff83414cb5d576a91740a1be7e Author: Stanislav Chzhen <s.chzhen@adguard.com> Date: Wed May 31 15:31:48 2023 +0300 filtering: imp code commit 9845a11cdae25eafe39a50f8cdc408fefe1fd746 Author: Stanislav Chzhen <s.chzhen@adguard.com> Date: Mon May 29 17:46:24 2023 +0300 filtering: add type check commit f4bcea8ad90d584ceff236ccce4f246e391e0e33 Author: Stanislav Chzhen <s.chzhen@adguard.com> Date: Mon May 29 17:37:25 2023 +0300 all: imp code commit 9de3a0f37eb65d0db2431c27dd4d69136c1485a7 Author: Stanislav Chzhen <s.chzhen@adguard.com> Date: Mon May 29 15:20:33 2023 +0300 filtering: imp err msg commit 1befab59cf2a12a3bc8296066ce1115dff5a8843 Author: Stanislav Chzhen <s.chzhen@adguard.com> Date: Mon May 29 14:31:14 2023 +0300 filtering: imp tests commit 09506f924f363f1563009cec0c44ad40d08b8ddd Author: Stanislav Chzhen <s.chzhen@adguard.com> Date: Fri May 26 18:44:12 2023 +0300 filtering: add tests commit 36c42f523fcfe7fa6dca5f6f13e6c79f491c1ad5 Author: Stanislav Chzhen <s.chzhen@adguard.com> Date: Fri May 26 17:57:01 2023 +0300 all: fix typos commit 69399cd3ea17691ff0848baf43ff09b18b82114c Author: Stanislav Chzhen <s.chzhen@adguard.com> Date: Thu May 25 18:50:51 2023 +0300 all: upd chlog ... and 2 more commits
580 lines
15 KiB
Go
580 lines
15 KiB
Go
package filtering
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"net"
|
|
"net/http"
|
|
"net/url"
|
|
"os"
|
|
"path/filepath"
|
|
"sync"
|
|
"time"
|
|
|
|
"github.com/AdguardTeam/AdGuardHome/internal/aghhttp"
|
|
"github.com/AdguardTeam/golibs/errors"
|
|
"github.com/AdguardTeam/golibs/log"
|
|
"github.com/miekg/dns"
|
|
"golang.org/x/exp/slices"
|
|
)
|
|
|
|
// validateFilterURL validates the filter list URL or file name.
|
|
func validateFilterURL(urlStr string) (err error) {
|
|
defer func() { err = errors.Annotate(err, "checking filter: %w") }()
|
|
|
|
if filepath.IsAbs(urlStr) {
|
|
_, err = os.Stat(urlStr)
|
|
if err != nil {
|
|
// Don't wrap the error since it's informative enough as is.
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
u, err := url.ParseRequestURI(urlStr)
|
|
if err != nil {
|
|
// Don't wrap the error since it's informative enough as is.
|
|
return err
|
|
} else if s := u.Scheme; s != aghhttp.SchemeHTTP && s != aghhttp.SchemeHTTPS {
|
|
return &url.Error{
|
|
Op: "Check scheme",
|
|
URL: urlStr,
|
|
Err: fmt.Errorf("only %v allowed", []string{aghhttp.SchemeHTTP, aghhttp.SchemeHTTPS}),
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
type filterAddJSON struct {
|
|
Name string `json:"name"`
|
|
URL string `json:"url"`
|
|
Whitelist bool `json:"whitelist"`
|
|
}
|
|
|
|
func (d *DNSFilter) handleFilteringAddURL(w http.ResponseWriter, r *http.Request) {
|
|
fj := filterAddJSON{}
|
|
err := json.NewDecoder(r.Body).Decode(&fj)
|
|
if err != nil {
|
|
aghhttp.Error(r, w, http.StatusBadRequest, "Failed to parse request body json: %s", err)
|
|
|
|
return
|
|
}
|
|
|
|
err = validateFilterURL(fj.URL)
|
|
if err != nil {
|
|
aghhttp.Error(r, w, http.StatusBadRequest, "%s", err)
|
|
|
|
return
|
|
}
|
|
|
|
// Check for duplicates
|
|
if d.filterExists(fj.URL) {
|
|
err = errFilterExists
|
|
aghhttp.Error(r, w, http.StatusBadRequest, "Filter with URL %q: %s", fj.URL, err)
|
|
|
|
return
|
|
}
|
|
|
|
// Set necessary properties
|
|
filt := FilterYAML{
|
|
Enabled: true,
|
|
URL: fj.URL,
|
|
Name: fj.Name,
|
|
white: fj.Whitelist,
|
|
Filter: Filter{
|
|
ID: assignUniqueFilterID(),
|
|
},
|
|
}
|
|
|
|
// Download the filter contents
|
|
ok, err := d.update(&filt)
|
|
if err != nil {
|
|
aghhttp.Error(
|
|
r,
|
|
w,
|
|
http.StatusBadRequest,
|
|
"Couldn't fetch filter from url %s: %s",
|
|
filt.URL,
|
|
err,
|
|
)
|
|
|
|
return
|
|
}
|
|
|
|
if !ok {
|
|
aghhttp.Error(
|
|
r,
|
|
w,
|
|
http.StatusBadRequest,
|
|
"Filter with URL %q is invalid (maybe it points to blank page?)",
|
|
filt.URL,
|
|
)
|
|
|
|
return
|
|
}
|
|
|
|
// URL is assumed valid so append it to filters, update config, write new
|
|
// file and reload it to engines.
|
|
err = d.filterAdd(filt)
|
|
if err != nil {
|
|
aghhttp.Error(r, w, http.StatusBadRequest, "Filter with URL %q: %s", filt.URL, err)
|
|
|
|
return
|
|
}
|
|
|
|
d.ConfigModified()
|
|
d.EnableFilters(true)
|
|
|
|
_, err = fmt.Fprintf(w, "OK %d rules\n", filt.RulesCount)
|
|
if err != nil {
|
|
aghhttp.Error(r, w, http.StatusInternalServerError, "Couldn't write body: %s", err)
|
|
}
|
|
}
|
|
|
|
func (d *DNSFilter) handleFilteringRemoveURL(w http.ResponseWriter, r *http.Request) {
|
|
type request struct {
|
|
URL string `json:"url"`
|
|
Whitelist bool `json:"whitelist"`
|
|
}
|
|
|
|
req := request{}
|
|
err := json.NewDecoder(r.Body).Decode(&req)
|
|
if err != nil {
|
|
aghhttp.Error(r, w, http.StatusBadRequest, "failed to parse request body json: %s", err)
|
|
|
|
return
|
|
}
|
|
|
|
var deleted FilterYAML
|
|
func() {
|
|
d.filtersMu.Lock()
|
|
defer d.filtersMu.Unlock()
|
|
|
|
filters := &d.Filters
|
|
if req.Whitelist {
|
|
filters = &d.WhitelistFilters
|
|
}
|
|
|
|
delIdx := slices.IndexFunc(*filters, func(flt FilterYAML) bool {
|
|
return flt.URL == req.URL
|
|
})
|
|
if delIdx == -1 {
|
|
log.Error("deleting filter with url %q: %s", req.URL, errFilterNotExist)
|
|
|
|
return
|
|
}
|
|
|
|
deleted = (*filters)[delIdx]
|
|
p := deleted.Path(d.DataDir)
|
|
err = os.Rename(p, p+".old")
|
|
if err != nil {
|
|
log.Error("deleting filter %d: renaming file %q: %s", deleted.ID, p, err)
|
|
|
|
return
|
|
}
|
|
|
|
*filters = slices.Delete(*filters, delIdx, delIdx+1)
|
|
|
|
log.Info("deleted filter %d", deleted.ID)
|
|
}()
|
|
|
|
d.ConfigModified()
|
|
d.EnableFilters(true)
|
|
|
|
// NOTE: The old files "filter.txt.old" aren't deleted. It's not really
|
|
// necessary, but will require the additional complicated code to run
|
|
// after enableFilters is done.
|
|
//
|
|
// TODO(a.garipov): Make sure the above comment is true.
|
|
|
|
_, err = fmt.Fprintf(w, "OK %d rules\n", deleted.RulesCount)
|
|
if err != nil {
|
|
aghhttp.Error(r, w, http.StatusInternalServerError, "couldn't write body: %s", err)
|
|
}
|
|
}
|
|
|
|
type filterURLReqData struct {
|
|
Name string `json:"name"`
|
|
URL string `json:"url"`
|
|
Enabled bool `json:"enabled"`
|
|
}
|
|
|
|
type filterURLReq struct {
|
|
Data *filterURLReqData `json:"data"`
|
|
URL string `json:"url"`
|
|
Whitelist bool `json:"whitelist"`
|
|
}
|
|
|
|
func (d *DNSFilter) handleFilteringSetURL(w http.ResponseWriter, r *http.Request) {
|
|
fj := filterURLReq{}
|
|
err := json.NewDecoder(r.Body).Decode(&fj)
|
|
if err != nil {
|
|
aghhttp.Error(r, w, http.StatusBadRequest, "decoding request: %s", err)
|
|
|
|
return
|
|
}
|
|
|
|
if fj.Data == nil {
|
|
aghhttp.Error(r, w, http.StatusBadRequest, "%s", errors.Error("data is absent"))
|
|
|
|
return
|
|
}
|
|
|
|
err = validateFilterURL(fj.Data.URL)
|
|
if err != nil {
|
|
aghhttp.Error(r, w, http.StatusBadRequest, "invalid url: %s", err)
|
|
|
|
return
|
|
}
|
|
|
|
filt := FilterYAML{
|
|
Enabled: fj.Data.Enabled,
|
|
Name: fj.Data.Name,
|
|
URL: fj.Data.URL,
|
|
}
|
|
|
|
restart, err := d.filterSetProperties(fj.URL, filt, fj.Whitelist)
|
|
if err != nil {
|
|
aghhttp.Error(r, w, http.StatusBadRequest, err.Error())
|
|
|
|
return
|
|
}
|
|
|
|
d.ConfigModified()
|
|
if restart {
|
|
d.EnableFilters(true)
|
|
}
|
|
}
|
|
|
|
// filteringRulesReq is the JSON structure for settings custom filtering rules.
|
|
type filteringRulesReq struct {
|
|
Rules []string `json:"rules"`
|
|
}
|
|
|
|
func (d *DNSFilter) handleFilteringSetRules(w http.ResponseWriter, r *http.Request) {
|
|
if aghhttp.WriteTextPlainDeprecated(w, r) {
|
|
return
|
|
}
|
|
|
|
req := &filteringRulesReq{}
|
|
err := json.NewDecoder(r.Body).Decode(req)
|
|
if err != nil {
|
|
aghhttp.Error(r, w, http.StatusBadRequest, "reading req: %s", err)
|
|
|
|
return
|
|
}
|
|
|
|
d.UserRules = req.Rules
|
|
d.ConfigModified()
|
|
d.EnableFilters(true)
|
|
}
|
|
|
|
func (d *DNSFilter) handleFilteringRefresh(w http.ResponseWriter, r *http.Request) {
|
|
type Req struct {
|
|
White bool `json:"whitelist"`
|
|
}
|
|
var err error
|
|
|
|
req := Req{}
|
|
err = json.NewDecoder(r.Body).Decode(&req)
|
|
if err != nil {
|
|
aghhttp.Error(r, w, http.StatusBadRequest, "json decode: %s", err)
|
|
|
|
return
|
|
}
|
|
|
|
var ok bool
|
|
resp := struct {
|
|
Updated int `json:"updated"`
|
|
}{}
|
|
resp.Updated, _, ok = d.tryRefreshFilters(!req.White, req.White, true)
|
|
if !ok {
|
|
aghhttp.Error(
|
|
r,
|
|
w,
|
|
http.StatusInternalServerError,
|
|
"filters update procedure is already running",
|
|
)
|
|
|
|
return
|
|
}
|
|
|
|
_ = aghhttp.WriteJSONResponse(w, r, resp)
|
|
}
|
|
|
|
type filterJSON struct {
|
|
URL string `json:"url"`
|
|
Name string `json:"name"`
|
|
LastUpdated string `json:"last_updated,omitempty"`
|
|
ID int64 `json:"id"`
|
|
RulesCount uint32 `json:"rules_count"`
|
|
Enabled bool `json:"enabled"`
|
|
}
|
|
|
|
type filteringConfig struct {
|
|
Filters []filterJSON `json:"filters"`
|
|
WhitelistFilters []filterJSON `json:"whitelist_filters"`
|
|
UserRules []string `json:"user_rules"`
|
|
Interval uint32 `json:"interval"` // in hours
|
|
Enabled bool `json:"enabled"`
|
|
}
|
|
|
|
func filterToJSON(f FilterYAML) filterJSON {
|
|
fj := filterJSON{
|
|
ID: f.ID,
|
|
Enabled: f.Enabled,
|
|
URL: f.URL,
|
|
Name: f.Name,
|
|
RulesCount: uint32(f.RulesCount),
|
|
}
|
|
|
|
if !f.LastUpdated.IsZero() {
|
|
fj.LastUpdated = f.LastUpdated.Format(time.RFC3339)
|
|
}
|
|
|
|
return fj
|
|
}
|
|
|
|
// Get filtering configuration
|
|
func (d *DNSFilter) handleFilteringStatus(w http.ResponseWriter, r *http.Request) {
|
|
resp := filteringConfig{}
|
|
d.filtersMu.RLock()
|
|
resp.Enabled = d.FilteringEnabled
|
|
resp.Interval = d.FiltersUpdateIntervalHours
|
|
for _, f := range d.Filters {
|
|
fj := filterToJSON(f)
|
|
resp.Filters = append(resp.Filters, fj)
|
|
}
|
|
for _, f := range d.WhitelistFilters {
|
|
fj := filterToJSON(f)
|
|
resp.WhitelistFilters = append(resp.WhitelistFilters, fj)
|
|
}
|
|
resp.UserRules = d.UserRules
|
|
d.filtersMu.RUnlock()
|
|
|
|
_ = aghhttp.WriteJSONResponse(w, r, resp)
|
|
}
|
|
|
|
// Set filtering configuration
|
|
func (d *DNSFilter) handleFilteringConfig(w http.ResponseWriter, r *http.Request) {
|
|
req := filteringConfig{}
|
|
err := json.NewDecoder(r.Body).Decode(&req)
|
|
if err != nil {
|
|
aghhttp.Error(r, w, http.StatusBadRequest, "json decode: %s", err)
|
|
|
|
return
|
|
}
|
|
|
|
if !ValidateUpdateIvl(req.Interval) {
|
|
aghhttp.Error(r, w, http.StatusBadRequest, "Unsupported interval")
|
|
|
|
return
|
|
}
|
|
|
|
func() {
|
|
d.filtersMu.Lock()
|
|
defer d.filtersMu.Unlock()
|
|
|
|
d.FilteringEnabled = req.Enabled
|
|
d.FiltersUpdateIntervalHours = req.Interval
|
|
}()
|
|
|
|
d.ConfigModified()
|
|
d.EnableFilters(true)
|
|
}
|
|
|
|
type checkHostRespRule struct {
|
|
Text string `json:"text"`
|
|
FilterListID int64 `json:"filter_list_id"`
|
|
}
|
|
|
|
type checkHostResp struct {
|
|
Reason string `json:"reason"`
|
|
|
|
// Rule is the text of the matched rule.
|
|
//
|
|
// Deprecated: Use Rules[*].Text.
|
|
Rule string `json:"rule"`
|
|
|
|
Rules []*checkHostRespRule `json:"rules"`
|
|
|
|
// for FilteredBlockedService:
|
|
SvcName string `json:"service_name"`
|
|
|
|
// for Rewrite:
|
|
CanonName string `json:"cname"` // CNAME value
|
|
IPList []net.IP `json:"ip_addrs"` // list of IP addresses
|
|
|
|
// FilterID is the ID of the rule's filter list.
|
|
//
|
|
// Deprecated: Use Rules[*].FilterListID.
|
|
FilterID int64 `json:"filter_id"`
|
|
}
|
|
|
|
func (d *DNSFilter) handleCheckHost(w http.ResponseWriter, r *http.Request) {
|
|
host := r.URL.Query().Get("name")
|
|
|
|
setts := d.Settings()
|
|
setts.FilteringEnabled = true
|
|
setts.ProtectionEnabled = true
|
|
|
|
d.ApplyBlockedServices(setts)
|
|
result, err := d.CheckHost(host, dns.TypeA, setts)
|
|
if err != nil {
|
|
aghhttp.Error(
|
|
r,
|
|
w,
|
|
http.StatusInternalServerError,
|
|
"couldn't apply filtering: %s: %s",
|
|
host,
|
|
err,
|
|
)
|
|
|
|
return
|
|
}
|
|
|
|
rulesLen := len(result.Rules)
|
|
resp := checkHostResp{
|
|
Reason: result.Reason.String(),
|
|
SvcName: result.ServiceName,
|
|
CanonName: result.CanonName,
|
|
IPList: result.IPList,
|
|
Rules: make([]*checkHostRespRule, len(result.Rules)),
|
|
}
|
|
|
|
if rulesLen > 0 {
|
|
resp.FilterID = result.Rules[0].FilterListID
|
|
resp.Rule = result.Rules[0].Text
|
|
}
|
|
|
|
for i, r := range result.Rules {
|
|
resp.Rules[i] = &checkHostRespRule{
|
|
FilterListID: r.FilterListID,
|
|
Text: r.Text,
|
|
}
|
|
}
|
|
|
|
_ = aghhttp.WriteJSONResponse(w, r, resp)
|
|
}
|
|
|
|
// setProtectedBool sets the value of a boolean pointer under a lock. l must
|
|
// protect the value under ptr.
|
|
//
|
|
// TODO(e.burkov): Make it generic?
|
|
func setProtectedBool(mu *sync.RWMutex, ptr *bool, val bool) {
|
|
mu.Lock()
|
|
defer mu.Unlock()
|
|
|
|
*ptr = val
|
|
}
|
|
|
|
// protectedBool gets the value of a boolean pointer under a read lock. l must
|
|
// protect the value under ptr.
|
|
//
|
|
// TODO(e.burkov): Make it generic?
|
|
func protectedBool(mu *sync.RWMutex, ptr *bool) (val bool) {
|
|
mu.RLock()
|
|
defer mu.RUnlock()
|
|
|
|
return *ptr
|
|
}
|
|
|
|
// handleSafeBrowsingEnable is the handler for the POST
|
|
// /control/safebrowsing/enable HTTP API.
|
|
func (d *DNSFilter) handleSafeBrowsingEnable(w http.ResponseWriter, r *http.Request) {
|
|
setProtectedBool(&d.confLock, &d.Config.SafeBrowsingEnabled, true)
|
|
d.Config.ConfigModified()
|
|
}
|
|
|
|
// handleSafeBrowsingDisable is the handler for the POST
|
|
// /control/safebrowsing/disable HTTP API.
|
|
func (d *DNSFilter) handleSafeBrowsingDisable(w http.ResponseWriter, r *http.Request) {
|
|
setProtectedBool(&d.confLock, &d.Config.SafeBrowsingEnabled, false)
|
|
d.Config.ConfigModified()
|
|
}
|
|
|
|
// handleSafeBrowsingStatus is the handler for the GET
|
|
// /control/safebrowsing/status HTTP API.
|
|
func (d *DNSFilter) handleSafeBrowsingStatus(w http.ResponseWriter, r *http.Request) {
|
|
resp := &struct {
|
|
Enabled bool `json:"enabled"`
|
|
}{
|
|
Enabled: protectedBool(&d.confLock, &d.Config.SafeBrowsingEnabled),
|
|
}
|
|
|
|
_ = aghhttp.WriteJSONResponse(w, r, resp)
|
|
}
|
|
|
|
// handleParentalEnable is the handler for the POST /control/parental/enable
|
|
// HTTP API.
|
|
func (d *DNSFilter) handleParentalEnable(w http.ResponseWriter, r *http.Request) {
|
|
setProtectedBool(&d.confLock, &d.Config.ParentalEnabled, true)
|
|
d.Config.ConfigModified()
|
|
}
|
|
|
|
// handleParentalDisable is the handler for the POST /control/parental/disable
|
|
// HTTP API.
|
|
func (d *DNSFilter) handleParentalDisable(w http.ResponseWriter, r *http.Request) {
|
|
setProtectedBool(&d.confLock, &d.Config.ParentalEnabled, false)
|
|
d.Config.ConfigModified()
|
|
}
|
|
|
|
// handleParentalStatus is the handler for the GET /control/parental/status
|
|
// HTTP API.
|
|
func (d *DNSFilter) handleParentalStatus(w http.ResponseWriter, r *http.Request) {
|
|
resp := &struct {
|
|
Enabled bool `json:"enabled"`
|
|
}{
|
|
Enabled: protectedBool(&d.confLock, &d.Config.ParentalEnabled),
|
|
}
|
|
|
|
_ = aghhttp.WriteJSONResponse(w, r, resp)
|
|
}
|
|
|
|
// RegisterFilteringHandlers - register handlers
|
|
func (d *DNSFilter) RegisterFilteringHandlers() {
|
|
registerHTTP := d.HTTPRegister
|
|
if registerHTTP == nil {
|
|
return
|
|
}
|
|
|
|
registerHTTP(http.MethodPost, "/control/safebrowsing/enable", d.handleSafeBrowsingEnable)
|
|
registerHTTP(http.MethodPost, "/control/safebrowsing/disable", d.handleSafeBrowsingDisable)
|
|
registerHTTP(http.MethodGet, "/control/safebrowsing/status", d.handleSafeBrowsingStatus)
|
|
|
|
registerHTTP(http.MethodPost, "/control/parental/enable", d.handleParentalEnable)
|
|
registerHTTP(http.MethodPost, "/control/parental/disable", d.handleParentalDisable)
|
|
registerHTTP(http.MethodGet, "/control/parental/status", d.handleParentalStatus)
|
|
|
|
registerHTTP(http.MethodPost, "/control/safesearch/enable", d.handleSafeSearchEnable)
|
|
registerHTTP(http.MethodPost, "/control/safesearch/disable", d.handleSafeSearchDisable)
|
|
registerHTTP(http.MethodGet, "/control/safesearch/status", d.handleSafeSearchStatus)
|
|
registerHTTP(http.MethodPut, "/control/safesearch/settings", d.handleSafeSearchSettings)
|
|
|
|
registerHTTP(http.MethodGet, "/control/rewrite/list", d.handleRewriteList)
|
|
registerHTTP(http.MethodPost, "/control/rewrite/add", d.handleRewriteAdd)
|
|
registerHTTP(http.MethodPut, "/control/rewrite/update", d.handleRewriteUpdate)
|
|
registerHTTP(http.MethodPost, "/control/rewrite/delete", d.handleRewriteDelete)
|
|
|
|
registerHTTP(http.MethodGet, "/control/blocked_services/services", d.handleBlockedServicesIDs)
|
|
registerHTTP(http.MethodGet, "/control/blocked_services/all", d.handleBlockedServicesAll)
|
|
registerHTTP(http.MethodGet, "/control/blocked_services/list", d.handleBlockedServicesList)
|
|
registerHTTP(http.MethodPost, "/control/blocked_services/set", d.handleBlockedServicesSet)
|
|
|
|
registerHTTP(http.MethodGet, "/control/filtering/status", d.handleFilteringStatus)
|
|
registerHTTP(http.MethodPost, "/control/filtering/config", d.handleFilteringConfig)
|
|
registerHTTP(http.MethodPost, "/control/filtering/add_url", d.handleFilteringAddURL)
|
|
registerHTTP(http.MethodPost, "/control/filtering/remove_url", d.handleFilteringRemoveURL)
|
|
registerHTTP(http.MethodPost, "/control/filtering/set_url", d.handleFilteringSetURL)
|
|
registerHTTP(http.MethodPost, "/control/filtering/refresh", d.handleFilteringRefresh)
|
|
registerHTTP(http.MethodPost, "/control/filtering/set_rules", d.handleFilteringSetRules)
|
|
registerHTTP(http.MethodGet, "/control/filtering/check_host", d.handleCheckHost)
|
|
}
|
|
|
|
// ValidateUpdateIvl returns false if i is not a valid filters update interval.
|
|
func ValidateUpdateIvl(i uint32) bool {
|
|
return i == 0 || i == 1 || i == 12 || i == 1*24 || i == 3*24 || i == 7*24
|
|
}
|