From 8d753808e1904e434116a40d243169af9e2d74df Mon Sep 17 00:00:00 2001 From: Daniel Date: Wed, 17 Nov 2021 14:12:19 +0100 Subject: [PATCH] Show compatibility notification when all global resolvers fail --- resolver/main.go | 50 +++++++++++++++++++++++++++++++++++++++++++++ resolver/resolve.go | 19 +++++++++++++---- resolver/scopes.go | 12 +++++------ 3 files changed, 71 insertions(+), 10 deletions(-) diff --git a/resolver/main.go b/resolver/main.go index 9d327be1..09c134a2 100644 --- a/resolver/main.go +++ b/resolver/main.go @@ -4,11 +4,14 @@ import ( "context" "net" "strings" + "sync" "time" "github.com/safing/portbase/log" "github.com/safing/portbase/modules" + "github.com/safing/portbase/notifications" "github.com/safing/portmaster/intel" + "github.com/tevino/abool" // module dependencies _ "github.com/safing/portmaster/core/base" @@ -105,3 +108,50 @@ func getLocalAddr(network string) net.Addr { } return nil } + +var ( + failingResolverNotification *notifications.Notification + failingResolverNotificationSet = abool.New() + failingResolverNotificationLock sync.Mutex +) + +func notifyAboutFailingResolvers(err error) { + failingResolverNotificationLock.Lock() + defer failingResolverNotificationLock.Unlock() + failingResolverNotificationSet.Set() + + // Check if already set. + if failingResolverNotification != nil { + return + } + + // Create new notification. + n := ¬ifications.Notification{ + EventID: "resolver:all-configured-resolvers-failed", + Type: notifications.Error, + Title: "Detected DNS Compatibility Issue", + Message: "Portmaster detected that something is interfering with its Secure DNS resolver. This could be a firewall or another secure DNS resolver software. Please check if you are running incompatible [software](https://docs.safing.io/portmaster/install/status/software-compatibility). Otherwise, please report the issue via [GitHub](https://github.com/safing/portmaster/issues) or send a mail to [support@safing.io](mailto:support@safing.io) so we can help you out.", + ShowOnSystem: true, + } + notifications.Notify(n) + + failingResolverNotification = n + n.AttachToModule(module) + + // Report the raw error as module error. + module.NewErrorMessage("resolving", err).Report() +} + +func resetFailingResolversNotification() { + if failingResolverNotificationSet.IsNotSet() { + return + } + + failingResolverNotificationLock.Lock() + defer failingResolverNotificationLock.Unlock() + + if failingResolverNotification != nil { + failingResolverNotification.Delete() + failingResolverNotification = nil + } +} diff --git a/resolver/resolve.go b/resolver/resolve.go index d4a123bd..9e9644a7 100644 --- a/resolver/resolve.go +++ b/resolver/resolve.go @@ -7,12 +7,12 @@ import ( "sync" "time" - "github.com/safing/portmaster/netenv" - "github.com/miekg/dns" "github.com/safing/portbase/database" "github.com/safing/portbase/log" + "github.com/safing/portmaster/compat" + "github.com/safing/portmaster/netenv" ) var ( @@ -313,13 +313,13 @@ retry: func resolveAndCache(ctx context.Context, q *Query, oldCache *RRCache) (rrCache *RRCache, err error) { //nolint:gocognit,gocyclo // get resolvers - resolvers, tryAll := GetResolversInScope(ctx, q) + resolvers, primarySource, tryAll := GetResolversInScope(ctx, q) if len(resolvers) == 0 { return nil, ErrNoCompliance } // check if we are online - if netenv.GetOnlineStatus() == netenv.StatusOffline { + if primarySource != ServerSourceEnv && netenv.GetOnlineStatus() == netenv.StatusOffline { if !netenv.IsConnectivityDomain(q.FQDN) { // we are offline and this is not an online check query return oldCache, ErrOffline @@ -391,6 +391,10 @@ resolveLoop: // Report a successful connection. resolver.Conn.ResetFailure() + // Reset failing resolvers notification, if querying in global scope. + if primarySource == ServerSourceConfigured { + resetFailingResolversNotification() + } break resolveLoop } @@ -401,6 +405,13 @@ resolveLoop: // tried all resolvers, possibly twice if i > 1 { err = fmt.Errorf("all %d query-compliant resolvers failed, last error: %s", len(resolvers), err) + + if primarySource == ServerSourceConfigured && + netenv.Online() && compat.SelfCheckIsFailing() { + notifyAboutFailingResolvers(err) + } else { + resetFailingResolversNotification() + } } } else if rrCache == nil /* defensive */ { err = ErrNotFound diff --git a/resolver/scopes.go b/resolver/scopes.go index 54819358..a291db22 100644 --- a/resolver/scopes.go +++ b/resolver/scopes.go @@ -109,20 +109,20 @@ func domainInScope(dotPrefixedFQDN string, scopeList []string) bool { } // GetResolversInScope returns all resolvers that are in scope the resolve the given query and options. -func GetResolversInScope(ctx context.Context, q *Query) (selected []*Resolver, tryAll bool) { //nolint:gocognit // TODO +func GetResolversInScope(ctx context.Context, q *Query) (selected []*Resolver, primarySource string, tryAll bool) { //nolint:gocognit // TODO resolversLock.RLock() defer resolversLock.RUnlock() // Internal use domains if domainInScope(q.dotPrefixedFQDN, internalSpecialUseDomains) { - return envResolvers, false + return envResolvers, ServerSourceEnv, false } // Special connectivity domains if netenv.IsConnectivityDomain(q.FQDN) && len(systemResolvers) > 0 { // Do not do compliance checks for connectivity domains. selected = append(selected, systemResolvers...) // dhcp assigned resolvers - return selected, false + return selected, ServerSourceOperatingSystem, false } // Prioritize search scopes @@ -137,7 +137,7 @@ func GetResolversInScope(ctx context.Context, q *Query) (selected []*Resolver, t selected = addResolvers(ctx, q, selected, mDNSResolvers) selected = addResolvers(ctx, q, selected, localResolvers) selected = addResolvers(ctx, q, selected, systemResolvers) - return selected, true + return selected, ServerSourceMDNS, true } // Special use domains @@ -145,12 +145,12 @@ func GetResolversInScope(ctx context.Context, q *Query) (selected []*Resolver, t domainInScope(q.dotPrefixedFQDN, specialServiceDomains) { selected = addResolvers(ctx, q, selected, localResolvers) selected = addResolvers(ctx, q, selected, systemResolvers) - return selected, true + return selected, "special", true } // Global domains selected = addResolvers(ctx, q, selected, globalResolvers) - return selected, false + return selected, ServerSourceConfigured, false } func addResolvers(ctx context.Context, q *Query, selected []*Resolver, addResolvers []*Resolver) []*Resolver {