Pull request: AG-28771 conf upstream mode

Squashed commit of the following:

commit afb5a0d8a499bccf7761baea40910f39c92b8a20
Merge: 09ac43c85 abf20c6de
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Mon Dec 25 12:55:45 2023 +0200

    Merge remote-tracking branch 'origin/master' into conf-ups-mode

commit 09ac43c859ef8cbd3bb0488d1a945589cd59ca19
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Fri Dec 22 14:36:07 2023 +0200

    openapi: imp docs

commit d0fbd4349e4bddde73c6e92f75854acfc481ac0d
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Fri Dec 22 11:47:10 2023 +0200

    all: changelog

commit 105f9c50738733b0736a768fb9ee09d2e7fbf42e
Merge: 62a2cf12d 4bc5c346a
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Fri Dec 22 11:27:21 2023 +0200

    Merge remote-tracking branch 'origin/master' into conf-ups-mode

    # Conflicts:
    #	openapi/CHANGELOG.md

commit 62a2cf12df694611888e840a5041a9c517cdfddb
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Fri Dec 22 10:52:59 2023 +0200

    openapi: imp docs

commit 87956c49240da44b216489920feff69996e3502b
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Thu Dec 21 12:08:07 2023 +0200

    dnsforward: imp code

commit bf74d67ad112735d557be3d8fac75964cd99e375
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Wed Dec 20 15:46:38 2023 +0200

    dnsforward: imp code

commit 3a98dee88809a25118a14a1f07eeecbfccb14cd9
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Wed Dec 20 15:41:06 2023 +0200

    dnsforward: imp code

commit 1499da1fa0319ac3ad914171e807446f2c4d2fdb
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Wed Dec 20 13:36:28 2023 +0200

    dnsforward: imp code

commit 228c61a5a0f73cc13655cef8bdaa1995b3f7fced
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Wed Dec 20 13:06:11 2023 +0200

    dnsforward: imp code

commit 069ee22c6d904db4e983135ce87a9fe8d12b7e9a
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Tue Dec 19 12:39:25 2023 +0200

    dnsforward: imp code

commit 90919f99a975862dcb07ac82fb740e4404e48bae
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Tue Dec 19 12:10:43 2023 +0200

    confmigrate: fix

commit a8c329950423b59098d1f2b16d1da7100dd54f8d
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Tue Dec 19 12:08:05 2023 +0200

    dnsforward: imp code

commit 58b53ccd97d353fab0df29f13425b5e341c8fdeb
Author: Dimitry Kolyshev <dkolyshev@adguard.com>
Date:   Mon Dec 18 15:10:01 2023 +0200

    all: conf upstream mode
This commit is contained in:
Dimitry Kolyshev 2023-12-25 14:16:48 +03:00
parent abf20c6dea
commit 1511fabeec
19 changed files with 297 additions and 51 deletions

View file

