diff --git a/resolver/api.go b/resolver/api.go index a31b19a1..72703462 100644 --- a/resolver/api.go +++ b/resolver/api.go @@ -12,7 +12,7 @@ func registerAPI() error { Path: "dns/clear", Write: api.PermitUser, BelongsTo: module, - ActionFunc: clearNameCache, + ActionFunc: clearNameCacheHandler, Name: "Clear cached DNS records", Description: "Deletes all saved DNS records from the database.", }); err != nil { diff --git a/resolver/config.go b/resolver/config.go index b274f119..b274ee23 100644 --- a/resolver/config.go +++ b/resolver/config.go @@ -31,7 +31,7 @@ var ( // Cloudflare (encrypted DNS, with malware protection) `dot://1.1.1.2:853?verify=cloudflare-dns.com&name=Cloudflare&blockedif=zeroip`, - // `dot://1.0.0.2:853?verify=cloudflare-dns.com&name=Cloudflare&blockedif=zeroip`, + `dot://1.0.0.2:853?verify=cloudflare-dns.com&name=Cloudflare&blockedif=zeroip`, // AdGuard (encrypted DNS, default flavor) // `dot://94.140.14.14:853?verify=dns.adguard.com&name=AdGuard&blockedif=zeroip`, @@ -107,8 +107,9 @@ The format is: "protocol://ip:port?parameter=value¶meter=value" - "search": specify prioritized domains/TLDs for this resolver (delimited by ",") - "search-only": use this resolver for domains in the "search" parameter only (no value) `, `"`, "`"), + Sensitive: true, OptType: config.OptTypeStringArray, - ExpertiseLevel: config.ExpertiseLevelExpert, + ExpertiseLevel: config.ExpertiseLevelUser, ReleaseLevel: config.ReleaseLevelStable, DefaultValue: defaultNameServers, ValidationRegex: fmt.Sprintf("^(%s|%s|%s)://.*", ServerTypeDoT, ServerTypeDNS, ServerTypeTCP), @@ -118,6 +119,14 @@ The format is: "protocol://ip:port?parameter=value¶meter=value" config.DisplayOrderAnnotation: cfgOptionNameServersOrder, config.CategoryAnnotation: "Servers", config.QuickSettingsAnnotation: []config.QuickSetting{ + { + Name: "Cloudflare (with Malware Filter)", + Action: config.QuickReplace, + Value: []string{ + "dot://1.1.1.2:853?verify=cloudflare-dns.com&name=Cloudflare&blockedif=zeroip", + "dot://1.0.0.2:853?verify=cloudflare-dns.com&name=Cloudflare&blockedif=zeroip", + }, + }, { Name: "Quad9", Action: config.QuickReplace, @@ -142,14 +151,6 @@ The format is: "protocol://ip:port?parameter=value¶meter=value" "dot://94.130.106.88:443?verify=dot1.applied-privacy.net&name=AppliedPrivacy", }, }, - { - Name: "Cloudflare (with Malware Filter)", - Action: config.QuickReplace, - Value: []string{ - "dot://1.1.1.2:853?verify=cloudflare-dns.com&name=Cloudflare&blockedif=zeroip", - "dot://1.0.0.2:853?verify=cloudflare-dns.com&name=Cloudflare&blockedif=zeroip", - }, - }, }, "self:detail:internalSpecialUseDomains": internalSpecialUseDomains, "self:detail:connectivityDomains": netenv.ConnectivityDomains, diff --git a/resolver/namerecord.go b/resolver/namerecord.go index 8e4841f2..e3db05e0 100644 --- a/resolver/namerecord.go +++ b/resolver/namerecord.go @@ -1,6 +1,7 @@ package resolver import ( + "context" "errors" "fmt" "sync" @@ -129,13 +130,18 @@ func (nameRecord *NameRecord) Save() error { return recordDatabase.PutNew(nameRecord) } -// clearNameCache clears all dns caches from the database. -func clearNameCache(ar *api.Request) (msg string, err error) { +// clearNameCacheHandler is an API handler that clears all dns caches from the database. +func clearNameCacheHandler(ar *api.Request) (msg string, err error) { log.Info("resolver: user requested dns cache clearing via action") + return clearNameCache(ar.Context()) +} + +// clearNameCache clears all dns caches from the database. +func clearNameCache(ctx context.Context) (msg string, err error) { recordDatabase.FlushCache() recordDatabase.ClearCache() - n, err := recordDatabase.Purge(ar.Context(), query.New(nameRecordsKeyPrefix)) + n, err := recordDatabase.Purge(ctx, query.New(nameRecordsKeyPrefix)) if err != nil { return "", err } diff --git a/resolver/resolvers.go b/resolver/resolvers.go index 0f7eaa16..4360d836 100644 --- a/resolver/resolvers.go +++ b/resolver/resolvers.go @@ -1,6 +1,7 @@ package resolver import ( + "context" "fmt" "net" "net/url" @@ -34,12 +35,13 @@ const ( ) var ( - globalResolvers []*Resolver // all (global) resolvers - localResolvers []*Resolver // all resolvers that are in site-local or link-local IP ranges - systemResolvers []*Resolver // all resolvers that were assigned by the system - localScopes []*Scope // list of scopes with a list of local resolvers that can resolve the scope - activeResolvers map[string]*Resolver // lookup map of all resolvers - resolversLock sync.RWMutex + globalResolvers []*Resolver // all (global) resolvers + localResolvers []*Resolver // all resolvers that are in site-local or link-local IP ranges + systemResolvers []*Resolver // all resolvers that were assigned by the system + localScopes []*Scope // list of scopes with a list of local resolvers that can resolve the scope + activeResolvers map[string]*Resolver // lookup map of all resolvers + currentResolverConfig []string // current active resolver config, to detect changes + resolversLock sync.RWMutex ) func indexOfScope(domain string, list []*Scope) int { @@ -285,8 +287,20 @@ func loadResolvers() { // Resolve module error about missing resolvers. module.Resolve(missingResolversErrorID) + // Check if settings were changed and clear name cache when they did. + newResolverConfig := configuredNameServers() + if len(currentResolverConfig) > 0 && + !utils.StringSliceEqual(currentResolverConfig, newResolverConfig) { + module.StartWorker("clear dns cache", func(ctx context.Context) error { + log.Info("resolver: clearing dns cache due to changed resolver config") + _, err := clearNameCache(ctx) + return err + }) + } + currentResolverConfig = newResolverConfig + newResolvers := append( - getConfiguredResolvers(configuredNameServers()), + getConfiguredResolvers(newResolverConfig), getSystemResolvers()..., )