diff --git a/intel/resolve.go b/intel/resolve.go index 9e5d02d6..5a760ce6 100644 --- a/intel/resolve.go +++ b/intel/resolve.go @@ -9,7 +9,6 @@ import ( "net" "strings" "sync" - "sync/atomic" "time" "github.com/miekg/dns" @@ -248,11 +247,12 @@ func intelligentResolve(ctx context.Context, fqdn string, qtype dns.Type, securi } log.Tracer(ctx).Warningf("intel: failed to resolve %s%s: all resolvers failed (or were skipped to fulfill the security level)", fqdn, qtype.String()) - log.Criticalf("intel: failed to resolve %s%s: all resolvers failed (or were skipped to fulfill the security level)", fqdn, qtype.String()) + log.Criticalf("intel: failed to resolve %s%s: all resolvers failed (or were skipped to fulfill the security level), resetting servers...", fqdn, qtype.String()) + go resetResolverFailStatus() + return nil // TODO: check if there would be resolvers available in lower security modes and alert user - } func tryResolver(ctx context.Context, resolver *Resolver, lastFailBoundary int64, fqdn string, qtype dns.Type, securityLevel uint8) (*RRCache, bool) { @@ -270,7 +270,7 @@ func tryResolver(ctx context.Context, resolver *Resolver, lastFailBoundary int64 return nil, false } // check if failed recently - if atomic.LoadInt64(resolver.LastFail) > lastFailBoundary { + if resolver.LastFail() > lastFailBoundary { log.Tracer(ctx).Tracef("intel: skipping resolver %s, because it failed recently", resolver) return nil, false } @@ -281,10 +281,10 @@ func tryResolver(ctx context.Context, resolver *Resolver, lastFailBoundary int64 return nil, false } // check if resolver is already initialized - if !resolver.Initialized.IsSet() { + if !resolver.Initialized() { // first should init, others wait resolver.InitLock.Lock() - if resolver.Initialized.IsSet() { + if resolver.Initialized() { // unlock immediately if resolver was initialized resolver.InitLock.Unlock() } else { @@ -292,7 +292,7 @@ func tryResolver(ctx context.Context, resolver *Resolver, lastFailBoundary int64 defer resolver.InitLock.Unlock() } // check if previous init failed - if atomic.LoadInt64(resolver.LastFail) > lastFailBoundary { + if resolver.LastFail() > lastFailBoundary { return nil, false } } @@ -300,21 +300,23 @@ func tryResolver(ctx context.Context, resolver *Resolver, lastFailBoundary int64 rrCache, err := query(ctx, resolver, fqdn, qtype) if err != nil { // check if failing is disabled - if atomic.LoadInt64(resolver.LastFail) == -1 { + if resolver.LastFail() == -1 { log.Tracer(ctx).Tracef("intel: non-failing resolver %s failed, moving to next: %s", resolver, err) // log.Tracef("intel: non-failing resolver %s failed (%s), moving to next", resolver, err) return nil, false } log.Tracer(ctx).Warningf("intel: resolver %s failed, moving to next: %s", resolver, err) log.Warningf("intel: resolver %s failed, moving to next: %s", resolver, err) - resolver.LockReason.Lock() - resolver.FailReason = err.Error() - resolver.LockReason.Unlock() - atomic.StoreInt64(resolver.LastFail, time.Now().Unix()) - resolver.Initialized.UnSet() + resolver.Lock() + resolver.failReason = err.Error() + resolver.lastFail = time.Now().Unix() + resolver.initialized = false + resolver.Unlock() return nil, false } - resolver.Initialized.SetToIf(false, true) + resolver.Lock() + resolver.initialized = true + resolver.Unlock() return rrCache, true } diff --git a/intel/resolver.go b/intel/resolver.go index 8c555859..9af94455 100644 --- a/intel/resolver.go +++ b/intel/resolver.go @@ -10,7 +10,6 @@ import ( "sync" "github.com/miekg/dns" - "github.com/tevino/abool" "github.com/Safing/portbase/log" @@ -20,6 +19,8 @@ import ( // Resolver holds information about an active resolver. type Resolver struct { + sync.Mutex + // static Server string ServerType string @@ -34,20 +35,51 @@ type Resolver struct { Search *[]string SkipFqdnBeforeInit string - // atomic - Initialized *abool.AtomicBool - InitLock sync.Mutex - LastFail *int64 - Expires *int64 + InitLock sync.Mutex // must be locked - LockReason sync.Mutex - FailReason string + initialized bool + lastFail int64 + failReason string + fails int + expires int64 - // TODO: add: - // Expiration (for server got from DHCP / ICMPv6) - // bootstrapping (first query is already sent, wait for it to either succeed or fail - think about http bootstrapping here!) - // expanded server info: type, server address, server port, options - so we do not have to parse this every time! + // TODO: add Expiration (for server got from DHCP / ICMPv6) +} + +// Initialized returns the internal initialized value while locking the Resolver. +func (r *Resolver) Initialized() bool { + r.Lock() + defer r.Unlock() + return r.initialized +} + +// LastFail returns the internal lastfail value while locking the Resolver. +func (r *Resolver) LastFail() int64 { + r.Lock() + defer r.Unlock() + return r.lastFail +} + +// FailReason returns the internal failreason value while locking the Resolver. +func (r *Resolver) FailReason() string { + r.Lock() + defer r.Unlock() + return r.failReason +} + +// Fails returns the internal fails value while locking the Resolver. +func (r *Resolver) Fails() int { + r.Lock() + defer r.Unlock() + return r.fails +} + +// Expires returns the internal expires value while locking the Resolver. +func (r *Resolver) Expires() int64 { + r.Lock() + defer r.Unlock() + return r.expires } func (r *Resolver) String() string { @@ -144,7 +176,6 @@ configuredServersLoop: continue configuredServersLoop } - var lastFail int64 new := &Resolver{ Server: server, ServerType: strings.ToLower(parts[0]), @@ -152,9 +183,7 @@ configuredServersLoop: ServerIP: ip, ServerIPScope: netutils.ClassifyIP(ip), ServerPort: port, - LastFail: &lastFail, Source: "config", - Initialized: abool.NewBool(false), } switch new.ServerType { @@ -197,7 +226,6 @@ assignedServersLoop: key = indexOfResolver(server, globalResolvers) if resetResolvers || key == -1 { - var lastFail int64 new := &Resolver{ Server: server, ServerType: "dns", @@ -205,9 +233,7 @@ assignedServersLoop: ServerIP: nameserver.IP, ServerIPScope: netutils.ClassifyIP(nameserver.IP), ServerPort: 53, - LastFail: &lastFail, Source: "dhcp", - Initialized: abool.NewBool(false), } new.clientManager = newDNSClientManager(new) @@ -288,3 +314,18 @@ assignedServersLoop: } } + +// resetResolverFailStatus resets all resolver failures. +func resetResolverFailStatus() { + resolversLock.Lock() + defer resolversLock.Unlock() + + log.Tracef("old: %+v %+v, ", globalResolvers, localResolvers) + for _, resolver := range append(globalResolvers, localResolvers...) { + resolver.Lock() + resolver.failReason = "" + resolver.lastFail = 0 + resolver.Unlock() + } + log.Tracef("new: %+v %+v, ", globalResolvers, localResolvers) +}