diff --git a/CHANGELOG.md b/CHANGELOG.md index 2b060ada..61f8706a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,8 @@ NOTE: Add new changes BELOW THIS COMMENT. ### Added +- The ability to set custom IP for EDNS Client Subnet by using the DNS-server + configuration section on the DNS settings page in the UI ([#1472]). - The ability to manage safesearch for each service by using the new `safe_search` field ([#1163]). @@ -68,6 +70,7 @@ In this release, the schema version has changed from 17 to 19. ([#5584]). [#1163]: https://github.com/AdguardTeam/AdGuardHome/issues/1163 +[#1472]: https://github.com/AdguardTeam/AdGuardHome/issues/1472 [#5567]: https://github.com/AdguardTeam/AdGuardHome/issues/5567 [#5584]: https://github.com/AdguardTeam/AdGuardHome/issues/5584 diff --git a/client/src/__locales/en.json b/client/src/__locales/en.json index ee0345d7..5ccd771b 100644 --- a/client/src/__locales/en.json +++ b/client/src/__locales/en.json @@ -290,6 +290,8 @@ "rate_limit": "Rate limit", "edns_enable": "Enable EDNS client subnet", "edns_cs_desc": "Add the EDNS Client Subnet option (ECS) to upstream requests and log the values sent by the clients in the query log.", + "edns_use_custom_ip": "Use custom IP for EDNS", + "edns_use_custom_ip_desc": "Allow to use custom IP for EDNS", "rate_limit_desc": "The number of requests per second allowed per client. Setting it to 0 means no limit.", "blocking_ipv4_desc": "IP address to be returned for a blocked A request", "blocking_ipv6_desc": "IP address to be returned for a blocked AAAA request", diff --git a/client/src/components/Settings/Dns/Config/Form.js b/client/src/components/Settings/Dns/Config/Form.js index a2dd2bf9..52d94741 100644 --- a/client/src/components/Settings/Dns/Config/Form.js +++ b/client/src/components/Settings/Dns/Config/Form.js @@ -13,15 +13,11 @@ import { validateIpv4, validateIpv6, validateRequiredValue, + validateIp, } from '../../../../helpers/validators'; import { BLOCKING_MODES, FORM_NAME, UINT32_RANGE } from '../../../../helpers/constants'; const checkboxes = [ - { - name: 'edns_cs_enabled', - placeholder: 'edns_enable', - subtitle: 'edns_cs_desc', - }, { name: 'dnssec_enabled', placeholder: 'dnssec_enable', @@ -66,6 +62,8 @@ const Form = ({ const { t } = useTranslation(); const { blocking_mode, + edns_cs_enabled, + edns_cs_use_custom, } = useSelector((state) => state.form[FORM_NAME.BLOCKING_MODE].values ?? {}, shallowEqual); return
@@ -92,6 +90,39 @@ const Form = ({ /> +
+
+ +
+
+
+
+ +
+ + {edns_cs_use_custom && ()} + +
{checkboxes.map(({ name, placeholder, subtitle }) =>
{ blocking_ipv4, blocking_ipv6, edns_cs_enabled, + edns_cs_use_custom, + edns_cs_custom_ip, dnssec_enabled, disable_ipv6, processingSetConfig, @@ -39,6 +41,8 @@ const Config = () => { edns_cs_enabled, disable_ipv6, dnssec_enabled, + edns_cs_use_custom, + edns_cs_custom_ip, }} onSubmit={handleFormSubmit} processing={processingSetConfig} diff --git a/internal/dnsforward/config.go b/internal/dnsforward/config.go index f18e7513..008b2539 100644 --- a/internal/dnsforward/config.go +++ b/internal/dnsforward/config.go @@ -200,7 +200,7 @@ type FilteringConfig struct { // EDNSClientSubnet is the settings list for EDNS Client Subnet. type EDNSClientSubnet struct { // CustomIP for EDNS Client Subnet. - CustomIP string `yaml:"custom_ip"` + CustomIP netip.Addr `yaml:"custom_ip"` // Enabled defines if EDNS Client Subnet is enabled. Enabled bool `yaml:"enabled"` @@ -340,15 +340,8 @@ func (s *Server) createProxyConfig() (conf proxy.Config, err error) { } if srvConf.EDNSClientSubnet.UseCustom { - // TODO(s.chzhen): Add wrapper around netip.Addr. - var ip net.IP - ip, err = netutil.ParseIP(srvConf.EDNSClientSubnet.CustomIP) - if err != nil { - return conf, fmt.Errorf("edns: %w", err) - } - // TODO(s.chzhen): Use netip.Addr instead of net.IP inside dnsproxy. - conf.EDNSAddr = ip + conf.EDNSAddr = net.IP(srvConf.EDNSClientSubnet.CustomIP.AsSlice()) } if srvConf.CacheSize != 0 { @@ -377,7 +370,7 @@ func (s *Server) createProxyConfig() (conf proxy.Config, err error) { err = s.prepareTLS(&conf) if err != nil { - return conf, fmt.Errorf("validating tls: %w", err) + return proxy.Config{}, fmt.Errorf("validating tls: %w", err) } if c := srvConf.DNSCryptConfig; c.Enabled { @@ -388,7 +381,7 @@ func (s *Server) createProxyConfig() (conf proxy.Config, err error) { } if conf.UpstreamConfig == nil || len(conf.UpstreamConfig.Upstreams) == 0 { - return conf, errors.Error("no default upstream servers configured") + return proxy.Config{}, errors.Error("no default upstream servers configured") } return conf, nil diff --git a/internal/dnsforward/http.go b/internal/dnsforward/http.go index 0c8b6726..924a3675 100644 --- a/internal/dnsforward/http.go +++ b/internal/dnsforward/http.go @@ -23,26 +23,78 @@ import ( ) // jsonDNSConfig is the JSON representation of the DNS server configuration. +// +// TODO(s.chzhen): Split it into smaller pieces. Use aghalg.NullBool instead +// of *bool. type jsonDNSConfig struct { - Upstreams *[]string `json:"upstream_dns"` - UpstreamsFile *string `json:"upstream_dns_file"` - Bootstraps *[]string `json:"bootstrap_dns"` - ProtectionEnabled *bool `json:"protection_enabled"` - RateLimit *uint32 `json:"ratelimit"` - BlockingMode *BlockingMode `json:"blocking_mode"` - EDNSCSEnabled *bool `json:"edns_cs_enabled"` - DNSSECEnabled *bool `json:"dnssec_enabled"` - DisableIPv6 *bool `json:"disable_ipv6"` - UpstreamMode *string `json:"upstream_mode"` - CacheSize *uint32 `json:"cache_size"` - CacheMinTTL *uint32 `json:"cache_ttl_min"` - CacheMaxTTL *uint32 `json:"cache_ttl_max"` - CacheOptimistic *bool `json:"cache_optimistic"` - ResolveClients *bool `json:"resolve_clients"` - UsePrivateRDNS *bool `json:"use_private_ptr_resolvers"` - LocalPTRUpstreams *[]string `json:"local_ptr_upstreams"` - BlockingIPv4 net.IP `json:"blocking_ipv4"` - BlockingIPv6 net.IP `json:"blocking_ipv6"` + // Upstreams is the list of upstream DNS servers. + Upstreams *[]string `json:"upstream_dns"` + + // UpstreamsFile is the file containing upstream DNS servers. + UpstreamsFile *string `json:"upstream_dns_file"` + + // Bootstraps is the list of DNS servers resolving IP addresses of the + // upstream DoH/DoT resolvers. + Bootstraps *[]string `json:"bootstrap_dns"` + + // ProtectionEnabled defines if protection is enabled. + ProtectionEnabled *bool `json:"protection_enabled"` + + // RateLimit is the number of requests per second allowed per client. + RateLimit *uint32 `json:"ratelimit"` + + // BlockingMode defines the way blocked responses are constructed. + BlockingMode *BlockingMode `json:"blocking_mode"` + + // EDNSCSEnabled defines if EDNS Client Subnet is enabled. + EDNSCSEnabled *bool `json:"edns_cs_enabled"` + + // EDNSCSUseCustom defines if EDNSCSCustomIP should be used. + EDNSCSUseCustom *bool `json:"edns_cs_use_custom"` + + // DNSSECEnabled defines if DNSSEC is enabled. + DNSSECEnabled *bool `json:"dnssec_enabled"` + + // DisableIPv6 defines if IPv6 addresses should be dropped. + DisableIPv6 *bool `json:"disable_ipv6"` + + // UpstreamMode defines the way DNS requests are constructed. + UpstreamMode *string `json:"upstream_mode"` + + // CacheSize in bytes. + CacheSize *uint32 `json:"cache_size"` + + // CacheMinTTL is custom minimum TTL for cached DNS responses. + CacheMinTTL *uint32 `json:"cache_ttl_min"` + + // CacheMaxTTL is custom maximum TTL for cached DNS responses. + CacheMaxTTL *uint32 `json:"cache_ttl_max"` + + // CacheOptimistic defines if expired entries should be served. + CacheOptimistic *bool `json:"cache_optimistic"` + + // ResolveClients defines if clients IPs should be resolved into hostnames. + ResolveClients *bool `json:"resolve_clients"` + + // UsePrivateRDNS defines if privates DNS resolvers should be used. + UsePrivateRDNS *bool `json:"use_private_ptr_resolvers"` + + // LocalPTRUpstreams is the list of local private DNS resolvers. + LocalPTRUpstreams *[]string `json:"local_ptr_upstreams"` + + // BlockingIPv4 is custom IPv4 address for blocked A requests. + BlockingIPv4 net.IP `json:"blocking_ipv4"` + + // BlockingIPv6 is custom IPv6 address for blocked AAAA requests. + BlockingIPv6 net.IP `json:"blocking_ipv6"` + + // EDNSCSCustomIP is custom IP for EDNS Client Subnet. + EDNSCSCustomIP netip.Addr `json:"edns_cs_custom_ip"` + + // DefaultLocalPTRUpstreams is used to pass the addresses from + // systemResolvers to the front-end. It's not a pointer to the slice since + // there is no need to omit it while decoding from JSON. + DefaultLocalPTRUpstreams []string `json:"default_local_ptr_upstreams,omitempty"` } func (s *Server) getDNSConfig() (c *jsonDNSConfig) { @@ -57,7 +109,11 @@ func (s *Server) getDNSConfig() (c *jsonDNSConfig) { blockingIPv4 := s.conf.BlockingIPv4 blockingIPv6 := s.conf.BlockingIPv6 ratelimit := s.conf.Ratelimit + + customIP := s.conf.EDNSClientSubnet.CustomIP enableEDNSClientSubnet := s.conf.EDNSClientSubnet.Enabled + useCustom := s.conf.EDNSClientSubnet.UseCustom + enableDNSSEC := s.conf.EnableDNSSEC aaaaDisabled := s.conf.AAAADisabled cacheSize := s.conf.CacheSize @@ -74,46 +130,40 @@ func (s *Server) getDNSConfig() (c *jsonDNSConfig) { upstreamMode = "parallel" } - return &jsonDNSConfig{ - Upstreams: &upstreams, - UpstreamsFile: &upstreamFile, - Bootstraps: &bootstraps, - ProtectionEnabled: &protectionEnabled, - BlockingMode: &blockingMode, - BlockingIPv4: blockingIPv4, - BlockingIPv6: blockingIPv6, - RateLimit: &ratelimit, - EDNSCSEnabled: &enableEDNSClientSubnet, - DNSSECEnabled: &enableDNSSEC, - DisableIPv6: &aaaaDisabled, - CacheSize: &cacheSize, - CacheMinTTL: &cacheMinTTL, - CacheMaxTTL: &cacheMaxTTL, - CacheOptimistic: &cacheOptimistic, - UpstreamMode: &upstreamMode, - ResolveClients: &resolveClients, - UsePrivateRDNS: &usePrivateRDNS, - LocalPTRUpstreams: &localPTRUpstreams, - } -} - -func (s *Server) handleGetConfig(w http.ResponseWriter, r *http.Request) { defLocalPTRUps, err := s.filterOurDNSAddrs(s.sysResolvers.Get()) if err != nil { log.Debug("getting dns configuration: %s", err) } - resp := struct { - jsonDNSConfig - // DefautLocalPTRUpstreams is used to pass the addresses from - // systemResolvers to the front-end. It's not a pointer to the slice - // since there is no need to omit it while decoding from JSON. - DefautLocalPTRUpstreams []string `json:"default_local_ptr_upstreams,omitempty"` - }{ - jsonDNSConfig: *s.getDNSConfig(), - DefautLocalPTRUpstreams: defLocalPTRUps, + return &jsonDNSConfig{ + Upstreams: &upstreams, + UpstreamsFile: &upstreamFile, + Bootstraps: &bootstraps, + ProtectionEnabled: &protectionEnabled, + BlockingMode: &blockingMode, + BlockingIPv4: blockingIPv4, + BlockingIPv6: blockingIPv6, + RateLimit: &ratelimit, + EDNSCSCustomIP: customIP, + EDNSCSEnabled: &enableEDNSClientSubnet, + EDNSCSUseCustom: &useCustom, + DNSSECEnabled: &enableDNSSEC, + DisableIPv6: &aaaaDisabled, + CacheSize: &cacheSize, + CacheMinTTL: &cacheMinTTL, + CacheMaxTTL: &cacheMaxTTL, + CacheOptimistic: &cacheOptimistic, + UpstreamMode: &upstreamMode, + ResolveClients: &resolveClients, + UsePrivateRDNS: &usePrivateRDNS, + LocalPTRUpstreams: &localPTRUpstreams, + DefaultLocalPTRUpstreams: defLocalPTRUps, } +} +// handleGetConfig handles requests to the GET /control/dns_info endpoint. +func (s *Server) handleGetConfig(w http.ResponseWriter, r *http.Request) { + resp := s.getDNSConfig() _ = aghhttp.WriteJSONResponse(w, r, resp) } @@ -204,6 +254,7 @@ func (req *jsonDNSConfig) checkCacheTTL() bool { return min <= max } +// handleSetConfig handles requests to the POST /control/dns_config endpoint. func (s *Server) handleSetConfig(w http.ResponseWriter, r *http.Request) { req := &jsonDNSConfig{} err := json.NewDecoder(r.Body).Decode(req) @@ -231,8 +282,8 @@ func (s *Server) handleSetConfig(w http.ResponseWriter, r *http.Request) { } } -// setConfigRestartable sets the server parameters. shouldRestart is true if -// the server should be restarted to apply changes. +// setConfig sets the server parameters. shouldRestart is true if the server +// should be restarted to apply changes. func (s *Server) setConfig(dc *jsonDNSConfig) (shouldRestart bool) { s.serverLock.Lock() defer s.serverLock.Unlock() @@ -250,6 +301,10 @@ func (s *Server) setConfig(dc *jsonDNSConfig) (shouldRestart bool) { s.conf.FastestAddr = *dc.UpstreamMode == "fastest_addr" } + if dc.EDNSCSUseCustom != nil && *dc.EDNSCSUseCustom { + s.conf.EDNSClientSubnet.CustomIP = dc.EDNSCSCustomIP + } + setIfNotNil(&s.conf.ProtectionEnabled, dc.ProtectionEnabled) setIfNotNil(&s.conf.EnableDNSSEC, dc.DNSSECEnabled) setIfNotNil(&s.conf.AAAADisabled, dc.DisableIPv6) @@ -281,6 +336,7 @@ func (s *Server) setConfigRestartable(dc *jsonDNSConfig) (shouldRestart bool) { setIfNotNil(&s.conf.UpstreamDNSFileName, dc.UpstreamsFile), setIfNotNil(&s.conf.BootstrapDNS, dc.Bootstraps), setIfNotNil(&s.conf.EDNSClientSubnet.Enabled, dc.EDNSCSEnabled), + setIfNotNil(&s.conf.EDNSClientSubnet.UseCustom, dc.EDNSCSUseCustom), setIfNotNil(&s.conf.CacheSize, dc.CacheSize), setIfNotNil(&s.conf.CacheMinTTL, dc.CacheMinTTL), setIfNotNil(&s.conf.CacheMaxTTL, dc.CacheMaxTTL), diff --git a/internal/dnsforward/http_test.go b/internal/dnsforward/http_test.go index ef2228c1..144568d3 100644 --- a/internal/dnsforward/http_test.go +++ b/internal/dnsforward/http_test.go @@ -181,6 +181,12 @@ func TestDNSForwardHTTP_handleSetConfig(t *testing.T) { }, { name: "edns_cs_enabled", wantSet: "", + }, { + name: "edns_cs_use_custom", + wantSet: "", + }, { + name: "edns_cs_use_custom_bad_ip", + wantSet: "decoding request: ParseAddr(\"bad.ip\"): unexpected character (at \"bad.ip\")", }, { name: "dnssec_enabled", wantSet: "", @@ -222,16 +228,20 @@ func TestDNSForwardHTTP_handleSetConfig(t *testing.T) { Req json.RawMessage `json:"req"` Want json.RawMessage `json:"want"` } - loadTestData(t, t.Name()+jsonExt, &data) + + testData := t.Name() + jsonExt + loadTestData(t, testData, &data) for _, tc := range testCases { + // NOTE: Do not use require.Contains, because the size of the data + // prevents it from printing a meaningful error message. caseData, ok := data[tc.name] - require.True(t, ok) + require.Truef(t, ok, "%q does not contain test data for test case %s", testData, tc.name) t.Run(tc.name, func(t *testing.T) { t.Cleanup(func() { s.conf = defaultConf - s.conf.FilteringConfig.EDNSClientSubnet.Enabled = false + s.conf.FilteringConfig.EDNSClientSubnet = &EDNSClientSubnet{} }) rBody := io.NopCloser(bytes.NewReader(caseData.Req)) diff --git a/internal/dnsforward/testdata/TestDNSForwardHTTP_handleGetConfig.json b/internal/dnsforward/testdata/TestDNSForwardHTTP_handleGetConfig.json index 3ac6f2f5..fe2c5666 100644 --- a/internal/dnsforward/testdata/TestDNSForwardHTTP_handleGetConfig.json +++ b/internal/dnsforward/testdata/TestDNSForwardHTTP_handleGetConfig.json @@ -26,7 +26,9 @@ "cache_optimistic": false, "resolve_clients": false, "use_private_ptr_resolvers": false, - "local_ptr_upstreams": [] + "local_ptr_upstreams": [], + "edns_cs_use_custom": false, + "edns_cs_custom_ip": "" }, "fastest_addr": { "upstream_dns": [ @@ -55,7 +57,9 @@ "cache_optimistic": false, "resolve_clients": false, "use_private_ptr_resolvers": false, - "local_ptr_upstreams": [] + "local_ptr_upstreams": [], + "edns_cs_use_custom": false, + "edns_cs_custom_ip": "" }, "parallel": { "upstream_dns": [ @@ -84,6 +88,8 @@ "cache_optimistic": false, "resolve_clients": false, "use_private_ptr_resolvers": false, - "local_ptr_upstreams": [] + "local_ptr_upstreams": [], + "edns_cs_use_custom": false, + "edns_cs_custom_ip": "" } } diff --git a/internal/dnsforward/testdata/TestDNSForwardHTTP_handleSetConfig.json b/internal/dnsforward/testdata/TestDNSForwardHTTP_handleSetConfig.json index f55359a9..ca8c963a 100644 --- a/internal/dnsforward/testdata/TestDNSForwardHTTP_handleSetConfig.json +++ b/internal/dnsforward/testdata/TestDNSForwardHTTP_handleSetConfig.json @@ -33,7 +33,9 @@ "cache_optimistic": false, "resolve_clients": false, "use_private_ptr_resolvers": false, - "local_ptr_upstreams": [] + "local_ptr_upstreams": [], + "edns_cs_use_custom": false, + "edns_cs_custom_ip": "" } }, "bootstraps": { @@ -66,7 +68,9 @@ "cache_optimistic": false, "resolve_clients": false, "use_private_ptr_resolvers": false, - "local_ptr_upstreams": [] + "local_ptr_upstreams": [], + "edns_cs_use_custom": false, + "edns_cs_custom_ip": "" } }, "blocking_mode_good": { @@ -100,7 +104,9 @@ "cache_optimistic": false, "resolve_clients": false, "use_private_ptr_resolvers": false, - "local_ptr_upstreams": [] + "local_ptr_upstreams": [], + "edns_cs_use_custom": false, + "edns_cs_custom_ip": "" } }, "blocking_mode_bad": { @@ -134,7 +140,9 @@ "cache_optimistic": false, "resolve_clients": false, "use_private_ptr_resolvers": false, - "local_ptr_upstreams": [] + "local_ptr_upstreams": [], + "edns_cs_use_custom": false, + "edns_cs_custom_ip": "" } }, "ratelimit": { @@ -168,7 +176,9 @@ "cache_optimistic": false, "resolve_clients": false, "use_private_ptr_resolvers": false, - "local_ptr_upstreams": [] + "local_ptr_upstreams": [], + "edns_cs_use_custom": false, + "edns_cs_custom_ip": "" } }, "edns_cs_enabled": { @@ -202,7 +212,85 @@ "cache_optimistic": false, "resolve_clients": false, "use_private_ptr_resolvers": false, - "local_ptr_upstreams": [] + "local_ptr_upstreams": [], + "edns_cs_use_custom": false, + "edns_cs_custom_ip": "" + } + }, + "edns_cs_use_custom": { + "req": { + "edns_cs_enabled": true, + "edns_cs_use_custom": true, + "edns_cs_custom_ip": "1.2.3.4" + }, + "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" + ], + "protection_enabled": true, + "ratelimit": 0, + "blocking_mode": "default", + "blocking_ipv4": "", + "blocking_ipv6": "", + "edns_cs_enabled": true, + "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": true, + "edns_cs_custom_ip": "1.2.3.4" + } + }, + "edns_cs_use_custom_bad_ip": { + "req": { + "edns_cs_enabled": true, + "edns_cs_use_custom": true, + "edns_cs_custom_ip": "bad.ip" + }, + "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" + ], + "protection_enabled": true, + "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": "" } }, "dnssec_enabled": { @@ -236,7 +324,9 @@ "cache_optimistic": false, "resolve_clients": false, "use_private_ptr_resolvers": false, - "local_ptr_upstreams": [] + "local_ptr_upstreams": [], + "edns_cs_use_custom": false, + "edns_cs_custom_ip": "" } }, "cache_size": { @@ -270,7 +360,9 @@ "cache_optimistic": false, "resolve_clients": false, "use_private_ptr_resolvers": false, - "local_ptr_upstreams": [] + "local_ptr_upstreams": [], + "edns_cs_use_custom": false, + "edns_cs_custom_ip": "" } }, "upstream_mode_parallel": { @@ -304,7 +396,9 @@ "cache_optimistic": false, "resolve_clients": false, "use_private_ptr_resolvers": false, - "local_ptr_upstreams": [] + "local_ptr_upstreams": [], + "edns_cs_use_custom": false, + "edns_cs_custom_ip": "" } }, "upstream_mode_fastest_addr": { @@ -338,7 +432,9 @@ "cache_optimistic": false, "resolve_clients": false, "use_private_ptr_resolvers": false, - "local_ptr_upstreams": [] + "local_ptr_upstreams": [], + "edns_cs_use_custom": false, + "edns_cs_custom_ip": "" } }, "upstream_dns_bad": { @@ -374,7 +470,9 @@ "cache_optimistic": false, "resolve_clients": false, "use_private_ptr_resolvers": false, - "local_ptr_upstreams": [] + "local_ptr_upstreams": [], + "edns_cs_use_custom": false, + "edns_cs_custom_ip": "" } }, "bootstraps_bad": { @@ -410,7 +508,9 @@ "cache_optimistic": false, "resolve_clients": false, "use_private_ptr_resolvers": false, - "local_ptr_upstreams": [] + "local_ptr_upstreams": [], + "edns_cs_use_custom": false, + "edns_cs_custom_ip": "" } }, "cache_bad_ttl": { @@ -445,7 +545,9 @@ "cache_optimistic": false, "resolve_clients": false, "use_private_ptr_resolvers": false, - "local_ptr_upstreams": [] + "local_ptr_upstreams": [], + "edns_cs_use_custom": false, + "edns_cs_custom_ip": "" } }, "upstream_mode_bad": { @@ -479,7 +581,9 @@ "cache_optimistic": false, "resolve_clients": false, "use_private_ptr_resolvers": false, - "local_ptr_upstreams": [] + "local_ptr_upstreams": [], + "edns_cs_use_custom": false, + "edns_cs_custom_ip": "" } }, "local_ptr_upstreams_good": { @@ -517,7 +621,9 @@ "use_private_ptr_resolvers": false, "local_ptr_upstreams": [ "123.123.123.123" - ] + ], + "edns_cs_use_custom": false, + "edns_cs_custom_ip": "" } }, "local_ptr_upstreams_bad": { @@ -554,7 +660,9 @@ "cache_optimistic": false, "resolve_clients": false, "use_private_ptr_resolvers": false, - "local_ptr_upstreams": [] + "local_ptr_upstreams": [], + "edns_cs_use_custom": false, + "edns_cs_custom_ip": "" } }, "local_ptr_upstreams_null": { @@ -588,7 +696,9 @@ "cache_optimistic": false, "resolve_clients": false, "use_private_ptr_resolvers": false, - "local_ptr_upstreams": [] + "local_ptr_upstreams": [], + "edns_cs_use_custom": false, + "edns_cs_custom_ip": "" } } } diff --git a/internal/home/config.go b/internal/home/config.go index 6aad27b6..30dcb69d 100644 --- a/internal/home/config.go +++ b/internal/home/config.go @@ -286,7 +286,7 @@ var config = &configuration{ CacheSize: 4 * 1024 * 1024, EDNSClientSubnet: &dnsforward.EDNSClientSubnet{ - CustomIP: "", + CustomIP: netip.Addr{}, Enabled: false, UseCustom: false, }, diff --git a/openapi/CHANGELOG.md b/openapi/CHANGELOG.md index 20dde662..3b43140e 100644 --- a/openapi/CHANGELOG.md +++ b/openapi/CHANGELOG.md @@ -4,6 +4,18 @@ ## v0.108.0: API changes +## v0.107.27: API changes + +### The new optional fields `"edns_cs_use_custom"` and `"edns_cs_custom_ip"` in `DNSConfig` + +* The new optional fields `"edns_cs_use_custom"` and `"edns_cs_custom_ip"` in + `POST /control/dns_config` method makes AdGuard Home use or not use the + custom IP for EDNS Client Subnet. + +* The new optional fields `"edns_cs_use_custom"` and `"edns_cs_custom_ip"` in + `GET /control/dns_info` method are set if AdGuard Home uses custom IP for + EDNS Client Subnet. + ## v0.107.23: API changes diff --git a/openapi/openapi.yaml b/openapi/openapi.yaml index 0bbac1e0..2ec4d858 100644 --- a/openapi/openapi.yaml +++ b/openapi/openapi.yaml @@ -1254,7 +1254,7 @@ 'example': 'en' 'DNSConfig': 'type': 'object' - 'description': 'Query log configuration' + 'description': 'DNS server configuration' 'properties': 'bootstrap_dns': 'type': 'array' @@ -1280,8 +1280,6 @@ 'type': 'string' 'protection_enabled': 'type': 'boolean' - 'dhcp_available': - 'type': 'boolean' 'ratelimit': 'type': 'integer' 'blocking_mode': @@ -1298,6 +1296,10 @@ 'type': 'string' 'edns_cs_enabled': 'type': 'boolean' + 'edns_cs_use_custom': + 'type': 'boolean' + 'edns_cs_custom_ip': + 'type': 'string' 'disable_ipv6': 'type': 'boolean' 'dnssec_enabled':