Pull request 1975: 3701-fallback-dns-api

Squashed commit of the following:

commit 0f96137c629d7df99d40e479f66dd78248185ca3
Merge: 0d640fe37 aac36a2d2
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Wed Aug 30 12:53:03 2023 +0300

    Merge branch 'master' into 3701-fallback-dns-api

commit 0d640fe37a563d9fcea1ff4b6c86f37629af0ebd
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Wed Aug 30 12:49:05 2023 +0300

    dnsforward: imp tests

commit 6b7f7aad76566b70852146bc6629ae7fb71eec09
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Fri Aug 25 18:51:34 2023 +0300

    client: add fallback dns field

commit c2ac7e3656431f7d44645786296e58ac7d012fed
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Thu Aug 24 13:22:07 2023 +0300

    all: add fallback dns api
This commit is contained in:
Stanislav Chzhen 2023-08-30 13:21:31 +03:00
parent aac36a2d2f
commit 1f5abecefc
12 changed files with 158 additions and 2 deletions

View File

@ -8,11 +8,14 @@
"load_balancing_desc": "Query one upstream server at a time. AdGuard Home uses its weighted random algorithm to pick the server so that the fastest server is used more often.", "load_balancing_desc": "Query one upstream server at a time. AdGuard Home uses its weighted random algorithm to pick the server so that the fastest server is used more often.",
"bootstrap_dns": "Bootstrap DNS servers", "bootstrap_dns": "Bootstrap DNS servers",
"bootstrap_dns_desc": "IP addresses of DNS servers used to resolve IP addresses of the DoH/DoT resolvers you specify as upstreams. Comments are not permitted.", "bootstrap_dns_desc": "IP addresses of DNS servers used to resolve IP addresses of the DoH/DoT resolvers you specify as upstreams. Comments are not permitted.",
"fallback_dns_title": "Fallback DNS servers",
"fallback_dns_desc": "List of fallback DNS servers used when upstream DNS servers are not responding. The syntax is the same as in the main upstreams field above.",
"fallback_dns_placeholder": "Enter one fallback DNS server per line",
"local_ptr_title": "Private reverse DNS servers", "local_ptr_title": "Private reverse DNS servers",
"local_ptr_desc": "The DNS servers that AdGuard Home uses for local PTR queries. These servers are used to resolve PTR requests for addresses in private IP ranges, for example \"192.168.12.34\", using reverse DNS. If not set, AdGuard Home uses the addresses of the default DNS resolvers of your OS except for the addresses of AdGuard Home itself.", "local_ptr_desc": "The DNS servers that AdGuard Home uses for local PTR queries. These servers are used to resolve PTR requests for addresses in private IP ranges, for example \"192.168.12.34\", using reverse DNS. If not set, AdGuard Home uses the addresses of the default DNS resolvers of your OS except for the addresses of AdGuard Home itself.",
"local_ptr_default_resolver": "By default, AdGuard Home uses the following reverse DNS resolvers: {{ip}}.", "local_ptr_default_resolver": "By default, AdGuard Home uses the following reverse DNS resolvers: {{ip}}.",
"local_ptr_no_default_resolver": "AdGuard Home could not determine suitable private reverse DNS resolvers for this system.", "local_ptr_no_default_resolver": "AdGuard Home could not determine suitable private reverse DNS resolvers for this system.",
"local_ptr_placeholder": "Enter one server address per line", "local_ptr_placeholder": "Enter one IP address per line",
"resolve_clients_title": "Enable reverse resolving of clients' IP addresses", "resolve_clients_title": "Enable reverse resolving of clients' IP addresses",
"resolve_clients_desc": "Reversely resolve clients' IP addresses into their hostnames by sending PTR queries to corresponding resolvers (private DNS servers for local clients, upstream servers for clients with public IP addresses).", "resolve_clients_desc": "Reversely resolve clients' IP addresses into their hostnames by sending PTR queries to corresponding resolvers (private DNS servers for local clients, upstream servers for clients with public IP addresses).",
"use_private_ptr_resolvers_title": "Use private reverse DNS resolvers", "use_private_ptr_resolvers_title": "Use private reverse DNS resolvers",

View File

