all: sync with master; upd chlog

This commit is contained in:
Ainar Garipov
2023-02-15 16:53:29 +03:00
parent 80eb339896
commit 66b831072c
55 changed files with 24817 additions and 798 deletions

View File

@@ -67,15 +67,18 @@ func (c *Client) closeUpstreams() (err error) {
type clientSource uint
// Client sources. The order determines the priority.
// Clients information sources. The order determines the priority.
const (
ClientSourceWHOIS clientSource = iota
ClientSourceNone clientSource = iota
ClientSourceWHOIS
ClientSourceARP
ClientSourceRDNS
ClientSourceDHCP
ClientSourceHostsFile
ClientSourcePersistent
)
// type check
var _ fmt.Stringer = clientSource(0)
// String returns a human-readable name of cs.
@@ -96,6 +99,7 @@ func (cs clientSource) String() (s string) {
}
}
// type check
var _ encoding.TextMarshaler = clientSource(0)
// MarshalText implements encoding.TextMarshaler for the clientSource.
@@ -332,23 +336,24 @@ func (clients *clientsContainer) onDHCPLeaseChanged(flags int) {
}
}
// exists checks if client with this IP address already exists.
func (clients *clientsContainer) exists(ip netip.Addr, source clientSource) (ok bool) {
// clientSource checks if client with this IP address already exists and returns
// the source which updated it last. It returns [ClientSourceNone] if the
// client doesn't exist.
func (clients *clientsContainer) clientSource(ip netip.Addr) (src clientSource) {
clients.lock.Lock()
defer clients.lock.Unlock()
_, ok = clients.findLocked(ip.String())
_, ok := clients.findLocked(ip.String())
if ok {
return true
return ClientSourcePersistent
}
rc, ok := clients.ipToRC[ip]
if !ok {
return false
return ClientSourceNone
}
// Return false if the new source has higher priority.
return source <= rc.Source
return rc.Source
}
func toQueryLogWHOIS(wi *RuntimeClientWHOISInfo) (cw *querylog.ClientWHOIS) {

View File

@@ -67,9 +67,9 @@ func TestClients(t *testing.T) {
assert.Equal(t, "client2", c.Name)
assert.False(t, clients.exists(cliNoneIP, ClientSourceHostsFile))
assert.True(t, clients.exists(cli1IP, ClientSourceHostsFile))
assert.True(t, clients.exists(cli2IP, ClientSourceHostsFile))
assert.Equal(t, clients.clientSource(cliNoneIP), ClientSourceNone)
assert.Equal(t, clients.clientSource(cli1IP), ClientSourcePersistent)
assert.Equal(t, clients.clientSource(cli2IP), ClientSourcePersistent)
})
t.Run("add_fail_name", func(t *testing.T) {
@@ -127,8 +127,8 @@ func TestClients(t *testing.T) {
})
require.NoError(t, err)
assert.False(t, clients.exists(cliOldIP, ClientSourceHostsFile))
assert.True(t, clients.exists(cliNewIP, ClientSourceHostsFile))
assert.Equal(t, clients.clientSource(cliOldIP), ClientSourceNone)
assert.Equal(t, clients.clientSource(cliNewIP), ClientSourcePersistent)
err = clients.Update("client1", &Client{
IDs: []string{cliNew},
@@ -157,7 +157,7 @@ func TestClients(t *testing.T) {
ok := clients.Del("client1-renamed")
require.True(t, ok)
assert.False(t, clients.exists(netip.MustParseAddr("1.1.1.2"), ClientSourceHostsFile))
assert.Equal(t, clients.clientSource(netip.MustParseAddr("1.1.1.2")), ClientSourceNone)
})
t.Run("del_fail", func(t *testing.T) {
@@ -176,18 +176,18 @@ func TestClients(t *testing.T) {
ok = clients.AddHost(ip, "host3", ClientSourceHostsFile)
assert.True(t, ok)
assert.True(t, clients.exists(ip, ClientSourceHostsFile))
assert.Equal(t, clients.clientSource(ip), ClientSourceHostsFile)
})
t.Run("dhcp_replaces_arp", func(t *testing.T) {
ip := netip.MustParseAddr("1.2.3.4")
ok := clients.AddHost(ip, "from_arp", ClientSourceARP)
assert.True(t, ok)
assert.True(t, clients.exists(ip, ClientSourceARP))
assert.Equal(t, clients.clientSource(ip), ClientSourceARP)
ok = clients.AddHost(ip, "from_dhcp", ClientSourceDHCP)
assert.True(t, ok)
assert.True(t, clients.exists(ip, ClientSourceDHCP))
assert.Equal(t, clients.clientSource(ip), ClientSourceDHCP)
})
t.Run("addhost_fail", func(t *testing.T) {

View File

@@ -6,6 +6,7 @@ import (
"net/netip"
"os"
"path/filepath"
"sort"
"sync"
"github.com/AdguardTeam/AdGuardHome/internal/aghalg"
@@ -112,8 +113,10 @@ type configuration struct {
// An active session is automatically refreshed once a day.
WebSessionTTLHours uint32 `yaml:"web_session_ttl"`
DNS dnsConfig `yaml:"dns"`
TLS tlsConfigSettings `yaml:"tls"`
DNS dnsConfig `yaml:"dns"`
TLS tlsConfigSettings `yaml:"tls"`
QueryLog queryLogConfig `yaml:"querylog"`
Stats statsConfig `yaml:"statistics"`
// Filters reflects the filters from [filtering.Config]. It's cloned to the
// config used in the filtering module at the startup. Afterwards it's
@@ -147,20 +150,6 @@ type dnsConfig struct {
BindHosts []netip.Addr `yaml:"bind_hosts"`
Port int `yaml:"port"`
// StatsInterval is the time interval for flushing statistics to the disk in
// days.
StatsInterval uint32 `yaml:"statistics_interval"`
// QueryLogEnabled defines if the query log is enabled.
QueryLogEnabled bool `yaml:"querylog_enabled"`
// QueryLogFileEnabled defines, if the query log is written to the file.
QueryLogFileEnabled bool `yaml:"querylog_file_enabled"`
// QueryLogInterval is the interval for query log's files rotation.
QueryLogInterval timeutil.Duration `yaml:"querylog_interval"`
// QueryLogMemSize is the number of entries kept in memory before they are
// flushed to disk.
QueryLogMemSize uint32 `yaml:"querylog_size_memory"`
// AnonymizeClientIP defines if clients' IP addresses should be anonymized
// in query log and statistics.
AnonymizeClientIP bool `yaml:"anonymize_client_ip"`
@@ -188,7 +177,7 @@ type dnsConfig struct {
UseDNS64 bool `yaml:"use_dns64"`
// DNS64Prefixes is the list of NAT64 prefixes to be used for DNS64.
DNS64Prefixes []string `yaml:"dns64_prefixes"`
DNS64Prefixes []netip.Prefix `yaml:"dns64_prefixes"`
// ServeHTTP3 defines if HTTP/3 is be allowed for incoming requests.
//
@@ -228,6 +217,37 @@ type tlsConfigSettings struct {
dnsforward.TLSConfig `yaml:",inline" json:",inline"`
}
type queryLogConfig struct {
// Enabled defines if the query log is enabled.
Enabled bool `yaml:"enabled"`
// FileEnabled defines, if the query log is written to the file.
FileEnabled bool `yaml:"file_enabled"`
// Interval is the interval for query log's files rotation.
Interval timeutil.Duration `yaml:"interval"`
// MemSize is the number of entries kept in memory before they are
// flushed to disk.
MemSize uint32 `yaml:"size_memory"`
// Ignored is the list of host names, which should not be written to
// log.
Ignored []string `yaml:"ignored"`
}
type statsConfig struct {
// Enabled defines if the statistics are enabled.
Enabled bool `yaml:"enabled"`
// Interval is the time interval for flushing statistics to the disk in
// days.
Interval uint32 `yaml:"interval"`
// Ignored is the list of host names, which should not be counted.
Ignored []string `yaml:"ignored"`
}
// config is the global configuration structure.
//
// TODO(a.garipov, e.burkov): This global is awful and must be removed.
@@ -238,13 +258,8 @@ var config = &configuration{
AuthBlockMin: 15,
WebSessionTTLHours: 30 * 24,
DNS: dnsConfig{
BindHosts: []netip.Addr{netip.IPv4Unspecified()},
Port: defaultPortDNS,
StatsInterval: 1,
QueryLogEnabled: true,
QueryLogFileEnabled: true,
QueryLogInterval: timeutil.Duration{Duration: 90 * timeutil.Day},
QueryLogMemSize: 1000,
BindHosts: []netip.Addr{netip.IPv4Unspecified()},
Port: defaultPortDNS,
FilteringConfig: dnsforward.FilteringConfig{
ProtectionEnabled: true, // whether or not use any of filtering features
BlockingMode: dnsforward.BlockingModeDefault,
@@ -282,6 +297,18 @@ var config = &configuration{
PortDNSOverTLS: defaultPortTLS, // needs to be passed through to dnsproxy
PortDNSOverQUIC: defaultPortQUIC,
},
QueryLog: queryLogConfig{
Enabled: true,
FileEnabled: true,
Interval: timeutil.Duration{Duration: 90 * timeutil.Day},
MemSize: 1000,
Ignored: []string{},
},
Stats: statsConfig{
Enabled: true,
Interval: 1,
Ignored: []string{},
},
// NOTE: Keep these parameters in sync with the one put into
// client/src/helpers/filters/filters.js by scripts/vetted-filters.
//
@@ -458,19 +485,24 @@ func (c *configuration) write() (err error) {
}
if Context.stats != nil {
sdc := stats.DiskConfig{}
Context.stats.WriteDiskConfig(&sdc)
config.DNS.StatsInterval = sdc.Interval
statsConf := stats.Config{}
Context.stats.WriteDiskConfig(&statsConf)
config.Stats.Interval = statsConf.LimitDays
config.Stats.Enabled = statsConf.Enabled
config.Stats.Ignored = statsConf.Ignored.Values()
sort.Strings(config.Stats.Ignored)
}
if Context.queryLog != nil {
dc := querylog.Config{}
Context.queryLog.WriteDiskConfig(&dc)
config.DNS.QueryLogEnabled = dc.Enabled
config.DNS.QueryLogFileEnabled = dc.FileEnabled
config.DNS.QueryLogInterval = timeutil.Duration{Duration: dc.RotationIvl}
config.DNS.QueryLogMemSize = dc.MemSize
config.DNS.AnonymizeClientIP = dc.AnonymizeClientIP
config.QueryLog.Enabled = dc.Enabled
config.QueryLog.FileEnabled = dc.FileEnabled
config.QueryLog.Interval = timeutil.Duration{Duration: dc.RotationIvl}
config.QueryLog.MemSize = dc.MemSize
config.QueryLog.Ignored = dc.Ignored.Values()
sort.Strings(config.QueryLog.Ignored)
}
if Context.filters != nil {

View File

@@ -20,7 +20,7 @@ import (
"github.com/AdguardTeam/AdGuardHome/internal/version"
"github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/log"
"github.com/lucas-clemente/quic-go/http3"
"github.com/quic-go/quic-go/http3"
)
// getAddrsResponse is the response for /install/get_addresses endpoint.

View File

@@ -7,6 +7,7 @@ import (
"net/url"
"os"
"path/filepath"
"strings"
"github.com/AdguardTeam/AdGuardHome/internal/aghalg"
"github.com/AdguardTeam/AdGuardHome/internal/aghhttp"
@@ -20,6 +21,7 @@ import (
"github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/log"
"github.com/AdguardTeam/golibs/netutil"
"github.com/AdguardTeam/golibs/stringutil"
"github.com/ameshkov/dnscrypt/v2"
yaml "gopkg.in/yaml.v3"
)
@@ -51,10 +53,18 @@ func initDNS() (err error) {
statsConf := stats.Config{
Filename: filepath.Join(baseDir, "stats.db"),
LimitDays: config.DNS.StatsInterval,
LimitDays: config.Stats.Interval,
ConfigModified: onConfigModified,
HTTPRegister: httpRegister,
Enabled: config.Stats.Enabled,
}
set, err := nonDupEmptyHostNames(config.Stats.Ignored)
if err != nil {
return fmt.Errorf("statistics: ignored list: %w", err)
}
statsConf.Ignored = set
Context.stats, err = stats.New(statsConf)
if err != nil {
return fmt.Errorf("init stats: %w", err)
@@ -66,12 +76,19 @@ func initDNS() (err error) {
HTTPRegister: httpRegister,
FindClient: Context.clients.findMultiple,
BaseDir: baseDir,
RotationIvl: config.DNS.QueryLogInterval.Duration,
MemSize: config.DNS.QueryLogMemSize,
Enabled: config.DNS.QueryLogEnabled,
FileEnabled: config.DNS.QueryLogFileEnabled,
AnonymizeClientIP: config.DNS.AnonymizeClientIP,
RotationIvl: config.QueryLog.Interval.Duration,
MemSize: config.QueryLog.MemSize,
Enabled: config.QueryLog.Enabled,
FileEnabled: config.QueryLog.FileEnabled,
}
set, err = nonDupEmptyHostNames(config.QueryLog.Ignored)
if err != nil {
return fmt.Errorf("querylog: ignored list: %w", err)
}
conf.Ignored = set
Context.queryLog = querylog.New(conf)
Context.filters, err = filtering.New(config.DNS.DnsfilterConf, nil)
@@ -515,3 +532,27 @@ func closeDNSServer() {
log.Debug("all dns modules are closed")
}
// nonDupEmptyHostNames returns nil and error, if list has duplicate or empty
// host name. Otherwise returns a set, which contains lowercase host names
// without dot at the end, and nil error.
func nonDupEmptyHostNames(list []string) (set *stringutil.Set, err error) {
set = stringutil.NewSet()
for _, v := range list {
host := strings.ToLower(strings.TrimSuffix(v, "."))
// TODO(a.garipov): Think about ignoring empty (".") names in
// the future.
if host == "" {
return nil, errors.Error("host name is empty")
}
if set.Has(host) {
return nil, fmt.Errorf("duplicate host name %q", host)
}
set.Add(host)
}
return set, nil
}

View File

@@ -8,6 +8,7 @@ import (
"github.com/AdguardTeam/AdGuardHome/internal/dnsforward"
"github.com/AdguardTeam/golibs/cache"
"github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/log"
)
@@ -16,10 +17,6 @@ type RDNS struct {
exchanger dnsforward.RDNSExchanger
clients *clientsContainer
// usePrivate is used to store the state of current private RDNS resolving
// settings and to react to it's changes.
usePrivate uint32
// ipCh used to pass client's IP to rDNS workerLoop.
ipCh chan netip.Addr
@@ -28,13 +25,21 @@ type RDNS struct {
// address will be resolved once again. If the address couldn't be
// resolved, cache prevents further attempts to resolve it for some time.
ipCache cache.Cache
// usePrivate stores the state of current private reverse-DNS resolving
// settings.
usePrivate atomic.Bool
}
// Default rDNS values.
// Default AdGuard Home reverse DNS values.
const (
defaultRDNSCacheSize = 10000
defaultRDNSCacheTTL = 1 * 60 * 60
defaultRDNSIPChSize = 256
revDNSCacheSize = 10000
// TODO(e.burkov): Make these values configurable.
revDNSCacheTTL = 24 * 60 * 60
revDNSFailureCacheTTL = 1 * 60 * 60
revDNSQueueSize = 256
)
// NewRDNS creates and returns initialized RDNS.
@@ -48,14 +53,13 @@ func NewRDNS(
clients: clients,
ipCache: cache.New(cache.Config{
EnableLRU: true,
MaxCount: defaultRDNSCacheSize,
MaxCount: revDNSCacheSize,
}),
ipCh: make(chan netip.Addr, defaultRDNSIPChSize),
}
if usePrivate {
rDNS.usePrivate = 1
ipCh: make(chan netip.Addr, revDNSQueueSize),
}
rDNS.usePrivate.Store(usePrivate)
go rDNS.workerLoop()
return rDNS
@@ -68,12 +72,8 @@ func NewRDNS(
// approach since only unresolved locally-served addresses should be removed.
// Implement when improving the cache.
func (r *RDNS) ensurePrivateCache() {
var usePrivate uint32
if r.exchanger.ResolvesPrivatePTR() {
usePrivate = 1
}
if atomic.CompareAndSwapUint32(&r.usePrivate, 1-usePrivate, usePrivate) {
usePrivate := r.exchanger.ResolvesPrivatePTR()
if r.usePrivate.CompareAndSwap(!usePrivate, usePrivate) {
r.ipCache.Clear()
}
}
@@ -84,25 +84,28 @@ func (r *RDNS) isCached(ip netip.Addr) (ok bool) {
ipBytes := ip.AsSlice()
now := uint64(time.Now().Unix())
if expire := r.ipCache.Get(ipBytes); len(expire) != 0 {
if binary.BigEndian.Uint64(expire) > now {
return true
}
return binary.BigEndian.Uint64(expire) > now
}
// The cache entry either expired or doesn't exist.
ttl := make([]byte, 8)
binary.BigEndian.PutUint64(ttl, now+defaultRDNSCacheTTL)
r.ipCache.Set(ipBytes, ttl)
return false
}
// cache caches the ip address for ttl seconds.
func (r *RDNS) cache(ip netip.Addr, ttl uint64) {
ipData := ip.AsSlice()
ttlData := [8]byte{}
binary.BigEndian.PutUint64(ttlData[:], uint64(time.Now().Unix())+ttl)
r.ipCache.Set(ipData, ttlData[:])
}
// Begin adds the ip to the resolving queue if it is not cached or already
// resolved.
func (r *RDNS) Begin(ip netip.Addr) {
r.ensurePrivateCache()
if r.isCached(ip) || r.clients.exists(ip, ClientSourceRDNS) {
if r.isCached(ip) || r.clients.clientSource(ip) > ClientSourceRDNS {
return
}
@@ -120,15 +123,21 @@ func (r *RDNS) workerLoop() {
defer log.OnPanic("rdns")
for ip := range r.ipCh {
ttl := uint64(revDNSCacheTTL)
host, err := r.exchanger.Exchange(ip.AsSlice())
if err != nil {
log.Debug("rdns: resolving %q: %s", ip, err)
continue
} else if host == "" {
continue
if errors.Is(err, dnsforward.ErrRDNSFailed) {
// Cache failure for a less time.
ttl = revDNSFailureCacheTTL
}
}
_ = r.clients.AddHost(ip, host, ClientSourceRDNS)
r.cache(ip, ttl)
if host != "" {
_ = r.clients.AddHost(ip, host, ClientSourceRDNS)
}
}
}

View File

@@ -76,7 +76,7 @@ func TestRDNS_Begin(t *testing.T) {
ipCache := cache.New(cache.Config{
EnableLRU: true,
MaxCount: defaultRDNSCacheSize,
MaxCount: revDNSCacheSize,
})
ttl := make([]byte, binary.Size(uint64(0)))
binary.BigEndian.PutUint64(ttl, uint64(time.Now().Add(100*time.Hour).Unix()))
@@ -153,7 +153,7 @@ func TestRDNS_ensurePrivateCache(t *testing.T) {
ipCache := cache.New(cache.Config{
EnableLRU: true,
MaxCount: defaultRDNSCacheSize,
MaxCount: revDNSCacheSize,
})
ex := &rDNSExchanger{
@@ -200,25 +200,29 @@ func TestRDNS_WorkerLoop(t *testing.T) {
errUpstream := aghtest.NewErrorUpstream()
testCases := []struct {
ups upstream.Upstream
cliIP netip.Addr
wantLog string
name string
ups upstream.Upstream
cliIP netip.Addr
wantLog string
name string
wantClientSource clientSource
}{{
ups: locUpstream,
cliIP: localIP,
wantLog: "",
name: "all_good",
ups: locUpstream,
cliIP: localIP,
wantLog: "",
name: "all_good",
wantClientSource: ClientSourceRDNS,
}, {
ups: errUpstream,
cliIP: netip.MustParseAddr("192.168.1.2"),
wantLog: `rdns: resolving "192.168.1.2": test upstream error`,
name: "resolve_error",
ups: errUpstream,
cliIP: netip.MustParseAddr("192.168.1.2"),
wantLog: `rdns: resolving "192.168.1.2": test upstream error`,
name: "resolve_error",
wantClientSource: ClientSourceNone,
}, {
ups: locUpstream,
cliIP: netip.MustParseAddr("2a00:1450:400c:c06::93"),
wantLog: "",
name: "ipv6_good",
ups: locUpstream,
cliIP: netip.MustParseAddr("2a00:1450:400c:c06::93"),
wantLog: "",
name: "ipv6_good",
wantClientSource: ClientSourceRDNS,
}}
for _, tc := range testCases {
@@ -237,6 +241,10 @@ func TestRDNS_WorkerLoop(t *testing.T) {
},
clients: cc,
ipCh: ch,
ipCache: cache.New(cache.Config{
EnableLRU: true,
MaxCount: revDNSCacheSize,
}),
}
t.Run(tc.name, func(t *testing.T) {
@@ -253,11 +261,9 @@ func TestRDNS_WorkerLoop(t *testing.T) {
if tc.wantLog != "" {
assert.Contains(t, w.String(), tc.wantLog)
return
}
assert.True(t, cc.exists(tc.cliIP, ClientSourceRDNS))
assert.Equal(t, tc.wantClientSource, cc.clientSource(tc.cliIP))
})
}
}

View File

@@ -22,7 +22,7 @@ import (
)
// currentSchemaVersion is the current schema version.
const currentSchemaVersion = 14
const currentSchemaVersion = 16
// These aliases are provided for convenience.
type (
@@ -87,6 +87,8 @@ func upgradeConfigSchema(oldVersion int, diskConf yobj) (err error) {
upgradeSchema11to12,
upgradeSchema12to13,
upgradeSchema13to14,
upgradeSchema14to15,
upgradeSchema15to16,
}
n := 0
@@ -802,6 +804,107 @@ func upgradeSchema13to14(diskConf yobj) (err error) {
return nil
}
// upgradeSchema14to15 performs the following changes:
//
// # BEFORE:
// 'dns':
// 'querylog_enabled': true
// 'querylog_file_enabled': true
// 'querylog_interval': '2160h'
// 'querylog_size_memory': 1000
//
// # AFTER:
// 'querylog':
// 'enabled': true
// 'file_enabled': true
// 'interval': '2160h'
// 'size_memory': 1000
// 'ignored': []
func upgradeSchema14to15(diskConf yobj) (err error) {
log.Printf("Upgrade yaml: 14 to 15")
diskConf["schema_version"] = 15
dnsVal, ok := diskConf["dns"]
if !ok {
return nil
}
dns, ok := dnsVal.(yobj)
if !ok {
return fmt.Errorf("unexpected type of dns: %T", dnsVal)
}
type temp struct {
from string
to string
val any
}
replaces := []temp{
{from: "querylog_enabled", to: "enabled", val: true},
{from: "querylog_file_enabled", to: "file_enabled", val: true},
{from: "querylog_interval", to: "interval", val: "2160h"},
{from: "querylog_size_memory", to: "size_memory", val: 1000},
}
qlog := map[string]any{
"ignored": []any{},
}
for _, r := range replaces {
v, has := dns[r.from]
if !has {
v = r.val
}
delete(dns, r.from)
qlog[r.to] = v
}
diskConf["querylog"] = qlog
return nil
}
// upgradeSchema15to16 performs the following changes:
//
// # BEFORE:
// 'dns':
// 'statistics_interval': 1
//
// # AFTER:
// 'statistics':
// 'enabled': true
// 'interval': 1
// 'ignored': []
func upgradeSchema15to16(diskConf yobj) (err error) {
log.Printf("Upgrade yaml: 15 to 16")
diskConf["schema_version"] = 16
dnsVal, ok := diskConf["dns"]
if !ok {
return nil
}
dns, ok := dnsVal.(yobj)
if !ok {
return fmt.Errorf("unexpected type of dns: %T", dnsVal)
}
stats := map[string]any{
"enabled": true,
"interval": 1,
"ignored": []any{},
}
k := "statistics_interval"
v, has := dns[k]
if has {
stats["enabled"] = v != 0
stats["interval"] = v
}
delete(dns, k)
diskConf["statistics"] = stats
return nil
}
// TODO(a.garipov): Replace with log.Output when we port it to our logging
// package.
func funcName() string {

View File

@@ -640,3 +640,110 @@ func TestUpgradeSchema13to14(t *testing.T) {
})
}
}
func TestUpgradeSchema14to15(t *testing.T) {
const newSchemaVer = 15
defaultWantObj := yobj{
"querylog": map[string]any{
"enabled": true,
"file_enabled": true,
"interval": "2160h",
"size_memory": 1000,
"ignored": []any{},
},
"dns": map[string]any{},
"schema_version": newSchemaVer,
}
testCases := []struct {
in yobj
want yobj
name string
}{{
in: yobj{
"dns": map[string]any{
"querylog_enabled": true,
"querylog_file_enabled": true,
"querylog_interval": "2160h",
"querylog_size_memory": 1000,
},
},
want: defaultWantObj,
name: "basic",
}, {
in: yobj{
"dns": map[string]any{},
},
want: defaultWantObj,
name: "default_values",
}}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
err := upgradeSchema14to15(tc.in)
require.NoError(t, err)
assert.Equal(t, tc.want, tc.in)
})
}
}
func TestUpgradeSchema15to16(t *testing.T) {
const newSchemaVer = 16
defaultWantObj := yobj{
"statistics": map[string]any{
"enabled": true,
"interval": 1,
"ignored": []any{},
},
"dns": map[string]any{},
"schema_version": newSchemaVer,
}
testCases := []struct {
in yobj
want yobj
name string
}{{
in: yobj{
"dns": map[string]any{
"statistics_interval": 1,
},
},
want: defaultWantObj,
name: "basic",
}, {
in: yobj{
"dns": map[string]any{},
},
want: defaultWantObj,
name: "default_values",
}, {
in: yobj{
"dns": map[string]any{
"statistics_interval": 0,
},
},
want: yobj{
"statistics": map[string]any{
"enabled": false,
"interval": 0,
"ignored": []any{},
},
"dns": map[string]any{},
"schema_version": newSchemaVer,
},
name: "stats_disabled",
}}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
err := upgradeSchema15to16(tc.in)
require.NoError(t, err)
assert.Equal(t, tc.want, tc.in)
})
}
}

View File

@@ -15,8 +15,8 @@ import (
"github.com/AdguardTeam/golibs/log"
"github.com/AdguardTeam/golibs/netutil"
"github.com/NYTimes/gziphandler"
"github.com/lucas-clemente/quic-go"
"github.com/lucas-clemente/quic-go/http3"
"github.com/quic-go/quic-go"
"github.com/quic-go/quic-go/http3"
"golang.org/x/net/http2"
"golang.org/x/net/http2/h2c"
)