all: sync with master; upd chlog
This commit is contained in:
@@ -50,10 +50,10 @@ func initBlockedServices() {
|
||||
// BlockedServices is the configuration of blocked services.
|
||||
type BlockedServices struct {
|
||||
// Schedule is blocked services schedule for every day of the week.
|
||||
Schedule *schedule.Weekly `yaml:"schedule"`
|
||||
Schedule *schedule.Weekly `json:"schedule" yaml:"schedule"`
|
||||
|
||||
// IDs is the names of blocked services.
|
||||
IDs []string `yaml:"ids"`
|
||||
IDs []string `json:"ids" yaml:"ids"`
|
||||
}
|
||||
|
||||
// Clone returns a deep copy of blocked services.
|
||||
@@ -83,12 +83,12 @@ func (s *BlockedServices) Validate() (err error) {
|
||||
|
||||
// ApplyBlockedServices - set blocked services settings for this DNS request
|
||||
func (d *DNSFilter) ApplyBlockedServices(setts *Settings) {
|
||||
d.confLock.RLock()
|
||||
defer d.confLock.RUnlock()
|
||||
d.confMu.RLock()
|
||||
defer d.confMu.RUnlock()
|
||||
|
||||
setts.ServicesRules = []ServiceEntry{}
|
||||
|
||||
bsvc := d.BlockedServices
|
||||
bsvc := d.conf.BlockedServices
|
||||
|
||||
// TODO(s.chzhen): Use startTime from [dnsforward.dnsContext].
|
||||
if !bsvc.Schedule.Contains(time.Now()) {
|
||||
@@ -114,25 +114,37 @@ func (d *DNSFilter) ApplyBlockedServicesList(setts *Settings, list []string) {
|
||||
}
|
||||
|
||||
func (d *DNSFilter) handleBlockedServicesIDs(w http.ResponseWriter, r *http.Request) {
|
||||
_ = aghhttp.WriteJSONResponse(w, r, serviceIDs)
|
||||
aghhttp.WriteJSONResponseOK(w, r, serviceIDs)
|
||||
}
|
||||
|
||||
func (d *DNSFilter) handleBlockedServicesAll(w http.ResponseWriter, r *http.Request) {
|
||||
_ = aghhttp.WriteJSONResponse(w, r, struct {
|
||||
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) {
|
||||
d.confLock.RLock()
|
||||
list := d.Config.BlockedServices.IDs
|
||||
d.confLock.RUnlock()
|
||||
var list []string
|
||||
func() {
|
||||
d.confMu.Lock()
|
||||
defer d.confMu.Unlock()
|
||||
|
||||
_ = aghhttp.WriteJSONResponse(w, r, list)
|
||||
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)
|
||||
@@ -142,11 +154,61 @@ func (d *DNSFilter) handleBlockedServicesSet(w http.ResponseWriter, r *http.Requ
|
||||
return
|
||||
}
|
||||
|
||||
d.confLock.Lock()
|
||||
d.Config.BlockedServices.IDs = list
|
||||
d.confLock.Unlock()
|
||||
func() {
|
||||
d.confMu.Lock()
|
||||
defer d.confMu.Unlock()
|
||||
|
||||
log.Debug("Updated blocked services list: %d", len(list))
|
||||
d.conf.BlockedServices.IDs = list
|
||||
log.Debug("Updated blocked services list: %d", len(list))
|
||||
}()
|
||||
|
||||
d.Config.ConfigModified()
|
||||
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()
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package filtering
|
||||
|
||||
import (
|
||||
"github.com/AdguardTeam/golibs/hostsfile"
|
||||
"github.com/AdguardTeam/urlfilter"
|
||||
"github.com/AdguardTeam/urlfilter/rules"
|
||||
"github.com/miekg/dns"
|
||||
)
|
||||
@@ -73,3 +75,59 @@ func (d *DNSFilter) processDNSRewrites(dnsr []*rules.NetworkRule) (res Result) {
|
||||
Reason: RewrittenRule,
|
||||
}
|
||||
}
|
||||
|
||||
// processDNSResultRewrites returns an empty Result if there are no dnsrewrite
|
||||
// rules in dnsres. Otherwise, it returns the processed Result.
|
||||
func (d *DNSFilter) processDNSResultRewrites(
|
||||
dnsres *urlfilter.DNSResult,
|
||||
host string,
|
||||
) (dnsRWRes Result) {
|
||||
dnsr := dnsres.DNSRewrites()
|
||||
if len(dnsr) == 0 {
|
||||
return Result{}
|
||||
}
|
||||
|
||||
res := d.processDNSRewrites(dnsr)
|
||||
if res.Reason == RewrittenRule && res.CanonName == host {
|
||||
// A rewrite of a host to itself. Go on and try matching other things.
|
||||
return Result{}
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
// appendRewriteResultFromHost appends the rewrite result from rec to vals and
|
||||
// resRules.
|
||||
func appendRewriteResultFromHost(
|
||||
vals []rules.RRValue,
|
||||
resRules []*ResultRule,
|
||||
rec *hostsfile.Record,
|
||||
qtype uint16,
|
||||
) (updatedVals []rules.RRValue, updatedRules []*ResultRule) {
|
||||
switch qtype {
|
||||
case dns.TypeA:
|
||||
if !rec.Addr.Is4() {
|
||||
return vals, resRules
|
||||
}
|
||||
|
||||
vals = append(vals, rec.Addr)
|
||||
case dns.TypeAAAA:
|
||||
if !rec.Addr.Is6() {
|
||||
return vals, resRules
|
||||
}
|
||||
|
||||
vals = append(vals, rec.Addr)
|
||||
case dns.TypePTR:
|
||||
for _, name := range rec.Names {
|
||||
vals = append(vals, name)
|
||||
}
|
||||
}
|
||||
|
||||
recText, _ := rec.MarshalText()
|
||||
resRules = append(resRules, &ResultRule{
|
||||
FilterListID: SysHostsListID,
|
||||
Text: string(recText),
|
||||
})
|
||||
|
||||
return vals, resRules
|
||||
}
|
||||
|
||||
@@ -1,10 +1,17 @@
|
||||
package filtering
|
||||
|
||||
import (
|
||||
"net"
|
||||
"fmt"
|
||||
"net/netip"
|
||||
"path"
|
||||
"testing"
|
||||
"testing/fstest"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/aghnet"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/aghtest"
|
||||
"github.com/AdguardTeam/golibs/netutil"
|
||||
"github.com/AdguardTeam/golibs/testutil"
|
||||
"github.com/AdguardTeam/urlfilter/rules"
|
||||
"github.com/miekg/dns"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
@@ -15,13 +22,13 @@ func TestDNSFilter_CheckHostRules_dnsrewrite(t *testing.T) {
|
||||
|cname^$dnsrewrite=new-cname
|
||||
|
||||
|a-record^$dnsrewrite=127.0.0.1
|
||||
|
||||
|aaaa-record^$dnsrewrite=::1
|
||||
|
||||
|txt-record^$dnsrewrite=NOERROR;TXT;hello-world
|
||||
|
||||
|refused^$dnsrewrite=REFUSED
|
||||
|
||||
|mapped^$dnsrewrite=NOERROR;AAAA;::ffff:127.0.0.1
|
||||
|
||||
|a-records^$dnsrewrite=127.0.0.1
|
||||
|a-records^$dnsrewrite=127.0.0.2
|
||||
|
||||
@@ -54,10 +61,11 @@ func TestDNSFilter_CheckHostRules_dnsrewrite(t *testing.T) {
|
||||
FilteringEnabled: true,
|
||||
}
|
||||
|
||||
ipv4p1 := net.IPv4(127, 0, 0, 1)
|
||||
ipv4p2 := net.IPv4(127, 0, 0, 2)
|
||||
ipv6p1 := net.IP{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}
|
||||
ipv6p2 := net.IP{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2}
|
||||
ipv4p1 := netutil.IPv4Localhost()
|
||||
ipv4p2 := ipv4p1.Next()
|
||||
ipv6p1 := netutil.IPv6Localhost()
|
||||
ipv6p2 := ipv6p1.Next()
|
||||
mapped := netip.AddrFrom16(ipv4p1.As16())
|
||||
|
||||
testCasesA := []struct {
|
||||
name string
|
||||
@@ -104,6 +112,11 @@ func TestDNSFilter_CheckHostRules_dnsrewrite(t *testing.T) {
|
||||
want: []any{ipv4p1},
|
||||
rcode: dns.RcodeSuccess,
|
||||
dtyp: dns.TypeA,
|
||||
}, {
|
||||
name: "mapped",
|
||||
want: []any{mapped},
|
||||
rcode: dns.RcodeSuccess,
|
||||
dtyp: dns.TypeAAAA,
|
||||
}}
|
||||
|
||||
for _, tc := range testCasesA {
|
||||
@@ -202,3 +215,154 @@ func TestDNSFilter_CheckHostRules_dnsrewrite(t *testing.T) {
|
||||
assert.Equal(t, "new-ptr-with-dot.", ptr)
|
||||
})
|
||||
}
|
||||
|
||||
func TestDNSFilter_CheckHost_hostsContainer(t *testing.T) {
|
||||
addrv4 := netip.MustParseAddr("1.2.3.4")
|
||||
addrv6 := netip.MustParseAddr("::1")
|
||||
addrMapped := netip.MustParseAddr("::ffff:1.2.3.4")
|
||||
|
||||
data := fmt.Sprintf(
|
||||
""+
|
||||
"%s v4.host.example\n"+
|
||||
"%s v6.host.example\n"+
|
||||
"%s mapped.host.example\n",
|
||||
addrv4,
|
||||
addrv6,
|
||||
addrMapped,
|
||||
)
|
||||
|
||||
files := fstest.MapFS{
|
||||
"hosts": &fstest.MapFile{
|
||||
Data: []byte(data),
|
||||
},
|
||||
}
|
||||
watcher := &aghtest.FSWatcher{
|
||||
OnEvents: func() (e <-chan struct{}) { return nil },
|
||||
OnAdd: func(name string) (err error) { return nil },
|
||||
OnClose: func() (err error) { return nil },
|
||||
}
|
||||
hc, err := aghnet.NewHostsContainer(files, watcher, "hosts")
|
||||
require.NoError(t, err)
|
||||
testutil.CleanupAndRequireSuccess(t, hc.Close)
|
||||
|
||||
f, _ := newForTest(t, &Config{EtcHosts: hc}, nil)
|
||||
setts := &Settings{
|
||||
FilteringEnabled: true,
|
||||
}
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
host string
|
||||
wantRules []*ResultRule
|
||||
wantResps []rules.RRValue
|
||||
dtyp uint16
|
||||
}{{
|
||||
name: "v4",
|
||||
host: "v4.host.example",
|
||||
dtyp: dns.TypeA,
|
||||
wantRules: []*ResultRule{{
|
||||
Text: "1.2.3.4 v4.host.example",
|
||||
FilterListID: SysHostsListID,
|
||||
}},
|
||||
wantResps: []rules.RRValue{addrv4},
|
||||
}, {
|
||||
name: "v6",
|
||||
host: "v6.host.example",
|
||||
dtyp: dns.TypeAAAA,
|
||||
wantRules: []*ResultRule{{
|
||||
Text: "::1 v6.host.example",
|
||||
FilterListID: SysHostsListID,
|
||||
}},
|
||||
wantResps: []rules.RRValue{addrv6},
|
||||
}, {
|
||||
name: "mapped",
|
||||
host: "mapped.host.example",
|
||||
dtyp: dns.TypeAAAA,
|
||||
wantRules: []*ResultRule{{
|
||||
Text: "::ffff:1.2.3.4 mapped.host.example",
|
||||
FilterListID: SysHostsListID,
|
||||
}},
|
||||
wantResps: []rules.RRValue{addrMapped},
|
||||
}, {
|
||||
name: "ptr",
|
||||
host: "4.3.2.1.in-addr.arpa",
|
||||
dtyp: dns.TypePTR,
|
||||
wantRules: []*ResultRule{{
|
||||
Text: "1.2.3.4 v4.host.example",
|
||||
FilterListID: SysHostsListID,
|
||||
}},
|
||||
wantResps: []rules.RRValue{"v4.host.example"},
|
||||
}, {
|
||||
name: "ptr-mapped",
|
||||
host: "4.0.3.0.2.0.1.0.f.f.f.f.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa",
|
||||
dtyp: dns.TypePTR,
|
||||
wantRules: []*ResultRule{{
|
||||
Text: "::ffff:1.2.3.4 mapped.host.example",
|
||||
FilterListID: SysHostsListID,
|
||||
}},
|
||||
wantResps: []rules.RRValue{"mapped.host.example"},
|
||||
}, {
|
||||
name: "not_found_v4",
|
||||
host: "non.existent.example",
|
||||
dtyp: dns.TypeA,
|
||||
wantRules: nil,
|
||||
wantResps: nil,
|
||||
}, {
|
||||
name: "not_found_v6",
|
||||
host: "non.existent.example",
|
||||
dtyp: dns.TypeAAAA,
|
||||
wantRules: nil,
|
||||
wantResps: nil,
|
||||
}, {
|
||||
name: "not_found_ptr",
|
||||
host: "4.3.2.2.in-addr.arpa",
|
||||
dtyp: dns.TypePTR,
|
||||
wantRules: nil,
|
||||
wantResps: nil,
|
||||
}, {
|
||||
name: "v4_mismatch",
|
||||
host: "v4.host.example",
|
||||
dtyp: dns.TypeAAAA,
|
||||
wantRules: nil,
|
||||
wantResps: nil,
|
||||
}, {
|
||||
name: "v6_mismatch",
|
||||
host: "v6.host.example",
|
||||
dtyp: dns.TypeA,
|
||||
wantRules: nil,
|
||||
wantResps: nil,
|
||||
}, {
|
||||
name: "wrong_ptr",
|
||||
host: "4.3.2.1.ip6.arpa",
|
||||
dtyp: dns.TypePTR,
|
||||
wantRules: nil,
|
||||
wantResps: nil,
|
||||
}, {
|
||||
name: "unsupported_type",
|
||||
host: "v4.host.example",
|
||||
dtyp: dns.TypeCNAME,
|
||||
wantRules: nil,
|
||||
wantResps: nil,
|
||||
}}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
var res Result
|
||||
res, err = f.CheckHost(tc.host, tc.dtyp, setts)
|
||||
require.NoError(t, err)
|
||||
|
||||
if len(tc.wantRules) == 0 {
|
||||
assert.Empty(t, res.Rules)
|
||||
assert.Nil(t, res.DNSRewriteResult)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
require.NotNil(t, res.DNSRewriteResult)
|
||||
require.Contains(t, res.DNSRewriteResult.Response, tc.dtyp)
|
||||
|
||||
assert.Equal(t, tc.wantResps, res.DNSRewriteResult.Response[tc.dtyp])
|
||||
assert.Equal(t, tc.wantRules, res.Rules)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,6 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/aghalg"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/aghrenameio"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/filtering/rulelist"
|
||||
"github.com/AdguardTeam/golibs/errors"
|
||||
@@ -54,6 +53,22 @@ func (filter *FilterYAML) Path(dataDir string) string {
|
||||
return filepath.Join(dataDir, filterDir, strconv.FormatInt(filter.ID, 10)+".txt")
|
||||
}
|
||||
|
||||
// ensureName sets provided title or default name for the filter if it doesn't
|
||||
// have name already.
|
||||
func (filter *FilterYAML) ensureName(title string) {
|
||||
if filter.Name != "" {
|
||||
return
|
||||
}
|
||||
|
||||
if title != "" {
|
||||
filter.Name = title
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
filter.Name = fmt.Sprintf("List %d", filter.ID)
|
||||
}
|
||||
|
||||
const (
|
||||
// errFilterNotExist is returned from [filterSetProperties] when there are
|
||||
// no lists with the desired URL to update.
|
||||
@@ -76,12 +91,12 @@ func (d *DNSFilter) filterSetProperties(
|
||||
newList FilterYAML,
|
||||
isAllowlist bool,
|
||||
) (shouldRestart bool, err error) {
|
||||
d.filtersMu.Lock()
|
||||
defer d.filtersMu.Unlock()
|
||||
d.conf.filtersMu.Lock()
|
||||
defer d.conf.filtersMu.Unlock()
|
||||
|
||||
filters := d.Filters
|
||||
filters := d.conf.Filters
|
||||
if isAllowlist {
|
||||
filters = d.WhitelistFilters
|
||||
filters = d.conf.WhitelistFilters
|
||||
}
|
||||
|
||||
i := slices.IndexFunc(filters, func(flt FilterYAML) bool { return flt.URL == listURL })
|
||||
@@ -147,8 +162,8 @@ func (d *DNSFilter) filterSetProperties(
|
||||
// filterExists returns true if a filter with the same url exists in d. It's
|
||||
// safe for concurrent use.
|
||||
func (d *DNSFilter) filterExists(url string) (ok bool) {
|
||||
d.filtersMu.RLock()
|
||||
defer d.filtersMu.RUnlock()
|
||||
d.conf.filtersMu.RLock()
|
||||
defer d.conf.filtersMu.RUnlock()
|
||||
|
||||
r := d.filterExistsLocked(url)
|
||||
|
||||
@@ -158,13 +173,13 @@ func (d *DNSFilter) filterExists(url string) (ok bool) {
|
||||
// filterExistsLocked returns true if d contains the filter with the same url.
|
||||
// d.filtersMu is expected to be locked.
|
||||
func (d *DNSFilter) filterExistsLocked(url string) (ok bool) {
|
||||
for _, f := range d.Filters {
|
||||
for _, f := range d.conf.Filters {
|
||||
if f.URL == url {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
for _, f := range d.WhitelistFilters {
|
||||
for _, f := range d.conf.WhitelistFilters {
|
||||
if f.URL == url {
|
||||
return true
|
||||
}
|
||||
@@ -179,8 +194,8 @@ func (d *DNSFilter) filterAdd(flt FilterYAML) (err error) {
|
||||
// Defer annotating to unlock sooner.
|
||||
defer func() { err = errors.Annotate(err, "adding filter: %w") }()
|
||||
|
||||
d.filtersMu.Lock()
|
||||
defer d.filtersMu.Unlock()
|
||||
d.conf.filtersMu.Lock()
|
||||
defer d.conf.filtersMu.Unlock()
|
||||
|
||||
// Check for duplicates.
|
||||
if d.filterExistsLocked(flt.URL) {
|
||||
@@ -188,9 +203,9 @@ func (d *DNSFilter) filterAdd(flt FilterYAML) (err error) {
|
||||
}
|
||||
|
||||
if flt.white {
|
||||
d.WhitelistFilters = append(d.WhitelistFilters, flt)
|
||||
d.conf.WhitelistFilters = append(d.conf.WhitelistFilters, flt)
|
||||
} else {
|
||||
d.Filters = append(d.Filters, flt)
|
||||
d.conf.Filters = append(d.conf.Filters, flt)
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -254,7 +269,7 @@ func (d *DNSFilter) periodicallyRefreshFilters() {
|
||||
ivl := 5 // use a dynamically increasing time interval
|
||||
for {
|
||||
isNetErr, ok := false, false
|
||||
if d.FiltersUpdateIntervalHours != 0 {
|
||||
if d.conf.FiltersUpdateIntervalHours != 0 {
|
||||
_, isNetErr, ok = d.tryRefreshFilters(true, true, false)
|
||||
if ok && !isNetErr {
|
||||
ivl = maxInterval
|
||||
@@ -292,8 +307,8 @@ func (d *DNSFilter) tryRefreshFilters(block, allow, force bool) (updated int, is
|
||||
func (d *DNSFilter) listsToUpdate(filters *[]FilterYAML, force bool) (toUpd []FilterYAML) {
|
||||
now := time.Now()
|
||||
|
||||
d.filtersMu.RLock()
|
||||
defer d.filtersMu.RUnlock()
|
||||
d.conf.filtersMu.RLock()
|
||||
defer d.conf.filtersMu.RUnlock()
|
||||
|
||||
for i := range *filters {
|
||||
flt := &(*filters)[i] // otherwise we will be operating on a copy
|
||||
@@ -303,7 +318,7 @@ func (d *DNSFilter) listsToUpdate(filters *[]FilterYAML, force bool) (toUpd []Fi
|
||||
}
|
||||
|
||||
if !force {
|
||||
exp := flt.LastUpdated.Add(time.Duration(d.FiltersUpdateIntervalHours) * time.Hour)
|
||||
exp := flt.LastUpdated.Add(time.Duration(d.conf.FiltersUpdateIntervalHours) * time.Hour)
|
||||
if now.Before(exp) {
|
||||
continue
|
||||
}
|
||||
@@ -349,8 +364,8 @@ func (d *DNSFilter) refreshFiltersArray(filters *[]FilterYAML, force bool) (int,
|
||||
|
||||
updateCount := 0
|
||||
|
||||
d.filtersMu.Lock()
|
||||
defer d.filtersMu.Unlock()
|
||||
d.conf.filtersMu.Lock()
|
||||
defer d.conf.filtersMu.Unlock()
|
||||
|
||||
for i := range updateFilters {
|
||||
uf := &updateFilters[i]
|
||||
@@ -412,10 +427,10 @@ func (d *DNSFilter) refreshFiltersIntl(block, allow, force bool) (int, bool) {
|
||||
isNetErr := false
|
||||
|
||||
if block {
|
||||
updNum, lists, toUpd, isNetErr = d.refreshFiltersArray(&d.Filters, force)
|
||||
updNum, lists, toUpd, isNetErr = d.refreshFiltersArray(&d.conf.Filters, force)
|
||||
}
|
||||
if allow {
|
||||
updNumAl, listsAl, toUpdAl, isNetErrAl := d.refreshFiltersArray(&d.WhitelistFilters, force)
|
||||
updNumAl, listsAl, toUpdAl, isNetErrAl := d.refreshFiltersArray(&d.conf.WhitelistFilters, force)
|
||||
|
||||
updNum += updNumAl
|
||||
lists = append(lists, listsAl...)
|
||||
@@ -436,7 +451,7 @@ func (d *DNSFilter) refreshFiltersIntl(block, allow, force bool) (int, bool) {
|
||||
continue
|
||||
}
|
||||
|
||||
p := uf.Path(d.DataDir)
|
||||
p := uf.Path(d.conf.DataDir)
|
||||
err := os.Remove(p + ".old")
|
||||
if err != nil {
|
||||
log.Debug("filtering: removing old filter file %q: %s", p, err)
|
||||
@@ -453,7 +468,7 @@ func (d *DNSFilter) update(filter *FilterYAML) (b bool, err error) {
|
||||
filter.LastUpdated = time.Now()
|
||||
if !b {
|
||||
chErr := os.Chtimes(
|
||||
filter.Path(d.DataDir),
|
||||
filter.Path(d.conf.DataDir),
|
||||
filter.LastUpdated,
|
||||
filter.LastUpdated,
|
||||
)
|
||||
@@ -476,7 +491,7 @@ func (d *DNSFilter) updateIntl(flt *FilterYAML) (ok bool, err error) {
|
||||
// users.
|
||||
//
|
||||
// See https://github.com/AdguardTeam/AdGuardHome/issues/3198.
|
||||
tmpFile, err := aghrenameio.NewPendingFile(flt.Path(d.DataDir), 0o644)
|
||||
tmpFile, err := aghrenameio.NewPendingFile(flt.Path(d.conf.DataDir), 0o644)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
@@ -517,7 +532,7 @@ func (d *DNSFilter) finalizeUpdate(
|
||||
return errors.WithDeferred(returned, file.Cleanup())
|
||||
}
|
||||
|
||||
log.Info("filtering: saving contents of filter %d into %q", id, flt.Path(d.DataDir))
|
||||
log.Info("filtering: saving contents of filter %d into %q", id, flt.Path(d.conf.DataDir))
|
||||
|
||||
err = file.CloseReplace()
|
||||
if err != nil {
|
||||
@@ -527,7 +542,7 @@ func (d *DNSFilter) finalizeUpdate(
|
||||
rulesCount := res.RulesCount
|
||||
log.Info("filtering: updated filter %d: %d bytes, %d rules", id, res.BytesWritten, rulesCount)
|
||||
|
||||
flt.Name = aghalg.Coalesce(flt.Name, res.Title)
|
||||
flt.ensureName(res.Title)
|
||||
flt.checksum = res.Checksum
|
||||
flt.RulesCount = rulesCount
|
||||
|
||||
@@ -557,7 +572,7 @@ func (d *DNSFilter) reader(fltURL string) (r io.ReadCloser, err error) {
|
||||
// readerFromURL returns an io.ReadCloser reading filtering-rule list data form
|
||||
// the filter's URL.
|
||||
func (d *DNSFilter) readerFromURL(fltURL string) (r io.ReadCloser, err error) {
|
||||
resp, err := d.HTTPClient.Get(fltURL)
|
||||
resp, err := d.conf.HTTPClient.Get(fltURL)
|
||||
if err != nil {
|
||||
// Don't wrap the error since it's informative enough as is.
|
||||
return nil, err
|
||||
@@ -572,7 +587,7 @@ func (d *DNSFilter) readerFromURL(fltURL string) (r io.ReadCloser, err error) {
|
||||
|
||||
// loads filter contents from the file in dataDir
|
||||
func (d *DNSFilter) load(flt *FilterYAML) (err error) {
|
||||
fileName := flt.Path(d.DataDir)
|
||||
fileName := flt.Path(d.conf.DataDir)
|
||||
|
||||
log.Debug("filtering: loading filter %d from %q", flt.ID, fileName)
|
||||
|
||||
@@ -601,45 +616,46 @@ func (d *DNSFilter) load(flt *FilterYAML) (err error) {
|
||||
return fmt.Errorf("parsing filter file: %w", err)
|
||||
}
|
||||
|
||||
flt.ensureName(res.Title)
|
||||
flt.RulesCount, flt.checksum, flt.LastUpdated = res.RulesCount, res.Checksum, st.ModTime()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *DNSFilter) EnableFilters(async bool) {
|
||||
d.filtersMu.RLock()
|
||||
defer d.filtersMu.RUnlock()
|
||||
d.conf.filtersMu.RLock()
|
||||
defer d.conf.filtersMu.RUnlock()
|
||||
|
||||
d.enableFiltersLocked(async)
|
||||
}
|
||||
|
||||
func (d *DNSFilter) enableFiltersLocked(async bool) {
|
||||
filters := make([]Filter, 1, len(d.Filters)+len(d.WhitelistFilters)+1)
|
||||
filters := make([]Filter, 1, len(d.conf.Filters)+len(d.conf.WhitelistFilters)+1)
|
||||
filters[0] = Filter{
|
||||
ID: CustomListID,
|
||||
Data: []byte(strings.Join(d.UserRules, "\n")),
|
||||
Data: []byte(strings.Join(d.conf.UserRules, "\n")),
|
||||
}
|
||||
|
||||
for _, filter := range d.Filters {
|
||||
for _, filter := range d.conf.Filters {
|
||||
if !filter.Enabled {
|
||||
continue
|
||||
}
|
||||
|
||||
filters = append(filters, Filter{
|
||||
ID: filter.ID,
|
||||
FilePath: filter.Path(d.DataDir),
|
||||
FilePath: filter.Path(d.conf.DataDir),
|
||||
})
|
||||
}
|
||||
|
||||
var allowFilters []Filter
|
||||
for _, filter := range d.WhitelistFilters {
|
||||
for _, filter := range d.conf.WhitelistFilters {
|
||||
if !filter.Enabled {
|
||||
continue
|
||||
}
|
||||
|
||||
allowFilters = append(allowFilters, Filter{
|
||||
ID: filter.ID,
|
||||
FilePath: filter.Path(d.DataDir),
|
||||
FilePath: filter.Path(d.conf.DataDir),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -648,5 +664,5 @@ func (d *DNSFilter) enableFiltersLocked(async bool) {
|
||||
log.Error("filtering: enabling filters: %s", err)
|
||||
}
|
||||
|
||||
d.SetEnabled(d.FilteringEnabled)
|
||||
d.SetEnabled(d.conf.FilteringEnabled)
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package filtering
|
||||
|
||||
import (
|
||||
"io/fs"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
@@ -16,6 +15,9 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// testTimeout is the common timeout for tests.
|
||||
const testTimeout = 5 * time.Second
|
||||
|
||||
// serveHTTPLocally starts a new HTTP server, that handles its index with h. It
|
||||
// also gracefully closes the listener when the test under t finishes.
|
||||
func serveHTTPLocally(t *testing.T, h http.Handler) (urlStr string) {
|
||||
@@ -50,7 +52,49 @@ func serveFiltersLocally(t *testing.T, fltContent []byte) (urlStr string) {
|
||||
}))
|
||||
}
|
||||
|
||||
func TestFilters(t *testing.T) {
|
||||
// updateAndAssert loads filter content from its URL and then asserts rules
|
||||
// count.
|
||||
func updateAndAssert(
|
||||
t *testing.T,
|
||||
dnsFilter *DNSFilter,
|
||||
f *FilterYAML,
|
||||
wantUpd require.BoolAssertionFunc,
|
||||
wantRulesCount int,
|
||||
) {
|
||||
t.Helper()
|
||||
|
||||
ok, err := dnsFilter.update(f)
|
||||
require.NoError(t, err)
|
||||
wantUpd(t, ok)
|
||||
|
||||
assert.Equal(t, wantRulesCount, f.RulesCount)
|
||||
|
||||
dir, err := os.ReadDir(filepath.Join(dnsFilter.conf.DataDir, filterDir))
|
||||
require.NoError(t, err)
|
||||
require.FileExists(t, f.Path(dnsFilter.conf.DataDir))
|
||||
|
||||
assert.Len(t, dir, 1)
|
||||
|
||||
err = dnsFilter.load(f)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
// newDNSFilter returns a new properly initialized DNS filter instance.
|
||||
func newDNSFilter(t *testing.T) (d *DNSFilter) {
|
||||
t.Helper()
|
||||
|
||||
dnsFilter, err := New(&Config{
|
||||
DataDir: t.TempDir(),
|
||||
HTTPClient: &http.Client{
|
||||
Timeout: testTimeout,
|
||||
},
|
||||
}, nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
return dnsFilter
|
||||
}
|
||||
|
||||
func TestDNSFilter_Update(t *testing.T) {
|
||||
const content = `||example.org^$third-party
|
||||
# Inline comment example
|
||||
||example.com^$third-party
|
||||
@@ -58,49 +102,20 @@ func TestFilters(t *testing.T) {
|
||||
`
|
||||
|
||||
fltContent := []byte(content)
|
||||
|
||||
addr := serveFiltersLocally(t, fltContent)
|
||||
|
||||
tempDir := t.TempDir()
|
||||
|
||||
filters, err := New(&Config{
|
||||
DataDir: tempDir,
|
||||
HTTPClient: &http.Client{
|
||||
Timeout: 5 * time.Second,
|
||||
},
|
||||
}, nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
f := &FilterYAML{
|
||||
URL: addr,
|
||||
URL: addr,
|
||||
Name: "test-filter",
|
||||
}
|
||||
|
||||
updateAndAssert := func(t *testing.T, want require.BoolAssertionFunc, wantRulesCount int) {
|
||||
var ok bool
|
||||
ok, err = filters.update(f)
|
||||
require.NoError(t, err)
|
||||
want(t, ok)
|
||||
|
||||
assert.Equal(t, wantRulesCount, f.RulesCount)
|
||||
|
||||
var dir []fs.DirEntry
|
||||
dir, err = os.ReadDir(filepath.Join(tempDir, filterDir))
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Len(t, dir, 1)
|
||||
|
||||
require.FileExists(t, f.Path(tempDir))
|
||||
|
||||
err = filters.load(f)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
dnsFilter := newDNSFilter(t)
|
||||
|
||||
t.Run("download", func(t *testing.T) {
|
||||
updateAndAssert(t, require.True, 3)
|
||||
updateAndAssert(t, dnsFilter, f, require.True, 3)
|
||||
})
|
||||
|
||||
t.Run("refresh_idle", func(t *testing.T) {
|
||||
updateAndAssert(t, require.False, 3)
|
||||
updateAndAssert(t, dnsFilter, f, require.False, 3)
|
||||
})
|
||||
|
||||
t.Run("refresh_actually", func(t *testing.T) {
|
||||
@@ -110,13 +125,51 @@ func TestFilters(t *testing.T) {
|
||||
f.URL = serveFiltersLocally(t, anotherContent)
|
||||
t.Cleanup(func() { f.URL = oldURL })
|
||||
|
||||
updateAndAssert(t, require.True, 1)
|
||||
updateAndAssert(t, dnsFilter, f, require.True, 1)
|
||||
})
|
||||
|
||||
t.Run("load_unload", func(t *testing.T) {
|
||||
err = filters.load(f)
|
||||
err := dnsFilter.load(f)
|
||||
require.NoError(t, err)
|
||||
|
||||
f.unload()
|
||||
})
|
||||
}
|
||||
|
||||
func TestFilterYAML_EnsureName(t *testing.T) {
|
||||
dnsFilter := newDNSFilter(t)
|
||||
|
||||
t.Run("title_custom", func(t *testing.T) {
|
||||
content := []byte("! Title: src-title\n||example.com^")
|
||||
|
||||
f := &FilterYAML{
|
||||
URL: serveFiltersLocally(t, content),
|
||||
Name: "user-custom",
|
||||
}
|
||||
|
||||
updateAndAssert(t, dnsFilter, f, require.True, 1)
|
||||
assert.Equal(t, "user-custom", f.Name)
|
||||
})
|
||||
|
||||
t.Run("title_from_src", func(t *testing.T) {
|
||||
content := []byte("! Title: src-title\n||example.com^")
|
||||
|
||||
f := &FilterYAML{
|
||||
URL: serveFiltersLocally(t, content),
|
||||
}
|
||||
|
||||
updateAndAssert(t, dnsFilter, f, require.True, 1)
|
||||
assert.Equal(t, "src-title", f.Name)
|
||||
})
|
||||
|
||||
t.Run("title_default", func(t *testing.T) {
|
||||
content := []byte("||example.com^")
|
||||
|
||||
f := &FilterYAML{
|
||||
URL: serveFiltersLocally(t, content),
|
||||
}
|
||||
|
||||
updateAndAssert(t, dnsFilter, f, require.True, 1)
|
||||
assert.Equal(t, "List 0", f.Name)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"io/fs"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/netip"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
@@ -14,13 +15,16 @@ import (
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/aghhttp"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/aghnet"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/filtering/rulelist"
|
||||
"github.com/AdguardTeam/golibs/errors"
|
||||
"github.com/AdguardTeam/golibs/hostsfile"
|
||||
"github.com/AdguardTeam/golibs/log"
|
||||
"github.com/AdguardTeam/golibs/mathutil"
|
||||
"github.com/AdguardTeam/golibs/netutil"
|
||||
"github.com/AdguardTeam/golibs/stringutil"
|
||||
"github.com/AdguardTeam/urlfilter"
|
||||
"github.com/AdguardTeam/urlfilter/filterlist"
|
||||
@@ -51,7 +55,7 @@ type ServiceEntry struct {
|
||||
// Settings are custom filtering settings for a client.
|
||||
type Settings struct {
|
||||
ClientName string
|
||||
ClientIP net.IP
|
||||
ClientIP netip.Addr
|
||||
ClientTags []string
|
||||
|
||||
ServicesRules []ServiceEntry
|
||||
@@ -73,35 +77,19 @@ type Resolver interface {
|
||||
|
||||
// Config allows you to configure DNS filtering with New() or just change variables directly.
|
||||
type Config struct {
|
||||
// BlockingIPv4 is the IP address to be returned for a blocked A request.
|
||||
BlockingIPv4 netip.Addr `yaml:"blocking_ipv4"`
|
||||
|
||||
// BlockingIPv6 is the IP address to be returned for a blocked AAAA request.
|
||||
BlockingIPv6 netip.Addr `yaml:"blocking_ipv6"`
|
||||
|
||||
// SafeBrowsingChecker is the safe browsing hash-prefix checker.
|
||||
SafeBrowsingChecker Checker `yaml:"-"`
|
||||
|
||||
// ParentControl is the parental control hash-prefix checker.
|
||||
ParentalControlChecker Checker `yaml:"-"`
|
||||
|
||||
// enabled is used to be returned within Settings.
|
||||
//
|
||||
// It is of type uint32 to be accessed by atomic.
|
||||
//
|
||||
// TODO(e.burkov): Use atomic.Bool in Go 1.19.
|
||||
enabled uint32
|
||||
|
||||
FilteringEnabled bool `yaml:"filtering_enabled"` // whether or not use filter lists
|
||||
FiltersUpdateIntervalHours uint32 `yaml:"filters_update_interval"` // time period to update filters (in hours)
|
||||
|
||||
ParentalEnabled bool `yaml:"parental_enabled"`
|
||||
SafeBrowsingEnabled bool `yaml:"safebrowsing_enabled"`
|
||||
|
||||
SafeBrowsingCacheSize uint `yaml:"safebrowsing_cache_size"` // (in bytes)
|
||||
SafeSearchCacheSize uint `yaml:"safesearch_cache_size"` // (in bytes)
|
||||
ParentalCacheSize uint `yaml:"parental_cache_size"` // (in bytes)
|
||||
// TODO(a.garipov): Use timeutil.Duration
|
||||
CacheTime uint `yaml:"cache_time"` // Element's TTL (in minutes)
|
||||
|
||||
SafeSearchConf SafeSearchConfig `yaml:"safe_search"`
|
||||
SafeSearch SafeSearch `yaml:"-"`
|
||||
|
||||
Rewrites []*LegacyRewrite `yaml:"rewrites"`
|
||||
SafeSearch SafeSearch `yaml:"-"`
|
||||
|
||||
// BlockedServices is the configuration of blocked services.
|
||||
// Per-client settings can override this configuration.
|
||||
@@ -120,11 +108,30 @@ type Config struct {
|
||||
// HTTPClient is the client to use for updating the remote filters.
|
||||
HTTPClient *http.Client `yaml:"-"`
|
||||
|
||||
// filtersMu protects filter lists.
|
||||
filtersMu *sync.RWMutex
|
||||
|
||||
// ProtectionDisabledUntil is the timestamp until when the protection is
|
||||
// disabled.
|
||||
ProtectionDisabledUntil *time.Time `yaml:"protection_disabled_until"`
|
||||
|
||||
SafeSearchConf SafeSearchConfig `yaml:"safe_search"`
|
||||
|
||||
// DataDir is used to store filters' contents.
|
||||
DataDir string `yaml:"-"`
|
||||
|
||||
// filtersMu protects filter lists.
|
||||
filtersMu *sync.RWMutex
|
||||
// BlockingMode defines the way how blocked responses are constructed.
|
||||
BlockingMode BlockingMode `yaml:"blocking_mode"`
|
||||
|
||||
// ParentalBlockHost is the IP (or domain name) which is used to respond to
|
||||
// DNS requests blocked by parental control.
|
||||
ParentalBlockHost string `yaml:"parental_block_host"`
|
||||
|
||||
// SafeBrowsingBlockHost is the IP (or domain name) which is used to respond
|
||||
// to DNS requests blocked by safe-browsing.
|
||||
SafeBrowsingBlockHost string `yaml:"safebrowsing_block_host"`
|
||||
|
||||
Rewrites []*LegacyRewrite `yaml:"rewrites"`
|
||||
|
||||
// Filters are the blocking filter lists.
|
||||
Filters []FilterYAML `yaml:"-"`
|
||||
@@ -134,8 +141,62 @@ type Config struct {
|
||||
|
||||
// UserRules is the global list of custom rules.
|
||||
UserRules []string `yaml:"-"`
|
||||
|
||||
SafeBrowsingCacheSize uint `yaml:"safebrowsing_cache_size"` // (in bytes)
|
||||
SafeSearchCacheSize uint `yaml:"safesearch_cache_size"` // (in bytes)
|
||||
ParentalCacheSize uint `yaml:"parental_cache_size"` // (in bytes)
|
||||
// TODO(a.garipov): Use timeutil.Duration
|
||||
CacheTime uint `yaml:"cache_time"` // Element's TTL (in minutes)
|
||||
|
||||
// enabled is used to be returned within Settings.
|
||||
//
|
||||
// It is of type uint32 to be accessed by atomic.
|
||||
//
|
||||
// TODO(e.burkov): Use atomic.Bool in Go 1.19.
|
||||
enabled uint32
|
||||
|
||||
// FiltersUpdateIntervalHours is the time period to update filters
|
||||
// (in hours).
|
||||
FiltersUpdateIntervalHours uint32 `yaml:"filters_update_interval"`
|
||||
|
||||
// BlockedResponseTTL is the time-to-live value for blocked responses. If
|
||||
// 0, then default value is used (3600).
|
||||
BlockedResponseTTL uint32 `yaml:"blocked_response_ttl"`
|
||||
|
||||
// FilteringEnabled indicates whether or not use filter lists.
|
||||
FilteringEnabled bool `yaml:"filtering_enabled"`
|
||||
|
||||
ParentalEnabled bool `yaml:"parental_enabled"`
|
||||
SafeBrowsingEnabled bool `yaml:"safebrowsing_enabled"`
|
||||
|
||||
// ProtectionEnabled defines whether or not use any of filtering features.
|
||||
ProtectionEnabled bool `yaml:"protection_enabled"`
|
||||
}
|
||||
|
||||
// BlockingMode is an enum of all allowed blocking modes.
|
||||
type BlockingMode string
|
||||
|
||||
// Allowed blocking modes.
|
||||
const (
|
||||
// BlockingModeCustomIP means respond with a custom IP address.
|
||||
BlockingModeCustomIP BlockingMode = "custom_ip"
|
||||
|
||||
// BlockingModeDefault is the same as BlockingModeNullIP for
|
||||
// Adblock-style rules, but responds with the IP address specified in
|
||||
// the rule when blocked by an `/etc/hosts`-style rule.
|
||||
BlockingModeDefault BlockingMode = "default"
|
||||
|
||||
// BlockingModeNullIP means respond with a zero IP address: "0.0.0.0"
|
||||
// for A requests and "::" for AAAA ones.
|
||||
BlockingModeNullIP BlockingMode = "null_ip"
|
||||
|
||||
// BlockingModeNXDOMAIN means respond with the NXDOMAIN code.
|
||||
BlockingModeNXDOMAIN BlockingMode = "nxdomain"
|
||||
|
||||
// BlockingModeREFUSED means respond with the REFUSED code.
|
||||
BlockingModeREFUSED BlockingMode = "refused"
|
||||
)
|
||||
|
||||
// LookupStats store stats collected during safebrowsing or parental checks
|
||||
type LookupStats struct {
|
||||
Requests uint64 // number of HTTP requests that were sent
|
||||
@@ -189,9 +250,11 @@ type DNSFilter struct {
|
||||
|
||||
engineLock sync.RWMutex
|
||||
|
||||
Config // for direct access by library users, even a = assignment
|
||||
// confLock protects Config.
|
||||
confLock sync.RWMutex
|
||||
// confMu protects conf.
|
||||
confMu *sync.RWMutex
|
||||
|
||||
// conf contains filtering parameters.
|
||||
conf *Config
|
||||
|
||||
// Channel for passing data to filters-initializer goroutine
|
||||
filtersInitializerChan chan filtersInitializerParams
|
||||
@@ -292,48 +355,38 @@ func (r Reason) In(reasons ...Reason) (ok bool) { return slices.Contains(reasons
|
||||
|
||||
// SetEnabled sets the status of the *DNSFilter.
|
||||
func (d *DNSFilter) SetEnabled(enabled bool) {
|
||||
atomic.StoreUint32(&d.enabled, mathutil.BoolToNumber[uint32](enabled))
|
||||
atomic.StoreUint32(&d.conf.enabled, mathutil.BoolToNumber[uint32](enabled))
|
||||
}
|
||||
|
||||
// Settings returns filtering settings.
|
||||
func (d *DNSFilter) Settings() (s *Settings) {
|
||||
d.confLock.RLock()
|
||||
defer d.confLock.RUnlock()
|
||||
d.confMu.RLock()
|
||||
defer d.confMu.RUnlock()
|
||||
|
||||
return &Settings{
|
||||
FilteringEnabled: atomic.LoadUint32(&d.Config.enabled) != 0,
|
||||
SafeSearchEnabled: d.Config.SafeSearchConf.Enabled,
|
||||
SafeBrowsingEnabled: d.Config.SafeBrowsingEnabled,
|
||||
ParentalEnabled: d.Config.ParentalEnabled,
|
||||
FilteringEnabled: atomic.LoadUint32(&d.conf.enabled) != 0,
|
||||
SafeSearchEnabled: d.conf.SafeSearchConf.Enabled,
|
||||
SafeBrowsingEnabled: d.conf.SafeBrowsingEnabled,
|
||||
ParentalEnabled: d.conf.ParentalEnabled,
|
||||
}
|
||||
}
|
||||
|
||||
// WriteDiskConfig - write configuration
|
||||
func (d *DNSFilter) WriteDiskConfig(c *Config) {
|
||||
func() {
|
||||
d.confLock.Lock()
|
||||
defer d.confLock.Unlock()
|
||||
d.confMu.Lock()
|
||||
defer d.confMu.Unlock()
|
||||
|
||||
*c = d.Config
|
||||
*c = *d.conf
|
||||
c.Rewrites = cloneRewrites(c.Rewrites)
|
||||
}()
|
||||
|
||||
d.filtersMu.RLock()
|
||||
defer d.filtersMu.RUnlock()
|
||||
d.conf.filtersMu.RLock()
|
||||
defer d.conf.filtersMu.RUnlock()
|
||||
|
||||
c.Filters = slices.Clone(d.Filters)
|
||||
c.WhitelistFilters = slices.Clone(d.WhitelistFilters)
|
||||
c.UserRules = slices.Clone(d.UserRules)
|
||||
}
|
||||
|
||||
// cloneRewrites returns a deep copy of entries.
|
||||
func cloneRewrites(entries []*LegacyRewrite) (clone []*LegacyRewrite) {
|
||||
clone = make([]*LegacyRewrite, len(entries))
|
||||
for i, rw := range entries {
|
||||
clone[i] = rw.clone()
|
||||
}
|
||||
|
||||
return clone
|
||||
c.Filters = slices.Clone(d.conf.Filters)
|
||||
c.WhitelistFilters = slices.Clone(d.conf.WhitelistFilters)
|
||||
c.UserRules = slices.Clone(d.conf.UserRules)
|
||||
}
|
||||
|
||||
// setFilters sets new filters, synchronously or asynchronously. When filters
|
||||
@@ -405,13 +458,84 @@ func (d *DNSFilter) reset() {
|
||||
}
|
||||
}
|
||||
|
||||
// ProtectionStatus returns the status of protection and time until it's
|
||||
// disabled if so.
|
||||
func (d *DNSFilter) ProtectionStatus() (status bool, disabledUntil *time.Time) {
|
||||
d.confMu.RLock()
|
||||
defer d.confMu.RUnlock()
|
||||
|
||||
return d.conf.ProtectionEnabled, d.conf.ProtectionDisabledUntil
|
||||
}
|
||||
|
||||
// SetProtectionStatus updates the status of protection and time until it's
|
||||
// disabled.
|
||||
func (d *DNSFilter) SetProtectionStatus(status bool, disabledUntil *time.Time) {
|
||||
d.confMu.Lock()
|
||||
defer d.confMu.Unlock()
|
||||
|
||||
d.conf.ProtectionEnabled = status
|
||||
d.conf.ProtectionDisabledUntil = disabledUntil
|
||||
}
|
||||
|
||||
// SetProtectionEnabled updates the status of protection.
|
||||
func (d *DNSFilter) SetProtectionEnabled(status bool) {
|
||||
d.confMu.Lock()
|
||||
defer d.confMu.Unlock()
|
||||
|
||||
d.conf.ProtectionEnabled = status
|
||||
}
|
||||
|
||||
// EtcHostsRecords returns the hosts records for the hostname.
|
||||
func (d *DNSFilter) EtcHostsRecords(hostname string) (recs []*hostsfile.Record) {
|
||||
if d.conf.EtcHosts != nil {
|
||||
return d.conf.EtcHosts.MatchName(hostname)
|
||||
}
|
||||
|
||||
return recs
|
||||
}
|
||||
|
||||
// SetBlockingMode sets blocking mode properties.
|
||||
func (d *DNSFilter) SetBlockingMode(mode BlockingMode, bIPv4, bIPv6 netip.Addr) {
|
||||
d.confMu.Lock()
|
||||
defer d.confMu.Unlock()
|
||||
|
||||
d.conf.BlockingMode = mode
|
||||
if mode == BlockingModeCustomIP {
|
||||
d.conf.BlockingIPv4 = bIPv4
|
||||
d.conf.BlockingIPv6 = bIPv6
|
||||
}
|
||||
}
|
||||
|
||||
// BlockingMode returns blocking mode properties.
|
||||
func (d *DNSFilter) BlockingMode() (mode BlockingMode, bIPv4, bIPv6 netip.Addr) {
|
||||
d.confMu.RLock()
|
||||
defer d.confMu.RUnlock()
|
||||
|
||||
return d.conf.BlockingMode, d.conf.BlockingIPv4, d.conf.BlockingIPv6
|
||||
}
|
||||
|
||||
// BlockedResponseTTL returns TTL for blocked responses.
|
||||
func (d *DNSFilter) BlockedResponseTTL() (ttl uint32) {
|
||||
return d.conf.BlockedResponseTTL
|
||||
}
|
||||
|
||||
// SafeBrowsingBlockHost returns a host for safe browsing blocked responses.
|
||||
func (d *DNSFilter) SafeBrowsingBlockHost() (host string) {
|
||||
return d.conf.SafeBrowsingBlockHost
|
||||
}
|
||||
|
||||
// ParentalBlockHost returns a host for parental protection blocked responses.
|
||||
func (d *DNSFilter) ParentalBlockHost() (host string) {
|
||||
return d.conf.ParentalBlockHost
|
||||
}
|
||||
|
||||
// ResultRule contains information about applied rules.
|
||||
type ResultRule struct {
|
||||
// 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"`
|
||||
IP netip.Addr `json:",omitempty"`
|
||||
// FilterListID is the ID of the rule's filter list.
|
||||
FilterListID int64 `json:",omitempty"`
|
||||
}
|
||||
@@ -437,7 +561,7 @@ type Result struct {
|
||||
|
||||
// IPList is the lookup rewrite result. It is empty unless Reason is set to
|
||||
// Rewritten.
|
||||
IPList []net.IP `json:",omitempty"`
|
||||
IPList []netip.Addr `json:",omitempty"`
|
||||
|
||||
// Rules are applied rules. If Rules are not empty, each rule is not nil.
|
||||
Rules []*ResultRule `json:",omitempty"`
|
||||
@@ -503,31 +627,50 @@ func (d *DNSFilter) matchSysHosts(
|
||||
qtype uint16,
|
||||
setts *Settings,
|
||||
) (res Result, err error) {
|
||||
if !setts.FilteringEnabled || d.EtcHosts == nil {
|
||||
// TODO(e.burkov): Where else is this checked?
|
||||
if !setts.FilteringEnabled || d.conf.EtcHosts == nil {
|
||||
return res, nil
|
||||
}
|
||||
|
||||
dnsres, _ := d.EtcHosts.MatchRequest(&urlfilter.DNSRequest{
|
||||
Hostname: host,
|
||||
SortedClientTags: setts.ClientTags,
|
||||
// TODO(e.burkov): Wait for urlfilter update to pass netip.Addr.
|
||||
ClientIP: setts.ClientIP.String(),
|
||||
ClientName: setts.ClientName,
|
||||
DNSType: qtype,
|
||||
})
|
||||
if dnsres == nil {
|
||||
return res, nil
|
||||
var recs []*hostsfile.Record
|
||||
switch qtype {
|
||||
case dns.TypeA, dns.TypeAAAA:
|
||||
recs = d.conf.EtcHosts.MatchName(host)
|
||||
case dns.TypePTR:
|
||||
var ip net.IP
|
||||
ip, err = netutil.IPFromReversedAddr(host)
|
||||
if err != nil {
|
||||
log.Debug("filtering: failed to parse PTR record %q: %s", host, err)
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
addr, _ := netip.AddrFromSlice(ip)
|
||||
recs = d.conf.EtcHosts.MatchAddr(addr)
|
||||
default:
|
||||
log.Debug("filtering: unsupported query type %s", dns.Type(qtype))
|
||||
}
|
||||
|
||||
dnsr := dnsres.DNSRewrites()
|
||||
if len(dnsr) == 0 {
|
||||
return res, nil
|
||||
var vals []rules.RRValue
|
||||
var resRules []*ResultRule
|
||||
resRulesLen := 0
|
||||
for _, rec := range recs {
|
||||
vals, resRules = appendRewriteResultFromHost(vals, resRules, rec, qtype)
|
||||
if len(resRules) > resRulesLen {
|
||||
resRulesLen = len(resRules)
|
||||
log.Debug("filtering: matched %s in %q", host, rec.Source)
|
||||
}
|
||||
}
|
||||
|
||||
res = d.processDNSRewrites(dnsr)
|
||||
res.Reason = RewrittenAutoHosts
|
||||
for _, r := range res.Rules {
|
||||
r.Text = stringutil.Coalesce(d.EtcHosts.Translate(r.Text), r.Text)
|
||||
if len(vals) > 0 {
|
||||
res.DNSRewriteResult = &DNSRewriteResult{
|
||||
Response: DNSRewriteResultResponse{
|
||||
qtype: vals,
|
||||
},
|
||||
RCode: dns.RcodeSuccess,
|
||||
}
|
||||
res.Rules = resRules
|
||||
res.Reason = RewrittenRule
|
||||
}
|
||||
|
||||
return res, nil
|
||||
@@ -543,10 +686,10 @@ func (d *DNSFilter) matchSysHosts(
|
||||
// accordingly. If the found rewrite has a special value of "A" or "AAAA", the
|
||||
// result is an exception.
|
||||
func (d *DNSFilter) processRewrites(host string, qtype uint16) (res Result) {
|
||||
d.confLock.RLock()
|
||||
defer d.confLock.RUnlock()
|
||||
d.confMu.RLock()
|
||||
defer d.confMu.RUnlock()
|
||||
|
||||
rewrites, matched := findRewrites(d.Rewrites, host, qtype)
|
||||
rewrites, matched := findRewrites(d.conf.Rewrites, host, qtype)
|
||||
if !matched {
|
||||
return Result{}
|
||||
}
|
||||
@@ -586,7 +729,7 @@ func (d *DNSFilter) processRewrites(host string, qtype uint16) (res Result) {
|
||||
|
||||
cnames.Add(host)
|
||||
res.CanonName = host
|
||||
rewrites, matched = findRewrites(d.Rewrites, host, qtype)
|
||||
rewrites, matched = findRewrites(d.conf.Rewrites, host, qtype)
|
||||
}
|
||||
|
||||
setRewriteResult(&res, host, rewrites, qtype)
|
||||
@@ -594,25 +737,6 @@ func (d *DNSFilter) processRewrites(host string, qtype uint16) (res Result) {
|
||||
return res
|
||||
}
|
||||
|
||||
// setRewriteResult sets the Reason or IPList of res if necessary. res must not
|
||||
// be nil.
|
||||
func setRewriteResult(res *Result, host string, rewrites []*LegacyRewrite, qtype uint16) {
|
||||
for _, rw := range rewrites {
|
||||
if rw.Type == qtype && (qtype == dns.TypeA || qtype == dns.TypeAAAA) {
|
||||
if rw.IP == nil {
|
||||
// "A"/"AAAA" exception: allow getting from upstream.
|
||||
res.Reason = NotFilteredNotFound
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
res.IPList = append(res.IPList, rw.IP)
|
||||
|
||||
log.Debug("rewrite: a/aaaa for %s is %s", host, rw.IP)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// matchBlockedServicesRules checks the host against the blocked services rules
|
||||
// in settings, if any. The err is always nil, it is only there to make this
|
||||
// a valid hostChecker function.
|
||||
@@ -794,22 +918,27 @@ func (d *DNSFilter) matchHostProcessDNSResult(
|
||||
return makeResult([]rules.Rule{dnsres.NetworkRule}, reason)
|
||||
}
|
||||
|
||||
if qtype == dns.TypeA && dnsres.HostRulesV4 != nil {
|
||||
res = makeResult(hostRulesToRules(dnsres.HostRulesV4), FilteredBlockList)
|
||||
for i, hr := range dnsres.HostRulesV4 {
|
||||
res.Rules[i].IP = hr.IP.To4()
|
||||
switch qtype {
|
||||
case dns.TypeA:
|
||||
if dnsres.HostRulesV4 != nil {
|
||||
res = makeResult(hostRulesToRules(dnsres.HostRulesV4), FilteredBlockList)
|
||||
for i, hr := range dnsres.HostRulesV4 {
|
||||
res.Rules[i].IP = hr.IP
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
case dns.TypeAAAA:
|
||||
if dnsres.HostRulesV6 != nil {
|
||||
res = makeResult(hostRulesToRules(dnsres.HostRulesV6), FilteredBlockList)
|
||||
for i, hr := range dnsres.HostRulesV6 {
|
||||
res.Rules[i].IP = hr.IP
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
if qtype == dns.TypeAAAA && dnsres.HostRulesV6 != nil {
|
||||
res = makeResult(hostRulesToRules(dnsres.HostRulesV6), FilteredBlockList)
|
||||
for i, hr := range dnsres.HostRulesV6 {
|
||||
res.Rules[i].IP = hr.IP.To16()
|
||||
return res
|
||||
}
|
||||
|
||||
return res
|
||||
default:
|
||||
// Go on.
|
||||
}
|
||||
|
||||
return hostResultForOtherQType(dnsres)
|
||||
@@ -844,7 +973,7 @@ func (d *DNSFilter) matchHost(
|
||||
Hostname: host,
|
||||
SortedClientTags: setts.ClientTags,
|
||||
// TODO(e.burkov): Wait for urlfilter update to pass net.IP.
|
||||
ClientIP: setts.ClientIP.String(),
|
||||
ClientIP: setts.ClientIP,
|
||||
ClientName: setts.ClientName,
|
||||
DNSType: rrtype,
|
||||
}
|
||||
@@ -895,26 +1024,6 @@ func (d *DNSFilter) matchHost(
|
||||
return res, nil
|
||||
}
|
||||
|
||||
// processDNSResultRewrites returns an empty Result if there are no dnsrewrite
|
||||
// rules in dnsres. Otherwise, it returns the processed Result.
|
||||
func (d *DNSFilter) processDNSResultRewrites(
|
||||
dnsres *urlfilter.DNSResult,
|
||||
host string,
|
||||
) (dnsRWRes Result) {
|
||||
dnsr := dnsres.DNSRewrites()
|
||||
if len(dnsr) == 0 {
|
||||
return Result{}
|
||||
}
|
||||
|
||||
res := d.processDNSRewrites(dnsr)
|
||||
if res.Reason == RewrittenRule && res.CanonName == host {
|
||||
// A rewrite of a host to itself. Go on and try matching other things.
|
||||
return Result{}
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
// makeResult returns a properly constructed Result.
|
||||
func makeResult(matchedRules []rules.Rule, reason Reason) (res Result) {
|
||||
resRules := make([]*ResultRule, len(matchedRules))
|
||||
@@ -951,6 +1060,7 @@ func New(c *Config, blockFilters []Filter) (d *DNSFilter, err error) {
|
||||
refreshLock: &sync.Mutex{},
|
||||
safeBrowsingChecker: c.SafeBrowsingChecker,
|
||||
parentalControlChecker: c.ParentalControlChecker,
|
||||
confMu: &sync.RWMutex{},
|
||||
}
|
||||
|
||||
d.safeSearch = c.SafeSearch
|
||||
@@ -977,17 +1087,16 @@ func New(c *Config, blockFilters []Filter) (d *DNSFilter, err error) {
|
||||
|
||||
defer func() { err = errors.Annotate(err, "filtering: %w") }()
|
||||
|
||||
d.Config = *c
|
||||
d.filtersMu = &sync.RWMutex{}
|
||||
d.conf = c
|
||||
d.conf.filtersMu = &sync.RWMutex{}
|
||||
|
||||
err = d.prepareRewrites()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("rewrites: preparing: %s", err)
|
||||
}
|
||||
|
||||
if d.BlockedServices != nil {
|
||||
err = d.BlockedServices.Validate()
|
||||
|
||||
if d.conf.BlockedServices != nil {
|
||||
err = d.conf.BlockedServices.Validate()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("filtering: %w", err)
|
||||
}
|
||||
@@ -1002,16 +1111,16 @@ func New(c *Config, blockFilters []Filter) (d *DNSFilter, err error) {
|
||||
}
|
||||
}
|
||||
|
||||
_ = os.MkdirAll(filepath.Join(d.DataDir, filterDir), 0o755)
|
||||
_ = os.MkdirAll(filepath.Join(d.conf.DataDir, filterDir), 0o755)
|
||||
|
||||
d.loadFilters(d.Filters)
|
||||
d.loadFilters(d.WhitelistFilters)
|
||||
d.loadFilters(d.conf.Filters)
|
||||
d.loadFilters(d.conf.WhitelistFilters)
|
||||
|
||||
d.Filters = deduplicateFilters(d.Filters)
|
||||
d.WhitelistFilters = deduplicateFilters(d.WhitelistFilters)
|
||||
d.conf.Filters = deduplicateFilters(d.conf.Filters)
|
||||
d.conf.WhitelistFilters = deduplicateFilters(d.conf.WhitelistFilters)
|
||||
|
||||
updateUniqueFilterID(d.Filters)
|
||||
updateUniqueFilterID(d.WhitelistFilters)
|
||||
updateUniqueFilterID(d.conf.Filters)
|
||||
updateUniqueFilterID(d.conf.WhitelistFilters)
|
||||
|
||||
return d, nil
|
||||
}
|
||||
|
||||
@@ -3,12 +3,13 @@ package filtering
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/netip"
|
||||
"testing"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/aghtest"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/filtering/hashprefix"
|
||||
"github.com/AdguardTeam/golibs/log"
|
||||
"github.com/AdguardTeam/golibs/netutil"
|
||||
"github.com/AdguardTeam/golibs/testutil"
|
||||
"github.com/AdguardTeam/urlfilter/rules"
|
||||
"github.com/miekg/dns"
|
||||
@@ -91,7 +92,7 @@ func (d *DNSFilter) checkMatchEmpty(t *testing.T, hostname string, setts *Settin
|
||||
assert.Falsef(t, res.IsFiltered, "host %q", hostname)
|
||||
}
|
||||
|
||||
func TestEtcHostsMatching(t *testing.T) {
|
||||
func TestDNSFilter_CheckHost_hostRules(t *testing.T) {
|
||||
addr := "216.239.38.120"
|
||||
addr6 := "::1"
|
||||
text := fmt.Sprintf(` %s google.com www.google.com # enforce google's safesearch
|
||||
@@ -149,8 +150,8 @@ func TestEtcHostsMatching(t *testing.T) {
|
||||
|
||||
require.Len(t, res.Rules, 2)
|
||||
|
||||
assert.Equal(t, res.Rules[0].IP, net.IP{0, 0, 0, 1})
|
||||
assert.Equal(t, res.Rules[1].IP, net.IP{0, 0, 0, 2})
|
||||
assert.Equal(t, res.Rules[0].IP, netip.AddrFrom4([4]byte{0, 0, 0, 1}))
|
||||
assert.Equal(t, res.Rules[1].IP, netip.AddrFrom4([4]byte{0, 0, 0, 2}))
|
||||
|
||||
// One IPv6 address.
|
||||
res, err = d.CheckHost("host2", dns.TypeAAAA, setts)
|
||||
@@ -160,7 +161,7 @@ func TestEtcHostsMatching(t *testing.T) {
|
||||
|
||||
require.Len(t, res.Rules, 1)
|
||||
|
||||
assert.Equal(t, res.Rules[0].IP, net.IPv6loopback)
|
||||
assert.Equal(t, res.Rules[0].IP, netutil.IPv6Localhost())
|
||||
}
|
||||
|
||||
// Safe Browsing.
|
||||
|
||||
@@ -27,7 +27,7 @@ func TestChcker_getQuestion(t *testing.T) {
|
||||
hashes := hostnameToHashes("1.2.3.sub.host.com")
|
||||
assert.Len(t, hashes, 3)
|
||||
|
||||
hash := sha256.Sum256([]byte("3.sub.host.com"))
|
||||
hash := hostnameHash(sha256.Sum256([]byte("3.sub.host.com")))
|
||||
hexPref1 := hex.EncodeToString(hash[:prefixLen])
|
||||
assert.True(t, slices.Contains(hashes, hash))
|
||||
|
||||
@@ -105,7 +105,7 @@ func TestChecker_storeInCache(t *testing.T) {
|
||||
// store in cache hashes for "3.sub.host.com" and "host.com"
|
||||
// and empty data for hash-prefix for "sub.host.com"
|
||||
hashes := []hostnameHash{}
|
||||
hash := sha256.Sum256([]byte("sub.host.com"))
|
||||
hash := hostnameHash(sha256.Sum256([]byte("sub.host.com")))
|
||||
hashes = append(hashes, hash)
|
||||
var hashesArray []hostnameHash
|
||||
hash4 := sha256.Sum256([]byte("3.sub.host.com"))
|
||||
|
||||
@@ -3,8 +3,8 @@ package filtering
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/netip"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
@@ -124,7 +124,7 @@ func (d *DNSFilter) handleFilteringAddURL(w http.ResponseWriter, r *http.Request
|
||||
return
|
||||
}
|
||||
|
||||
d.ConfigModified()
|
||||
d.conf.ConfigModified()
|
||||
d.EnableFilters(true)
|
||||
|
||||
_, err = fmt.Fprintf(w, "OK %d rules\n", filt.RulesCount)
|
||||
@@ -149,12 +149,12 @@ func (d *DNSFilter) handleFilteringRemoveURL(w http.ResponseWriter, r *http.Requ
|
||||
|
||||
var deleted FilterYAML
|
||||
func() {
|
||||
d.filtersMu.Lock()
|
||||
defer d.filtersMu.Unlock()
|
||||
d.conf.filtersMu.Lock()
|
||||
defer d.conf.filtersMu.Unlock()
|
||||
|
||||
filters := &d.Filters
|
||||
filters := &d.conf.Filters
|
||||
if req.Whitelist {
|
||||
filters = &d.WhitelistFilters
|
||||
filters = &d.conf.WhitelistFilters
|
||||
}
|
||||
|
||||
delIdx := slices.IndexFunc(*filters, func(flt FilterYAML) bool {
|
||||
@@ -167,7 +167,7 @@ func (d *DNSFilter) handleFilteringRemoveURL(w http.ResponseWriter, r *http.Requ
|
||||
}
|
||||
|
||||
deleted = (*filters)[delIdx]
|
||||
p := deleted.Path(d.DataDir)
|
||||
p := deleted.Path(d.conf.DataDir)
|
||||
err = os.Rename(p, p+".old")
|
||||
if err != nil && !errors.Is(err, os.ErrNotExist) {
|
||||
log.Error("deleting filter %d: renaming file %q: %s", deleted.ID, p, err)
|
||||
@@ -180,7 +180,7 @@ func (d *DNSFilter) handleFilteringRemoveURL(w http.ResponseWriter, r *http.Requ
|
||||
log.Info("deleted filter %d", deleted.ID)
|
||||
}()
|
||||
|
||||
d.ConfigModified()
|
||||
d.conf.ConfigModified()
|
||||
d.EnableFilters(true)
|
||||
|
||||
// NOTE: The old files "filter.txt.old" aren't deleted. It's not really
|
||||
@@ -242,7 +242,7 @@ func (d *DNSFilter) handleFilteringSetURL(w http.ResponseWriter, r *http.Request
|
||||
return
|
||||
}
|
||||
|
||||
d.ConfigModified()
|
||||
d.conf.ConfigModified()
|
||||
if restart {
|
||||
d.EnableFilters(true)
|
||||
}
|
||||
@@ -266,8 +266,8 @@ func (d *DNSFilter) handleFilteringSetRules(w http.ResponseWriter, r *http.Reque
|
||||
return
|
||||
}
|
||||
|
||||
d.UserRules = req.Rules
|
||||
d.ConfigModified()
|
||||
d.conf.UserRules = req.Rules
|
||||
d.conf.ConfigModified()
|
||||
d.EnableFilters(true)
|
||||
}
|
||||
|
||||
@@ -301,7 +301,7 @@ func (d *DNSFilter) handleFilteringRefresh(w http.ResponseWriter, r *http.Reques
|
||||
return
|
||||
}
|
||||
|
||||
_ = aghhttp.WriteJSONResponse(w, r, resp)
|
||||
aghhttp.WriteJSONResponseOK(w, r, resp)
|
||||
}
|
||||
|
||||
type filterJSON struct {
|
||||
@@ -340,21 +340,21 @@ func filterToJSON(f FilterYAML) filterJSON {
|
||||
// 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 {
|
||||
d.conf.filtersMu.RLock()
|
||||
resp.Enabled = d.conf.FilteringEnabled
|
||||
resp.Interval = d.conf.FiltersUpdateIntervalHours
|
||||
for _, f := range d.conf.Filters {
|
||||
fj := filterToJSON(f)
|
||||
resp.Filters = append(resp.Filters, fj)
|
||||
}
|
||||
for _, f := range d.WhitelistFilters {
|
||||
for _, f := range d.conf.WhitelistFilters {
|
||||
fj := filterToJSON(f)
|
||||
resp.WhitelistFilters = append(resp.WhitelistFilters, fj)
|
||||
}
|
||||
resp.UserRules = d.UserRules
|
||||
d.filtersMu.RUnlock()
|
||||
resp.UserRules = d.conf.UserRules
|
||||
d.conf.filtersMu.RUnlock()
|
||||
|
||||
_ = aghhttp.WriteJSONResponse(w, r, resp)
|
||||
aghhttp.WriteJSONResponseOK(w, r, resp)
|
||||
}
|
||||
|
||||
// Set filtering configuration
|
||||
@@ -374,14 +374,14 @@ func (d *DNSFilter) handleFilteringConfig(w http.ResponseWriter, r *http.Request
|
||||
}
|
||||
|
||||
func() {
|
||||
d.filtersMu.Lock()
|
||||
defer d.filtersMu.Unlock()
|
||||
d.conf.filtersMu.Lock()
|
||||
defer d.conf.filtersMu.Unlock()
|
||||
|
||||
d.FilteringEnabled = req.Enabled
|
||||
d.FiltersUpdateIntervalHours = req.Interval
|
||||
d.conf.FilteringEnabled = req.Enabled
|
||||
d.conf.FiltersUpdateIntervalHours = req.Interval
|
||||
}()
|
||||
|
||||
d.ConfigModified()
|
||||
d.conf.ConfigModified()
|
||||
d.EnableFilters(true)
|
||||
}
|
||||
|
||||
@@ -404,8 +404,8 @@ type checkHostResp struct {
|
||||
SvcName string `json:"service_name"`
|
||||
|
||||
// for Rewrite:
|
||||
CanonName string `json:"cname"` // CNAME value
|
||||
IPList []net.IP `json:"ip_addrs"` // list of IP addresses
|
||||
CanonName string `json:"cname"` // CNAME value
|
||||
IPList []netip.Addr `json:"ip_addrs"` // list of IP addresses
|
||||
|
||||
// FilterID is the ID of the rule's filter list.
|
||||
//
|
||||
@@ -456,7 +456,7 @@ func (d *DNSFilter) handleCheckHost(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
}
|
||||
|
||||
_ = aghhttp.WriteJSONResponse(w, r, resp)
|
||||
aghhttp.WriteJSONResponseOK(w, r, resp)
|
||||
}
|
||||
|
||||
// setProtectedBool sets the value of a boolean pointer under a lock. l must
|
||||
@@ -484,15 +484,15 @@ func protectedBool(mu *sync.RWMutex, ptr *bool) (val bool) {
|
||||
// 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()
|
||||
setProtectedBool(d.confMu, &d.conf.SafeBrowsingEnabled, true)
|
||||
d.conf.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()
|
||||
setProtectedBool(d.confMu, &d.conf.SafeBrowsingEnabled, false)
|
||||
d.conf.ConfigModified()
|
||||
}
|
||||
|
||||
// handleSafeBrowsingStatus is the handler for the GET
|
||||
@@ -501,24 +501,24 @@ func (d *DNSFilter) handleSafeBrowsingStatus(w http.ResponseWriter, r *http.Requ
|
||||
resp := &struct {
|
||||
Enabled bool `json:"enabled"`
|
||||
}{
|
||||
Enabled: protectedBool(&d.confLock, &d.Config.SafeBrowsingEnabled),
|
||||
Enabled: protectedBool(d.confMu, &d.conf.SafeBrowsingEnabled),
|
||||
}
|
||||
|
||||
_ = aghhttp.WriteJSONResponse(w, r, resp)
|
||||
aghhttp.WriteJSONResponseOK(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()
|
||||
setProtectedBool(d.confMu, &d.conf.ParentalEnabled, true)
|
||||
d.conf.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()
|
||||
setProtectedBool(d.confMu, &d.conf.ParentalEnabled, false)
|
||||
d.conf.ConfigModified()
|
||||
}
|
||||
|
||||
// handleParentalStatus is the handler for the GET /control/parental/status
|
||||
@@ -527,15 +527,15 @@ func (d *DNSFilter) handleParentalStatus(w http.ResponseWriter, r *http.Request)
|
||||
resp := &struct {
|
||||
Enabled bool `json:"enabled"`
|
||||
}{
|
||||
Enabled: protectedBool(&d.confLock, &d.Config.ParentalEnabled),
|
||||
Enabled: protectedBool(d.confMu, &d.conf.ParentalEnabled),
|
||||
}
|
||||
|
||||
_ = aghhttp.WriteJSONResponse(w, r, resp)
|
||||
aghhttp.WriteJSONResponseOK(w, r, resp)
|
||||
}
|
||||
|
||||
// RegisterFilteringHandlers - register handlers
|
||||
func (d *DNSFilter) RegisterFilteringHandlers() {
|
||||
registerHTTP := d.HTTPRegister
|
||||
registerHTTP := d.conf.HTTPRegister
|
||||
if registerHTTP == nil {
|
||||
return
|
||||
}
|
||||
@@ -560,9 +560,14 @@ func (d *DNSFilter) RegisterFilteringHandlers() {
|
||||
|
||||
registerHTTP(http.MethodGet, "/control/blocked_services/services", d.handleBlockedServicesIDs)
|
||||
registerHTTP(http.MethodGet, "/control/blocked_services/all", d.handleBlockedServicesAll)
|
||||
|
||||
// Deprecated handlers.
|
||||
registerHTTP(http.MethodGet, "/control/blocked_services/list", d.handleBlockedServicesList)
|
||||
registerHTTP(http.MethodPost, "/control/blocked_services/set", d.handleBlockedServicesSet)
|
||||
|
||||
registerHTTP(http.MethodGet, "/control/blocked_services/get", d.handleBlockedServicesGet)
|
||||
registerHTTP(http.MethodPut, "/control/blocked_services/update", d.handleBlockedServicesUpdate)
|
||||
|
||||
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)
|
||||
|
||||
@@ -18,9 +18,9 @@ func TestItem_equal(t *testing.T) {
|
||||
}
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
left *Item
|
||||
right *Item
|
||||
name string
|
||||
want bool
|
||||
}{{
|
||||
name: "nil_left",
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
package rewrite
|
||||
|
||||
import (
|
||||
"net"
|
||||
"net/netip"
|
||||
"testing"
|
||||
|
||||
"github.com/AdguardTeam/golibs/netutil"
|
||||
"github.com/AdguardTeam/urlfilter"
|
||||
"github.com/AdguardTeam/urlfilter/rules"
|
||||
"github.com/miekg/dns"
|
||||
@@ -45,33 +46,43 @@ func TestDefaultStorage_CRUD(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDefaultStorage_MatchRequest(t *testing.T) {
|
||||
var (
|
||||
addr1v4 = netip.AddrFrom4([4]byte{1, 2, 3, 4})
|
||||
addr2v4 = netip.AddrFrom4([4]byte{1, 2, 3, 5})
|
||||
addr3v4 = netip.AddrFrom4([4]byte{1, 2, 3, 6})
|
||||
addr4v4 = netip.AddrFrom4([4]byte{1, 2, 3, 7})
|
||||
|
||||
addr1v6 = netip.MustParseAddr("1:2:3::4")
|
||||
addr2v6 = netip.MustParseAddr("1234::5678")
|
||||
)
|
||||
|
||||
items := []*Item{{
|
||||
// This one and below are about CNAME, A and AAAA.
|
||||
Domain: "somecname",
|
||||
Answer: "somehost.com",
|
||||
}, {
|
||||
Domain: "somehost.com",
|
||||
Answer: "0.0.0.0",
|
||||
Answer: netip.IPv4Unspecified().String(),
|
||||
}, {
|
||||
Domain: "host.com",
|
||||
Answer: "1.2.3.4",
|
||||
Answer: addr1v4.String(),
|
||||
}, {
|
||||
Domain: "host.com",
|
||||
Answer: "1.2.3.5",
|
||||
Answer: addr2v4.String(),
|
||||
}, {
|
||||
Domain: "host.com",
|
||||
Answer: "1:2:3::4",
|
||||
Answer: addr1v6.String(),
|
||||
}, {
|
||||
Domain: "www.host.com",
|
||||
Answer: "host.com",
|
||||
}, {
|
||||
// This one is a wildcard.
|
||||
Domain: "*.host.com",
|
||||
Answer: "1.2.3.5",
|
||||
Answer: addr2v4.String(),
|
||||
}, {
|
||||
// This one and below are about wildcard overriding.
|
||||
Domain: "a.host.com",
|
||||
Answer: "1.2.3.4",
|
||||
Answer: addr1v4.String(),
|
||||
}, {
|
||||
// This one is about CNAME and wildcard interacting.
|
||||
Domain: "*.host2.com",
|
||||
@@ -89,13 +100,13 @@ func TestDefaultStorage_MatchRequest(t *testing.T) {
|
||||
Answer: "x.host.com",
|
||||
}, {
|
||||
Domain: "*.hostboth.com",
|
||||
Answer: "1.2.3.6",
|
||||
Answer: addr3v4.String(),
|
||||
}, {
|
||||
Domain: "*.hostboth.com",
|
||||
Answer: "1234::5678",
|
||||
Answer: addr2v6.String(),
|
||||
}, {
|
||||
Domain: "BIGHOST.COM",
|
||||
Answer: "1.2.3.7",
|
||||
Answer: addr4v4.String(),
|
||||
}, {
|
||||
Domain: "*.issue4016.com",
|
||||
Answer: "sub.issue4016.com",
|
||||
@@ -123,12 +134,12 @@ func TestDefaultStorage_MatchRequest(t *testing.T) {
|
||||
name: "rewritten_a",
|
||||
host: "www.host.com",
|
||||
wantDNSRewrites: []*rules.DNSRewrite{{
|
||||
Value: net.IP{1, 2, 3, 4}.To16(),
|
||||
Value: addr1v4,
|
||||
NewCNAME: "",
|
||||
RCode: dns.RcodeSuccess,
|
||||
RRType: dns.TypeA,
|
||||
}, {
|
||||
Value: net.IP{1, 2, 3, 5}.To16(),
|
||||
Value: addr2v4,
|
||||
NewCNAME: "",
|
||||
RCode: dns.RcodeSuccess,
|
||||
RRType: dns.TypeA,
|
||||
@@ -138,7 +149,7 @@ func TestDefaultStorage_MatchRequest(t *testing.T) {
|
||||
name: "rewritten_aaaa",
|
||||
host: "www.host.com",
|
||||
wantDNSRewrites: []*rules.DNSRewrite{{
|
||||
Value: net.ParseIP("1:2:3::4"),
|
||||
Value: addr1v6,
|
||||
NewCNAME: "",
|
||||
RCode: dns.RcodeSuccess,
|
||||
RRType: dns.TypeAAAA,
|
||||
@@ -148,7 +159,7 @@ func TestDefaultStorage_MatchRequest(t *testing.T) {
|
||||
name: "wildcard_match",
|
||||
host: "abc.host.com",
|
||||
wantDNSRewrites: []*rules.DNSRewrite{{
|
||||
Value: net.IP{1, 2, 3, 5}.To16(),
|
||||
Value: addr2v4,
|
||||
NewCNAME: "",
|
||||
RCode: dns.RcodeSuccess,
|
||||
RRType: dns.TypeA,
|
||||
@@ -169,12 +180,12 @@ func TestDefaultStorage_MatchRequest(t *testing.T) {
|
||||
name: "wildcard_cname_interaction",
|
||||
host: "www.host2.com",
|
||||
wantDNSRewrites: []*rules.DNSRewrite{{
|
||||
Value: net.IP{1, 2, 3, 4}.To16(),
|
||||
Value: addr1v4,
|
||||
NewCNAME: "",
|
||||
RCode: dns.RcodeSuccess,
|
||||
RRType: dns.TypeA,
|
||||
}, {
|
||||
Value: net.IP{1, 2, 3, 5}.To16(),
|
||||
Value: addr2v4,
|
||||
NewCNAME: "",
|
||||
RCode: dns.RcodeSuccess,
|
||||
RRType: dns.TypeA,
|
||||
@@ -184,7 +195,7 @@ func TestDefaultStorage_MatchRequest(t *testing.T) {
|
||||
name: "two_cnames",
|
||||
host: "b.host.com",
|
||||
wantDNSRewrites: []*rules.DNSRewrite{{
|
||||
Value: net.IP{0, 0, 0, 0}.To16(),
|
||||
Value: netip.IPv4Unspecified(),
|
||||
NewCNAME: "",
|
||||
RCode: dns.RcodeSuccess,
|
||||
RRType: dns.TypeA,
|
||||
@@ -194,7 +205,7 @@ func TestDefaultStorage_MatchRequest(t *testing.T) {
|
||||
name: "two_cnames_and_wildcard",
|
||||
host: "b.host3.com",
|
||||
wantDNSRewrites: []*rules.DNSRewrite{{
|
||||
Value: net.IP{1, 2, 3, 5}.To16(),
|
||||
Value: addr2v4,
|
||||
NewCNAME: "",
|
||||
RCode: dns.RcodeSuccess,
|
||||
RRType: dns.TypeA,
|
||||
@@ -204,7 +215,7 @@ func TestDefaultStorage_MatchRequest(t *testing.T) {
|
||||
name: "issue3343",
|
||||
host: "www.hostboth.com",
|
||||
wantDNSRewrites: []*rules.DNSRewrite{{
|
||||
Value: net.ParseIP("1234::5678"),
|
||||
Value: addr2v6,
|
||||
NewCNAME: "",
|
||||
RCode: dns.RcodeSuccess,
|
||||
RRType: dns.TypeAAAA,
|
||||
@@ -214,7 +225,7 @@ func TestDefaultStorage_MatchRequest(t *testing.T) {
|
||||
name: "issue3351",
|
||||
host: "bighost.com",
|
||||
wantDNSRewrites: []*rules.DNSRewrite{{
|
||||
Value: net.IP{1, 2, 3, 7}.To16(),
|
||||
Value: addr4v4,
|
||||
NewCNAME: "",
|
||||
RCode: dns.RcodeSuccess,
|
||||
RRType: dns.TypeA,
|
||||
@@ -255,16 +266,22 @@ func TestDefaultStorage_MatchRequest(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDefaultStorage_MatchRequest_Levels(t *testing.T) {
|
||||
var (
|
||||
addr1 = netip.AddrFrom4([4]byte{1, 1, 1, 1})
|
||||
addr2 = netip.AddrFrom4([4]byte{2, 2, 2, 2})
|
||||
addr3 = netip.AddrFrom4([4]byte{3, 3, 3, 3})
|
||||
)
|
||||
|
||||
// Exact host, wildcard L2, wildcard L3.
|
||||
items := []*Item{{
|
||||
Domain: "host.com",
|
||||
Answer: "1.1.1.1",
|
||||
Answer: addr1.String(),
|
||||
}, {
|
||||
Domain: "*.host.com",
|
||||
Answer: "2.2.2.2",
|
||||
Answer: addr2.String(),
|
||||
}, {
|
||||
Domain: "*.sub.host.com",
|
||||
Answer: "3.3.3.3",
|
||||
Answer: addr3.String(),
|
||||
}}
|
||||
|
||||
s, err := NewDefaultStorage(-1, items)
|
||||
@@ -279,7 +296,7 @@ func TestDefaultStorage_MatchRequest_Levels(t *testing.T) {
|
||||
name: "exact_match",
|
||||
host: "host.com",
|
||||
wantDNSRewrites: []*rules.DNSRewrite{{
|
||||
Value: net.IP{1, 1, 1, 1}.To16(),
|
||||
Value: addr1,
|
||||
NewCNAME: "",
|
||||
RCode: dns.RcodeSuccess,
|
||||
RRType: dns.TypeA,
|
||||
@@ -289,7 +306,7 @@ func TestDefaultStorage_MatchRequest_Levels(t *testing.T) {
|
||||
name: "l2_match",
|
||||
host: "sub.host.com",
|
||||
wantDNSRewrites: []*rules.DNSRewrite{{
|
||||
Value: net.IP{2, 2, 2, 2}.To16(),
|
||||
Value: addr2,
|
||||
NewCNAME: "",
|
||||
RCode: dns.RcodeSuccess,
|
||||
RRType: dns.TypeA,
|
||||
@@ -300,7 +317,7 @@ func TestDefaultStorage_MatchRequest_Levels(t *testing.T) {
|
||||
// name: "l3_match",
|
||||
// host: "my.sub.host.com",
|
||||
// wantDNSRewrites: []*rules.DNSRewrite{{
|
||||
// Value: net.IP{3, 3, 3, 3}.To16(),
|
||||
// Value: addr3,
|
||||
// NewCNAME: "",
|
||||
// RCode: dns.RcodeSuccess,
|
||||
// RRType: dns.TypeA,
|
||||
@@ -321,10 +338,12 @@ func TestDefaultStorage_MatchRequest_Levels(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDefaultStorage_MatchRequest_ExceptionCNAME(t *testing.T) {
|
||||
addr := netip.AddrFrom4([4]byte{2, 2, 2, 2})
|
||||
|
||||
// Wildcard and exception for a sub-domain.
|
||||
items := []*Item{{
|
||||
Domain: "*.host.com",
|
||||
Answer: "2.2.2.2",
|
||||
Answer: addr.String(),
|
||||
}, {
|
||||
Domain: "sub.host.com",
|
||||
Answer: "sub.host.com",
|
||||
@@ -345,7 +364,7 @@ func TestDefaultStorage_MatchRequest_ExceptionCNAME(t *testing.T) {
|
||||
name: "match_subdomain",
|
||||
host: "my.host.com",
|
||||
wantDNSRewrites: []*rules.DNSRewrite{{
|
||||
Value: net.IP{2, 2, 2, 2}.To16(),
|
||||
Value: addr,
|
||||
NewCNAME: "",
|
||||
RCode: dns.RcodeSuccess,
|
||||
RRType: dns.TypeA,
|
||||
@@ -377,16 +396,18 @@ func TestDefaultStorage_MatchRequest_ExceptionCNAME(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDefaultStorage_MatchRequest_ExceptionIP(t *testing.T) {
|
||||
addr := netip.AddrFrom4([4]byte{1, 2, 3, 4})
|
||||
|
||||
// Exception for AAAA record.
|
||||
items := []*Item{{
|
||||
Domain: "host.com",
|
||||
Answer: "1.2.3.4",
|
||||
Answer: addr.String(),
|
||||
}, {
|
||||
Domain: "host.com",
|
||||
Answer: "AAAA",
|
||||
}, {
|
||||
Domain: "host2.com",
|
||||
Answer: "::1",
|
||||
Answer: netutil.IPv6Localhost().String(),
|
||||
}, {
|
||||
Domain: "host2.com",
|
||||
Answer: "A",
|
||||
@@ -407,7 +428,7 @@ func TestDefaultStorage_MatchRequest_ExceptionIP(t *testing.T) {
|
||||
name: "match_A",
|
||||
host: "host.com",
|
||||
wantDNSRewrites: []*rules.DNSRewrite{{
|
||||
Value: net.IP{1, 2, 3, 4}.To16(),
|
||||
Value: addr,
|
||||
NewCNAME: "",
|
||||
RCode: dns.RcodeSuccess,
|
||||
RRType: dns.TypeA,
|
||||
@@ -427,7 +448,7 @@ func TestDefaultStorage_MatchRequest_ExceptionIP(t *testing.T) {
|
||||
name: "match_AAAA_host2.com",
|
||||
host: "host2.com",
|
||||
wantDNSRewrites: []*rules.DNSRewrite{{
|
||||
Value: net.ParseIP("::1"),
|
||||
Value: netutil.IPv6Localhost(),
|
||||
NewCNAME: "",
|
||||
RCode: dns.RcodeSuccess,
|
||||
RRType: dns.TypeAAAA,
|
||||
|
||||
@@ -15,22 +15,27 @@ type rewriteEntryJSON struct {
|
||||
Answer string `json:"answer"`
|
||||
}
|
||||
|
||||
// handleRewriteList is the handler for the GET /control/rewrite/list HTTP API.
|
||||
func (d *DNSFilter) handleRewriteList(w http.ResponseWriter, r *http.Request) {
|
||||
arr := []*rewriteEntryJSON{}
|
||||
|
||||
d.confLock.Lock()
|
||||
for _, ent := range d.Config.Rewrites {
|
||||
jsent := rewriteEntryJSON{
|
||||
Domain: ent.Domain,
|
||||
Answer: ent.Answer,
|
||||
}
|
||||
arr = append(arr, &jsent)
|
||||
}
|
||||
d.confLock.Unlock()
|
||||
func() {
|
||||
d.confMu.RLock()
|
||||
defer d.confMu.RUnlock()
|
||||
|
||||
_ = aghhttp.WriteJSONResponse(w, r, arr)
|
||||
for _, ent := range d.conf.Rewrites {
|
||||
jsonEnt := rewriteEntryJSON{
|
||||
Domain: ent.Domain,
|
||||
Answer: ent.Answer,
|
||||
}
|
||||
arr = append(arr, &jsonEnt)
|
||||
}
|
||||
}()
|
||||
|
||||
aghhttp.WriteJSONResponseOK(w, r, arr)
|
||||
}
|
||||
|
||||
// handleRewriteAdd is the handler for the POST /control/rewrite/add HTTP API.
|
||||
func (d *DNSFilter) handleRewriteAdd(w http.ResponseWriter, r *http.Request) {
|
||||
rwJSON := rewriteEntryJSON{}
|
||||
err := json.NewDecoder(r.Body).Decode(&rwJSON)
|
||||
@@ -54,14 +59,24 @@ func (d *DNSFilter) handleRewriteAdd(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
d.confLock.Lock()
|
||||
d.Config.Rewrites = append(d.Config.Rewrites, rw)
|
||||
d.confLock.Unlock()
|
||||
log.Debug("rewrite: added element: %s -> %s [%d]", rw.Domain, rw.Answer, len(d.Config.Rewrites))
|
||||
func() {
|
||||
d.confMu.Lock()
|
||||
defer d.confMu.Unlock()
|
||||
|
||||
d.Config.ConfigModified()
|
||||
d.conf.Rewrites = append(d.conf.Rewrites, rw)
|
||||
log.Debug(
|
||||
"rewrite: added element: %s -> %s [%d]",
|
||||
rw.Domain,
|
||||
rw.Answer,
|
||||
len(d.conf.Rewrites),
|
||||
)
|
||||
}()
|
||||
|
||||
d.conf.ConfigModified()
|
||||
}
|
||||
|
||||
// handleRewriteDelete is the handler for the POST /control/rewrite/delete HTTP
|
||||
// API.
|
||||
func (d *DNSFilter) handleRewriteDelete(w http.ResponseWriter, r *http.Request) {
|
||||
jsent := rewriteEntryJSON{}
|
||||
err := json.NewDecoder(r.Body).Decode(&jsent)
|
||||
@@ -77,20 +92,23 @@ func (d *DNSFilter) handleRewriteDelete(w http.ResponseWriter, r *http.Request)
|
||||
}
|
||||
arr := []*LegacyRewrite{}
|
||||
|
||||
d.confLock.Lock()
|
||||
for _, ent := range d.Config.Rewrites {
|
||||
if ent.equal(entDel) {
|
||||
log.Debug("rewrite: removed element: %s -> %s", ent.Domain, ent.Answer)
|
||||
func() {
|
||||
d.confMu.Lock()
|
||||
defer d.confMu.Unlock()
|
||||
|
||||
continue
|
||||
for _, ent := range d.conf.Rewrites {
|
||||
if ent.equal(entDel) {
|
||||
log.Debug("rewrite: removed element: %s -> %s", ent.Domain, ent.Answer)
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
arr = append(arr, ent)
|
||||
}
|
||||
d.conf.Rewrites = arr
|
||||
}()
|
||||
|
||||
arr = append(arr, ent)
|
||||
}
|
||||
d.Config.Rewrites = arr
|
||||
d.confLock.Unlock()
|
||||
|
||||
d.Config.ConfigModified()
|
||||
d.conf.ConfigModified()
|
||||
}
|
||||
|
||||
// rewriteUpdateJSON is a struct for JSON object with rewrite rule update info.
|
||||
@@ -132,21 +150,21 @@ func (d *DNSFilter) handleRewriteUpdate(w http.ResponseWriter, r *http.Request)
|
||||
index := -1
|
||||
defer func() {
|
||||
if index >= 0 {
|
||||
d.Config.ConfigModified()
|
||||
d.conf.ConfigModified()
|
||||
}
|
||||
}()
|
||||
|
||||
d.confLock.Lock()
|
||||
defer d.confLock.Unlock()
|
||||
d.confMu.Lock()
|
||||
defer d.confMu.Unlock()
|
||||
|
||||
index = slices.IndexFunc(d.Config.Rewrites, rwDel.equal)
|
||||
index = slices.IndexFunc(d.conf.Rewrites, rwDel.equal)
|
||||
if index == -1 {
|
||||
aghhttp.Error(r, w, http.StatusBadRequest, "target rule not found")
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
d.Config.Rewrites = slices.Replace(d.Config.Rewrites, index, index+1, rwAdd)
|
||||
d.conf.Rewrites = slices.Replace(d.conf.Rewrites, index, index+1, rwAdd)
|
||||
|
||||
log.Debug("rewrite: removed element: %s -> %s", rwDel.Domain, rwDel.Answer)
|
||||
log.Debug("rewrite: added element: %s -> %s", rwAdd.Domain, rwAdd.Answer)
|
||||
|
||||
@@ -2,10 +2,11 @@ package filtering
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"net/netip"
|
||||
"strings"
|
||||
|
||||
"github.com/AdguardTeam/golibs/errors"
|
||||
"github.com/AdguardTeam/golibs/log"
|
||||
"github.com/AdguardTeam/golibs/mathutil"
|
||||
"github.com/miekg/dns"
|
||||
"golang.org/x/exp/slices"
|
||||
@@ -26,22 +27,12 @@ type LegacyRewrite struct {
|
||||
|
||||
// IP is the IP address that should be used in the response if Type is
|
||||
// dns.TypeA or dns.TypeAAAA.
|
||||
IP net.IP `yaml:"-"`
|
||||
IP netip.Addr `yaml:"-"`
|
||||
|
||||
// Type is the DNS record type: A, AAAA, or CNAME.
|
||||
Type uint16 `yaml:"-"`
|
||||
}
|
||||
|
||||
// clone returns a deep clone of rw.
|
||||
func (rw *LegacyRewrite) clone() (cloneRW *LegacyRewrite) {
|
||||
return &LegacyRewrite{
|
||||
Domain: rw.Domain,
|
||||
Answer: rw.Answer,
|
||||
IP: slices.Clone(rw.IP),
|
||||
Type: rw.Type,
|
||||
}
|
||||
}
|
||||
|
||||
// equal returns true if the rw is equal to the other.
|
||||
func (rw *LegacyRewrite) equal(other *LegacyRewrite) (ok bool) {
|
||||
return rw.Domain == other.Domain && rw.Answer == other.Answer
|
||||
@@ -61,11 +52,11 @@ func (rw *LegacyRewrite) matchesQType(qt uint16) (ok bool) {
|
||||
|
||||
// If the types match or the entry is set to allow only the other type,
|
||||
// include them.
|
||||
return rw.Type == qt || rw.IP == nil
|
||||
return rw.Type == qt || rw.IP == netip.Addr{}
|
||||
}
|
||||
|
||||
// normalize makes sure that the a new or decoded entry is normalized with
|
||||
// regards to domain name case, IP length, and so on.
|
||||
// normalize makes sure that the new or decoded entry is normalized with regards
|
||||
// to domain name case, IP length, and so on.
|
||||
//
|
||||
// If rw is nil, it returns an errors.
|
||||
func (rw *LegacyRewrite) normalize() (err error) {
|
||||
@@ -80,12 +71,12 @@ func (rw *LegacyRewrite) normalize() (err error) {
|
||||
|
||||
switch rw.Answer {
|
||||
case "AAAA":
|
||||
rw.IP = nil
|
||||
rw.IP = netip.Addr{}
|
||||
rw.Type = dns.TypeAAAA
|
||||
|
||||
return nil
|
||||
case "A":
|
||||
rw.IP = nil
|
||||
rw.IP = netip.Addr{}
|
||||
rw.Type = dns.TypeA
|
||||
|
||||
return nil
|
||||
@@ -93,19 +84,18 @@ func (rw *LegacyRewrite) normalize() (err error) {
|
||||
// Go on.
|
||||
}
|
||||
|
||||
ip := net.ParseIP(rw.Answer)
|
||||
if ip == nil {
|
||||
ip, err := netip.ParseAddr(rw.Answer)
|
||||
if err != nil {
|
||||
log.Debug("normalizing legacy rewrite: %s", err)
|
||||
rw.Type = dns.TypeCNAME
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
ip4 := ip.To4()
|
||||
if ip4 != nil {
|
||||
rw.IP = ip4
|
||||
rw.IP = ip
|
||||
if ip.Is4() {
|
||||
rw.Type = dns.TypeA
|
||||
} else {
|
||||
rw.IP = ip
|
||||
rw.Type = dns.TypeAAAA
|
||||
}
|
||||
|
||||
@@ -122,29 +112,34 @@ func matchDomainWildcard(host, wildcard string) (ok bool) {
|
||||
return isWildcard(wildcard) && strings.HasSuffix(host, wildcard[1:])
|
||||
}
|
||||
|
||||
// legacyRewriteSortsBefore sorts rewrites according to the following priority:
|
||||
// Compare is used to sort rewrites according to the following priority:
|
||||
//
|
||||
// 1. A and AAAA > CNAME;
|
||||
// 2. wildcard > exact;
|
||||
// 3. lower level wildcard > higher level wildcard;
|
||||
func legacyRewriteSortsBefore(a, b *LegacyRewrite) (sortsBefore bool) {
|
||||
if a.Type == dns.TypeCNAME && b.Type != dns.TypeCNAME {
|
||||
return true
|
||||
} else if a.Type != dns.TypeCNAME && b.Type == dns.TypeCNAME {
|
||||
return false
|
||||
func (rw *LegacyRewrite) Compare(b *LegacyRewrite) (res int) {
|
||||
if rw.Type == dns.TypeCNAME && b.Type != dns.TypeCNAME {
|
||||
return -1
|
||||
} else if rw.Type != dns.TypeCNAME && b.Type == dns.TypeCNAME {
|
||||
return 1
|
||||
}
|
||||
|
||||
if aIsWld, bIsWld := isWildcard(a.Domain), isWildcard(b.Domain); aIsWld != bIsWld {
|
||||
return bIsWld
|
||||
aIsWld, bIsWld := isWildcard(rw.Domain), isWildcard(b.Domain)
|
||||
if aIsWld == bIsWld {
|
||||
// Both are either wildcards or both aren't.
|
||||
return len(rw.Domain) - len(b.Domain)
|
||||
}
|
||||
|
||||
// Both are either wildcards or both aren't.
|
||||
return len(a.Domain) > len(b.Domain)
|
||||
if aIsWld {
|
||||
return 1
|
||||
}
|
||||
|
||||
return -1
|
||||
}
|
||||
|
||||
// prepareRewrites normalizes and validates all legacy DNS rewrites.
|
||||
func (d *DNSFilter) prepareRewrites() (err error) {
|
||||
for i, r := range d.Rewrites {
|
||||
for i, r := range d.conf.Rewrites {
|
||||
err = r.normalize()
|
||||
if err != nil {
|
||||
return fmt.Errorf("at index %d: %w", i, err)
|
||||
@@ -181,7 +176,7 @@ func findRewrites(
|
||||
return nil, matched
|
||||
}
|
||||
|
||||
slices.SortFunc(rewrites, legacyRewriteSortsBefore)
|
||||
slices.SortFunc(rewrites, (*LegacyRewrite).Compare)
|
||||
|
||||
for i, r := range rewrites {
|
||||
if isWildcard(r.Domain) {
|
||||
@@ -195,3 +190,37 @@ func findRewrites(
|
||||
|
||||
return rewrites, matched
|
||||
}
|
||||
|
||||
// setRewriteResult sets the Reason or IPList of res if necessary. res must not
|
||||
// be nil.
|
||||
func setRewriteResult(res *Result, host string, rewrites []*LegacyRewrite, qtype uint16) {
|
||||
for _, rw := range rewrites {
|
||||
if rw.Type == qtype && (qtype == dns.TypeA || qtype == dns.TypeAAAA) {
|
||||
if rw.IP == (netip.Addr{}) {
|
||||
// "A"/"AAAA" exception: allow getting from upstream.
|
||||
res.Reason = NotFilteredNotFound
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
res.IPList = append(res.IPList, rw.IP)
|
||||
|
||||
log.Debug("rewrite: a/aaaa for %s is %s", host, rw.IP)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// cloneRewrites returns a deep copy of entries.
|
||||
func cloneRewrites(entries []*LegacyRewrite) (clone []*LegacyRewrite) {
|
||||
clone = make([]*LegacyRewrite, len(entries))
|
||||
for i, rw := range entries {
|
||||
clone[i] = &LegacyRewrite{
|
||||
Domain: rw.Domain,
|
||||
Answer: rw.Answer,
|
||||
IP: rw.IP,
|
||||
Type: rw.Type,
|
||||
}
|
||||
}
|
||||
|
||||
return clone
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package filtering
|
||||
|
||||
import (
|
||||
"net"
|
||||
"net/netip"
|
||||
"testing"
|
||||
|
||||
"github.com/miekg/dns"
|
||||
@@ -15,33 +16,43 @@ func TestRewrites(t *testing.T) {
|
||||
d, _ := newForTest(t, nil, nil)
|
||||
t.Cleanup(d.Close)
|
||||
|
||||
d.Rewrites = []*LegacyRewrite{{
|
||||
var (
|
||||
addr1v4 = netip.AddrFrom4([4]byte{1, 2, 3, 4})
|
||||
addr2v4 = netip.AddrFrom4([4]byte{1, 2, 3, 5})
|
||||
addr3v4 = netip.AddrFrom4([4]byte{1, 2, 3, 6})
|
||||
addr4v4 = netip.AddrFrom4([4]byte{1, 2, 3, 7})
|
||||
|
||||
addr1v6 = netip.MustParseAddr("1:2:3::4")
|
||||
addr2v6 = netip.MustParseAddr("1234::5678")
|
||||
)
|
||||
|
||||
d.conf.Rewrites = []*LegacyRewrite{{
|
||||
// This one and below are about CNAME, A and AAAA.
|
||||
Domain: "somecname",
|
||||
Answer: "somehost.com",
|
||||
}, {
|
||||
Domain: "somehost.com",
|
||||
Answer: "0.0.0.0",
|
||||
Answer: netip.IPv4Unspecified().String(),
|
||||
}, {
|
||||
Domain: "host.com",
|
||||
Answer: "1.2.3.4",
|
||||
Answer: addr1v4.String(),
|
||||
}, {
|
||||
Domain: "host.com",
|
||||
Answer: "1.2.3.5",
|
||||
Answer: addr2v4.String(),
|
||||
}, {
|
||||
Domain: "host.com",
|
||||
Answer: "1:2:3::4",
|
||||
Answer: addr1v6.String(),
|
||||
}, {
|
||||
Domain: "www.host.com",
|
||||
Answer: "host.com",
|
||||
}, {
|
||||
// This one is a wildcard.
|
||||
Domain: "*.host.com",
|
||||
Answer: "1.2.3.5",
|
||||
Answer: addr2v4.String(),
|
||||
}, {
|
||||
// This one and below are about wildcard overriding.
|
||||
Domain: "a.host.com",
|
||||
Answer: "1.2.3.4",
|
||||
Answer: addr1v4.String(),
|
||||
}, {
|
||||
// This one is about CNAME and wildcard interacting.
|
||||
Domain: "*.host2.com",
|
||||
@@ -59,13 +70,13 @@ func TestRewrites(t *testing.T) {
|
||||
Answer: "x.host.com",
|
||||
}, {
|
||||
Domain: "*.hostboth.com",
|
||||
Answer: "1.2.3.6",
|
||||
Answer: addr3v4.String(),
|
||||
}, {
|
||||
Domain: "*.hostboth.com",
|
||||
Answer: "1234::5678",
|
||||
Answer: addr2v6.String(),
|
||||
}, {
|
||||
Domain: "BIGHOST.COM",
|
||||
Answer: "1.2.3.7",
|
||||
Answer: addr4v4.String(),
|
||||
}, {
|
||||
Domain: "*.issue4016.com",
|
||||
Answer: "sub.issue4016.com",
|
||||
@@ -77,7 +88,7 @@ func TestRewrites(t *testing.T) {
|
||||
name string
|
||||
host string
|
||||
wantCName string
|
||||
wantIPs []net.IP
|
||||
wantIPs []netip.Addr
|
||||
wantReason Reason
|
||||
dtyp uint16
|
||||
}{{
|
||||
@@ -91,63 +102,63 @@ func TestRewrites(t *testing.T) {
|
||||
name: "rewritten_a",
|
||||
host: "www.host.com",
|
||||
wantCName: "host.com",
|
||||
wantIPs: []net.IP{{1, 2, 3, 4}, {1, 2, 3, 5}},
|
||||
wantIPs: []netip.Addr{addr1v4, addr2v4},
|
||||
wantReason: Rewritten,
|
||||
dtyp: dns.TypeA,
|
||||
}, {
|
||||
name: "rewritten_aaaa",
|
||||
host: "www.host.com",
|
||||
wantCName: "host.com",
|
||||
wantIPs: []net.IP{net.ParseIP("1:2:3::4")},
|
||||
wantIPs: []netip.Addr{addr1v6},
|
||||
wantReason: Rewritten,
|
||||
dtyp: dns.TypeAAAA,
|
||||
}, {
|
||||
name: "wildcard_match",
|
||||
host: "abc.host.com",
|
||||
wantCName: "",
|
||||
wantIPs: []net.IP{{1, 2, 3, 5}},
|
||||
wantIPs: []netip.Addr{addr2v4},
|
||||
wantReason: Rewritten,
|
||||
dtyp: dns.TypeA,
|
||||
}, {
|
||||
name: "wildcard_override",
|
||||
host: "a.host.com",
|
||||
wantCName: "",
|
||||
wantIPs: []net.IP{{1, 2, 3, 4}},
|
||||
wantIPs: []netip.Addr{addr1v4},
|
||||
wantReason: Rewritten,
|
||||
dtyp: dns.TypeA,
|
||||
}, {
|
||||
name: "wildcard_cname_interaction",
|
||||
host: "www.host2.com",
|
||||
wantCName: "host.com",
|
||||
wantIPs: []net.IP{{1, 2, 3, 4}, {1, 2, 3, 5}},
|
||||
wantIPs: []netip.Addr{addr1v4, addr2v4},
|
||||
wantReason: Rewritten,
|
||||
dtyp: dns.TypeA,
|
||||
}, {
|
||||
name: "two_cnames",
|
||||
host: "b.host.com",
|
||||
wantCName: "somehost.com",
|
||||
wantIPs: []net.IP{{0, 0, 0, 0}},
|
||||
wantIPs: []netip.Addr{netip.IPv4Unspecified()},
|
||||
wantReason: Rewritten,
|
||||
dtyp: dns.TypeA,
|
||||
}, {
|
||||
name: "two_cnames_and_wildcard",
|
||||
host: "b.host3.com",
|
||||
wantCName: "x.host.com",
|
||||
wantIPs: []net.IP{{1, 2, 3, 5}},
|
||||
wantIPs: []netip.Addr{addr2v4},
|
||||
wantReason: Rewritten,
|
||||
dtyp: dns.TypeA,
|
||||
}, {
|
||||
name: "issue3343",
|
||||
host: "www.hostboth.com",
|
||||
wantCName: "",
|
||||
wantIPs: []net.IP{net.ParseIP("1234::5678")},
|
||||
wantIPs: []netip.Addr{addr2v6},
|
||||
wantReason: Rewritten,
|
||||
dtyp: dns.TypeAAAA,
|
||||
}, {
|
||||
name: "issue3351",
|
||||
host: "bighost.com",
|
||||
wantCName: "",
|
||||
wantIPs: []net.IP{{1, 2, 3, 7}},
|
||||
wantIPs: []netip.Addr{addr4v4},
|
||||
wantReason: Rewritten,
|
||||
dtyp: dns.TypeA,
|
||||
}, {
|
||||
@@ -191,7 +202,7 @@ func TestRewritesLevels(t *testing.T) {
|
||||
d, _ := newForTest(t, nil, nil)
|
||||
t.Cleanup(d.Close)
|
||||
// Exact host, wildcard L2, wildcard L3.
|
||||
d.Rewrites = []*LegacyRewrite{{
|
||||
d.conf.Rewrites = []*LegacyRewrite{{
|
||||
Domain: "host.com",
|
||||
Answer: "1.1.1.1",
|
||||
Type: dns.TypeA,
|
||||
@@ -238,7 +249,7 @@ func TestRewritesExceptionCNAME(t *testing.T) {
|
||||
d, _ := newForTest(t, nil, nil)
|
||||
t.Cleanup(d.Close)
|
||||
// Wildcard and exception for a sub-domain.
|
||||
d.Rewrites = []*LegacyRewrite{{
|
||||
d.conf.Rewrites = []*LegacyRewrite{{
|
||||
Domain: "*.host.com",
|
||||
Answer: "2.2.2.2",
|
||||
}, {
|
||||
@@ -254,25 +265,25 @@ func TestRewritesExceptionCNAME(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
host string
|
||||
want net.IP
|
||||
want netip.Addr
|
||||
}{{
|
||||
name: "match_subdomain",
|
||||
host: "my.host.com",
|
||||
want: net.IP{2, 2, 2, 2},
|
||||
want: netip.AddrFrom4([4]byte{2, 2, 2, 2}),
|
||||
}, {
|
||||
name: "exception_cname",
|
||||
host: "sub.host.com",
|
||||
want: nil,
|
||||
want: netip.Addr{},
|
||||
}, {
|
||||
name: "exception_wildcard",
|
||||
host: "my.sub.host.com",
|
||||
want: nil,
|
||||
want: netip.Addr{},
|
||||
}}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
r := d.processRewrites(tc.host, dns.TypeA)
|
||||
if tc.want == nil {
|
||||
if tc.want == (netip.Addr{}) {
|
||||
assert.Equal(t, NotFilteredNotFound, r.Reason, "got %s", r.Reason)
|
||||
|
||||
return
|
||||
@@ -280,7 +291,7 @@ func TestRewritesExceptionCNAME(t *testing.T) {
|
||||
|
||||
assert.Equal(t, Rewritten, r.Reason)
|
||||
require.Len(t, r.IPList, 1)
|
||||
assert.True(t, tc.want.Equal(r.IPList[0]))
|
||||
assert.Equal(t, tc.want, r.IPList[0])
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -289,7 +300,7 @@ func TestRewritesExceptionIP(t *testing.T) {
|
||||
d, _ := newForTest(t, nil, nil)
|
||||
t.Cleanup(d.Close)
|
||||
// Exception for AAAA record.
|
||||
d.Rewrites = []*LegacyRewrite{{
|
||||
d.conf.Rewrites = []*LegacyRewrite{{
|
||||
Domain: "host.com",
|
||||
Answer: "1.2.3.4",
|
||||
Type: dns.TypeA,
|
||||
@@ -314,58 +325,58 @@ func TestRewritesExceptionIP(t *testing.T) {
|
||||
require.NoError(t, d.prepareRewrites())
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
host string
|
||||
want []net.IP
|
||||
dtyp uint16
|
||||
name string
|
||||
host string
|
||||
want []netip.Addr
|
||||
dtyp uint16
|
||||
wantReason Reason
|
||||
}{{
|
||||
name: "match_A",
|
||||
host: "host.com",
|
||||
want: []net.IP{{1, 2, 3, 4}},
|
||||
dtyp: dns.TypeA,
|
||||
name: "match_A",
|
||||
host: "host.com",
|
||||
want: []netip.Addr{netip.AddrFrom4([4]byte{1, 2, 3, 4})},
|
||||
dtyp: dns.TypeA,
|
||||
wantReason: Rewritten,
|
||||
}, {
|
||||
name: "exception_AAAA_host.com",
|
||||
host: "host.com",
|
||||
want: nil,
|
||||
dtyp: dns.TypeAAAA,
|
||||
name: "exception_AAAA_host.com",
|
||||
host: "host.com",
|
||||
want: nil,
|
||||
dtyp: dns.TypeAAAA,
|
||||
wantReason: NotFilteredNotFound,
|
||||
}, {
|
||||
name: "exception_A_host2.com",
|
||||
host: "host2.com",
|
||||
want: nil,
|
||||
dtyp: dns.TypeA,
|
||||
name: "exception_A_host2.com",
|
||||
host: "host2.com",
|
||||
want: nil,
|
||||
dtyp: dns.TypeA,
|
||||
wantReason: NotFilteredNotFound,
|
||||
}, {
|
||||
name: "match_AAAA_host2.com",
|
||||
host: "host2.com",
|
||||
want: []net.IP{net.ParseIP("::1")},
|
||||
dtyp: dns.TypeAAAA,
|
||||
name: "match_AAAA_host2.com",
|
||||
host: "host2.com",
|
||||
want: []netip.Addr{netip.MustParseAddr("::1")},
|
||||
dtyp: dns.TypeAAAA,
|
||||
wantReason: Rewritten,
|
||||
}, {
|
||||
name: "exception_A_host3.com",
|
||||
host: "host3.com",
|
||||
want: nil,
|
||||
dtyp: dns.TypeA,
|
||||
name: "exception_A_host3.com",
|
||||
host: "host3.com",
|
||||
want: nil,
|
||||
dtyp: dns.TypeA,
|
||||
wantReason: NotFilteredNotFound,
|
||||
}, {
|
||||
name: "match_AAAA_host3.com",
|
||||
host: "host3.com",
|
||||
want: []net.IP{},
|
||||
dtyp: dns.TypeAAAA,
|
||||
name: "match_AAAA_host3.com",
|
||||
host: "host3.com",
|
||||
want: nil,
|
||||
dtyp: dns.TypeAAAA,
|
||||
wantReason: Rewritten,
|
||||
}}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name+"_"+tc.host, func(t *testing.T) {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
if tc.name != "match_AAAA_host3.com" {
|
||||
t.SkipNow()
|
||||
}
|
||||
|
||||
r := d.processRewrites(tc.host, tc.dtyp)
|
||||
if tc.want == nil {
|
||||
assert.Equal(t, NotFilteredNotFound, r.Reason)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
assert.Equalf(t, Rewritten, r.Reason, "got %s", r.Reason)
|
||||
|
||||
require.Len(t, r.IPList, len(tc.want))
|
||||
|
||||
for _, ip := range tc.want {
|
||||
assert.True(t, ip.Equal(r.IPList[0]))
|
||||
}
|
||||
assert.Equal(t, tc.want, r.IPList)
|
||||
assert.Equal(t, tc.wantReason, r.Reason)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"encoding/gob"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/netip"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
@@ -239,10 +240,9 @@ func (ss *Default) newResult(
|
||||
}
|
||||
|
||||
if rewrite.RRType == qtype {
|
||||
v := rewrite.Value
|
||||
ip, ok := v.(net.IP)
|
||||
if !ok || ip == nil {
|
||||
return nil, fmt.Errorf("expected ip rewrite value, got %T(%[1]v)", v)
|
||||
ip, ok := rewrite.Value.(netip.Addr)
|
||||
if !ok || ip == (netip.Addr{}) {
|
||||
return nil, fmt.Errorf("expected ip rewrite value, got %T(%[1]v)", rewrite.Value)
|
||||
}
|
||||
|
||||
res.Rules[0].IP = ip
|
||||
@@ -267,12 +267,13 @@ func (ss *Default) newResult(
|
||||
for _, ip := range ips {
|
||||
// TODO(a.garipov): Remove this filtering once the resolver we use
|
||||
// actually learns about network.
|
||||
ip = fitToProto(ip, qtype)
|
||||
if ip == nil {
|
||||
addr := fitToProto(ip, qtype)
|
||||
if addr == (netip.Addr{}) {
|
||||
continue
|
||||
}
|
||||
|
||||
res.Rules[0].IP = ip
|
||||
// TODO(e.burkov): Rules[0]?
|
||||
res.Rules[0].IP = addr
|
||||
}
|
||||
|
||||
return res, nil
|
||||
@@ -293,17 +294,16 @@ func qtypeToProto(qtype rules.RRType) (proto string) {
|
||||
|
||||
// fitToProto returns a non-nil IP address if ip is the correct protocol version
|
||||
// for qtype. qtype is expected to be either [dns.TypeA] or [dns.TypeAAAA].
|
||||
func fitToProto(ip net.IP, qtype rules.RRType) (res net.IP) {
|
||||
ip4 := ip.To4()
|
||||
if qtype == dns.TypeA {
|
||||
return ip4
|
||||
func fitToProto(ip net.IP, qtype rules.RRType) (res netip.Addr) {
|
||||
if ip4 := ip.To4(); qtype == dns.TypeA {
|
||||
if ip4 != nil {
|
||||
return netip.AddrFrom4([4]byte(ip4))
|
||||
}
|
||||
} else if ip = ip.To16(); ip != nil && qtype == dns.TypeAAAA {
|
||||
return netip.AddrFrom16([16]byte(ip))
|
||||
}
|
||||
|
||||
if ip4 == nil {
|
||||
return ip
|
||||
}
|
||||
|
||||
return nil
|
||||
return netip.Addr{}
|
||||
}
|
||||
|
||||
// setCacheResult stores data in cache for host. qtype is expected to be either
|
||||
|
||||
@@ -3,6 +3,7 @@ package safesearch
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
"net/netip"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@@ -33,7 +34,7 @@ var defaultSafeSearchConf = filtering.SafeSearchConfig{
|
||||
YouTube: true,
|
||||
}
|
||||
|
||||
var yandexIP = net.IPv4(213, 180, 193, 56)
|
||||
var yandexIP = netip.AddrFrom4([4]byte{213, 180, 193, 56})
|
||||
|
||||
func newForTest(t testing.TB, ssConf filtering.SafeSearchConfig) (ss *Default) {
|
||||
ss, err := NewDefault(ssConf, "", testCacheSize, testCacheTTL)
|
||||
@@ -93,7 +94,7 @@ func TestSafeSearchCacheGoogle(t *testing.T) {
|
||||
OnLookupIP: func(_ context.Context, _, host string) (ips []net.IP, err error) {
|
||||
ip4, ip6 := aghtest.HostToIPs(host)
|
||||
|
||||
return []net.IP{ip4, ip6}, nil
|
||||
return []net.IP{ip4.AsSlice(), ip6.AsSlice()}, nil
|
||||
},
|
||||
}
|
||||
|
||||
@@ -109,14 +110,14 @@ func TestSafeSearchCacheGoogle(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
require.Len(t, res.Rules, 1)
|
||||
|
||||
assert.True(t, res.Rules[0].IP.Equal(wantIP))
|
||||
assert.Equal(t, wantIP, res.Rules[0].IP)
|
||||
|
||||
// Check cache.
|
||||
cachedValue, isFound := ss.getCachedResult(domain, testQType)
|
||||
require.True(t, isFound)
|
||||
require.Len(t, cachedValue.Rules, 1)
|
||||
|
||||
assert.True(t, cachedValue.Rules[0].IP.Equal(wantIP))
|
||||
assert.Equal(t, wantIP, cachedValue.Rules[0].IP)
|
||||
}
|
||||
|
||||
const googleHost = "www.google.com"
|
||||
|
||||
@@ -3,6 +3,7 @@ package safesearch_test
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
"net/netip"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@@ -43,7 +44,7 @@ var testConf = filtering.SafeSearchConfig{
|
||||
|
||||
// yandexIP is the expected IP address of Yandex safe search results. Keep in
|
||||
// sync with the rules data.
|
||||
var yandexIP = net.IPv4(213, 180, 193, 56)
|
||||
var yandexIP = netip.AddrFrom4([4]byte{213, 180, 193, 56})
|
||||
|
||||
func TestDefault_CheckHost_yandex(t *testing.T) {
|
||||
conf := testConf
|
||||
@@ -87,7 +88,7 @@ func TestDefault_CheckHost_yandexAAAA(t *testing.T) {
|
||||
// once the TODO in [safesearch.Default.newResult] is resolved.
|
||||
require.Len(t, res.Rules, 1)
|
||||
|
||||
assert.Nil(t, res.Rules[0].IP)
|
||||
assert.Empty(t, res.Rules[0].IP)
|
||||
assert.EqualValues(t, filtering.SafeSearchListID, res.Rules[0].FilterListID)
|
||||
}
|
||||
|
||||
@@ -96,7 +97,7 @@ func TestDefault_CheckHost_google(t *testing.T) {
|
||||
OnLookupIP: func(_ context.Context, _, host string) (ips []net.IP, err error) {
|
||||
ip4, ip6 := aghtest.HostToIPs(host)
|
||||
|
||||
return []net.IP{ip4, ip6}, nil
|
||||
return []net.IP{ip4.AsSlice(), ip6.AsSlice()}, nil
|
||||
},
|
||||
}
|
||||
|
||||
@@ -178,7 +179,7 @@ func TestDefault_CheckHost_duckduckgoAAAA(t *testing.T) {
|
||||
// once the TODO in [safesearch.Default.newResult] is resolved.
|
||||
require.Len(t, res.Rules, 1)
|
||||
|
||||
assert.Nil(t, res.Rules[0].IP)
|
||||
assert.Empty(t, res.Rules[0].IP)
|
||||
assert.EqualValues(t, filtering.SafeSearchListID, res.Rules[0].FilterListID)
|
||||
}
|
||||
|
||||
|
||||
@@ -12,8 +12,8 @@ import (
|
||||
//
|
||||
// Deprecated: Use handleSafeSearchSettings.
|
||||
func (d *DNSFilter) handleSafeSearchEnable(w http.ResponseWriter, r *http.Request) {
|
||||
setProtectedBool(&d.confLock, &d.Config.SafeSearchConf.Enabled, true)
|
||||
d.Config.ConfigModified()
|
||||
setProtectedBool(d.confMu, &d.conf.SafeSearchConf.Enabled, true)
|
||||
d.conf.ConfigModified()
|
||||
}
|
||||
|
||||
// handleSafeSearchDisable is the handler for POST /control/safesearch/disable
|
||||
@@ -21,8 +21,8 @@ func (d *DNSFilter) handleSafeSearchEnable(w http.ResponseWriter, r *http.Reques
|
||||
//
|
||||
// Deprecated: Use handleSafeSearchSettings.
|
||||
func (d *DNSFilter) handleSafeSearchDisable(w http.ResponseWriter, r *http.Request) {
|
||||
setProtectedBool(&d.confLock, &d.Config.SafeSearchConf.Enabled, false)
|
||||
d.Config.ConfigModified()
|
||||
setProtectedBool(d.confMu, &d.conf.SafeSearchConf.Enabled, false)
|
||||
d.conf.ConfigModified()
|
||||
}
|
||||
|
||||
// handleSafeSearchStatus is the handler for GET /control/safesearch/status
|
||||
@@ -30,13 +30,13 @@ func (d *DNSFilter) handleSafeSearchDisable(w http.ResponseWriter, r *http.Reque
|
||||
func (d *DNSFilter) handleSafeSearchStatus(w http.ResponseWriter, r *http.Request) {
|
||||
var resp SafeSearchConfig
|
||||
func() {
|
||||
d.confLock.RLock()
|
||||
defer d.confLock.RUnlock()
|
||||
d.confMu.RLock()
|
||||
defer d.confMu.RUnlock()
|
||||
|
||||
resp = d.Config.SafeSearchConf
|
||||
resp = d.conf.SafeSearchConf
|
||||
}()
|
||||
|
||||
_ = aghhttp.WriteJSONResponse(w, r, resp)
|
||||
aghhttp.WriteJSONResponseOK(w, r, resp)
|
||||
}
|
||||
|
||||
// handleSafeSearchSettings is the handler for PUT /control/safesearch/settings
|
||||
@@ -59,13 +59,13 @@ func (d *DNSFilter) handleSafeSearchSettings(w http.ResponseWriter, r *http.Requ
|
||||
}
|
||||
|
||||
func() {
|
||||
d.confLock.Lock()
|
||||
defer d.confLock.Unlock()
|
||||
d.confMu.Lock()
|
||||
defer d.confMu.Unlock()
|
||||
|
||||
d.Config.SafeSearchConf = conf
|
||||
d.conf.SafeSearchConf = conf
|
||||
}()
|
||||
|
||||
d.Config.ConfigModified()
|
||||
d.conf.ConfigModified()
|
||||
|
||||
aghhttp.OK(w)
|
||||
}
|
||||
|
||||
@@ -30,10 +30,13 @@ var blockedServices = []blockedService{{
|
||||
}, {
|
||||
ID: "activision_blizzard",
|
||||
Name: "Activision Blizzard",
|
||||
IconSVG: []byte("<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"-237 0 1572 1572\"><path d=\"m549.1.2 548.4 1571.4H798l-74.2-200H374.5l-74.3 200H.7zM626 1085.1l-83-274.3-82.9 274.3z\"/></svg>"),
|
||||
IconSVG: []byte("<svg xmlns=\"http://www.w3.org/2000/svg\" fill=\"currentColor\" viewBox=\"-237 0 1572 1572\"><path d=\"m549.1.2 548.4 1571.4H798l-74.2-200H374.5l-74.3 200H.7zM626 1085.1l-83-274.3-82.9 274.3z\"/></svg>"),
|
||||
Rules: []string{
|
||||
"||activision.com^",
|
||||
"||activisionblizzard.com^",
|
||||
"||callofduty.com^",
|
||||
"||callofdutyleague.com^",
|
||||
"||codmwest.com^",
|
||||
"||demonware.net^",
|
||||
},
|
||||
}, {
|
||||
@@ -287,6 +290,53 @@ var blockedServices = []blockedService{{
|
||||
"||bnet.163.com^",
|
||||
"||bnet.cn^",
|
||||
},
|
||||
}, {
|
||||
ID: "betano",
|
||||
Name: "Betano",
|
||||
IconSVG: []byte("<svg xmlns=\"http://www.w3.org/2000/svg\" fill=\"currentColor\" viewBox=\"0 0 50 50\"><path d=\"M9 3a1.0001 1.0001 0 0 0-1 1v42a1.0001 1.0001 0 0 0 1 1h17.7383c4.6 0 8.412-1.1874 11.0957-3.4395 2.6837-2.252 4.166-5.5679 4.166-9.494 0-5.0374-3.1967-8.9574-7.8125-10.422 3.3907-1.6449 5.7715-5.1833 5.7715-9.166 0-3.4752-1.3196-6.4288-3.7168-8.4297C33.845 4.048 30.4469 3 26.3379 3H9zm1 2h16.3379c3.769 0 6.6787.9611 8.623 2.584 1.9443 1.6228 2.998 3.9088 2.998 6.8945 0 3.8013-2.7237 7.2947-6.1718 8.045A1.0001 1.0001 0 0 0 31 23.5v.5a1.0001 1.0001 0 0 0 .871.9922c4.9067.6337 8.129 4.204 8.129 9.0742 0 3.4368-1.2203 6.0892-3.4531 7.9629C34.314 43.903 30.9943 45 26.7383 45H10V5zm7 4a1.0001 1.0001 0 0 0-1 1v12a1.0001 1.0001 0 0 0 1 1h6.0605c2.7307 0 4.9369-.546 6.5196-1.748S32 18.1768 32 16c0-2.1224-.7643-3.9577-2.1934-5.1816C28.3776 9.5944 26.3741 9 23.9961 9H17zm1 2h5.996c2.0451 0 3.5427.5096 4.5099 1.3379C29.473 13.1662 30 14.3314 30 16c0 1.7042-.5389 2.8304-1.629 3.6582C27.281 20.486 25.519 21 23.0606 21H18V11zm-1 15a1.0001 1.0001 0 0 0-1 1v13a1.0001 1.0001 0 0 0 1 1h7.629c2.8718 0 5.1969-.5884 6.8534-1.8887C33.139 37.811 34 35.8007 34 33.4277c0-2.3405-.902-4.3298-2.6074-5.5957C29.687 26.5662 27.3054 26 24.3418 26H17zm1 2h6.3418c2.7024 0 4.6504.5436 5.8574 1.4395C31.4062 30.3353 32 31.5613 32 33.4277c0 1.922-.5915 3.197-1.754 4.1094C29.0838 38.4496 27.223 39 24.629 39H18V28z\"/></svg>"),
|
||||
Rules: []string{
|
||||
"||betano.bg^",
|
||||
"||betano.ca^",
|
||||
"||betano.com^",
|
||||
"||betano.cz^",
|
||||
"||betano.de^",
|
||||
"||betano.ng^",
|
||||
"||betano.pt^",
|
||||
},
|
||||
}, {
|
||||
ID: "betfair",
|
||||
Name: "Betfair",
|
||||
IconSVG: []byte("<svg xmlns=\"http://www.w3.org/2000/svg\" fill=\"currentColor\" viewBox=\"10 240 160 160\"><path d=\"M137.95 269.613H96.725V290.3H74.688l42.65 49.825L160 290.3h-22.05v-20.688M20.337 351.587h22.05v20.7h41.226v-20.712h22.05l-42.638-49.788-42.688 49.8\"/></svg>"),
|
||||
Rules: []string{
|
||||
"||betfair.com.au^",
|
||||
"||betfair.com^",
|
||||
"||betfair.es^",
|
||||
"||betfair.it^",
|
||||
"||betfair.ro^",
|
||||
"||betfair.se^",
|
||||
},
|
||||
}, {
|
||||
ID: "betway",
|
||||
Name: "Betway",
|
||||
IconSVG: []byte("<svg xmlns=\"http://www.w3.org/2000/svg\" fill=\"currentColor\" viewBox=\"0 -150 418 418\"><path d=\"M23.73 24.54a42.662 42.662 0 0 1 5.76-1.28 45.4 45.4 0 0 1 7-.48 30.89 30.89 0 0 1 12.88 2.58 27.47 27.47 0 0 1 9.56 7A31.091 31.091 0 0 1 64.82 43a41 41 0 0 1 2 13 42.17 42.17 0 0 1-2.78 16 29.33 29.33 0 0 1-7.8 11.18 32.71 32.71 0 0 1-11.86 6.58 49.611 49.611 0 0 1-15 2.17 76.328 76.328 0 0 1-16.14-1.56A93.775 93.775 0 0 1 0 86.51V0h23.73v24.54zm0 49.09a22 22 0 0 0 6.24.82 12.66 12.66 0 0 0 9.9-4.27c2.533-2.853 3.797-7.217 3.79-13.09 0-5.7-1.243-9.903-3.73-12.61a12.22 12.22 0 0 0-9.42-4.07 17.06 17.06 0 0 0-3.53.34 28 28 0 0 0-3.25.88v32zm69.02-11.8c.18 4 1.47 7.097 3.87 9.29a12.91 12.91 0 0 0 9.15 3.33 16.75 16.75 0 0 0 7.66-1.63 18.48 18.48 0 0 0 5.9-5l14.51 11.39a32.174 32.174 0 0 1-5 5 33.45 33.45 0 0 1-14.65 6.72 54.08 54.08 0 0 1-10.51.94 39.47 39.47 0 0 1-13.22-2.17 29.73 29.73 0 0 1-18.1-17.15 38.33 38.33 0 0 1-2.71-15 39.27 39.27 0 0 1 2.58-14.71 31.76 31.76 0 0 1 7-10.92A29.2 29.2 0 0 1 90 25.09a37.8 37.8 0 0 1 13.36-2.31c9.853 0 17.693 3.187 23.52 9.56s8.787 16.203 8.88 29.49H92.75zm20.75-12.61a13.52 13.52 0 0 0-3.19-8.33 8.74 8.74 0 0 0-6.71-3.06A9.65 9.65 0 0 0 96.48 41a13.52 13.52 0 0 0-3.6 8.27l20.62-.05zm21.69-8.4V24.28h9.36V7.59h23.73v16.69h10.06l5.86 16.54h-15.92v26.57a15.07 15.07 0 0 0 .74 5.29c.5 1.27 1.61 1.9 3.33 1.9A10.62 10.62 0 0 0 177 73.5a23.121 23.121 0 0 0 4-2.44l6.1 14.37a35.208 35.208 0 0 1-9.69 4.75 40.998 40.998 0 0 1-12.14 1.62 27.2 27.2 0 0 1-9.83-1.56 16 16 0 0 1-6.44-4.4 16.68 16.68 0 0 1-3.46-6.92 37.889 37.889 0 0 1-1-9.08v-29l-9.35-.02zm70.3 49.63L176.17 7.56h22.94l17 51.84 10.17-35.12h15.42l10.17 35.12 9.76-35.12h24.27l-23.46 66.17h-18.17l-10.44-34.72-10.3 34.72zm117.63 0-1.9-5.16a22.659 22.659 0 0 1-7.32 4.41 28.91 28.91 0 0 1-10.71 1.83 26.2 26.2 0 0 1-8.55-1.35 19.5 19.5 0 0 1-6.84-4 18.58 18.58 0 0 1-4.55-6.3 20.58 20.58 0 0 1-1.62-8.41 18.07 18.07 0 0 1 6.84-14.78 27.46 27.46 0 0 1 7.4-4.2 40.168 40.168 0 0 1 8.94-2.31c2.9-.45 5.58-.79 8.07-1s4.77-.39 6.85-.48v-1.64c0-2.72-.86-4.7-2.58-6a10 10 0 0 0-6.1-1.9 18.55 18.55 0 0 0-7.39 1.49 30.17 30.17 0 0 0-6.66 4.1L286.37 33a42.588 42.588 0 0 1 12.14-7.46 43.002 43.002 0 0 1 16.07-2.71c8.667 0 15.58 2.103 20.74 6.31s7.737 10.737 7.73 19.59v41.72h-19.93zm-3.39-30c-1.36.09-2.71.2-4.07.34s-2.76.34-4.2.61c-2.8.45-4.8 1.42-6 2.91a7.79 7.79 0 0 0-1.76 5 6.08 6.08 0 0 0 2 4.68 7.73 7.73 0 0 0 5.49 1.83 12.9 12.9 0 0 0 4.67-.81 15.638 15.638 0 0 0 3.87-2.17V60.45z\"/><path d=\"M349.12 97.77a23.76 23.76 0 0 0 4.48 1.9 15.88 15.88 0 0 0 4.74.81 7.93 7.93 0 0 0 4.14-1 7.19 7.19 0 0 0 2.64-3.39l3.8-8.54-28.07-63.27h25.76L380 62.92l13.29-38.64h24.4l-33.08 78.37c-2.347 5.507-5.533 9.393-9.56 11.66a28.49 28.49 0 0 1-14.17 3.39 39.2 39.2 0 0 1-9.49-1.08 50.372 50.372 0 0 1-8.27-2.85l6-16z\"/></svg>"),
|
||||
Rules: []string{
|
||||
"||betway.be^",
|
||||
"||betway.bet.ar^",
|
||||
"||betway.co.za^",
|
||||
"||betway.com.gh^",
|
||||
"||betway.com.ng^",
|
||||
"||betway.com^",
|
||||
"||betway.de^",
|
||||
"||betway.es^",
|
||||
"||betway.fr^",
|
||||
"||betway.it^",
|
||||
"||betway.mx^",
|
||||
"||betway.pl^",
|
||||
"||betway.se^",
|
||||
"||betwaygroup.com^",
|
||||
"||betwaysatta.com^",
|
||||
"||vietnambetway88.com^",
|
||||
},
|
||||
}, {
|
||||
ID: "bilibili",
|
||||
Name: "Bilibili",
|
||||
@@ -336,6 +386,16 @@ var blockedServices = []blockedService{{
|
||||
"||mincdn.com^",
|
||||
"||yo9.com^",
|
||||
},
|
||||
}, {
|
||||
ID: "blaze",
|
||||
Name: "Blaze",
|
||||
IconSVG: []byte("<svg xmlns=\"http://www.w3.org/2000/svg\" fill=\"currentColor\" viewBox=\"-5 0 40 40\"><path d=\"m25.087 27.122-7.63 7.63a3.606 3.606 0 0 1-2.947 1.037 3.6 3.6 0 0 1-2.163-1.036l-7.63-7.631a3.603 3.603 0 0 1-1.058-2.553 3.6 3.6 0 0 1 1.058-2.556l7.63-7.63a3.605 3.605 0 0 1 2.86-1.046 3.61 3.61 0 0 1 2.25 1.045l7.63 7.631a3.601 3.601 0 0 1 1.058 2.553 3.6 3.6 0 0 1-1.058 2.556Zm4.225-5.226.007-.003-.037-.101c-.029-.074-.052-.149-.083-.222L24.571 9.112l-2.785 3.99L14.905 0S4.055 16.137 1.304 20.252h.005c-2.022 3.021-1.703 7.144.965 9.812l7.131 7.131a7.773 7.773 0 0 0 10.993 0l7.132-7.131c2.21-2.21 2.79-5.414 1.782-8.168Z\"/><path d=\"M14.902 17.182a2.244 2.244 0 1 0 0 4.488 2.244 2.244 0 0 0 0-4.488Zm0 10.283a2.244 2.244 0 1 0 0 4.488 2.244 2.244 0 0 0 0-4.488Zm5.141-5.142a2.244 2.244 0 1 0 0 4.489 2.244 2.244 0 0 0 0-4.489ZM7.516 24.567a2.245 2.245 0 1 0 4.489 0 2.245 2.245 0 0 0-4.49 0Z\"/></svg>"),
|
||||
Rules: []string{
|
||||
"||blaze.bet^",
|
||||
"||blaze.com.br^",
|
||||
"||blaze.com^",
|
||||
"||blazecareers.com^",
|
||||
},
|
||||
}, {
|
||||
ID: "blizzard_entertainment",
|
||||
Name: "Blizzard Entertainment",
|
||||
@@ -344,12 +404,12 @@ var blockedServices = []blockedService{{
|
||||
"||battle.net^",
|
||||
"||battlenet.com.cn^",
|
||||
"||blizzard.cn^",
|
||||
"||blizzard.com^",
|
||||
"||blizzardgames.cn^",
|
||||
"||blz-contentstack.com^",
|
||||
"||blzstatic.cn^",
|
||||
"||bnet.163.com^",
|
||||
"||bnet.cn^",
|
||||
"||lizzard.com^",
|
||||
},
|
||||
}, {
|
||||
ID: "claro",
|
||||
@@ -855,6 +915,29 @@ var blockedServices = []blockedService{{
|
||||
"||easyanticheat.net^",
|
||||
"||epicgames.com^",
|
||||
},
|
||||
}, {
|
||||
ID: "espn",
|
||||
Name: "ESPN",
|
||||
IconSVG: []byte("<svg xmlns=\"http://www.w3.org/2000/svg\" fill=\"currentColor\" viewBox=\"0 0 50 50\"><path d=\"M9.9 6a1 1 0 0 0-.98.89L7.9 15.86a1 1 0 0 0 1 1.11h34.66a1 1 0 0 0 .99-.88l1.02-8.98a1 1 0 0 0-1-1.11H9.91zM8.48 21a1 1 0 0 0-1 .89L5.02 43.92a1 1 0 0 0 .99 1.1h34.67a1 1 0 0 0 .99-.87l1.02-8.02a1 1 0 0 0-1-1.13H21.16l.48-4h20.49a1 1 0 0 0 .99-.87l1.02-8a1 1 0 0 0-1-1.13H8.48z\"/></svg>"),
|
||||
Rules: []string{
|
||||
"||es.pn^",
|
||||
"||espn.cl^",
|
||||
"||espn.co.uk^",
|
||||
"||espn.com.ar^",
|
||||
"||espn.com.au^",
|
||||
"||espn.com.co^",
|
||||
"||espn.com.ec^",
|
||||
"||espn.com.mx^",
|
||||
"||espn.com.pa^",
|
||||
"||espn.com.pe^",
|
||||
"||espn.com.uy^",
|
||||
"||espn.com.ve^",
|
||||
"||espn.com^",
|
||||
"||espn.in",
|
||||
"||espn.net^",
|
||||
"||espncdn.com^",
|
||||
"||espncricinfo.com^",
|
||||
},
|
||||
}, {
|
||||
ID: "facebook",
|
||||
Name: "Facebook",
|
||||
@@ -1304,6 +1387,14 @@ var blockedServices = []blockedService{{
|
||||
"||zuckerberg.com^",
|
||||
"||zuckerberg.net^",
|
||||
},
|
||||
}, {
|
||||
ID: "fifa",
|
||||
Name: "FIFA",
|
||||
IconSVG: []byte("<svg xmlns=\"http://www.w3.org/2000/svg\" fill=\"currentColor\" viewBox=\"0 0 24 24\"><path d=\"M0 8.06v7.88h2.49v-2.85H4.2l.68-1.72h-2.4v-1.6H5.4l.63-1.7zm6.8 0v7.88h2.46V8.06zm4.15 0v7.88h2.49v-2.85h1.72l.68-1.72h-2.4v-1.6h2.92l.64-1.7zm7.66 0-2.83 7.88h2.38l.3-1.06h2.77l.32 1.06H24l-2.84-7.88zm1.24 2.03.98 3.27H18.9z\"/></svg>"),
|
||||
Rules: []string{
|
||||
"||fifa.com^",
|
||||
"||fifaplus.com^",
|
||||
},
|
||||
}, {
|
||||
ID: "flickr",
|
||||
Name: "Flickr",
|
||||
@@ -1360,6 +1451,23 @@ var blockedServices = []blockedService{{
|
||||
"||mask-h2.icloud.com^$dnsrewrite=NXDOMAIN;;",
|
||||
"||mask.icloud.com^$dnsrewrite=NXDOMAIN;;",
|
||||
},
|
||||
}, {
|
||||
ID: "iheartradio",
|
||||
Name: "iHeartRadio",
|
||||
IconSVG: []byte("<svg xmlns=\"http://www.w3.org/2000/svg\" fill=\"currentColor\" viewBox=\"0 -4 33 33\"><path d=\"M23.997.25c-3.053 0-5.79 1.613-7.402 4.003C15.01 1.892 12.275.25 9.193.25A8.826 8.826 0 0 0 .35 9.092c0 3.11 1.958 5.328 4.003 7.402l8.007 7.575c.432.403 1.123.086 1.123-.49v-5.904c0-1.7 1.383-3.082 3.082-3.082 1.7 0 3.082 1.383 3.082 3.082v5.904c0 .576.69.864 1.123.49l8.007-7.575c2.045-2.074 4.003-4.291 4.003-7.402C32.84 4.196 28.893.25 23.997.25ZM8.587 15.918a.573.573 0 0 1-.46.202.688.688 0 0 1-.403-.144c-2.65-2.333-3.975-4.781-3.975-7.258v-.03c0-2.13 1.296-4.55 3.024-5.615a.607.607 0 1 1 .634 1.036c-1.383.864-2.448 2.88-2.448 4.58v.029c0 2.102 1.21 4.233 3.571 6.336.26.202.288.605.058.864Zm3.947-2.275a.618.618 0 0 1-.548.317.487.487 0 0 1-.288-.087c-1.785-1.008-2.995-2.966-2.995-4.896v-.029a4.925 4.925 0 0 1 2.65-4.378c.288-.144.662-.029.835.26.144.287.029.662-.26.835a3.664 3.664 0 0 0-1.987 3.283c0 1.498.95 3.053 2.362 3.83.288.173.375.548.23.865Zm4.06-1.584a2.643 2.643 0 0 1-2.65-2.65 2.644 2.644 0 0 1 2.65-2.65c1.47 0 2.65 1.181 2.65 2.65.03 1.469-1.18 2.65-2.65 2.65Zm4.926 1.814a.556.556 0 0 1-.288.087.652.652 0 0 1-.547-.317c-.173-.288-.058-.663.23-.836 1.411-.806 2.362-2.332 2.362-3.83 0-1.383-.778-2.65-1.988-3.283-.288-.144-.403-.519-.259-.836.144-.288.518-.403.835-.259a4.925 4.925 0 0 1 2.65 4.378v.029c0 1.9-1.21 3.86-2.995 4.867Zm7.949-5.184c0 2.477-1.325 4.925-3.975 7.258-.115.115-.259.144-.403.144a.573.573 0 0 1-.46-.202.602.602 0 0 1 .057-.864c2.362-2.102 3.571-4.234 3.571-6.336V8.66c0-1.728-1.065-3.744-2.448-4.58a.607.607 0 1 1 .634-1.036c1.728 1.094 3.024 3.514 3.024 5.616v.029Z\"/></svg>"),
|
||||
Rules: []string{
|
||||
"||937theriver.com^",
|
||||
"||iheart.com^",
|
||||
"||iheart.mx^",
|
||||
"||iheartmedia.com^",
|
||||
"||iheartradio.ca^",
|
||||
"||iheartradio.co.nz^",
|
||||
"||iheartradio.com^",
|
||||
"||ihrdev.com^",
|
||||
"||ihrhls.com^",
|
||||
"||ihrint.com^",
|
||||
"||ihrstage.com^",
|
||||
},
|
||||
}, {
|
||||
ID: "imgur",
|
||||
Name: "Imgur",
|
||||
@@ -1561,7 +1669,6 @@ var blockedServices = []blockedService{{
|
||||
"||cyberplace.social^",
|
||||
"||defcon.social^",
|
||||
"||det.social^",
|
||||
"||fosstodon.org^",
|
||||
"||glasgow.social^",
|
||||
"||h4.io^",
|
||||
"||hachyderm.io^",
|
||||
@@ -1597,6 +1704,7 @@ var blockedServices = []blockedService{{
|
||||
"||mastodon.sdf.org^",
|
||||
"||mastodon.social^",
|
||||
"||mastodon.social^",
|
||||
"||mastodon.top^",
|
||||
"||mastodon.uno^",
|
||||
"||mastodon.world^",
|
||||
"||mastodon.zaclys.com^",
|
||||
@@ -1605,6 +1713,7 @@ var blockedServices = []blockedService{{
|
||||
"||mastodont.cat^",
|
||||
"||mastodontech.de^",
|
||||
"||mastodontti.fi^",
|
||||
"||mastouille.fr^",
|
||||
"||mathstodon.xyz^",
|
||||
"||metalhead.club^",
|
||||
"||mindly.social^",
|
||||
@@ -1632,7 +1741,6 @@ var blockedServices = []blockedService{{
|
||||
"||social.anoxinon.de^",
|
||||
"||social.cologne^",
|
||||
"||social.dev-wiki.de^",
|
||||
"||social.linux.pizza^",
|
||||
"||social.politicaconciencia.org^",
|
||||
"||social.vivaldi.net^",
|
||||
"||stranger.social^",
|
||||
@@ -1654,6 +1762,32 @@ var blockedServices = []blockedService{{
|
||||
"||wien.rocks^",
|
||||
"||wxw.moe^",
|
||||
},
|
||||
}, {
|
||||
ID: "mercado_libre",
|
||||
Name: "Mercado Libre",
|
||||
IconSVG: []byte("<svg xmlns=\"http://www.w3.org/2000/svg\" fill=\"currentColor\" viewBox=\"0 0 256 256\"><path d=\"M114 56.6c-10.8 1.3-23.7 4-32.6 6.9C31.3 79.9 8.6 119.2 29 154.7c9.3 16.2 31 31.5 55 38.7 16 4.7 25.2 6 44 6 12.6 0 20-.5 26.5-1.7 48.6-9.5 79.9-36.8 79.9-69.7 0-14.3-5.3-26.4-16.6-38.6-13.8-14.7-35-25.4-60.7-30.5-9.3-1.8-35.3-3.2-43.1-2.3zm39 7.1c16.2 2.6 38.4 10.9 47.5 17.9 2.4 1.8 1.6 2.5-6.2 4.9-9.5 3-21.9 1.7-39.2-3.9-22.2-7.2-29-7.2-41.2.3-5 3.2-13.2 5.4-23.5 6.6-6.6.7-17.8-1.4-28.8-5.6-6.9-2.5-6.9-2.6 4.9-8.6C90.9 62.9 122 58.7 153 63.7zm-13.7 19.9c3.9.9 10.1 2.8 13.6 4.1 9.3 3.5 21.8 6.3 28.1 6.3 6.4 0 15.6-2.2 21.7-5.1 5.3-2.5 6.5-1.9 13.4 6.4 5.1 6.1 11.9 18.6 11.9 21.8 0 1.2-1.3 1.9-4.7 2.4-7.3 1.1-20.9 5.3-29.6 9.1l-7.8 3.4-11.7-10.9C153.4 101.6 141 93 133.6 93c-4.8 0-13.2 3.9-22.7 10.5-5.8 4.1-8.7 5.5-11.3 5.5-4.9 0-5.2-1.4-1.3-6 6.8-8 21.5-18.5 28-20 4.7-1.2 5.2-1.1 13 .6zm-65.6 9.5c6.7 1.6 10.8 2 17.1 1.6l8.3-.5-4.6 4.9c-7 7.5-6.2 12.8 2.3 15.4 4.1 1.2 8.6-.4 15.7-5.6 13.7-10 20.2-12.2 26.8-9 6.1 3 19.9 14.4 34.5 28.6l12.7 12.3-2.1 2.9c-1.2 1.5-3 2.8-4.1 2.8-1.2 0-6.5-4-12.2-9.2-10-9-13.1-10.5-13.1-6.3 0 1 3.8 5.3 8.4 9.5 4.7 4.2 8.7 8.3 9 9 .7 2-3.2 6.5-5.6 6.5-1.2 0-5.3-3.2-9.9-7.6-5.2-5.1-8.3-7.5-9.4-7.1-2.9 1.2-1.5 4.2 5.4 11.2 6 6 6.8 7.3 6 9.3-.4 1.2-2 2.7-3.4 3.4-2.4 1.1-3 .8-8.8-5-6.3-6.2-9.2-7.5-10.4-4.6-.4 1 .9 3.1 3.7 5.9 2.3 2.4 4 4.8 3.6 5.3-.9 1.5-5.7 2.1-8.3 1.1-1.9-.7-2.3-1.6-2.3-5 0-7.5-4.2-12.9-10-12.9-1.5 0-2-.7-2-2.8 0-6.4-5.9-11.1-11.9-9.5-1.6.4-3-.3-5.2-2.5-3.5-3.5-8.1-4.8-12.1-3.3-2 .8-3.4.6-6.4-1-4.7-2.3-6.8-2.4-10.3-.3-2.6 1.6-3 1.5-6.7-.8-9-5.7-29.7-12.8-37-12.8-1.2 0-2.5-.4-2.8-.9-.3-.5 1.2-4.6 3.5-9 2.8-5.7 6.1-10.2 10.9-15.1l6.8-6.9 7.6 3c4.2 1.6 11.5 3.9 16.3 5zm-31.2 31.6c3.9 1 11.5 4 17 6.8l10 5-.3 4.3c-.3 3.7.1 4.7 3.1 7.7 2.1 2.1 4.5 3.5 6 3.5 1.8 0 2.8.8 3.7 3 1.7 4.2 4.7 6 9.7 6 3.7 0 4.5.4 5.7 2.9 1.7 3.2 3.4 4.1 7.9 4.1 2 0 4.1.9 5.8 2.6 4 3.8 10.2 4.9 14.9 2.6 3.1-1.5 4.1-1.6 6.6-.5 4.2 1.8 9.4 1.6 12.9-.5 2-1.2 4.3-1.7 7.1-1.4 3.5.3 4.7-.1 7.8-2.8 2-1.8 3.6-3.8 3.6-4.5 0-.7 1.7-1.5 3.9-1.9 3.6-.5 9.1-5.1 9.1-7.6 0-.4 1.4-1 3.1-1.4 6.8-1.3 11.7-7.6 10.6-13.5-.6-2.7-.3-3 5.1-5.4 6.7-3 20.1-7.2 25.7-8.2 2.2-.4 4.8-.9 5.9-1.2 1.7-.5 1.8 0 1.4 7.5-.8 14-9.6 28.6-23.5 39.1-20.3 15.3-47 23.3-77.3 23.3-23.5 0-44.1-4.6-62-13.8-17.3-9-28.2-19.3-34.9-33.3-3-6.4-3.6-8.7-3.9-16.4l-.5-9 4.4.6c2.4.4 7.6 1.5 11.4 2.4zm42 13.3c1.8 3.5 3.5 3.8 6 1 1.4-1.5 2.9-2 5.4-1.8 3 .2 3.9.9 5 3.5 1.6 3.7 3.5 4.7 6.1 3.3 3.1-1.6 6.8-1.2 8 1 .6 1.1.7 3.7.4 6l-.7 4h4.6c3.7 0 4.9.5 6.6 2.6 2.7 3.5 2.6 5.9-.4 8.9-4.3 4.4-10.6 2.4-12.9-3.9-.6-1.8-2.3-1.8-6.9 0-1.8.7-4.7-2.1-4.7-4.5 0-3.2-2.1-4.1-6.8-2.8-5.1 1.4-7.3-.2-7.7-5.4-.2-1.9-.3-3.8-.4-4.2-.1-.4-2.3-.5-5.1-.1-4.7.6-5 .5-6-2-2.6-7 6.1-12.2 9.5-5.6z\"/></svg>"),
|
||||
Rules: []string{
|
||||
"||mercadolibre.cl^",
|
||||
"||mercadolibre.co.cr^",
|
||||
"||mercadolibre.com.ar^",
|
||||
"||mercadolibre.com.bo^",
|
||||
"||mercadolibre.com.co^",
|
||||
"||mercadolibre.com.do^",
|
||||
"||mercadolibre.com.ec^",
|
||||
"||mercadolibre.com.gt^",
|
||||
"||mercadolibre.com.hn^",
|
||||
"||mercadolibre.com.mx^",
|
||||
"||mercadolibre.com.ni^",
|
||||
"||mercadolibre.com.pa^",
|
||||
"||mercadolibre.com.pe^",
|
||||
"||mercadolibre.com.py^",
|
||||
"||mercadolibre.com.sv^",
|
||||
"||mercadolibre.com.uy^",
|
||||
"||mercadolibre.com.ve^",
|
||||
"||mercadolibre.com^",
|
||||
"||mercadolivre.com.br^",
|
||||
"||mlstatic.com^",
|
||||
},
|
||||
}, {
|
||||
ID: "minecraft",
|
||||
Name: "Minecraft",
|
||||
@@ -1764,6 +1898,14 @@ var blockedServices = []blockedService{{
|
||||
"||origin.tv^",
|
||||
"||signin.ea.com^",
|
||||
},
|
||||
}, {
|
||||
ID: "paramountplus",
|
||||
Name: "Paramount Plus",
|
||||
IconSVG: []byte("<svg xmlns=\"http://www.w3.org/2000/svg\" fill=\"currentColor\" viewBox=\"180 60 200 200\"><path d=\"M260.2 203.3c-.9-.4-2.4-2 .1-6.6l5.6-11.8c.2-.4-.2-.8-.5-.5l-4.9 5c-2.3 2.4-6.3 9.3-7.1 10.6l-6 9.9c.6 0 1 .7.7 1.2l-5.5 9.2c-1.3 2.3 1.1 3.9 1.4 3.4 8.6-13.9 13.6-12.8 13.6-12.8l2.9-6.7c.3-.4.1-.8-.3-.9\"/><path d=\"M279.8 87.2c-49.3 0-89.3 40-89.3 89.3 0 19.9 6.5 38.2 17.4 53 3.7-1.6 5.8-4 7.3-5.9l16.6-21.3c.4-.4.8-.8 1.3-1l2.5-1.1 27.3-34.7 4-3.1 8.1-11.3c.2-.3.5-.6.8-.8l3.6-2.6c.9-.6 2.1-.7 3 0l4.3 3a17 17 0 0 1 5.4 6.2l17.3 30.3c.4.7.7 1 1.4 1.3 3.4 1.7 5.4 2 9.9 6.8 2.1 2.3 11.1 12.4 23.8 28.1 1.8 2.4 4 4.4 7.2 5.8a88.95 88.95 0 0 0 17.4-52.9c0-49.1-40-89.1-89.3-89.1m-65.2 94.1-5.8-1.9-3.6 4.9v-6.1l-5.8-1.9 5.8-1.9v-6.1l3.6 4.9 5.8-1.9-3.6 4.9 3.6 5.1zm-1.3 20-1.9 5.8-1.9-5.8h-6.1l4.9-3.6-1.9-5.8 4.9 3.6 4.9-3.6-1.9 5.8 4.9 3.6h-5.9zm1.1-46.1 1.9 5.8-4.9-3.6-4.9 3.6 1.9-5.8-4.9-3.6h6.1l1.9-5.8 1.9 5.8h6.1l-5.1 3.6zm9.8-13.1-3.6-4.9-5.8 1.9 3.6-4.9-3.6-4.9 5.8 1.9 3.6-4.9v6.1l5.8 1.9-5.8 1.9v5.9zm15.2-21.3-1.9 5.8-1.9-5.8h-6.1l4.9-3.6-1.9-5.8 4.9 3.6 4.9-3.6-1.9 5.8 4.9 3.6h-5.9zm19.2-9.7L255 116v-6.1l-5.8-1.9 5.8-1.9v-6l3.6 4.9 5.8-1.9-3.6 4.9 3.6 4.9-5.8-1.8zm24.3-5.7 1.9 5.8-4.9-3.6-4.9 3.6 1.9-5.8-4.9-3.6h6.1L280 96l1.9 5.8h6.1l-5.1 3.6zm21.8 4.5v6.1l-3.6-4.9-5.8 1.9 3.6-4.9-3.6-4.9 5.8 1.9 3.6-4.9v6.1l5.8 1.9-5.8 1.7zm40.3 61.5 5.8 1.9 3.6-4.9v6.1l5.8 1.9-5.8 1.9v6.1l-3.6-4.9-5.8 1.9 3.6-4.9-3.6-5.1zm-22.9-44.8-1.9-5.8h-6.1l4.9-3.6-1.9-5.8 4.9 3.6 4.9-3.6-1.9 5.8 4.9 3.6H324l-1.9 5.8zm13.4 15.5v-6l-5.8-1.9 5.8-1.9v-6.1l3.6 4.9 5.8-1.9-3.6 4.9 3.6 4.9-5.8-1.9-3.6 5zm10.9 9.5 1.9-5.8 1.9 5.8h6.1l-4.9 3.6 1.9 5.8-4.9-3.6-4.9 3.6 1.9-5.8-4.9-3.6h5.9zm3.8 49.7-1.9 5.8-1.9-5.8h-6.1l4.9-3.6-1.9-5.8 4.9 3.6 4.9-3.6-1.9 5.8 4.9 3.6h-5.9z\"/><path d=\"M312.2 228.1c.4-.6 1.1-2.3-.2-5.5l-3.9-10.6c-.5-1.4.6-2.2 1.4-1.3 0 0 7.5 8.6 9.4 12.2l3.7 6.1c3.2.2 11.9.5 20.3.5-.8-.8-1.6-1.7-2.4-2.7C326.1 209 317 199.1 317 199c-2.9-3.2-4.3-3.8-6.5-4.8-.3-.1-.7-.3-1-.5v2.7c0 .4-.4.5-.6.2l-21.1-37.1-.1-.1c-.9-1.7-2.2-3.2-3.8-4.4l-2.1-1.4-10.1 23.2c1.6 0 2.6 1.6 2 3l-9.3 21.5h8.5c3.3 0 6.6.6 9.6 1.9l2.2.9s-6.8 14-6.8 21.3c0 1.3.2 2.7.6 4h15.6l-.7-4.4c.2-.1 9.6 2.2 18.8 3.1\"/></svg>"),
|
||||
Rules: []string{
|
||||
"||paramountplus.com^",
|
||||
"||pplusstatic.com^",
|
||||
},
|
||||
}, {
|
||||
ID: "pinterest",
|
||||
Name: "Pinterest",
|
||||
@@ -1833,6 +1975,13 @@ var blockedServices = []blockedService{{
|
||||
"||sonyentertainmentnetwork.com",
|
||||
"||station.sony.com",
|
||||
},
|
||||
}, {
|
||||
ID: "pluto_tv",
|
||||
Name: "Pluto TV",
|
||||
IconSVG: []byte("<svg xmlns=\"http://www.w3.org/2000/svg\" fill=\"currentColor\" viewBox=\"0 -80 560 560\"><path d=\"M357.3 164.3c-19.7 0-35.7 16-35.7 35.7s16 35.7 35.7 35.7 35.7-16 35.7-35.7-16-35.7-35.7-35.7m0 52.1a16.41 16.41 0 0 1 0-32.8c9 0 16.4 7.4 16.4 16.4 0 9.1-7.4 16.4-16.4 16.4m-40.6-32.3v-18.4h-16.6v-18l-20 8.1v9.9h-12.3v18.4H280v23.7c0 8.2 2.4 15 7 19.6 4.6 4.6 11.4 7 19.7 7h9.9V216h-6.8c-6.8 0-9.8-3-9.8-9.8v-22.1h16.7zM240 173.8v30.1c0 6.9-5.6 12.6-12.6 12.6-6.9 0-12.6-5.7-12.6-12.6v-37.4l-19.8 8v28.8c0 17.9 14.5 32.5 32.4 32.5s32.5-14.5 32.5-32.5v-37.6l-19.9 8.1zm-73.6-18v78.5h20v-86.5zm-41.1 8.5c-8.3 0-15.4 2.8-20.7 8.2l-2.8-6.7-16.2 6.5v80h20v-23.6c5.2 4.7 12 7 19.7 7 9.2 0 17.6-3.7 23.8-10.5 6.1-6.6 9.4-15.6 9.4-25.2 0-19.9-14.6-35.7-33.2-35.7m-3.1 52.1c-9.2 0-16.7-7.4-16.7-16.4 0-9.1 7.5-16.4 16.7-16.4s16.7 7.4 16.7 16.4-7.5 16.4-16.7 16.4m316.5-52.1c-19.7 0-35.7 16-35.7 35.7s16 35.7 35.7 35.7 35.7-16 35.7-35.7c0-19.7-16-35.7-35.7-35.7m14.1 53.1h-6.7l-9.5-23H427v11.9c0 2.6 1.2 4.1 4.1 4.1h4.2v7h-5.2c-7.1 0-10.8-4-10.8-10.6v-12.4H414v-6.7h5.3V183l7.6-3.1v7.8h15l7.4 19.3 7.5-19.4h8.2l-12.2 29.8z\"/></svg>"),
|
||||
Rules: []string{
|
||||
"||pluto.tv^",
|
||||
},
|
||||
}, {
|
||||
ID: "qq",
|
||||
Name: "QQ",
|
||||
@@ -1879,8 +2028,14 @@ var blockedServices = []blockedService{{
|
||||
Name: "Roblox",
|
||||
IconSVG: []byte("<svg xmlns=\"http://www.w3.org/2000/svg\" fill=\"currentColor\" viewBox=\"0 0 24 24\"><path d=\"m13.383 14.341-3.726-.958.959-3.726 3.726.959-.96 3.726zM4.913 0 0 19.088 19.088 24 24 4.912 4.912 0z\" /></svg>"),
|
||||
Rules: []string{
|
||||
"||blox.com^",
|
||||
"||rbx.cn^",
|
||||
"||rbx.com^",
|
||||
"||rbxadder.com^",
|
||||
"||rbxcdn.com^",
|
||||
"||rbxcdn.net^",
|
||||
"||rbxinfra.com^",
|
||||
"||rbxinfra.net^",
|
||||
"||roblox.cn^",
|
||||
"||roblox.com^",
|
||||
"||roblox.qq.com^",
|
||||
@@ -1889,12 +2044,22 @@ var blockedServices = []blockedService{{
|
||||
},
|
||||
}, {
|
||||
ID: "rockstar_games",
|
||||
Name: "Rockstar games",
|
||||
Name: "Rockstar Games",
|
||||
IconSVG: []byte("<svg xmlns=\"http://www.w3.org/2000/svg\" fill=\"currentColor\" viewBox=\"0 0 50 50\"><path d=\"M12 3c-4.96 0-9 4.04-9 9v26c0 4.96 4.04 9 9 9h26c4.96 0 9-4.04 9-9V12c0-4.96-4.04-9-9-9H12zm0 2h26c3.88 0 7 3.12 7 7v26c0 3.88-3.12 7-7 7H12c-3.88 0-7-3.12-7-7V12c0-3.88 3.12-7 7-7zm3.72 5a1 1 0 0 0-.97.79l-3.87 18a1 1 0 0 0 .98 1.21h4.27a1 1 0 0 0 .97-.79L18.47 23h2.07c.94 0 1.12.15 1.36.73.24.57.3 1.76.1 3.4-.08.68-.05 1.22.02 1.6v.03a1 1 0 0 0 .3.97l3.37 3.12-2.6 5.74a1 1 0 0 0 1.43 1.26l5.58-3.39 4.29 3.33a1 1 0 0 0 1.6-.98l-1.09-5.56 4.7-3.47a1 1 0 0 0-.6-1.8h-4.86l-.82-5.14a1 1 0 0 0-.98-.84 1 1 0 0 0-.88.51l-2.77 5a14.3 14.3 0 0 1 .06-2.83c.15-1.48.01-2.64-.18-3.45-.06-.28-.08-.25-.15-.45.3-.17.4-.13.77-.5.8-.8 1.6-2.18 1.75-4.26.17-2.26-.55-3.98-1.92-4.9C27.65 10.17 25.91 10 24 10h-8.28zm.81 2H24c1.75 0 3.13.25 3.9.77.76.52 1.18 1.27 1.05 3.1-.13 1.67-.69 2.51-1.17 3a2 2 0 0 1-.82.56 1 1 0 0 0-.6 1.44s.12.21.27.82c.14.6.26 1.53.13 2.79a14.24 14.24 0 0 0-.01 3.52h-2.76c-.01-.19-.04-.32 0-.62.22-1.78.25-3.21-.24-4.42A3.38 3.38 0 0 0 20.54 21h-2.87a1 1 0 0 0-.98.78L15.32 28H13.1l3.44-16zm2.76 1.03a1 1 0 0 0-.98.8l-.98 4.94a1 1 0 0 0 .98 1.2h4.47c.79 0 1.65-.12 2.44-.58a3.6 3.6 0 0 0 1.68-2.41 3.3 3.3 0 0 0-.72-2.92 3.35 3.35 0 0 0-2.47-1.03h-4.42zm.82 2h3.6c.41 0 .79.16 1 .4.22.22.36.52.23 1.15-.13.62-.36.88-.72 1.08a3 3 0 0 1-1.44.3h-3.25l.58-2.93zm11.7 10.99.49 3.11a1 1 0 0 0 .98.84h2.69l-2.76 2.05a1 1 0 0 0-.4 1l.7 3.56-2.73-2.12a1 1 0 0 0-1.13-.07l-3.4 2.07 1.56-3.44a1 1 0 0 0-.23-1.15L25.55 30H29a1 1 0 0 0 .88-.51l1.92-3.47z\"/></svg>"),
|
||||
Rules: []string{
|
||||
"||rockstargames.com^",
|
||||
"||rsg.sc^",
|
||||
},
|
||||
}, {
|
||||
ID: "shein",
|
||||
Name: "Shein",
|
||||
IconSVG: []byte("<svg xmlns=\"http://www.w3.org/2000/svg\" fill=\"currentColor\" viewBox=\"0 0 50 50\"><path d=\"M14 4C8.49 4 4 8.49 4 14v22c0 5.51 4.49 10 10 10h22c5.51 0 10-4.49 10-10V14c0-5.51-4.49-10-10-10H14zm0 2h22c4.43 0 8 3.57 8 8v22c0 4.43-3.57 8-8 8H14c-4.43 0-8-3.57-8-8V14c0-4.43 3.57-8 8-8zm11 7c-4 0-8.5 2-8.5 6.5 0 7.75 13 6.25 13 11 0 3.25-3.5 3.5-4.5 3.5-2 0-5-1-6.5-2L16 34.5c2.5 1.5 5.5 2.75 9 2.75 3 0 8.5-1.5 8.5-6.75 0-7-13-6.75-13-11 0-3 3.5-3.5 4.5-3.5 1.25 0 3.75.5 5 1.5l2.5-2.25C31 14 29 13 25 13z\"/></svg>"),
|
||||
Rules: []string{
|
||||
"||shein.co.uk^",
|
||||
"||shein.com^",
|
||||
"||shein.se^",
|
||||
"||sheinsz.ltwebstatic.com^",
|
||||
},
|
||||
}, {
|
||||
ID: "shopee",
|
||||
Name: "Shopee",
|
||||
@@ -2105,8 +2270,8 @@ var blockedServices = []blockedService{{
|
||||
},
|
||||
}, {
|
||||
ID: "twitter",
|
||||
Name: "Twitter",
|
||||
IconSVG: []byte("<svg xmlns=\"http://www.w3.org/2000/svg\" fill=\"currentColor\" viewBox=\"0 0 24 24\"><path d=\"M22.398 5.55a8.583 8.583 0 0 1-2.449.673 4.252 4.252 0 0 0 1.875-2.364 8.66 8.66 0 0 1-2.71 1.04A4.251 4.251 0 0 0 16 3.546a4.27 4.27 0 0 0-4.266 4.27c0 .335.036.66.11.972a12.126 12.126 0 0 1-8.797-4.46 4.259 4.259 0 0 0-.578 2.148c0 1.48.754 2.785 1.898 3.55a4.273 4.273 0 0 1-1.933-.535v.055a4.27 4.27 0 0 0 3.425 4.183c-.359.098-.734.149-1.125.149-.273 0-.543-.027-.804-.074a4.276 4.276 0 0 0 3.988 2.965 8.562 8.562 0 0 1-5.3 1.824 8.82 8.82 0 0 1-1.02-.059 12.088 12.088 0 0 0 6.543 1.918c7.851 0 12.14-6.504 12.14-12.144 0-.184-.004-.368-.011-.551a8.599 8.599 0 0 0 2.128-2.207zm0 0\" /></svg>"),
|
||||
Name: "X (formerly Twitter)",
|
||||
IconSVG: []byte("<svg xmlns=\"http://www.w3.org/2000/svg\" fill=\"currentColor\" viewBox=\"0 0 24 24\"><path d=\"M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231zm-1.161 17.52h1.833L7.084 4.126H5.117z\"/></svg>"),
|
||||
Rules: []string{
|
||||
"||ads-twitter.com^",
|
||||
"||cms-twdigitalassets.com^",
|
||||
@@ -2130,6 +2295,7 @@ var blockedServices = []blockedService{{
|
||||
"||twttr.net^",
|
||||
"||twvid.com^",
|
||||
"||vine.co^",
|
||||
"||x.com^",
|
||||
},
|
||||
}, {
|
||||
ID: "ubisoft",
|
||||
|
||||
Reference in New Issue
Block a user