@ -50,6 +50,10 @@ export const setDnsConfig = (config) => async (dispatch) => {
data.bootstrap_dns = splitByNewLine(config.bootstrap_dns); data.bootstrap_dns = splitByNewLine(config.bootstrap_dns);
hasDnsSettings = true; hasDnsSettings = true;
} }
if (Object.prototype.hasOwnProperty.call(data, 'fallback_dns')) {
data.fallback_dns = splitByNewLine(config.fallback_dns);
hasDnsSettings = true;
}
if (Object.prototype.hasOwnProperty.call(data, 'local_ptr_upstreams')) { if (Object.prototype.hasOwnProperty.call(data, 'local_ptr_upstreams')) {
data.local_ptr_upstreams = splitByNewLine(config.local_ptr_upstreams); data.local_ptr_upstreams = splitByNewLine(config.local_ptr_upstreams);
hasDnsSettings = true; hasDnsSettings = true;

View File

@ -179,6 +179,30 @@ const Form = ({
<Examples /> <Examples />
<hr /> <hr />
</div> </div>
<div className="col-12">
<label
className="form__label form__label--with-desc"
htmlFor="fallback_dns"
>
<Trans>fallback_dns_title</Trans>
</label>
<div className="form__desc form__desc--top">
<Trans>fallback_dns_desc</Trans>
</div>
<Field
id="fallback_dns"
name="fallback_dns"
component={renderTextareaField}
type="text"
className="form-control form-control--textarea form-control--textarea-small font-monospace"
placeholder={t('fallback_dns_placeholder')}
disabled={processingSetConfig}
normalizeOnBlur={removeEmptyLines}
/>
</div>
<div className="col-12">
<hr />
</div>
<div className="col-12 mb-2"> <div className="col-12 mb-2">
<label <label
className="form__label form__label--with-desc" className="form__label form__label--with-desc"
@ -286,6 +310,7 @@ Form.propTypes = {
invalid: PropTypes.bool, invalid: PropTypes.bool,
initialValues: PropTypes.object, initialValues: PropTypes.object,
upstream_dns: PropTypes.string, upstream_dns: PropTypes.string,
fallback_dns: PropTypes.string,
bootstrap_dns: PropTypes.string, bootstrap_dns: PropTypes.string,
}; };

View File

@ -10,6 +10,7 @@ const Upstream = () => {
const dispatch = useDispatch(); const dispatch = useDispatch();
const { const {
upstream_dns, upstream_dns,
fallback_dns,
bootstrap_dns, bootstrap_dns,
upstream_mode, upstream_mode,
resolve_clients, resolve_clients,
@ -21,6 +22,7 @@ const Upstream = () => {
const handleSubmit = (values) => { const handleSubmit = (values) => {
const { const {
fallback_dns,
bootstrap_dns, bootstrap_dns,
upstream_dns, upstream_dns,
upstream_mode, upstream_mode,
@ -30,6 +32,7 @@ const Upstream = () => {
} = values; } = values;
const dnsConfig = { const dnsConfig = {
fallback_dns,
bootstrap_dns, bootstrap_dns,
upstream_mode, upstream_mode,
resolve_clients, resolve_clients,
@ -52,6 +55,7 @@ const Upstream = () => {
<Form <Form
initialValues={{ initialValues={{
upstream_dns: upstreamDns, upstream_dns: upstreamDns,
fallback_dns,
bootstrap_dns, bootstrap_dns,
upstream_mode, upstream_mode,
resolve_clients, resolve_clients,

View File

@ -15,6 +15,7 @@ const dnsConfig = handleActions(
blocking_ipv4, blocking_ipv4,
blocking_ipv6, blocking_ipv6,
upstream_dns, upstream_dns,
fallback_dns,
bootstrap_dns, bootstrap_dns,
local_ptr_upstreams, local_ptr_upstreams,
...values ...values
@ -26,6 +27,7 @@ const dnsConfig = handleActions(
blocking_ipv4: blocking_ipv4 || DEFAULT_BLOCKING_IPV4, blocking_ipv4: blocking_ipv4 || DEFAULT_BLOCKING_IPV4,
blocking_ipv6: blocking_ipv6 || DEFAULT_BLOCKING_IPV6, blocking_ipv6: blocking_ipv6 || DEFAULT_BLOCKING_IPV6,
upstream_dns: (upstream_dns && upstream_dns.join('\n')) || '', upstream_dns: (upstream_dns && upstream_dns.join('\n')) || '',
fallback_dns: (fallback_dns && fallback_dns.join('\n')) || '',
bootstrap_dns: (bootstrap_dns && bootstrap_dns.join('\n')) || '', bootstrap_dns: (bootstrap_dns && bootstrap_dns.join('\n')) || '',
local_ptr_upstreams: (local_ptr_upstreams && local_ptr_upstreams.join('\n')) || '', local_ptr_upstreams: (local_ptr_upstreams && local_ptr_upstreams.join('\n')) || '',
processingGetConfig: false, processingGetConfig: false,

View File

@ -118,7 +118,8 @@ type FilteringConfig struct {
// resolvers (plain DNS only). // resolvers (plain DNS only).
BootstrapDNS []string `yaml:"bootstrap_dns"` BootstrapDNS []string `yaml:"bootstrap_dns"`
// FallbackDNS is the list of fallback DNS servers. // FallbackDNS is the list of fallback DNS servers used when upstream DNS
// servers are not responding.
FallbackDNS []string `yaml:"fallback_dns"` FallbackDNS []string `yaml:"fallback_dns"`
// AllServers, if true, parallel queries to all configured upstream servers // AllServers, if true, parallel queries to all configured upstream servers

View File

@ -36,6 +36,10 @@ type jsonDNSConfig struct {
// upstream DoH/DoT resolvers. // upstream DoH/DoT resolvers.
Bootstraps *[]string `json:"bootstrap_dns"` Bootstraps *[]string `json:"bootstrap_dns"`
// Fallbacks is the list of fallback DNS servers used when upstream DNS
// servers are not responding.
Fallbacks *[]string `json:"fallback_dns"`
// ProtectionEnabled defines if protection is enabled. // ProtectionEnabled defines if protection is enabled.
ProtectionEnabled *bool `json:"protection_enabled"` ProtectionEnabled *bool `json:"protection_enabled"`
@ -108,6 +112,7 @@ func (s *Server) getDNSConfig() (c *jsonDNSConfig) {
upstreams := stringutil.CloneSliceOrEmpty(s.conf.UpstreamDNS) upstreams := stringutil.CloneSliceOrEmpty(s.conf.UpstreamDNS)
upstreamFile := s.conf.UpstreamDNSFileName upstreamFile := s.conf.UpstreamDNSFileName
bootstraps := stringutil.CloneSliceOrEmpty(s.conf.BootstrapDNS) bootstraps := stringutil.CloneSliceOrEmpty(s.conf.BootstrapDNS)
fallbacks := stringutil.CloneSliceOrEmpty(s.conf.FallbackDNS)
blockingMode := s.conf.BlockingMode blockingMode := s.conf.BlockingMode
blockingIPv4 := s.conf.BlockingIPv4 blockingIPv4 := s.conf.BlockingIPv4
blockingIPv6 := s.conf.BlockingIPv6 blockingIPv6 := s.conf.BlockingIPv6
@ -143,6 +148,7 @@ func (s *Server) getDNSConfig() (c *jsonDNSConfig) {
Upstreams: &upstreams, Upstreams: &upstreams,
UpstreamsFile: &upstreamFile, UpstreamsFile: &upstreamFile,
Bootstraps: &bootstraps, Bootstraps: &bootstraps,
Fallbacks: &fallbacks,
ProtectionEnabled: &protectionEnabled, ProtectionEnabled: &protectionEnabled,
BlockingMode: &blockingMode, BlockingMode: &blockingMode,
BlockingIPv4: blockingIPv4, BlockingIPv4: blockingIPv4,
@ -207,6 +213,20 @@ func (req *jsonDNSConfig) checkBootstrap() (err error) {
return nil return nil
} }
// checkFallbacks returns an error if any fallback address is invalid.
func (req *jsonDNSConfig) checkFallbacks() (err error) {
if req.Fallbacks == nil {
return nil
}
err = ValidateUpstreams(*req.Fallbacks)
if err != nil {
return fmt.Errorf("validating fallback servers: %w", err)
}
return nil
}
// validate returns an error if any field of req is invalid. // validate returns an error if any field of req is invalid.
func (req *jsonDNSConfig) validate(privateNets netutil.SubnetSet) (err error) { func (req *jsonDNSConfig) validate(privateNets netutil.SubnetSet) (err error) {
if req.Upstreams != nil { if req.Upstreams != nil {
@ -228,6 +248,11 @@ func (req *jsonDNSConfig) validate(privateNets netutil.SubnetSet) (err error) {
return err return err
} }
err = req.checkFallbacks()
if err != nil {
return err
}
err = req.checkBlockingMode() err = req.checkBlockingMode()
if err != nil { if err != nil {
return err return err
@ -341,6 +366,7 @@ func (s *Server) setConfigRestartable(dc *jsonDNSConfig) (shouldRestart bool) {
setIfNotNil(&s.conf.LocalPTRResolvers, dc.LocalPTRUpstreams), setIfNotNil(&s.conf.LocalPTRResolvers, dc.LocalPTRUpstreams),
setIfNotNil(&s.conf.UpstreamDNSFileName, dc.UpstreamsFile), setIfNotNil(&s.conf.UpstreamDNSFileName, dc.UpstreamsFile),
setIfNotNil(&s.conf.BootstrapDNS, dc.Bootstraps), setIfNotNil(&s.conf.BootstrapDNS, dc.Bootstraps),
setIfNotNil(&s.conf.FallbackDNS, dc.Fallbacks),
setIfNotNil(&s.conf.EDNSClientSubnet.Enabled, dc.EDNSCSEnabled), setIfNotNil(&s.conf.EDNSClientSubnet.Enabled, dc.EDNSCSEnabled),
setIfNotNil(&s.conf.EDNSClientSubnet.UseCustom, dc.EDNSCSUseCustom), setIfNotNil(&s.conf.EDNSClientSubnet.UseCustom, dc.EDNSCSUseCustom),
setIfNotNil(&s.conf.CacheSize, dc.CacheSize), setIfNotNil(&s.conf.CacheSize, dc.CacheSize),
@ -730,6 +756,7 @@ func (s *Server) handleTestUpstreamDNS(w http.ResponseWriter, r *http.Request) {
result := make(map[string]string, upsNum) result := make(map[string]string, upsNum)
resCh := make(chan upsCheckResult, upsNum) resCh := make(chan upsCheckResult, upsNum)
// TODO(s.chzhen): Check fallback DNS servers.
for _, ups := range req.Upstreams { for _, ups := range req.Upstreams {
go func(ups string) { go func(ups string) {
resCh <- upsCheckResult{ resCh <- upsCheckResult{

View File

@ -72,6 +72,7 @@ func TestDNSForwardHTTP_handleGetConfig(t *testing.T) {
ProtectionEnabled: true, ProtectionEnabled: true,
BlockingMode: BlockingModeDefault, BlockingMode: BlockingModeDefault,
UpstreamDNS: []string{"8.8.8.8:53", "8.8.4.4:53"}, UpstreamDNS: []string{"8.8.8.8:53", "8.8.4.4:53"},
FallbackDNS: []string{"9.9.9.10"},
EDNSClientSubnet: &EDNSClientSubnet{Enabled: false}, EDNSClientSubnet: &EDNSClientSubnet{Enabled: false},
}, },
ConfigModified: func() {}, ConfigModified: func() {},
@ -225,6 +226,9 @@ func TestDNSForwardHTTP_handleSetConfig(t *testing.T) {
}, { }, {
name: "local_ptr_upstreams_null", name: "local_ptr_upstreams_null",
wantSet: "", wantSet: "",
}, {
name: "fallbacks",
wantSet: "",
}} }}
var data map[string]struct { var data map[string]struct {

View File

@ -11,6 +11,9 @@
"2620:fe::10", "2620:fe::10",
"2620:fe::fe:10" "2620:fe::fe:10"
], ],
"fallback_dns": [
"9.9.9.10"
],
"protection_enabled": true, "protection_enabled": true,
"protection_disabled_until": null, "protection_disabled_until": null,
"ratelimit": 0, "ratelimit": 0,
@ -43,6 +46,9 @@
"2620:fe::10", "2620:fe::10",
"2620:fe::fe:10" "2620:fe::fe:10"
], ],
"fallback_dns": [
"9.9.9.10"
],
"protection_enabled": true, "protection_enabled": true,
"protection_disabled_until": null, "protection_disabled_until": null,
"ratelimit": 0, "ratelimit": 0,
@ -75,6 +81,9 @@
"2620:fe::10", "2620:fe::10",
"2620:fe::fe:10" "2620:fe::fe:10"
], ],
"fallback_dns": [
"9.9.9.10"
],
"protection_enabled": true, "protection_enabled": true,
"protection_disabled_until": null, "protection_disabled_until": null,
"ratelimit": 0, "ratelimit": 0,

View File

@ -18,6 +18,7 @@
"2620:fe::10", "2620:fe::10",
"2620:fe::fe:10" "2620:fe::fe:10"
], ],
"fallback_dns": [],
"protection_enabled": true, "protection_enabled": true,
"protection_disabled_until": null, "protection_disabled_until": null,
"ratelimit": 0, "ratelimit": 0,
@ -54,6 +55,7 @@
"bootstrap_dns": [ "bootstrap_dns": [
"9.9.9.10" "9.9.9.10"
], ],
"fallback_dns": [],
"protection_enabled": true, "protection_enabled": true,
"protection_disabled_until": null, "protection_disabled_until": null,
"ratelimit": 0, "ratelimit": 0,
@ -91,6 +93,7 @@
"2620:fe::10", "2620:fe::10",
"2620:fe::fe:10" "2620:fe::fe:10"
], ],
"fallback_dns": [],
"protection_enabled": true, "protection_enabled": true,
"protection_disabled_until": null, "protection_disabled_until": null,
"ratelimit": 0, "ratelimit": 0,
@ -128,6 +131,7 @@
"2620:fe::10", "2620:fe::10",
"2620:fe::fe:10" "2620:fe::fe:10"
], ],
"fallback_dns": [],
"protection_enabled": true, "protection_enabled": true,
"protection_disabled_until": null, "protection_disabled_until": null,
"ratelimit": 0, "ratelimit": 0,
@ -165,6 +169,7 @@
"2620:fe::10", "2620:fe::10",
"2620:fe::fe:10" "2620:fe::fe:10"
], ],
"fallback_dns": [],
"protection_enabled": true, "protection_enabled": true,
"protection_disabled_until": null, "protection_disabled_until": null,
"ratelimit": 6, "ratelimit": 6,
@ -202,6 +207,7 @@
"2620:fe::10", "2620:fe::10",
"2620:fe::fe:10" "2620:fe::fe:10"
], ],
"fallback_dns": [],
"protection_enabled": true, "protection_enabled": true,
"protection_disabled_until": null, "protection_disabled_until": null,
"ratelimit": 0, "ratelimit": 0,
@ -241,6 +247,7 @@
"2620:fe::10", "2620:fe::10",
"2620:fe::fe:10" "2620:fe::fe:10"
], ],
"fallback_dns": [],
"protection_enabled": true, "protection_enabled": true,
"protection_disabled_until": null, "protection_disabled_until": null,
"ratelimit": 0, "ratelimit": 0,
@ -280,6 +287,7 @@
"2620:fe::10", "2620:fe::10",
"2620:fe::fe:10" "2620:fe::fe:10"
], ],
"fallback_dns": [],
"protection_enabled": true, "protection_enabled": true,
"protection_disabled_until": null, "protection_disabled_until": null,
"ratelimit": 0, "ratelimit": 0,
@ -317,6 +325,7 @@
"2620:fe::10", "2620:fe::10",
"2620:fe::fe:10" "2620:fe::fe:10"
], ],
"fallback_dns": [],
"protection_enabled": true, "protection_enabled": true,
"protection_disabled_until": null, "protection_disabled_until": null,
"ratelimit": 0, "ratelimit": 0,
@ -354,6 +363,7 @@
"2620:fe::10", "2620:fe::10",
"2620:fe::fe:10" "2620:fe::fe:10"
], ],
"fallback_dns": [],
"protection_enabled": true, "protection_enabled": true,
"protection_disabled_until": null, "protection_disabled_until": null,
"ratelimit": 0, "ratelimit": 0,
@ -391,6 +401,7 @@
"2620:fe::10", "2620:fe::10",
"2620:fe::fe:10" "2620:fe::fe:10"
], ],
"fallback_dns": [],
"protection_enabled": true, "protection_enabled": true,
"protection_disabled_until": null, "protection_disabled_until": null,
"ratelimit": 0, "ratelimit": 0,
@ -428,6 +439,7 @@
"2620:fe::10", "2620:fe::10",
"2620:fe::fe:10" "2620:fe::fe:10"
], ],
"fallback_dns": [],
"protection_enabled": true, "protection_enabled": true,
"protection_disabled_until": null, "protection_disabled_until": null,
"ratelimit": 0, "ratelimit": 0,
@ -467,6 +479,7 @@
"2620:fe::10", "2620:fe::10",
"2620:fe::fe:10" "2620:fe::fe:10"
], ],
"fallback_dns": [],
"protection_enabled": true, "protection_enabled": true,
"protection_disabled_until": null, "protection_disabled_until": null,
"ratelimit": 0, "ratelimit": 0,
@ -506,6 +519,7 @@
"2620:fe::10", "2620:fe::10",
"2620:fe::fe:10" "2620:fe::fe:10"
], ],
"fallback_dns": [],
"protection_enabled": true, "protection_enabled": true,
"protection_disabled_until": null, "protection_disabled_until": null,
"ratelimit": 0, "ratelimit": 0,
@ -544,6 +558,7 @@
"2620:fe::10", "2620:fe::10",
"2620:fe::fe:10" "2620:fe::fe:10"
], ],
"fallback_dns": [],
"protection_enabled": true, "protection_enabled": true,
"protection_disabled_until": null, "protection_disabled_until": null,
"ratelimit": 0, "ratelimit": 0,
@ -581,6 +596,7 @@
"2620:fe::10", "2620:fe::10",
"2620:fe::fe:10" "2620:fe::fe:10"
], ],
"fallback_dns": [],
"protection_enabled": true, "protection_enabled": true,
"protection_disabled_until": null, "protection_disabled_until": null,
"ratelimit": 0, "ratelimit": 0,
@ -620,6 +636,7 @@
"2620:fe::10", "2620:fe::10",
"2620:fe::fe:10" "2620:fe::fe:10"
], ],
"fallback_dns": [],
"protection_enabled": true, "protection_enabled": true,
"protection_disabled_until": null, "protection_disabled_until": null,
"ratelimit": 0, "ratelimit": 0,
@ -662,6 +679,7 @@
"2620:fe::10", "2620:fe::10",
"2620:fe::fe:10" "2620:fe::fe:10"
], ],
"fallback_dns": [],
"protection_enabled": true, "protection_enabled": true,
"protection_disabled_until": null, "protection_disabled_until": null,
"ratelimit": 0, "ratelimit": 0,
@ -699,6 +717,49 @@
"2620:fe::10", "2620:fe::10",
"2620:fe::fe:10" "2620:fe::fe:10"
], ],
"fallback_dns": [],
"protection_enabled": true,
"protection_disabled_until": null,
"ratelimit": 0,
"blocking_mode": "default",
"blocking_ipv4": "",
"blocking_ipv6": "",
"edns_cs_enabled": false,
"dnssec_enabled": false,
"disable_ipv6": false,
"upstream_mode": "",
"cache_size": 0,
"cache_ttl_min": 0,
"cache_ttl_max": 0,
"cache_optimistic": false,
"resolve_clients": false,
"use_private_ptr_resolvers": false,
"local_ptr_upstreams": [],
"edns_cs_use_custom": false,
"edns_cs_custom_ip": ""
}
},
"fallbacks": {
"req": {
"fallback_dns": [
"9.9.9.10"
]
},
"want": {
"upstream_dns": [
"8.8.8.8:53",
"8.8.4.4:53"
],
"upstream_dns_file": "",
"bootstrap_dns": [
"9.9.9.10",
"149.112.112.10",
"2620:fe::10",
"2620:fe::fe:10"
],
"fallback_dns": [
"9.9.9.10"
],
"protection_enabled": true, "protection_enabled": true,
"protection_disabled_until": null, "protection_disabled_until": null,
"ratelimit": 0, "ratelimit": 0,

View File

@ -6,6 +6,12 @@
## v0.107.37: API changes ## v0.107.37: API changes
### The new field `"fallback_dns"` in `DNSConfig` object
* The new field `"fallback_dns"` in `GET /control/dns_info` and `POST
/control/dns_config` is the list of fallback DNS servers used when upstream
DNS servers are not responding.
### Deprecated blocked services APIs ### Deprecated blocked services APIs
* The `GET /control/blocked_services/list` HTTP API; use the new `GET * The `GET /control/blocked_services/list` HTTP API; use the new `GET

View File

@ -1432,6 +1432,16 @@
'example': 'example':
- 'tls://1.1.1.1' - 'tls://1.1.1.1'
- 'tls://1.0.0.1' - 'tls://1.0.0.1'
'fallback_dns':
'type': 'array'
'description': >
List of fallback DNS servers used when upstream DNS servers are not
responding. Empty value will clear the list.
'items':
'type': 'string'
'example':
- '8.8.8.8'
- '1.1.1.1:53'
'upstream_dns_file': 'upstream_dns_file':
'type': 'string' 'type': 'string'
'protection_enabled': 'protection_enabled':