@ -31,6 +31,31 @@ NOTE: Add new changes BELOW THIS COMMENT.
- Ability to disable plain-DNS serving via UI if an encrypted protocol is
already used ([#1660]).
### Changed
- The field `"upstream_mode"` in `POST /control/dns_config` and
`GET /control/dns_info` HTTP APIs now accepts `load_balance` value. Check
`openapi/CHANGELOG.md` for more details.
#### Configuration changes
- The properties `dns.'all_servers` and `dns.fastest_addr` were removed, their
values migrated to newly added field `dns.upstream_mode` that describes the
logic through which upstreams will be used.
```yaml
# BEFORE:
'dns':
# …
'all_servers': true
'fastest_addr': true
# AFTER:
'dns':
# …
'upstream_mode': 'parallel'
```
### Fixed
- Statistics for 7 days displayed as 168 hours on the dashboard.

View file

@ -2,4 +2,4 @@
package configmigrate
// LastSchemaVersion is the most recent schema version.
const LastSchemaVersion uint = 27
const LastSchemaVersion uint = 28

View file

@ -4,6 +4,7 @@ import (
"testing"
"time"
"github.com/AdguardTeam/AdGuardHome/internal/dnsforward"
"github.com/AdguardTeam/AdGuardHome/internal/filtering"
"github.com/AdguardTeam/golibs/testutil"
"github.com/AdguardTeam/golibs/timeutil"
@ -1646,3 +1647,84 @@ func TestUpgradeSchema26to27(t *testing.T) {
})
}
}
func TestUpgradeSchema27to28(t *testing.T) {
const newSchemaVer = 28
testCases := []struct {
in yobj
want yobj
name string
}{{
name: "empty",
in: yobj{},
want: yobj{
"schema_version": newSchemaVer,
},
}, {
name: "load_balance",
in: yobj{
"dns": yobj{
"all_servers": false,
"fastest_addr": false,
},
},
want: yobj{
"dns": yobj{
"upstream_mode": dnsforward.UpstreamModeLoadBalance,
},
"schema_version": newSchemaVer,
},
}, {
name: "parallel",
in: yobj{
"dns": yobj{
"all_servers": true,
"fastest_addr": false,
},
},
want: yobj{
"dns": yobj{
"upstream_mode": dnsforward.UpstreamModeParallel,
},
"schema_version": newSchemaVer,
},
}, {
name: "parallel_fastest",
in: yobj{
"dns": yobj{
"all_servers": true,
"fastest_addr": true,
},
},
want: yobj{
"dns": yobj{
"upstream_mode": dnsforward.UpstreamModeParallel,
},
"schema_version": newSchemaVer,
},
}, {
name: "load_balance",
in: yobj{
"dns": yobj{
"all_servers": false,
"fastest_addr": true,
},
},
want: yobj{
"dns": yobj{
"upstream_mode": dnsforward.UpstreamModeFastestAddr,
},
"schema_version": newSchemaVer,
},
}}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
err := migrateTo28(tc.in)
require.NoError(t, err)
assert.Equal(t, tc.want, tc.in)
})
}
}

View file

