From 1ec3ec24ae2cf33a1aa95f168ebdc13aa06e7fa4 Mon Sep 17 00:00:00 2001 From: Stanislav Chzhen Date: Wed, 16 Jul 2025 22:16:37 +0300 Subject: [PATCH] all: http api --- CHANGELOG.md | 6 +++++ client/src/__locales/en.json | 3 +++ .../components/Settings/Dns/Cache/Form.tsx | 26 ++++++++++++++++++- .../components/Settings/Dns/Cache/index.tsx | 3 ++- client/src/initialState.ts | 1 + internal/configmigrate/v30.go | 6 +---- internal/dnsforward/http.go | 19 +++++++++++--- .../TestDNSForwardHTTP_handleGetConfig.json | 3 +++ .../TestDNSForwardHTTP_handleSetConfig.json | 25 ++++++++++++++++++ openapi/CHANGELOG.md | 4 +++ openapi/openapi.yaml | 4 +++ 11 files changed, 89 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0a8f2230..4758933c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,12 @@ NOTE: Add new changes BELOW THIS COMMENT. - Go version has been updated to prevent the possibility of exploiting the Go vulnerabilities fixed in [1.24.5][go-1.24.5]. +### Added + +- Ability to enable or disable the global DNS response cache from the Web UI. + +- A new `"cache_enabled"` field to the HTTP API (`GET /control/dns_info` and `POST /control/dns_config`). See `openapi/openapi.yaml` for the full description. + ### Changed #### Configuration changes diff --git a/client/src/__locales/en.json b/client/src/__locales/en.json index e8e17250..81c57196 100644 --- a/client/src/__locales/en.json +++ b/client/src/__locales/en.json @@ -655,7 +655,10 @@ "safe_search": "Safe Search", "blocklist": "Blocklist", "milliseconds_abbreviation": "ms", + "cache_enabled": "Enable cache", + "cache_enabled_desc": "Store DNS responses locally.", "cache_size": "Cache size", + "cache_size_validation": "Cache size must be greater than zero when cache is enabled.", "cache_size_desc": "DNS cache size (in bytes). To disable caching, set to 0.", "cache_ttl_min_override": "Override minimum TTL", "cache_ttl_max_override": "Override maximum TTL", diff --git a/client/src/components/Settings/Dns/Cache/Form.tsx b/client/src/components/Settings/Dns/Cache/Form.tsx index aea6a299..81154a6e 100644 --- a/client/src/components/Settings/Dns/Cache/Form.tsx +++ b/client/src/components/Settings/Dns/Cache/Form.tsx @@ -32,6 +32,7 @@ const INPUTS_FIELDS = [ ]; type FormData = { + cache_enabled: boolean; cache_size: number; cache_ttl_min: number; cache_ttl_max: number; @@ -58,6 +59,7 @@ const Form = ({ initialValues, onSubmit }: CacheFormProps) => { } = useForm({ mode: 'onBlur', defaultValues: { + cache_enabled: initialValues?.cache_enabled || false, cache_size: initialValues?.cache_size || 0, cache_ttl_min: initialValues?.cache_ttl_min || 0, cache_ttl_max: initialValues?.cache_ttl_max || 0, @@ -65,10 +67,13 @@ const Form = ({ initialValues, onSubmit }: CacheFormProps) => { }, }); + const cache_enabled = watch('cache_enabled'); + const cache_size = watch('cache_size'); const cache_ttl_min = watch('cache_ttl_min'); const cache_ttl_max = watch('cache_ttl_max'); const minExceedsMax = cache_ttl_min > 0 && cache_ttl_max > 0 && cache_ttl_min > cache_ttl_max; + const cacheSizeZeroWhenEnabled = cache_enabled && cache_size === 0; const handleClearCache = () => { if (window.confirm(t('confirm_dns_cache_clear'))) { @@ -79,6 +84,24 @@ const Form = ({ initialValues, onSubmit }: CacheFormProps) => { return (
+
+
+ ( + + )} + /> +
+
+ {INPUTS_FIELDS.map(({ name, title, description, placeholder }) => (
@@ -106,6 +129,7 @@ const Form = ({ initialValues, onSubmit }: CacheFormProps) => {
))} + {cacheSizeZeroWhenEnabled && {t('cache_size_validation')}} {minExceedsMax && {t('ttl_cache_validation')}}
@@ -133,7 +157,7 @@ const Form = ({ initialValues, onSubmit }: CacheFormProps) => { type="submit" data-testid="dns_save" className="btn btn-success btn-standard btn-large" - disabled={isSubmitting || !isDirty || processingSetConfig || minExceedsMax}> + disabled={isSubmitting || !isDirty || processingSetConfig || minExceedsMax || cacheSizeZeroWhenEnabled}> {t('save_btn')} diff --git a/client/src/components/Settings/Dns/Cache/index.tsx b/client/src/components/Settings/Dns/Cache/index.tsx index 1eeff122..51c04c41 100644 --- a/client/src/components/Settings/Dns/Cache/index.tsx +++ b/client/src/components/Settings/Dns/Cache/index.tsx @@ -13,7 +13,7 @@ import { RootState } from '../../../../initialState'; const CacheConfig = () => { const { t } = useTranslation(); const dispatch = useDispatch(); - const { cache_size, cache_ttl_max, cache_ttl_min, cache_optimistic } = useSelector( + const { cache_enabled, cache_size, cache_ttl_max, cache_ttl_min, cache_optimistic } = useSelector( (state: RootState) => state.dnsConfig, shallowEqual, ); @@ -32,6 +32,7 @@ const CacheConfig = () => {
0 { - dnsConf["cache_enabled"] = true - } else { - dnsConf["cache_enabled"] = false - } + dnsConf["cache_enabled"] = cacheSize > 0 return nil } diff --git a/internal/dnsforward/http.go b/internal/dnsforward/http.go index 59f3fde8..2cb30f24 100644 --- a/internal/dnsforward/http.go +++ b/internal/dnsforward/http.go @@ -93,6 +93,9 @@ type jsonDNSConfig struct { // CacheMaxTTL is custom maximum TTL for cached DNS responses. CacheMaxTTL *uint32 `json:"cache_ttl_max"` + // CacheEnabled defines if the DNS cache should be used. + CacheEnabled *bool `json:"cache_enabled"` + // CacheOptimistic defines if expired entries should be served. CacheOptimistic *bool `json:"cache_optimistic"` @@ -162,6 +165,7 @@ func (s *Server) getDNSConfig() (c *jsonDNSConfig) { enableDNSSEC := s.conf.EnableDNSSEC aaaaDisabled := s.conf.AAAADisabled + cacheEnabled := s.conf.CacheEnabled cacheSize := s.conf.CacheSize cacheMinTTL := s.conf.CacheMinTTL cacheMaxTTL := s.conf.CacheMaxTTL @@ -207,6 +211,7 @@ func (s *Server) getDNSConfig() (c *jsonDNSConfig) { DNSSECEnabled: &enableDNSSEC, DisableIPv6: &aaaaDisabled, BlockedResponseTTL: &blockedResponseTTL, + CacheEnabled: &cacheEnabled, CacheSize: &cacheSize, CacheMinTTL: &cacheMinTTL, CacheMaxTTL: &cacheMaxTTL, @@ -305,7 +310,7 @@ func (req *jsonDNSConfig) validate( return err } - err = req.checkCacheTTL() + err = req.checkCacheSettings() if err != nil { // Don't wrap the error since it's informative enough as is. return err @@ -421,9 +426,14 @@ func (req *jsonDNSConfig) validateUpstreamDNSServers( return nil } -// checkCacheTTL returns an error if the configuration of the cache TTL is -// invalid. -func (req *jsonDNSConfig) checkCacheTTL() (err error) { +// checkCacheSettings returns an error if the cache configuration is invalid. +func (req *jsonDNSConfig) checkCacheSettings() (err error) { + if req.CacheEnabled != nil && *req.CacheEnabled { + if req.CacheSize == nil || *req.CacheSize == 0 { + return errors.Error("cache_size must be greater than 0 when cache_enabled is true") + } + } + if req.CacheMinTTL == nil && req.CacheMaxTTL == nil { return nil } @@ -596,6 +606,7 @@ func (s *Server) setConfigRestartable(dc *jsonDNSConfig) (shouldRestart bool) { setIfNotNil(&s.conf.FallbackDNS, dc.Fallbacks), setIfNotNil(&s.conf.EDNSClientSubnet.Enabled, dc.EDNSCSEnabled), setIfNotNil(&s.conf.EDNSClientSubnet.UseCustom, dc.EDNSCSUseCustom), + setIfNotNil(&s.conf.CacheEnabled, dc.CacheEnabled), setIfNotNil(&s.conf.CacheSize, dc.CacheSize), setIfNotNil(&s.conf.CacheMinTTL, dc.CacheMinTTL), setIfNotNil(&s.conf.CacheMaxTTL, dc.CacheMaxTTL), diff --git a/internal/dnsforward/testdata/TestDNSForwardHTTP_handleGetConfig.json b/internal/dnsforward/testdata/TestDNSForwardHTTP_handleGetConfig.json index d4e76a20..9a3e4750 100644 --- a/internal/dnsforward/testdata/TestDNSForwardHTTP_handleGetConfig.json +++ b/internal/dnsforward/testdata/TestDNSForwardHTTP_handleGetConfig.json @@ -32,6 +32,7 @@ "cache_size": 0, "cache_ttl_min": 0, "cache_ttl_max": 0, + "cache_enabled": false, "cache_optimistic": false, "resolve_clients": false, "use_private_ptr_resolvers": false, @@ -72,6 +73,7 @@ "cache_size": 0, "cache_ttl_min": 0, "cache_ttl_max": 0, + "cache_enabled": false, "cache_optimistic": false, "resolve_clients": false, "use_private_ptr_resolvers": false, @@ -112,6 +114,7 @@ "cache_size": 0, "cache_ttl_min": 0, "cache_ttl_max": 0, + "cache_enabled": false, "cache_optimistic": false, "resolve_clients": false, "use_private_ptr_resolvers": false, diff --git a/internal/dnsforward/testdata/TestDNSForwardHTTP_handleSetConfig.json b/internal/dnsforward/testdata/TestDNSForwardHTTP_handleSetConfig.json index d0967ece..21947f0f 100644 --- a/internal/dnsforward/testdata/TestDNSForwardHTTP_handleSetConfig.json +++ b/internal/dnsforward/testdata/TestDNSForwardHTTP_handleSetConfig.json @@ -37,6 +37,7 @@ "cache_size": 0, "cache_ttl_min": 0, "cache_ttl_max": 0, + "cache_enabled": false, "cache_optimistic": false, "resolve_clients": false, "use_private_ptr_resolvers": false, @@ -79,6 +80,7 @@ "cache_size": 0, "cache_ttl_min": 0, "cache_ttl_max": 0, + "cache_enabled": false, "cache_optimistic": false, "resolve_clients": false, "use_private_ptr_resolvers": false, @@ -122,6 +124,7 @@ "cache_size": 0, "cache_ttl_min": 0, "cache_ttl_max": 0, + "cache_enabled": false, "cache_optimistic": false, "resolve_clients": false, "use_private_ptr_resolvers": false, @@ -165,6 +168,7 @@ "cache_size": 0, "cache_ttl_min": 0, "cache_ttl_max": 0, + "cache_enabled": false, "cache_optimistic": false, "resolve_clients": false, "use_private_ptr_resolvers": false, @@ -208,6 +212,7 @@ "cache_size": 0, "cache_ttl_min": 0, "cache_ttl_max": 0, + "cache_enabled": false, "cache_optimistic": false, "resolve_clients": false, "use_private_ptr_resolvers": false, @@ -253,6 +258,7 @@ "cache_size": 0, "cache_ttl_min": 0, "cache_ttl_max": 0, + "cache_enabled": false, "cache_optimistic": false, "resolve_clients": false, "use_private_ptr_resolvers": false, @@ -299,6 +305,7 @@ "cache_size": 0, "cache_ttl_min": 0, "cache_ttl_max": 0, + "cache_enabled": false, "cache_optimistic": false, "resolve_clients": false, "use_private_ptr_resolvers": false, @@ -342,6 +349,7 @@ "cache_size": 0, "cache_ttl_min": 0, "cache_ttl_max": 0, + "cache_enabled": false, "cache_optimistic": false, "resolve_clients": false, "use_private_ptr_resolvers": false, @@ -387,6 +395,7 @@ "cache_size": 0, "cache_ttl_min": 0, "cache_ttl_max": 0, + "cache_enabled": false, "cache_optimistic": false, "resolve_clients": false, "use_private_ptr_resolvers": false, @@ -432,6 +441,7 @@ "cache_size": 0, "cache_ttl_min": 0, "cache_ttl_max": 0, + "cache_enabled": false, "cache_optimistic": false, "resolve_clients": false, "use_private_ptr_resolvers": false, @@ -475,6 +485,7 @@ "cache_size": 0, "cache_ttl_min": 0, "cache_ttl_max": 0, + "cache_enabled": false, "cache_optimistic": false, "resolve_clients": false, "use_private_ptr_resolvers": false, @@ -518,6 +529,7 @@ "cache_size": 1024, "cache_ttl_min": 0, "cache_ttl_max": 0, + "cache_enabled": false, "cache_optimistic": false, "resolve_clients": false, "use_private_ptr_resolvers": false, @@ -561,6 +573,7 @@ "cache_size": 0, "cache_ttl_min": 0, "cache_ttl_max": 0, + "cache_enabled": false, "cache_optimistic": false, "resolve_clients": false, "use_private_ptr_resolvers": false, @@ -604,6 +617,7 @@ "cache_size": 0, "cache_ttl_min": 0, "cache_ttl_max": 0, + "cache_enabled": false, "cache_optimistic": false, "resolve_clients": false, "use_private_ptr_resolvers": false, @@ -649,6 +663,7 @@ "cache_size": 0, "cache_ttl_min": 0, "cache_ttl_max": 0, + "cache_enabled": false, "cache_optimistic": false, "resolve_clients": false, "use_private_ptr_resolvers": false, @@ -694,6 +709,7 @@ "cache_size": 0, "cache_ttl_min": 0, "cache_ttl_max": 0, + "cache_enabled": false, "cache_optimistic": false, "resolve_clients": false, "use_private_ptr_resolvers": false, @@ -738,6 +754,7 @@ "cache_size": 0, "cache_ttl_min": 0, "cache_ttl_max": 0, + "cache_enabled": false, "cache_optimistic": false, "resolve_clients": false, "use_private_ptr_resolvers": false, @@ -781,6 +798,7 @@ "cache_size": 0, "cache_ttl_min": 0, "cache_ttl_max": 0, + "cache_enabled": false, "cache_optimistic": false, "resolve_clients": false, "use_private_ptr_resolvers": false, @@ -826,6 +844,7 @@ "cache_size": 0, "cache_ttl_min": 0, "cache_ttl_max": 0, + "cache_enabled": false, "cache_optimistic": false, "resolve_clients": false, "use_private_ptr_resolvers": false, @@ -874,6 +893,7 @@ "cache_size": 0, "cache_ttl_min": 0, "cache_ttl_max": 0, + "cache_enabled": false, "cache_optimistic": false, "resolve_clients": false, "use_private_ptr_resolvers": false, @@ -917,6 +937,7 @@ "cache_size": 0, "cache_ttl_min": 0, "cache_ttl_max": 0, + "cache_enabled": false, "cache_optimistic": false, "resolve_clients": false, "use_private_ptr_resolvers": false, @@ -964,6 +985,7 @@ "cache_size": 0, "cache_ttl_min": 0, "cache_ttl_max": 0, + "cache_enabled": false, "cache_optimistic": false, "resolve_clients": false, "use_private_ptr_resolvers": false, @@ -1007,6 +1029,7 @@ "cache_size": 0, "cache_ttl_min": 0, "cache_ttl_max": 0, + "cache_enabled": false, "cache_optimistic": false, "resolve_clients": false, "use_private_ptr_resolvers": false, @@ -1053,6 +1076,7 @@ "cache_size": 0, "cache_ttl_min": 0, "cache_ttl_max": 0, + "cache_enabled": false, "cache_optimistic": false, "resolve_clients": false, "use_private_ptr_resolvers": false, @@ -1096,6 +1120,7 @@ "cache_size": 0, "cache_ttl_min": 0, "cache_ttl_max": 0, + "cache_enabled": false, "cache_optimistic": false, "resolve_clients": false, "use_private_ptr_resolvers": false, diff --git a/openapi/CHANGELOG.md b/openapi/CHANGELOG.md index 20132c42..179ae3f1 100644 --- a/openapi/CHANGELOG.md +++ b/openapi/CHANGELOG.md @@ -4,6 +4,10 @@ ## v0.108.0: API changes +## v0.107.64: API changes + +- The new field `"cache_enabled"` in `GET /control/dns_info` and `POST /control/dns_config` enables or disables the DNS response cache. + ## v0.107.58: API changes ### The ability to check rules for query types and/or clients: GET /control/check_host diff --git a/openapi/openapi.yaml b/openapi/openapi.yaml index 77315d41..8e46d70b 100644 --- a/openapi/openapi.yaml +++ b/openapi/openapi.yaml @@ -1559,6 +1559,10 @@ 'type': 'integer' 'cache_ttl_max': 'type': 'integer' + 'cache_enabled': + 'type': 'boolean' + 'description': | + Enables or disables the DNS response cache. 'cache_optimistic': 'type': 'boolean' 'upstream_mode':