mirror of
https://github.com/AdguardTeam/AdGuardHome.git
synced 2025-10-26 11:27:18 +00:00
all: rewrites enabled
This commit is contained in:
parent
6dbf114ff9
commit
f1bf45aa42
32
CHANGELOG.md
32
CHANGELOG.md
@ -19,8 +19,40 @@ NOTE: Add new changes BELOW THIS COMMENT.
|
||||
-->
|
||||
### Added
|
||||
|
||||
- New DNS rewrite settings endpoints `GET /control/rewrite/settings` and `PUT /control/rewrite/settings/update`. See `openapi/openapi.yaml` for details.
|
||||
- New fields `"groups"` and `"group_id"` added to the HTTP API (`GET /control/blocked_services/all`). See `openapi/openapi.yaml` for the full description.
|
||||
|
||||
### Changed
|
||||
|
||||
- `POST /control/rewrite/add` and `PUT /control/rewrite/update` now accept the optional field "enabled". See `openapi/openapi.yaml` for details.
|
||||
|
||||
#### Configuration changes
|
||||
|
||||
In this release, the schema version has changed from 30 to 31.
|
||||
|
||||
- Added a new boolean field `filtering.rewrites_enabled` to globally enable/disable DNS rewrites.
|
||||
- Added a new boolean field `enabled` for each entry in `filtering.rewrites` to toggle individual rewrites.
|
||||
|
||||
```yaml
|
||||
# BEFORE:
|
||||
'filtering':
|
||||
'rewrites':
|
||||
- 'domain': test.example
|
||||
'answer': 192.0.2.0
|
||||
# …
|
||||
|
||||
# AFTER:
|
||||
'filtering':
|
||||
'rewrites_enabled': true
|
||||
'rewrites':
|
||||
- 'domain': test.example
|
||||
'answer': 192.0.2.0
|
||||
'enabled': true
|
||||
# …
|
||||
```
|
||||
|
||||
To roll back this change, set `schema_version` back to `30`.
|
||||
|
||||
<!--
|
||||
NOTE: Add new changes ABOVE THIS COMMENT.
|
||||
-->
|
||||
|
||||
@ -2,4 +2,4 @@
|
||||
package configmigrate
|
||||
|
||||
// LastSchemaVersion is the most recent schema version.
|
||||
const LastSchemaVersion uint = 30
|
||||
const LastSchemaVersion uint = 31
|
||||
|
||||
@ -126,6 +126,7 @@ func (m *Migrator) upgradeConfigSchema(current, target uint, diskConf yobj) (err
|
||||
27: migrateTo28,
|
||||
28: m.migrateTo29,
|
||||
29: m.migrateTo30,
|
||||
30: m.migrateTo31,
|
||||
}
|
||||
|
||||
for i, migrate := range upgrades[current:target] {
|
||||
|
||||
@ -199,6 +199,10 @@ func TestMigrateConfig_Migrate(t *testing.T) {
|
||||
yamlEqFunc: require.YAMLEq,
|
||||
name: "v30",
|
||||
targetVersion: 30,
|
||||
}, {
|
||||
yamlEqFunc: require.YAMLEq,
|
||||
name: "v31",
|
||||
targetVersion: 31,
|
||||
}}
|
||||
|
||||
for _, tc := range testCases {
|
||||
|
||||
122
internal/configmigrate/testdata/TestMigrateConfig_Migrate/v31/input.yml
vendored
Normal file
122
internal/configmigrate/testdata/TestMigrateConfig_Migrate/v31/input.yml
vendored
Normal file
@ -0,0 +1,122 @@
|
||||
http:
|
||||
address: 127.0.0.1:3000
|
||||
session_ttl: 3h
|
||||
pprof:
|
||||
enabled: true
|
||||
port: 6060
|
||||
users:
|
||||
- name: testuser
|
||||
password: testpassword
|
||||
dns:
|
||||
bind_hosts:
|
||||
- 127.0.0.1
|
||||
port: 53
|
||||
parental_sensitivity: 0
|
||||
upstream_dns:
|
||||
- tls://1.1.1.1
|
||||
- tls://1.0.0.1
|
||||
- quic://8.8.8.8:784
|
||||
bootstrap_dns:
|
||||
- 8.8.8.8:53
|
||||
cache_size: 4194304
|
||||
edns_client_subnet:
|
||||
enabled: true
|
||||
use_custom: false
|
||||
custom_ip: ""
|
||||
filtering:
|
||||
filtering_enabled: true
|
||||
parental_enabled: false
|
||||
safebrowsing_enabled: false
|
||||
rewrites:
|
||||
- domain: test.example
|
||||
answer: 192.0.2.0
|
||||
safe_fs_patterns: []
|
||||
safe_search:
|
||||
enabled: false
|
||||
bing: true
|
||||
duckduckgo: true
|
||||
google: true
|
||||
pixabay: true
|
||||
yandex: true
|
||||
youtube: true
|
||||
protection_enabled: true
|
||||
blocked_services:
|
||||
schedule:
|
||||
time_zone: Local
|
||||
ids:
|
||||
- 500px
|
||||
blocked_response_ttl: 10
|
||||
filters:
|
||||
- url: https://adaway.org/hosts.txt
|
||||
name: AdAway
|
||||
enabled: false
|
||||
- url: FILEPATH
|
||||
name: Local Filter
|
||||
enabled: false
|
||||
clients:
|
||||
persistent:
|
||||
- name: localhost
|
||||
ids:
|
||||
- 127.0.0.1
|
||||
- aa:aa:aa:aa:aa:aa
|
||||
use_global_settings: true
|
||||
use_global_blocked_services: true
|
||||
filtering_enabled: false
|
||||
parental_enabled: false
|
||||
safebrowsing_enabled: false
|
||||
safe_search:
|
||||
enabled: true
|
||||
bing: true
|
||||
duckduckgo: true
|
||||
google: true
|
||||
pixabay: true
|
||||
yandex: true
|
||||
youtube: true
|
||||
blocked_services:
|
||||
schedule:
|
||||
time_zone: Local
|
||||
ids:
|
||||
- 500px
|
||||
runtime_sources:
|
||||
whois: true
|
||||
arp: true
|
||||
rdns: true
|
||||
dhcp: true
|
||||
hosts: true
|
||||
dhcp:
|
||||
enabled: false
|
||||
interface_name: vboxnet0
|
||||
local_domain_name: local
|
||||
dhcpv4:
|
||||
gateway_ip: 192.168.0.1
|
||||
subnet_mask: 255.255.255.0
|
||||
range_start: 192.168.0.10
|
||||
range_end: 192.168.0.250
|
||||
lease_duration: 1234
|
||||
icmp_timeout_msec: 10
|
||||
schema_version: 29
|
||||
user_rules: []
|
||||
querylog:
|
||||
enabled: true
|
||||
file_enabled: true
|
||||
interval: 720h
|
||||
size_memory: 1000
|
||||
ignored:
|
||||
- '|.^'
|
||||
statistics:
|
||||
enabled: true
|
||||
interval: 240h
|
||||
ignored:
|
||||
- '|.^'
|
||||
os:
|
||||
group: ''
|
||||
rlimit_nofile: 123
|
||||
user: ''
|
||||
log:
|
||||
file: ""
|
||||
max_backups: 0
|
||||
max_size: 100
|
||||
max_age: 3
|
||||
compress: true
|
||||
local_time: false
|
||||
verbose: true
|
||||
124
internal/configmigrate/testdata/TestMigrateConfig_Migrate/v31/output.yml
vendored
Normal file
124
internal/configmigrate/testdata/TestMigrateConfig_Migrate/v31/output.yml
vendored
Normal file
@ -0,0 +1,124 @@
|
||||
http:
|
||||
address: 127.0.0.1:3000
|
||||
session_ttl: 3h
|
||||
pprof:
|
||||
enabled: true
|
||||
port: 6060
|
||||
users:
|
||||
- name: testuser
|
||||
password: testpassword
|
||||
dns:
|
||||
bind_hosts:
|
||||
- 127.0.0.1
|
||||
port: 53
|
||||
parental_sensitivity: 0
|
||||
upstream_dns:
|
||||
- tls://1.1.1.1
|
||||
- tls://1.0.0.1
|
||||
- quic://8.8.8.8:784
|
||||
bootstrap_dns:
|
||||
- 8.8.8.8:53
|
||||
cache_enabled: true
|
||||
cache_size: 4194304
|
||||
edns_client_subnet:
|
||||
enabled: true
|
||||
use_custom: false
|
||||
custom_ip: ""
|
||||
filtering:
|
||||
filtering_enabled: true
|
||||
parental_enabled: false
|
||||
safebrowsing_enabled: false
|
||||
rewrites:
|
||||
- domain: test.example
|
||||
answer: 192.0.2.0
|
||||
enabled: true
|
||||
safe_fs_patterns: []
|
||||
safe_search:
|
||||
enabled: false
|
||||
bing: true
|
||||
duckduckgo: true
|
||||
google: true
|
||||
pixabay: true
|
||||
yandex: true
|
||||
youtube: true
|
||||
protection_enabled: true
|
||||
blocked_services:
|
||||
schedule:
|
||||
time_zone: Local
|
||||
ids:
|
||||
- 500px
|
||||
blocked_response_ttl: 10
|
||||
filters:
|
||||
- url: https://adaway.org/hosts.txt
|
||||
name: AdAway
|
||||
enabled: false
|
||||
- url: FILEPATH
|
||||
name: Local Filter
|
||||
enabled: false
|
||||
clients:
|
||||
persistent:
|
||||
- name: localhost
|
||||
ids:
|
||||
- 127.0.0.1
|
||||
- aa:aa:aa:aa:aa:aa
|
||||
use_global_settings: true
|
||||
use_global_blocked_services: true
|
||||
filtering_enabled: false
|
||||
parental_enabled: false
|
||||
safebrowsing_enabled: false
|
||||
safe_search:
|
||||
enabled: true
|
||||
bing: true
|
||||
duckduckgo: true
|
||||
google: true
|
||||
pixabay: true
|
||||
yandex: true
|
||||
youtube: true
|
||||
blocked_services:
|
||||
schedule:
|
||||
time_zone: Local
|
||||
ids:
|
||||
- 500px
|
||||
runtime_sources:
|
||||
whois: true
|
||||
arp: true
|
||||
rdns: true
|
||||
dhcp: true
|
||||
hosts: true
|
||||
dhcp:
|
||||
enabled: false
|
||||
interface_name: vboxnet0
|
||||
local_domain_name: local
|
||||
dhcpv4:
|
||||
gateway_ip: 192.168.0.1
|
||||
subnet_mask: 255.255.255.0
|
||||
range_start: 192.168.0.10
|
||||
range_end: 192.168.0.250
|
||||
lease_duration: 1234
|
||||
icmp_timeout_msec: 10
|
||||
schema_version: 31
|
||||
user_rules: []
|
||||
querylog:
|
||||
enabled: true
|
||||
file_enabled: true
|
||||
interval: 720h
|
||||
size_memory: 1000
|
||||
ignored:
|
||||
- '|.^'
|
||||
statistics:
|
||||
enabled: true
|
||||
interval: 240h
|
||||
ignored:
|
||||
- '|.^'
|
||||
os:
|
||||
group: ''
|
||||
rlimit_nofile: 123
|
||||
user: ''
|
||||
log:
|
||||
file: ""
|
||||
max_backups: 0
|
||||
max_size: 100
|
||||
max_age: 3
|
||||
compress: true
|
||||
local_time: false
|
||||
verbose: true
|
||||
41
internal/configmigrate/v31.go
Normal file
41
internal/configmigrate/v31.go
Normal file
@ -0,0 +1,41 @@
|
||||
package configmigrate
|
||||
|
||||
// migrateTo31 performs the following changes:
|
||||
//
|
||||
// # BEFORE:
|
||||
// 'filtering':
|
||||
// 'rewrites':
|
||||
// - 'domain': test.example
|
||||
// 'answer': 192.0.2.0
|
||||
// # …
|
||||
// # …
|
||||
//
|
||||
// # AFTER:
|
||||
// 'filtering':
|
||||
// 'rewrites':
|
||||
// - 'domain': test.example
|
||||
// 'answer': 192.0.2.0
|
||||
// 'enabled': true
|
||||
// # …
|
||||
// # …
|
||||
func (m Migrator) migrateTo31(diskConf yobj) (err error) {
|
||||
diskConf["schema_version"] = 31
|
||||
|
||||
fltConf, ok, err := fieldVal[yobj](diskConf, "filtering")
|
||||
if !ok {
|
||||
return err
|
||||
}
|
||||
|
||||
rewrites, ok, err := fieldVal[yarr](fltConf, "rewrites")
|
||||
if !ok {
|
||||
return err
|
||||
}
|
||||
|
||||
for i := range rewrites {
|
||||
if r, isYobj := rewrites[i].(yobj); isYobj {
|
||||
r["enabled"] = true
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@ -1247,18 +1247,22 @@ func TestRewrite(t *testing.T) {
|
||||
BlockedServices: emptyFilteringBlockedServices(),
|
||||
BlockingMode: filtering.BlockingModeDefault,
|
||||
Rewrites: []*filtering.LegacyRewrite{{
|
||||
Domain: "test.com",
|
||||
Answer: "1.2.3.4",
|
||||
Type: dns.TypeA,
|
||||
Domain: "test.com",
|
||||
Answer: "1.2.3.4",
|
||||
Type: dns.TypeA,
|
||||
Enabled: true,
|
||||
}, {
|
||||
Domain: "alias.test.com",
|
||||
Answer: "test.com",
|
||||
Type: dns.TypeCNAME,
|
||||
Domain: "alias.test.com",
|
||||
Answer: "test.com",
|
||||
Type: dns.TypeCNAME,
|
||||
Enabled: true,
|
||||
}, {
|
||||
Domain: "my.alias.example.org",
|
||||
Answer: "example.org",
|
||||
Type: dns.TypeCNAME,
|
||||
Domain: "my.alias.example.org",
|
||||
Answer: "example.org",
|
||||
Type: dns.TypeCNAME,
|
||||
Enabled: true,
|
||||
}},
|
||||
RewritesEnabled: true,
|
||||
}
|
||||
f, err := filtering.New(c, nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
@ -138,6 +138,7 @@ type Config struct {
|
||||
// to DNS requests blocked by safe-browsing.
|
||||
SafeBrowsingBlockHost string `yaml:"safebrowsing_block_host"`
|
||||
|
||||
// Rewrites is a list of legacy DNS rewrite records.
|
||||
Rewrites []*LegacyRewrite `yaml:"rewrites"`
|
||||
|
||||
// Filters are the blocking filter lists.
|
||||
@ -177,6 +178,9 @@ type Config struct {
|
||||
// FilteringEnabled indicates whether or not use filter lists.
|
||||
FilteringEnabled bool `yaml:"filtering_enabled"`
|
||||
|
||||
// RewritesEnabled indicates whether legacy rewrites are applied.
|
||||
RewritesEnabled bool `yaml:"rewrites_enabled"`
|
||||
|
||||
ParentalEnabled bool `yaml:"parental_enabled"`
|
||||
SafeBrowsingEnabled bool `yaml:"safebrowsing_enabled"`
|
||||
|
||||
@ -542,6 +546,10 @@ func (d *DNSFilter) processRewrites(host string, qtype uint16) (res Result) {
|
||||
|
||||
ctx := context.TODO()
|
||||
|
||||
if !d.conf.RewritesEnabled {
|
||||
return Result{}
|
||||
}
|
||||
|
||||
rewrites, matched := findRewrites(d.conf.Rewrites, host, qtype)
|
||||
if !matched {
|
||||
return Result{}
|
||||
@ -549,8 +557,22 @@ func (d *DNSFilter) processRewrites(host string, qtype uint16) (res Result) {
|
||||
|
||||
res.Reason = Rewritten
|
||||
|
||||
return d.handleRewriteLoop(ctx, host, qtype, rewrites, matched, &res)
|
||||
}
|
||||
|
||||
// handleRewriteLoop performs filtering rewrite processing based on the legacy
|
||||
// rewrite records. res must not be nil.
|
||||
func (d *DNSFilter) handleRewriteLoop(
|
||||
ctx context.Context,
|
||||
host string,
|
||||
qtype uint16,
|
||||
rewrites []*LegacyRewrite,
|
||||
matched bool,
|
||||
res *Result,
|
||||
) (resResult Result) {
|
||||
cnames := container.NewMapSet[string]()
|
||||
origHost := host
|
||||
|
||||
for matched && len(rewrites) > 0 && rewrites[0].Type == dns.TypeCNAME {
|
||||
rw := rewrites[0]
|
||||
rwPat := rw.Domain
|
||||
@ -577,7 +599,7 @@ func (d *DNSFilter) processRewrites(host string, qtype uint16) (res Result) {
|
||||
if cnames.Has(host) {
|
||||
d.logger.InfoContext(ctx, "cname loop", "host", host, "original", origHost)
|
||||
|
||||
return res
|
||||
return *res
|
||||
}
|
||||
|
||||
cnames.Add(host)
|
||||
@ -585,9 +607,9 @@ func (d *DNSFilter) processRewrites(host string, qtype uint16) (res Result) {
|
||||
rewrites, matched = findRewrites(d.conf.Rewrites, host, qtype)
|
||||
}
|
||||
|
||||
d.setRewriteResult(ctx, &res, host, rewrites, qtype)
|
||||
d.setRewriteResult(ctx, res, host, rewrites, qtype)
|
||||
|
||||
return res
|
||||
return *res
|
||||
}
|
||||
|
||||
// matchBlockedServicesRules checks the host against the blocked services rules
|
||||
|
||||
@ -45,7 +45,8 @@ func newForTest(t testing.TB, c *Config, filters []Filter) (f *DNSFilter, setts
|
||||
} else {
|
||||
// It must not be nil.
|
||||
c = &Config{
|
||||
Logger: testLogger,
|
||||
Logger: testLogger,
|
||||
RewritesEnabled: true,
|
||||
}
|
||||
}
|
||||
f, err := New(c, filters)
|
||||
|
||||
@ -645,9 +645,11 @@ func (d *DNSFilter) RegisterFilteringHandlers() {
|
||||
registerHTTP(http.MethodPut, "/control/safesearch/settings", d.handleSafeSearchSettings)
|
||||
|
||||
registerHTTP(http.MethodGet, "/control/rewrite/list", d.handleRewriteList)
|
||||
registerHTTP(http.MethodGet, "/control/rewrite/settings", d.handleRewriteSettings)
|
||||
registerHTTP(http.MethodPost, "/control/rewrite/add", d.handleRewriteAdd)
|
||||
registerHTTP(http.MethodPut, "/control/rewrite/update", d.handleRewriteUpdate)
|
||||
registerHTTP(http.MethodPost, "/control/rewrite/delete", d.handleRewriteDelete)
|
||||
registerHTTP(http.MethodPut, "/control/rewrite/settings/update", d.handleRewriteSettingsUpdate)
|
||||
registerHTTP(http.MethodPut, "/control/rewrite/update", d.handleRewriteUpdate)
|
||||
|
||||
registerHTTP(http.MethodGet, "/control/blocked_services/services", d.handleBlockedServicesIDs)
|
||||
registerHTTP(http.MethodGet, "/control/blocked_services/all", d.handleBlockedServicesAll)
|
||||
|
||||
@ -9,6 +9,8 @@ import (
|
||||
)
|
||||
|
||||
// Item is a single DNS rewrite record.
|
||||
//
|
||||
// TODO(s.chzhen): Add "Enabled" property.
|
||||
type Item struct {
|
||||
// Domain is the domain pattern for which this rewrite should work.
|
||||
Domain string `yaml:"domain"`
|
||||
|
||||
@ -5,13 +5,26 @@ import (
|
||||
"net/http"
|
||||
"slices"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/aghalg"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/aghhttp"
|
||||
)
|
||||
|
||||
// rewriteEntryJSON is a single entry of the DNS rewrite.
|
||||
//
|
||||
// TODO(d.kolyshev): Use [rewrite.Item] instead.
|
||||
type rewriteEntryJSON struct {
|
||||
Domain string `json:"domain"`
|
||||
Answer string `json:"answer"`
|
||||
Domain string `json:"domain"`
|
||||
Answer string `json:"answer"`
|
||||
Enabled aghalg.NullBool `json:"enabled"`
|
||||
}
|
||||
|
||||
// rewriteSettings contains DNS rewrite settings.
|
||||
type rewriteSettings struct {
|
||||
// Enabled indicates whether legacy rewrites are applied.
|
||||
//
|
||||
// TODO(s.chzhen): Consider using [aghalg.NullBool] so "{}" won't
|
||||
// accidentally disable rewrites on decode.
|
||||
Enabled bool `json:"enabled"`
|
||||
}
|
||||
|
||||
// handleRewriteList is the handler for the GET /control/rewrite/list HTTP API.
|
||||
@ -24,8 +37,9 @@ func (d *DNSFilter) handleRewriteList(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
for _, ent := range d.conf.Rewrites {
|
||||
jsonEnt := rewriteEntryJSON{
|
||||
Domain: ent.Domain,
|
||||
Answer: ent.Answer,
|
||||
Domain: ent.Domain,
|
||||
Answer: ent.Answer,
|
||||
Enabled: aghalg.BoolToNullBool(ent.Enabled),
|
||||
}
|
||||
arr = append(arr, &jsonEnt)
|
||||
}
|
||||
@ -46,9 +60,15 @@ func (d *DNSFilter) handleRewriteAdd(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
enabled := true
|
||||
if rwJSON.Enabled != aghalg.NBNull {
|
||||
enabled = rwJSON.Enabled == aghalg.NBTrue
|
||||
}
|
||||
|
||||
rw := &LegacyRewrite{
|
||||
Domain: rwJSON.Domain,
|
||||
Answer: rwJSON.Answer,
|
||||
Domain: rwJSON.Domain,
|
||||
Answer: rwJSON.Answer,
|
||||
Enabled: enabled,
|
||||
}
|
||||
|
||||
err = rw.normalize(ctx, d.logger)
|
||||
@ -174,6 +194,12 @@ func (d *DNSFilter) handleRewriteUpdate(w http.ResponseWriter, r *http.Request)
|
||||
return
|
||||
}
|
||||
|
||||
if updateJSON.Update.Enabled == aghalg.NBNull {
|
||||
rwAdd.Enabled = d.conf.Rewrites[index].Enabled
|
||||
} else {
|
||||
rwAdd.Enabled = updateJSON.Update.Enabled == aghalg.NBTrue
|
||||
}
|
||||
|
||||
d.conf.Rewrites = slices.Replace(d.conf.Rewrites, index, index+1, rwAdd)
|
||||
|
||||
d.logger.DebugContext(
|
||||
@ -189,3 +215,28 @@ func (d *DNSFilter) handleRewriteUpdate(w http.ResponseWriter, r *http.Request)
|
||||
"answer", rwAdd.Answer,
|
||||
)
|
||||
}
|
||||
|
||||
// handleRewriteSettings is the handler for the GET /control/rewrite/settings
|
||||
// HTTP API.
|
||||
func (d *DNSFilter) handleRewriteSettings(w http.ResponseWriter, r *http.Request) {
|
||||
resp := &rewriteSettings{
|
||||
Enabled: protectedBool(d.confMu, &d.conf.RewritesEnabled),
|
||||
}
|
||||
|
||||
aghhttp.WriteJSONResponseOK(w, r, resp)
|
||||
}
|
||||
|
||||
// handleRewriteSettingsUpdate is the handler for the PUT
|
||||
// /control/rewrite/settings/update HTTP API.
|
||||
func (d *DNSFilter) handleRewriteSettingsUpdate(w http.ResponseWriter, r *http.Request) {
|
||||
req := &rewriteSettings{}
|
||||
err := json.NewDecoder(r.Body).Decode(req)
|
||||
if err != nil {
|
||||
aghhttp.Error(r, w, http.StatusBadRequest, "json.Decode: %s", err)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
setProtectedBool(d.confMu, &d.conf.RewritesEnabled, req.Enabled)
|
||||
d.conf.ConfModifier.Apply(r.Context())
|
||||
}
|
||||
|
||||
@ -4,12 +4,14 @@ import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/aghalg"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/aghtest"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/filtering"
|
||||
"github.com/AdguardTeam/golibs/logutil/slogutil"
|
||||
@ -20,8 +22,18 @@ import (
|
||||
|
||||
// TODO(d.kolyshev): Use [rewrite.Item] instead.
|
||||
type rewriteJSON struct {
|
||||
Domain string `json:"domain"`
|
||||
Answer string `json:"answer"`
|
||||
Domain string `json:"domain"`
|
||||
Answer string `json:"answer"`
|
||||
Enabled aghalg.NullBool `json:"enabled"`
|
||||
}
|
||||
|
||||
// newRewriteJSON returns a freshly initialized *rewriteJSON.
|
||||
func newRewriteJSON(domain, answer string, enabled aghalg.NullBool) (rw *rewriteJSON) {
|
||||
return &rewriteJSON{
|
||||
Domain: domain,
|
||||
Answer: answer,
|
||||
Enabled: enabled,
|
||||
}
|
||||
}
|
||||
|
||||
type rewriteUpdateJSON struct {
|
||||
@ -38,16 +50,33 @@ const (
|
||||
deleteURL = "/control/rewrite/delete"
|
||||
updateURL = "/control/rewrite/update"
|
||||
|
||||
decodeErrorMsg = "json.Decode: json: cannot unmarshal string into Go value of type" +
|
||||
" filtering.rewriteEntryJSON\n"
|
||||
decodeMsg = "json.Decode: json: cannot unmarshal string into Go value of type"
|
||||
decodeErrorMsg = decodeMsg + " filtering.rewriteEntryJSON\n"
|
||||
decodeUpdateErrorMsg = decodeMsg + " filtering.rewriteUpdateJSON\n"
|
||||
)
|
||||
|
||||
func TestDNSFilter_handleRewriteHTTP(t *testing.T) {
|
||||
confModCh := make(chan struct{})
|
||||
reqCh := make(chan struct{})
|
||||
func TestDNSFilter_HandleRewriteHTTP(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
const (
|
||||
exampleDomain = "example.local"
|
||||
exampleAnswer = "example.rewrite"
|
||||
oneDomain = "one.local"
|
||||
oneAnswer = "one.rewrite"
|
||||
disabledDomain = "disabled.local"
|
||||
disabledAnswer = "disabled.rewrite"
|
||||
addDomain = "add.local"
|
||||
addAnswer = "add.rewrite"
|
||||
updDomain = "upd.local"
|
||||
updAnswer = "upd.rewrite"
|
||||
invDomain = "inv.local"
|
||||
invAnswer = "inv.rewrite"
|
||||
)
|
||||
|
||||
testRewrites := []*rewriteJSON{
|
||||
{Domain: "example.local", Answer: "example.rewrite"},
|
||||
{Domain: "one.local", Answer: "one.rewrite"},
|
||||
newRewriteJSON(exampleDomain, exampleAnswer, aghalg.NBTrue),
|
||||
newRewriteJSON(oneDomain, oneAnswer, aghalg.NBTrue),
|
||||
newRewriteJSON(disabledDomain, disabledAnswer, aghalg.NBFalse),
|
||||
}
|
||||
|
||||
testRewritesJSON, mErr := json.Marshal(testRewrites)
|
||||
@ -72,16 +101,48 @@ func TestDNSFilter_handleRewriteHTTP(t *testing.T) {
|
||||
wantBody: string(testRewritesJSON) + "\n",
|
||||
wantList: testRewrites,
|
||||
}, {
|
||||
name: "add",
|
||||
name: "add_enabled_null",
|
||||
url: addURL,
|
||||
method: http.MethodPost,
|
||||
reqData: rewriteJSON{Domain: "add.local", Answer: "add.rewrite"},
|
||||
reqData: rewriteJSON{Domain: addDomain, Answer: addAnswer},
|
||||
wantConfMod: true,
|
||||
wantStatus: http.StatusOK,
|
||||
wantBody: "",
|
||||
wantList: append(
|
||||
testRewrites,
|
||||
&rewriteJSON{Domain: "add.local", Answer: "add.rewrite"},
|
||||
newRewriteJSON(addDomain, addAnswer, aghalg.NBTrue),
|
||||
),
|
||||
}, {
|
||||
name: "add_enabled_false",
|
||||
url: addURL,
|
||||
method: http.MethodPost,
|
||||
reqData: rewriteJSON{
|
||||
Domain: addDomain,
|
||||
Answer: addAnswer,
|
||||
Enabled: aghalg.NBFalse,
|
||||
},
|
||||
wantConfMod: true,
|
||||
wantStatus: http.StatusOK,
|
||||
wantBody: "",
|
||||
wantList: append(
|
||||
testRewrites,
|
||||
newRewriteJSON(addDomain, addAnswer, aghalg.NBFalse),
|
||||
),
|
||||
}, {
|
||||
name: "add_enabled_true",
|
||||
url: addURL,
|
||||
method: http.MethodPost,
|
||||
reqData: rewriteJSON{
|
||||
Domain: addDomain,
|
||||
Answer: addAnswer,
|
||||
Enabled: aghalg.NBTrue,
|
||||
},
|
||||
wantConfMod: true,
|
||||
wantStatus: http.StatusOK,
|
||||
wantBody: "",
|
||||
wantList: append(
|
||||
testRewrites,
|
||||
newRewriteJSON(addDomain, addAnswer, aghalg.NBTrue),
|
||||
),
|
||||
}, {
|
||||
name: "add_error",
|
||||
@ -96,11 +157,14 @@ func TestDNSFilter_handleRewriteHTTP(t *testing.T) {
|
||||
name: "delete",
|
||||
url: deleteURL,
|
||||
method: http.MethodPost,
|
||||
reqData: rewriteJSON{Domain: "one.local", Answer: "one.rewrite"},
|
||||
reqData: rewriteJSON{Domain: oneDomain, Answer: oneAnswer},
|
||||
wantConfMod: true,
|
||||
wantStatus: http.StatusOK,
|
||||
wantBody: "",
|
||||
wantList: []*rewriteJSON{{Domain: "example.local", Answer: "example.rewrite"}},
|
||||
wantList: []*rewriteJSON{
|
||||
newRewriteJSON(exampleDomain, exampleAnswer, aghalg.NBTrue),
|
||||
newRewriteJSON(disabledDomain, disabledAnswer, aghalg.NBFalse),
|
||||
},
|
||||
}, {
|
||||
name: "delete_error",
|
||||
url: deleteURL,
|
||||
@ -111,19 +175,56 @@ func TestDNSFilter_handleRewriteHTTP(t *testing.T) {
|
||||
wantBody: decodeErrorMsg,
|
||||
wantList: testRewrites,
|
||||
}, {
|
||||
name: "update",
|
||||
name: "update_enabled_null",
|
||||
url: updateURL,
|
||||
method: http.MethodPut,
|
||||
reqData: rewriteUpdateJSON{
|
||||
Target: rewriteJSON{Domain: "one.local", Answer: "one.rewrite"},
|
||||
Update: rewriteJSON{Domain: "upd.local", Answer: "upd.rewrite"},
|
||||
Target: rewriteJSON{Domain: oneDomain, Answer: oneAnswer},
|
||||
Update: rewriteJSON{Domain: updDomain, Answer: updAnswer},
|
||||
},
|
||||
wantConfMod: true,
|
||||
wantStatus: http.StatusOK,
|
||||
wantBody: "",
|
||||
wantList: []*rewriteJSON{
|
||||
{Domain: "example.local", Answer: "example.rewrite"},
|
||||
{Domain: "upd.local", Answer: "upd.rewrite"},
|
||||
newRewriteJSON(exampleDomain, exampleAnswer, aghalg.NBTrue),
|
||||
newRewriteJSON(updDomain, updAnswer, aghalg.NBTrue),
|
||||
newRewriteJSON(disabledDomain, disabledAnswer, aghalg.NBFalse),
|
||||
},
|
||||
}, {
|
||||
name: "update_enabled_false",
|
||||
url: updateURL,
|
||||
method: http.MethodPut,
|
||||
reqData: rewriteUpdateJSON{
|
||||
Target: rewriteJSON{Domain: oneDomain, Answer: oneAnswer},
|
||||
Update: rewriteJSON{
|
||||
Domain: updDomain,
|
||||
Answer: updAnswer,
|
||||
Enabled: aghalg.NBFalse,
|
||||
},
|
||||
},
|
||||
wantConfMod: true,
|
||||
wantStatus: http.StatusOK,
|
||||
wantBody: "",
|
||||
wantList: []*rewriteJSON{
|
||||
newRewriteJSON(exampleDomain, exampleAnswer, aghalg.NBTrue),
|
||||
newRewriteJSON(updDomain, updAnswer, aghalg.NBFalse),
|
||||
newRewriteJSON(disabledDomain, disabledAnswer, aghalg.NBFalse),
|
||||
},
|
||||
}, {
|
||||
name: "update_enabled_true",
|
||||
url: updateURL,
|
||||
method: http.MethodPut,
|
||||
reqData: rewriteUpdateJSON{
|
||||
Target: rewriteJSON{Domain: oneDomain, Answer: oneAnswer},
|
||||
Update: rewriteJSON{Domain: updDomain, Answer: updAnswer, Enabled: aghalg.NBTrue},
|
||||
},
|
||||
wantConfMod: true,
|
||||
wantStatus: http.StatusOK,
|
||||
wantBody: "",
|
||||
wantList: []*rewriteJSON{
|
||||
newRewriteJSON(exampleDomain, exampleAnswer, aghalg.NBTrue),
|
||||
newRewriteJSON(updDomain, updAnswer, aghalg.NBTrue),
|
||||
newRewriteJSON(disabledDomain, disabledAnswer, aghalg.NBFalse),
|
||||
},
|
||||
}, {
|
||||
name: "update_error",
|
||||
@ -132,16 +233,15 @@ func TestDNSFilter_handleRewriteHTTP(t *testing.T) {
|
||||
reqData: "invalid_json",
|
||||
wantConfMod: false,
|
||||
wantStatus: http.StatusBadRequest,
|
||||
wantBody: "json.Decode: json: cannot unmarshal string into Go value of type" +
|
||||
" filtering.rewriteUpdateJSON\n",
|
||||
wantList: testRewrites,
|
||||
wantBody: decodeUpdateErrorMsg,
|
||||
wantList: testRewrites,
|
||||
}, {
|
||||
name: "update_error_target",
|
||||
url: updateURL,
|
||||
method: http.MethodPut,
|
||||
reqData: rewriteUpdateJSON{
|
||||
Target: rewriteJSON{Domain: "inv.local", Answer: "inv.rewrite"},
|
||||
Update: rewriteJSON{Domain: "upd.local", Answer: "upd.rewrite"},
|
||||
Target: rewriteJSON{Domain: invDomain, Answer: invAnswer},
|
||||
Update: rewriteJSON{Domain: updDomain, Answer: updAnswer},
|
||||
},
|
||||
wantConfMod: false,
|
||||
wantStatus: http.StatusBadRequest,
|
||||
@ -151,6 +251,11 @@ func TestDNSFilter_handleRewriteHTTP(t *testing.T) {
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
confModCh := make(chan struct{})
|
||||
reqCh := make(chan struct{})
|
||||
|
||||
handlers := make(map[string]http.Handler)
|
||||
confModifier := &aghtest.ConfigModifier{}
|
||||
confModifier.OnApply = func(_ context.Context) {
|
||||
@ -229,10 +334,74 @@ func assertRewritesList(tb testing.TB, handler http.Handler, wantList []*rewrite
|
||||
func rewriteEntriesToLegacyRewrites(entries []*rewriteJSON) (rw []*filtering.LegacyRewrite) {
|
||||
for _, entry := range entries {
|
||||
rw = append(rw, &filtering.LegacyRewrite{
|
||||
Domain: entry.Domain,
|
||||
Answer: entry.Answer,
|
||||
Domain: entry.Domain,
|
||||
Answer: entry.Answer,
|
||||
Enabled: entry.Enabled == aghalg.NBTrue,
|
||||
})
|
||||
}
|
||||
|
||||
return rw
|
||||
}
|
||||
|
||||
func TestDNSFilter_HandleRewriteSettings(t *testing.T) {
|
||||
const (
|
||||
enabled = "enabled"
|
||||
|
||||
path = "/control/rewrite/settings"
|
||||
pathUpdate = path + "/update"
|
||||
)
|
||||
|
||||
var (
|
||||
wantEnabled = fmt.Sprintf("{%q:%s}", enabled, "true")
|
||||
wantDisabled = fmt.Sprintf("{%q:%s}", enabled, "false")
|
||||
)
|
||||
|
||||
confUpdated := false
|
||||
confModifier := &aghtest.ConfigModifier{
|
||||
OnApply: func(_ context.Context) {
|
||||
confUpdated = true
|
||||
},
|
||||
}
|
||||
handlers := make(map[string]http.Handler)
|
||||
|
||||
d, err := filtering.New(&filtering.Config{
|
||||
Logger: slogutil.NewDiscardLogger(),
|
||||
ConfModifier: confModifier,
|
||||
HTTPRegister: func(_, url string, handler http.HandlerFunc) {
|
||||
handlers[url] = handler
|
||||
},
|
||||
RewritesEnabled: false,
|
||||
}, nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
t.Cleanup(d.Close)
|
||||
|
||||
require.True(t, t.Run("register", func(t *testing.T) {
|
||||
d.RegisterFilteringHandlers()
|
||||
require.NotEmpty(t, handlers)
|
||||
require.Contains(t, handlers, path)
|
||||
require.Contains(t, handlers, pathUpdate)
|
||||
|
||||
r := httptest.NewRequest(http.MethodGet, path, nil)
|
||||
w := httptest.NewRecorder()
|
||||
handlers[path].ServeHTTP(w, r)
|
||||
require.Equal(t, http.StatusOK, w.Code)
|
||||
|
||||
assert.JSONEq(t, wantDisabled, w.Body.String())
|
||||
}))
|
||||
|
||||
require.True(t, t.Run("update", func(t *testing.T) {
|
||||
r := httptest.NewRequest(http.MethodPut, path, bytes.NewReader([]byte(wantEnabled)))
|
||||
w := httptest.NewRecorder()
|
||||
handlers[pathUpdate].ServeHTTP(w, r)
|
||||
require.Equal(t, http.StatusOK, w.Code)
|
||||
|
||||
r = httptest.NewRequest(http.MethodGet, path, nil)
|
||||
w = httptest.NewRecorder()
|
||||
handlers[path].ServeHTTP(w, r)
|
||||
require.Equal(t, http.StatusOK, w.Code)
|
||||
|
||||
assert.True(t, confUpdated)
|
||||
assert.JSONEq(t, wantEnabled, w.Body.String())
|
||||
}))
|
||||
}
|
||||
|
||||
@ -32,6 +32,9 @@ type LegacyRewrite struct {
|
||||
|
||||
// Type is the DNS record type: A, AAAA, or CNAME.
|
||||
Type uint16 `yaml:"-"`
|
||||
|
||||
// Enabled indicates whether this rewrite is active.
|
||||
Enabled bool `yaml:"enabled"`
|
||||
}
|
||||
|
||||
// equal returns true if the rw is equal to the other.
|
||||
@ -162,6 +165,10 @@ func findRewrites(
|
||||
qtype uint16,
|
||||
) (rewrites []*LegacyRewrite, matched bool) {
|
||||
for _, e := range entries {
|
||||
if !e.Enabled {
|
||||
continue
|
||||
}
|
||||
|
||||
if e.Domain != host && !matchDomainWildcard(host, e.Domain) {
|
||||
continue
|
||||
}
|
||||
@ -176,6 +183,11 @@ func findRewrites(
|
||||
return nil, matched
|
||||
}
|
||||
|
||||
return finalizeRewrites(rewrites), matched
|
||||
}
|
||||
|
||||
// finalizeRewrites sorts rewrites and truncates wildcard ones.
|
||||
func finalizeRewrites(rewrites []*LegacyRewrite) (resRewrites []*LegacyRewrite) {
|
||||
slices.SortFunc(rewrites, (*LegacyRewrite).Compare)
|
||||
|
||||
for i, r := range rewrites {
|
||||
@ -188,7 +200,7 @@ func findRewrites(
|
||||
}
|
||||
}
|
||||
|
||||
return rewrites, matched
|
||||
return rewrites
|
||||
}
|
||||
|
||||
// setRewriteResult sets the Reason or IPList of res if necessary. res must not
|
||||
|
||||
@ -29,64 +29,86 @@ func TestRewrites(t *testing.T) {
|
||||
|
||||
d.conf.Rewrites = []*LegacyRewrite{{
|
||||
// This one and below are about CNAME, A and AAAA.
|
||||
Domain: "somecname",
|
||||
Answer: "somehost.com",
|
||||
Domain: "somecname",
|
||||
Answer: "somehost.com",
|
||||
Enabled: true,
|
||||
}, {
|
||||
Domain: "somehost.com",
|
||||
Answer: netip.IPv4Unspecified().String(),
|
||||
Domain: "somehost.com",
|
||||
Answer: netip.IPv4Unspecified().String(),
|
||||
Enabled: true,
|
||||
}, {
|
||||
Domain: "host.com",
|
||||
Answer: addr1v4.String(),
|
||||
Domain: "host.com",
|
||||
Answer: addr1v4.String(),
|
||||
Enabled: true,
|
||||
}, {
|
||||
Domain: "host.com",
|
||||
Answer: addr2v4.String(),
|
||||
Domain: "host.com",
|
||||
Answer: addr2v4.String(),
|
||||
Enabled: true,
|
||||
}, {
|
||||
Domain: "host.com",
|
||||
Answer: addr1v6.String(),
|
||||
Domain: "host.com",
|
||||
Answer: addr1v6.String(),
|
||||
Enabled: true,
|
||||
}, {
|
||||
Domain: "www.host.com",
|
||||
Answer: "host.com",
|
||||
Domain: "www.host.com",
|
||||
Answer: "host.com",
|
||||
Enabled: true,
|
||||
}, {
|
||||
// This one is a wildcard.
|
||||
Domain: "*.host.com",
|
||||
Answer: addr2v4.String(),
|
||||
Domain: "*.host.com",
|
||||
Answer: addr2v4.String(),
|
||||
Enabled: true,
|
||||
}, {
|
||||
// This one and below are about wildcard overriding.
|
||||
Domain: "a.host.com",
|
||||
Answer: addr1v4.String(),
|
||||
Domain: "a.host.com",
|
||||
Answer: addr1v4.String(),
|
||||
Enabled: true,
|
||||
}, {
|
||||
// This one is about CNAME and wildcard interacting.
|
||||
Domain: "*.host2.com",
|
||||
Answer: "host.com",
|
||||
Domain: "*.host2.com",
|
||||
Answer: "host.com",
|
||||
Enabled: true,
|
||||
}, {
|
||||
// This one and below are about 2 level CNAME.
|
||||
Domain: "b.host.com",
|
||||
Answer: "somecname",
|
||||
Domain: "b.host.com",
|
||||
Answer: "somecname",
|
||||
Enabled: true,
|
||||
}, {
|
||||
// This one and below are about 2 level CNAME and wildcard.
|
||||
Domain: "b.host3.com",
|
||||
Answer: "a.host3.com",
|
||||
Domain: "b.host3.com",
|
||||
Answer: "a.host3.com",
|
||||
Enabled: true,
|
||||
}, {
|
||||
Domain: "a.host3.com",
|
||||
Answer: "x.host.com",
|
||||
Domain: "a.host3.com",
|
||||
Answer: "x.host.com",
|
||||
Enabled: true,
|
||||
}, {
|
||||
Domain: "*.hostboth.com",
|
||||
Answer: addr3v4.String(),
|
||||
Domain: "*.hostboth.com",
|
||||
Answer: addr3v4.String(),
|
||||
Enabled: true,
|
||||
}, {
|
||||
Domain: "*.hostboth.com",
|
||||
Answer: addr2v6.String(),
|
||||
Domain: "*.hostboth.com",
|
||||
Answer: addr2v6.String(),
|
||||
Enabled: true,
|
||||
}, {
|
||||
Domain: "BIGHOST.COM",
|
||||
Answer: addr4v4.String(),
|
||||
Domain: "BIGHOST.COM",
|
||||
Answer: addr4v4.String(),
|
||||
Enabled: true,
|
||||
}, {
|
||||
Domain: "*.issue4016.com",
|
||||
Answer: "sub.issue4016.com",
|
||||
Domain: "*.issue4016.com",
|
||||
Answer: "sub.issue4016.com",
|
||||
Enabled: true,
|
||||
}, {
|
||||
Domain: "*.sub.issue6226.com",
|
||||
Answer: addr2v4.String(),
|
||||
Domain: "*.sub.issue6226.com",
|
||||
Answer: addr2v4.String(),
|
||||
Enabled: true,
|
||||
}, {
|
||||
Domain: "*.issue6226.com",
|
||||
Answer: addr1v4.String(),
|
||||
Domain: "*.issue6226.com",
|
||||
Answer: addr1v4.String(),
|
||||
Enabled: true,
|
||||
}, {
|
||||
Domain: "disabled.rewrite.test",
|
||||
Answer: addr1v4.String(),
|
||||
Enabled: false,
|
||||
}}
|
||||
|
||||
ctx := testutil.ContextWithTimeout(t, testTimeout)
|
||||
@ -204,6 +226,13 @@ func TestRewrites(t *testing.T) {
|
||||
wantIPs: []netip.Addr{addr2v4},
|
||||
wantReason: Rewritten,
|
||||
dtyp: dns.TypeA,
|
||||
}, {
|
||||
name: "not_filtered_disabled_rewrite",
|
||||
host: "disabled.rewrite.test",
|
||||
wantCName: "",
|
||||
wantIPs: nil,
|
||||
wantReason: NotFilteredNotFound,
|
||||
dtyp: dns.TypeA,
|
||||
}}
|
||||
|
||||
for _, tc := range testCases {
|
||||
@ -225,17 +254,20 @@ func TestRewritesLevels(t *testing.T) {
|
||||
t.Cleanup(d.Close)
|
||||
// Exact host, wildcard L2, wildcard L3.
|
||||
d.conf.Rewrites = []*LegacyRewrite{{
|
||||
Domain: "host.com",
|
||||
Answer: "1.1.1.1",
|
||||
Type: dns.TypeA,
|
||||
Domain: "host.com",
|
||||
Answer: "1.1.1.1",
|
||||
Type: dns.TypeA,
|
||||
Enabled: true,
|
||||
}, {
|
||||
Domain: "*.host.com",
|
||||
Answer: "2.2.2.2",
|
||||
Type: dns.TypeA,
|
||||
Domain: "*.host.com",
|
||||
Answer: "2.2.2.2",
|
||||
Type: dns.TypeA,
|
||||
Enabled: true,
|
||||
}, {
|
||||
Domain: "*.sub.host.com",
|
||||
Answer: "3.3.3.3",
|
||||
Type: dns.TypeA,
|
||||
Domain: "*.sub.host.com",
|
||||
Answer: "3.3.3.3",
|
||||
Type: dns.TypeA,
|
||||
Enabled: true,
|
||||
}}
|
||||
|
||||
ctx := testutil.ContextWithTimeout(t, testTimeout)
|
||||
@ -273,14 +305,17 @@ func TestRewritesExceptionCNAME(t *testing.T) {
|
||||
t.Cleanup(d.Close)
|
||||
// Wildcard and exception for a sub-domain.
|
||||
d.conf.Rewrites = []*LegacyRewrite{{
|
||||
Domain: "*.host.com",
|
||||
Answer: "2.2.2.2",
|
||||
Domain: "*.host.com",
|
||||
Answer: "2.2.2.2",
|
||||
Enabled: true,
|
||||
}, {
|
||||
Domain: "sub.host.com",
|
||||
Answer: "sub.host.com",
|
||||
Domain: "sub.host.com",
|
||||
Answer: "sub.host.com",
|
||||
Enabled: true,
|
||||
}, {
|
||||
Domain: "*.sub.host.com",
|
||||
Answer: "*.sub.host.com",
|
||||
Domain: "*.sub.host.com",
|
||||
Answer: "*.sub.host.com",
|
||||
Enabled: true,
|
||||
}}
|
||||
|
||||
ctx := testutil.ContextWithTimeout(t, testTimeout)
|
||||
@ -325,25 +360,30 @@ func TestRewritesExceptionIP(t *testing.T) {
|
||||
t.Cleanup(d.Close)
|
||||
// Exception for AAAA record.
|
||||
d.conf.Rewrites = []*LegacyRewrite{{
|
||||
Domain: "host.com",
|
||||
Answer: "1.2.3.4",
|
||||
Type: dns.TypeA,
|
||||
Domain: "host.com",
|
||||
Answer: "1.2.3.4",
|
||||
Type: dns.TypeA,
|
||||
Enabled: true,
|
||||
}, {
|
||||
Domain: "host.com",
|
||||
Answer: "AAAA",
|
||||
Type: dns.TypeAAAA,
|
||||
Domain: "host.com",
|
||||
Answer: "AAAA",
|
||||
Type: dns.TypeAAAA,
|
||||
Enabled: true,
|
||||
}, {
|
||||
Domain: "host2.com",
|
||||
Answer: "::1",
|
||||
Type: dns.TypeAAAA,
|
||||
Domain: "host2.com",
|
||||
Answer: "::1",
|
||||
Type: dns.TypeAAAA,
|
||||
Enabled: true,
|
||||
}, {
|
||||
Domain: "host2.com",
|
||||
Answer: "A",
|
||||
Type: dns.TypeA,
|
||||
Domain: "host2.com",
|
||||
Answer: "A",
|
||||
Type: dns.TypeA,
|
||||
Enabled: true,
|
||||
}, {
|
||||
Domain: "host3.com",
|
||||
Answer: "A",
|
||||
Type: dns.TypeA,
|
||||
Domain: "host3.com",
|
||||
Answer: "A",
|
||||
Type: dns.TypeA,
|
||||
Enabled: true,
|
||||
}}
|
||||
|
||||
ctx := testutil.ContextWithTimeout(t, testTimeout)
|
||||
|
||||
@ -530,6 +530,8 @@ var config = &configuration{
|
||||
FilteringEnabled: true,
|
||||
FiltersUpdateIntervalHours: 24,
|
||||
|
||||
RewritesEnabled: true,
|
||||
|
||||
ParentalEnabled: false,
|
||||
SafeBrowsingEnabled: false,
|
||||
|
||||
|
||||
@ -2,7 +2,23 @@
|
||||
|
||||
<!-- TODO(a.garipov): Reformat in accordance with the KeepAChangelog spec. -->
|
||||
|
||||
## v0.107.67: API changes
|
||||
## v0.107.68: API changes
|
||||
|
||||
### New HTTP APIs 'GET /control/rewrite/settings' and 'PUT /control/rewrite/settings/update'
|
||||
|
||||
- New HTTP APIs to manage global DNS rewrites.
|
||||
|
||||
```json
|
||||
{
|
||||
"enabled": true
|
||||
}
|
||||
```
|
||||
|
||||
### New `"enabled"` field in 'POST /control/rewrite/add' and 'PUT /control/rewrite/update'
|
||||
|
||||
- New optional field `"enabled"` indicates whether the rewrite is active.
|
||||
|
||||
### The blocked services groups
|
||||
|
||||
- The new field `"groups"` in `GET /control/blocked_services/all` is a list of service group. Groups make it possible to block multiple services with equal `"group_id"` at once.
|
||||
|
||||
|
||||
@ -16,6 +16,8 @@
|
||||
- 'basicAuth': []
|
||||
|
||||
'tags':
|
||||
- 'name': 'blocked_services'
|
||||
'description': 'Blocked services controls'
|
||||
- 'name': 'clients'
|
||||
'description': 'Clients list operations'
|
||||
- 'name': 'dhcp'
|
||||
@ -34,6 +36,8 @@
|
||||
'description': 'Apple .mobileconfig'
|
||||
- 'name': 'parental'
|
||||
'description': 'Blocking adult and explicit materials'
|
||||
- 'name': 'rewrite'
|
||||
'description': 'DNS rewrites'
|
||||
- 'name': 'safebrowsing'
|
||||
'description': 'Blocking malware/phishing sites'
|
||||
- 'name': 'safesearch'
|
||||
@ -1153,6 +1157,30 @@
|
||||
'responses':
|
||||
'200':
|
||||
'description': 'OK.'
|
||||
'/rewrite/settings':
|
||||
'get':
|
||||
'tags':
|
||||
- 'rewrite'
|
||||
'operationId': 'rewriteSettingsGet'
|
||||
'summary': 'Get rewrite settings'
|
||||
'responses':
|
||||
'200':
|
||||
'description': 'OK.'
|
||||
'content':
|
||||
'application/json':
|
||||
'schema':
|
||||
'$ref': '#/components/schemas/RewriteSettings'
|
||||
'/rewrite/settings/update':
|
||||
'put':
|
||||
'tags':
|
||||
- 'rewrite'
|
||||
'operationId': 'rewriteSettingsUpdate'
|
||||
'summary': 'Update rewrite settings'
|
||||
'requestBody':
|
||||
'$ref': '#/components/requestBodies/RewriteSettings'
|
||||
'responses':
|
||||
'200':
|
||||
'description': 'OK.'
|
||||
'/rewrite/update':
|
||||
'put':
|
||||
'tags':
|
||||
@ -1414,6 +1442,12 @@
|
||||
'schema':
|
||||
'$ref': '#/components/schemas/RewriteEntry'
|
||||
'required': true
|
||||
'RewriteSettings':
|
||||
'content':
|
||||
'application/json':
|
||||
'schema':
|
||||
'$ref': '#/components/schemas/RewriteSettings'
|
||||
'required': true
|
||||
'RewriteUpdate':
|
||||
'content':
|
||||
'application/json':
|
||||
@ -3002,6 +3036,23 @@
|
||||
'type': 'string'
|
||||
'description': 'value of A, AAAA or CNAME DNS record'
|
||||
'example': '127.0.0.1'
|
||||
'enabled':
|
||||
'type': 'boolean'
|
||||
'description': >
|
||||
Optional. If omitted on add, defaults to `true`. On update, omitted
|
||||
preserves previous value.
|
||||
'example': true
|
||||
'default': true
|
||||
'RewriteSettings':
|
||||
'type': 'object'
|
||||
'description': 'DNS rewrite settings'
|
||||
'required':
|
||||
- 'enabled'
|
||||
'properties':
|
||||
'enabled':
|
||||
'type': 'boolean'
|
||||
'description': 'indicates whether rewrites are applied'
|
||||
'example': true
|
||||
'BlockedServicesArray':
|
||||
'type': 'array'
|
||||
'items':
|
||||
|
||||
Loading…
Reference in New Issue
Block a user