@ -119,6 +119,7 @@ func (m *Migrator) upgradeConfigSchema(current, target uint, diskConf yobj) (err
24: migrateTo25,
25: migrateTo26,
26: migrateTo27,
27: migrateTo28,
}
for i, migrate := range upgrades[current:target] {

View file

@ -0,0 +1,47 @@
package configmigrate
import (
"github.com/AdguardTeam/AdGuardHome/internal/dnsforward"
)
// migrateTo28 performs the following changes:
//
// # BEFORE:
// 'dns':
// 'all_servers': true
// 'fastest_addr': true
// # …
// # …
//
// # AFTER:
// 'dns':
// 'upstream_mode': 'parallel'
// # …
// # …
func migrateTo28(diskConf yobj) (err error) {
diskConf["schema_version"] = 28
dns, ok, err := fieldVal[yobj](diskConf, "dns")
if !ok {
return err
}
allServers, _, _ := fieldVal[bool](dns, "all_servers")
fastestAddr, _, _ := fieldVal[bool](dns, "fastest_addr")
var upstreamModeType dnsforward.UpstreamMode
if allServers {
upstreamModeType = dnsforward.UpstreamModeParallel
} else if fastestAddr {
upstreamModeType = dnsforward.UpstreamModeFastestAddr
} else {
upstreamModeType = dnsforward.UpstreamModeLoadBalance
}
dns["upstream_mode"] = upstreamModeType
delete(dns, "all_servers")
delete(dns, "fastest_addr")
return nil
}

View file

@ -89,12 +89,8 @@ type Config struct {
// servers are not responding.
FallbackDNS []string `yaml:"fallback_dns"`
// AllServers, if true, parallel queries to all configured upstream servers
// are enabled.
AllServers bool `yaml:"all_servers"`
// FastestAddr, if true, use Fastest Address algorithm.
FastestAddr bool `yaml:"fastest_addr"`
// UpstreamMode determines the logic through which upstreams will be used.
UpstreamMode UpstreamMode `yaml:"upstream_mode"`
// FastestTimeout replaces the default timeout for dialing IP addresses
// when FastestAddr is true.
@ -294,6 +290,16 @@ type ServerConfig struct {
ServePlainDNS bool
}
// UpstreamMode is a enumeration of upstream mode representations. See
// [proxy.UpstreamModeType].
type UpstreamMode string
const (
UpstreamModeLoadBalance UpstreamMode = "load_balance"
UpstreamModeParallel UpstreamMode = "parallel"
UpstreamModeFastestAddr UpstreamMode = "fastest_addr"
)
// newProxyConfig creates and validates configuration for the main proxy.
func (s *Server) newProxyConfig() (conf *proxy.Config, err error) {
srvConf := s.conf
@ -328,12 +334,10 @@ func (s *Server) newProxyConfig() (conf *proxy.Config, err error) {
conf.CacheSizeBytes = int(srvConf.CacheSize)
}
setProxyUpstreamMode(
conf,
srvConf.AllServers,
srvConf.FastestAddr,
srvConf.FastestTimeout.Duration,
)
err = setProxyUpstreamMode(conf, srvConf.UpstreamMode, srvConf.FastestTimeout.Duration)
if err != nil {
return nil, fmt.Errorf("upstream mode: %w", err)
}
conf.BogusNXDomain, err = parseBogusNXDOMAIN(srvConf.BogusNXDomain)
if err != nil {

View file

@ -290,6 +290,7 @@ func TestServer_HandleDNSRequest_dns64(t *testing.T) {
TCPListenAddrs: []*net.TCPAddr{{}},
UseDNS64: true,
Config: Config{
UpstreamMode: UpstreamModeLoadBalance,
EDNSClientSubnet: &EDNSClientSubnet{Enabled: false},
},
ServePlainDNS: true,

View file

@ -703,12 +703,10 @@ func (s *Server) prepareInternalProxy() (err error) {
MaxGoroutines: int(s.conf.MaxGoroutines),
}
setProxyUpstreamMode(
conf,
srvConf.AllServers,
srvConf.FastestAddr,
srvConf.FastestTimeout.Duration,
)
err = setProxyUpstreamMode(conf, srvConf.UpstreamMode, srvConf.FastestTimeout.Duration)
if err != nil {
return fmt.Errorf("invalid upstream mode: %w", err)
}
// TODO(a.garipov): Make a proper constructor for proxy.Proxy.
p := &proxy.Proxy{

View file

@ -177,6 +177,7 @@ func createTestTLS(t *testing.T, tlsConf TLSConfig) (s *Server, certPem []byte)
UDPListenAddrs: []*net.UDPAddr{{}},
TCPListenAddrs: []*net.TCPAddr{{}},
Config: Config{
UpstreamMode: UpstreamModeLoadBalance,
EDNSClientSubnet: &EDNSClientSubnet{Enabled: false},
},
ServePlainDNS: true,
@ -305,6 +306,7 @@ func TestServer(t *testing.T) {
UDPListenAddrs: []*net.UDPAddr{{}},
TCPListenAddrs: []*net.TCPAddr{{}},
Config: Config{
UpstreamMode: UpstreamModeLoadBalance,
EDNSClientSubnet: &EDNSClientSubnet{Enabled: false},
},
ServePlainDNS: true,
@ -344,6 +346,7 @@ func TestServer_timeout(t *testing.T) {
srvConf := &ServerConfig{
UpstreamTimeout: testTimeout,
Config: Config{
UpstreamMode: UpstreamModeLoadBalance,
EDNSClientSubnet: &EDNSClientSubnet{Enabled: false},
},
ServePlainDNS: true,
@ -362,6 +365,7 @@ func TestServer_timeout(t *testing.T) {
s, err := NewServer(DNSCreateParams{DNSFilter: createTestDNSFilter(t)})
require.NoError(t, err)
s.conf.Config.UpstreamMode = UpstreamModeLoadBalance
s.conf.Config.EDNSClientSubnet = &EDNSClientSubnet{
Enabled: false,
}
@ -379,6 +383,7 @@ func TestServer_Prepare_fallbacks(t *testing.T) {
"#tls://1.1.1.1",
"8.8.8.8",
},
UpstreamMode: UpstreamModeLoadBalance,
EDNSClientSubnet: &EDNSClientSubnet{Enabled: false},
},
ServePlainDNS: true,
@ -401,6 +406,7 @@ func TestServerWithProtectionDisabled(t *testing.T) {
UDPListenAddrs: []*net.UDPAddr{{}},
TCPListenAddrs: []*net.TCPAddr{{}},
Config: Config{
UpstreamMode: UpstreamModeLoadBalance,
EDNSClientSubnet: &EDNSClientSubnet{Enabled: false},
},
ServePlainDNS: true,
@ -478,7 +484,8 @@ func TestServerRace(t *testing.T) {
UDPListenAddrs: []*net.UDPAddr{{}},
TCPListenAddrs: []*net.TCPAddr{{}},
Config: Config{
UpstreamDNS: []string{"8.8.8.8:53", "8.8.4.4:53"},
UpstreamMode: UpstreamModeLoadBalance,
UpstreamDNS: []string{"8.8.8.8:53", "8.8.4.4:53"},
},
ConfigModified: func() {},
ServePlainDNS: true,
@ -531,6 +538,7 @@ func TestSafeSearch(t *testing.T) {
UDPListenAddrs: []*net.UDPAddr{{}},
TCPListenAddrs: []*net.TCPAddr{{}},
Config: Config{
UpstreamMode: UpstreamModeLoadBalance,
EDNSClientSubnet: &EDNSClientSubnet{
Enabled: false,
},
@ -614,6 +622,7 @@ func TestInvalidRequest(t *testing.T) {
UDPListenAddrs: []*net.UDPAddr{{}},
TCPListenAddrs: []*net.TCPAddr{{}},
Config: Config{
UpstreamMode: UpstreamModeLoadBalance,
EDNSClientSubnet: &EDNSClientSubnet{
Enabled: false,
},
@ -643,6 +652,7 @@ func TestBlockedRequest(t *testing.T) {
UDPListenAddrs: []*net.UDPAddr{{}},
TCPListenAddrs: []*net.TCPAddr{{}},
Config: Config{
UpstreamMode: UpstreamModeLoadBalance,
EDNSClientSubnet: &EDNSClientSubnet{
Enabled: false,
},
@ -678,7 +688,8 @@ func TestServerCustomClientUpstream(t *testing.T) {
UDPListenAddrs: []*net.UDPAddr{{}},
TCPListenAddrs: []*net.TCPAddr{{}},
Config: Config{
CacheSize: defaultCacheSize,
CacheSize: defaultCacheSize,
UpstreamMode: UpstreamModeLoadBalance,
EDNSClientSubnet: &EDNSClientSubnet{
Enabled: false,
},
@ -756,6 +767,7 @@ func TestBlockCNAMEProtectionEnabled(t *testing.T) {
UDPListenAddrs: []*net.UDPAddr{{}},
TCPListenAddrs: []*net.TCPAddr{{}},
Config: Config{
UpstreamMode: UpstreamModeLoadBalance,
EDNSClientSubnet: &EDNSClientSubnet{
Enabled: false,
},
@ -789,6 +801,7 @@ func TestBlockCNAME(t *testing.T) {
UDPListenAddrs: []*net.UDPAddr{{}},
TCPListenAddrs: []*net.TCPAddr{{}},
Config: Config{
UpstreamMode: UpstreamModeLoadBalance,
EDNSClientSubnet: &EDNSClientSubnet{
Enabled: false,
},
@ -864,6 +877,7 @@ func TestClientRulesForCNAMEMatching(t *testing.T) {
FilterHandler: func(_ netip.Addr, _ string, settings *filtering.Settings) {
settings.FilteringEnabled = false
},
UpstreamMode: UpstreamModeLoadBalance,
EDNSClientSubnet: &EDNSClientSubnet{
Enabled: false,
},
@ -909,6 +923,7 @@ func TestNullBlockedRequest(t *testing.T) {
UDPListenAddrs: []*net.UDPAddr{{}},
TCPListenAddrs: []*net.TCPAddr{{}},
Config: Config{
UpstreamMode: UpstreamModeLoadBalance,
EDNSClientSubnet: &EDNSClientSubnet{
Enabled: false,
},
@ -974,7 +989,8 @@ func TestBlockedCustomIP(t *testing.T) {
UDPListenAddrs: []*net.UDPAddr{{}},
TCPListenAddrs: []*net.TCPAddr{{}},
Config: Config{
UpstreamDNS: []string{"8.8.8.8:53", "8.8.4.4:53"},
UpstreamDNS: []string{"8.8.8.8:53", "8.8.4.4:53"},
UpstreamMode: UpstreamModeLoadBalance,
EDNSClientSubnet: &EDNSClientSubnet{
Enabled: false,
},
@ -1027,6 +1043,7 @@ func TestBlockedByHosts(t *testing.T) {
UDPListenAddrs: []*net.UDPAddr{{}},
TCPListenAddrs: []*net.TCPAddr{{}},
Config: Config{
UpstreamMode: UpstreamModeLoadBalance,
EDNSClientSubnet: &EDNSClientSubnet{
Enabled: false,
},
@ -1078,6 +1095,7 @@ func TestBlockedBySafeBrowsing(t *testing.T) {
UDPListenAddrs: []*net.UDPAddr{{}},
TCPListenAddrs: []*net.TCPAddr{{}},
Config: Config{
UpstreamMode: UpstreamModeLoadBalance,
EDNSClientSubnet: &EDNSClientSubnet{
Enabled: false,
},
@ -1136,7 +1154,8 @@ func TestRewrite(t *testing.T) {
UDPListenAddrs: []*net.UDPAddr{{}},
TCPListenAddrs: []*net.TCPAddr{{}},
Config: Config{
UpstreamDNS: []string{"8.8.8.8:53"},
UpstreamDNS: []string{"8.8.8.8:53"},
UpstreamMode: UpstreamModeLoadBalance,
EDNSClientSubnet: &EDNSClientSubnet{
Enabled: false,
},
@ -1265,6 +1284,7 @@ func TestPTRResponseFromDHCPLeases(t *testing.T) {
s.conf.TCPListenAddrs = []*net.TCPAddr{{}}
s.conf.UpstreamDNS = []string{"127.0.0.1:53"}
s.conf.Config.EDNSClientSubnet = &EDNSClientSubnet{Enabled: false}
s.conf.Config.UpstreamMode = UpstreamModeLoadBalance
err = s.Prepare(&s.conf)
require.NoError(t, err)
@ -1347,6 +1367,7 @@ func TestPTRResponseFromHosts(t *testing.T) {
s.conf.TCPListenAddrs = []*net.TCPAddr{{}}
s.conf.UpstreamDNS = []string{"127.0.0.1:53"}
s.conf.Config.EDNSClientSubnet = &EDNSClientSubnet{Enabled: false}
s.conf.Config.UpstreamMode = UpstreamModeLoadBalance
err = s.Prepare(&s.conf)
require.NoError(t, err)

View file

@ -38,6 +38,7 @@ func TestServer_FilterDNSRewrite(t *testing.T) {
BlockingMode: filtering.BlockingModeDefault,
}, ServerConfig{
Config: Config{
UpstreamMode: UpstreamModeLoadBalance,
EDNSClientSubnet: &EDNSClientSubnet{Enabled: false},
},
ServePlainDNS: true,

View file

@ -31,6 +31,7 @@ func TestHandleDNSRequest_handleDNSRequest(t *testing.T) {
UDPListenAddrs: []*net.UDPAddr{{}},
TCPListenAddrs: []*net.TCPAddr{{}},
Config: Config{
UpstreamMode: UpstreamModeLoadBalance,
EDNSClientSubnet: &EDNSClientSubnet{
Enabled: false,
},

View file

@ -70,7 +70,7 @@ type jsonDNSConfig struct {
DisableIPv6 *bool `json:"disable_ipv6"`
// UpstreamMode defines the way DNS requests are constructed.
UpstreamMode *string `json:"upstream_mode"`
UpstreamMode *jsonUpstreamMode `json:"upstream_mode"`
// BlockedResponseTTL is the TTL for blocked responses.
BlockedResponseTTL *uint32 `json:"blocked_response_ttl"`
@ -114,6 +114,21 @@ type jsonDNSConfig struct {
DefaultLocalPTRUpstreams []string `json:"default_local_ptr_upstreams,omitempty"`
}
// jsonUpstreamMode is a enumeration of upstream modes.
type jsonUpstreamMode string
const (
// jsonUpstreamModeEmpty is the default value on frontend, it is used as
// jsonUpstreamModeLoadBalance mode.
//
// Deprecated: Use jsonUpstreamModeLoadBalance instead.
jsonUpstreamModeEmpty jsonUpstreamMode = ""
jsonUpstreamModeLoadBalance jsonUpstreamMode = "load_balance"
jsonUpstreamModeParallel jsonUpstreamMode = "parallel"
jsonUpstreamModeFastestAddr jsonUpstreamMode = "fastest_addr"
)
func (s *Server) getDNSConfig() (c *jsonDNSConfig) {
protectionEnabled, protectionDisabledUntil := s.UpdatedProtectionStatus()
@ -145,11 +160,16 @@ func (s *Server) getDNSConfig() (c *jsonDNSConfig) {
usePrivateRDNS := s.conf.UsePrivateRDNS
localPTRUpstreams := stringutil.CloneSliceOrEmpty(s.conf.LocalPTRResolvers)
var upstreamMode string
if s.conf.FastestAddr {
upstreamMode = "fastest_addr"
} else if s.conf.AllServers {
upstreamMode = "parallel"
var upstreamMode jsonUpstreamMode
switch s.conf.UpstreamMode {
case UpstreamModeLoadBalance:
// TODO(d.kolyshev): Support jsonUpstreamModeLoadBalance on frontend instead
// of jsonUpstreamModeEmpty.
upstreamMode = jsonUpstreamModeEmpty
case UpstreamModeParallel:
upstreamMode = jsonUpstreamModeParallel
case UpstreamModeFastestAddr:
upstreamMode = jsonUpstreamModeFastestAddr
}
defPTRUps, err := s.defaultLocalPTRUpstreams()
@ -222,18 +242,22 @@ func (req *jsonDNSConfig) checkBlockingMode() (err error) {
return validateBlockingMode(*req.BlockingMode, req.BlockingIPv4, req.BlockingIPv6)
}
// checkUpstreamsMode returns an error if the upstream mode is invalid.
func (req *jsonDNSConfig) checkUpstreamsMode() (err error) {
// checkUpstreamMode returns an error if the upstream mode is invalid.
func (req *jsonDNSConfig) checkUpstreamMode() (err error) {
if req.UpstreamMode == nil {
return nil
}
mode := *req.UpstreamMode
if ok := slices.Contains([]string{"", "fastest_addr", "parallel"}, mode); !ok {
return fmt.Errorf("upstream_mode: incorrect value %q", mode)
switch um := *req.UpstreamMode; um {
case
jsonUpstreamModeEmpty,
jsonUpstreamModeLoadBalance,
jsonUpstreamModeParallel,
jsonUpstreamModeFastestAddr:
return nil
default:
return fmt.Errorf("upstream_mode: incorrect value %q", um)
}
return nil
}
// checkBootstrap returns an error if any bootstrap address is invalid.
@ -297,7 +321,7 @@ func (req *jsonDNSConfig) validate(privateNets netutil.SubnetSet) (err error) {
return err
}
err = req.checkUpstreamsMode()
err = req.checkUpstreamMode()
if err != nil {
// Don't wrap the error since it's informative enough as is.
return err
@ -446,8 +470,9 @@ func (s *Server) setConfig(dc *jsonDNSConfig) (shouldRestart bool) {
}
if dc.UpstreamMode != nil {
s.conf.AllServers = *dc.UpstreamMode == "parallel"
s.conf.FastestAddr = *dc.UpstreamMode == "fastest_addr"
s.conf.UpstreamMode = mustParseUpstreamMode(*dc.UpstreamMode)
} else {
s.conf.UpstreamMode = UpstreamModeLoadBalance
}
if dc.EDNSCSUseCustom != nil && *dc.EDNSCSUseCustom {
@ -460,6 +485,22 @@ func (s *Server) setConfig(dc *jsonDNSConfig) (shouldRestart bool) {
return s.setConfigRestartable(dc)
}
// mustParseUpstreamMode returns an upstream mode parsed from jsonUpstreamMode.
// Panics in case of invalid value.
func mustParseUpstreamMode(mode jsonUpstreamMode) (um UpstreamMode) {
switch mode {
case jsonUpstreamModeEmpty, jsonUpstreamModeLoadBalance:
return UpstreamModeLoadBalance
case jsonUpstreamModeParallel:
return UpstreamModeParallel
case jsonUpstreamModeFastestAddr:
return UpstreamModeFastestAddr
default:
// Should never happen, since the value should be validated.
panic(fmt.Errorf("unexpected upstream mode: %q", mode))
}
}
// setIfNotNil sets the value pointed at by currentPtr to the value pointed at
// by newPtr if newPtr is not nil. currentPtr must not be nil.
func setIfNotNil[T any](currentPtr, newPtr *T) (hasSet bool) {

View file

@ -77,6 +77,7 @@ func TestDNSForwardHTTP_handleGetConfig(t *testing.T) {
FallbackDNS: []string{"9.9.9.10"},
RatelimitSubnetLenIPv4: 24,
RatelimitSubnetLenIPv6: 56,
UpstreamMode: UpstreamModeLoadBalance,
EDNSClientSubnet: &EDNSClientSubnet{Enabled: false},
},
ConfigModified: func() {},
@ -103,7 +104,7 @@ func TestDNSForwardHTTP_handleGetConfig(t *testing.T) {
}, {
conf: func() ServerConfig {
conf := defaultConf
conf.FastestAddr = true
conf.UpstreamMode = UpstreamModeFastestAddr
return conf
},
@ -111,7 +112,7 @@ func TestDNSForwardHTTP_handleGetConfig(t *testing.T) {
}, {
conf: func() ServerConfig {
conf := defaultConf
conf.AllServers = true
conf.UpstreamMode = UpstreamModeParallel
return conf
},
@ -157,6 +158,7 @@ func TestDNSForwardHTTP_handleSetConfig(t *testing.T) {
UpstreamDNS: []string{"8.8.8.8:53", "8.8.4.4:53"},
RatelimitSubnetLenIPv4: 24,
RatelimitSubnetLenIPv6: 56,
UpstreamMode: UpstreamModeLoadBalance,
EDNSClientSubnet: &EDNSClientSubnet{Enabled: false},
},
ConfigModified: func() {},
@ -523,6 +525,7 @@ func TestServer_HandleTestUpstreamDNS(t *testing.T) {
TCPListenAddrs: []*net.TCPAddr{{}},
UpstreamTimeout: upsTimeout,
Config: Config{
UpstreamMode: UpstreamModeLoadBalance,
EDNSClientSubnet: &EDNSClientSubnet{Enabled: false},
},
ServePlainDNS: true,

View file

@ -79,6 +79,7 @@ func TestServer_ProcessInitial(t *testing.T) {
c := ServerConfig{
Config: Config{
AAAADisabled: tc.aaaaDisabled,
UpstreamMode: UpstreamModeLoadBalance,
EDNSClientSubnet: &EDNSClientSubnet{Enabled: false},
},
ServePlainDNS: true,
@ -179,6 +180,7 @@ func TestServer_ProcessFilteringAfterResponse(t *testing.T) {
c := ServerConfig{
Config: Config{
AAAADisabled: tc.aaaaDisabled,
UpstreamMode: UpstreamModeLoadBalance,
EDNSClientSubnet: &EDNSClientSubnet{Enabled: false},
},
ServePlainDNS: true,
@ -694,6 +696,7 @@ func TestServer_ProcessRestrictLocal(t *testing.T) {
// TODO(s.chzhen): Add tests where EDNSClientSubnet.Enabled is true.
// Improve Config declaration for tests.
Config: Config{
UpstreamMode: UpstreamModeLoadBalance,
EDNSClientSubnet: &EDNSClientSubnet{Enabled: false},
},
ServePlainDNS: true,
@ -770,6 +773,7 @@ func TestServer_ProcessLocalPTR_usingResolvers(t *testing.T) {
UDPListenAddrs: []*net.UDPAddr{{}},
TCPListenAddrs: []*net.TCPAddr{{}},
Config: Config{
UpstreamMode: UpstreamModeLoadBalance,
EDNSClientSubnet: &EDNSClientSubnet{Enabled: false},
},
ServePlainDNS: true,

View file

@ -17,6 +17,7 @@ func TestGenAnswerHTTPS_andSVCB(t *testing.T) {
BlockingMode: filtering.BlockingModeDefault,
}, ServerConfig{
Config: Config{
UpstreamMode: UpstreamModeLoadBalance,
EDNSClientSubnet: &EDNSClientSubnet{Enabled: false},
},
ServePlainDNS: true,

View file

@ -136,18 +136,22 @@ func UpstreamHTTPVersions(http3 bool) (v []upstream.HTTPVersion) {
// based on provided parameters.
func setProxyUpstreamMode(
conf *proxy.Config,
allServers bool,
fastestAddr bool,
upstreamMode UpstreamMode,
fastestTimeout time.Duration,
) {
if allServers {
) (err error) {
switch upstreamMode {
case UpstreamModeParallel:
conf.UpstreamMode = proxy.UModeParallel
} else if fastestAddr {
case UpstreamModeFastestAddr:
conf.UpstreamMode = proxy.UModeFastestAddr
conf.FastestPingTimeout = fastestTimeout
} else {
case UpstreamModeLoadBalance:
conf.UpstreamMode = proxy.UModeLoadBalance
default:
return fmt.Errorf("unexpected value %q", upstreamMode)
}
return nil
}
// createBootstrap returns a bootstrap resolver based on the configuration of s.

View file

@ -315,7 +315,7 @@ var config = &configuration{
RatelimitSubnetLenIPv4: 24,
RatelimitSubnetLenIPv6: 56,
RefuseAny: true,
AllServers: false,
UpstreamMode: dnsforward.UpstreamModeLoadBalance,
HandleDDR: true,
FastestTimeout: timeutil.Duration{
Duration: fastip.DefaultPingWaitTimeout,

View file

@ -6,6 +6,13 @@
## v0.107.44: API changes
### The field `"upstream_mode"` in `DNSConfig`
* The field `"upstream_mode"` in `POST /control/dns_config` and
`GET /control/dns_info` now accepts `load_balance` value. Note that, the usage
of an empty string or field absence is considered to as deprecated and is not
recommended. Use `load_balance` instead.
### Type correction in `Client`
* Field `upstreams_cache_size` of object `Client` now correctly has type

View file

@ -1524,10 +1524,15 @@
'cache_optimistic':
'type': 'boolean'
'upstream_mode':
'type': 'string'
'enum':
- ''
- 'parallel'
- 'fastest_addr'
- const: ''
deprecated: true
description: Use `load_balance` instead.
- const: 'fastest_addr'
- const: 'load_balance'
- const: 'parallel'
'description': Upstream modes enumeration.
'use_private_ptr_resolvers':
'type': 'boolean'
'resolve_clients':