Update DNS resolver, reset resolvers if all failed

This commit is contained in:
Daniel 2019-05-22 16:06:57 +02:00
parent 1f13af8f75
commit ad7a1024d7
2 changed files with 75 additions and 32 deletions

View file

@ -9,7 +9,6 @@ import (
"net" "net"
"strings" "strings"
"sync" "sync"
"sync/atomic"
"time" "time"
"github.com/miekg/dns" "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.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 return nil
// TODO: check if there would be resolvers available in lower security modes and alert user // 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) { 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 return nil, false
} }
// check if failed recently // 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) log.Tracer(ctx).Tracef("intel: skipping resolver %s, because it failed recently", resolver)
return nil, false return nil, false
} }
@ -281,10 +281,10 @@ func tryResolver(ctx context.Context, resolver *Resolver, lastFailBoundary int64
return nil, false return nil, false
} }
// check if resolver is already initialized // check if resolver is already initialized
if !resolver.Initialized.IsSet() { if !resolver.Initialized() {
// first should init, others wait // first should init, others wait
resolver.InitLock.Lock() resolver.InitLock.Lock()
if resolver.Initialized.IsSet() { if resolver.Initialized() {
// unlock immediately if resolver was initialized // unlock immediately if resolver was initialized
resolver.InitLock.Unlock() resolver.InitLock.Unlock()
} else { } else {
@ -292,7 +292,7 @@ func tryResolver(ctx context.Context, resolver *Resolver, lastFailBoundary int64
defer resolver.InitLock.Unlock() defer resolver.InitLock.Unlock()
} }
// check if previous init failed // check if previous init failed
if atomic.LoadInt64(resolver.LastFail) > lastFailBoundary { if resolver.LastFail() > lastFailBoundary {
return nil, false return nil, false
} }
} }
@ -300,21 +300,23 @@ func tryResolver(ctx context.Context, resolver *Resolver, lastFailBoundary int64
rrCache, err := query(ctx, resolver, fqdn, qtype) rrCache, err := query(ctx, resolver, fqdn, qtype)
if err != nil { if err != nil {
// check if failing is disabled // 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.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) // log.Tracef("intel: non-failing resolver %s failed (%s), moving to next", resolver, err)
return nil, false return nil, false
} }
log.Tracer(ctx).Warningf("intel: resolver %s failed, moving to next: %s", resolver, err) 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) log.Warningf("intel: resolver %s failed, moving to next: %s", resolver, err)
resolver.LockReason.Lock() resolver.Lock()
resolver.FailReason = err.Error() resolver.failReason = err.Error()
resolver.LockReason.Unlock() resolver.lastFail = time.Now().Unix()
atomic.StoreInt64(resolver.LastFail, time.Now().Unix()) resolver.initialized = false
resolver.Initialized.UnSet() resolver.Unlock()
return nil, false return nil, false
} }
resolver.Initialized.SetToIf(false, true) resolver.Lock()
resolver.initialized = true
resolver.Unlock()
return rrCache, true return rrCache, true
} }

View file

@ -10,7 +10,6 @@ import (
"sync" "sync"
"github.com/miekg/dns" "github.com/miekg/dns"
"github.com/tevino/abool"
"github.com/Safing/portbase/log" "github.com/Safing/portbase/log"
@ -20,6 +19,8 @@ import (
// Resolver holds information about an active resolver. // Resolver holds information about an active resolver.
type Resolver struct { type Resolver struct {
sync.Mutex
// static // static
Server string Server string
ServerType string ServerType string
@ -34,20 +35,51 @@ type Resolver struct {
Search *[]string Search *[]string
SkipFqdnBeforeInit string SkipFqdnBeforeInit string
// atomic
Initialized *abool.AtomicBool
InitLock sync.Mutex InitLock sync.Mutex
LastFail *int64
Expires *int64
// must be locked // must be locked
LockReason sync.Mutex initialized bool
FailReason string lastFail int64
failReason string
fails int
expires int64
// TODO: add: // TODO: add Expiration (for server got from DHCP / ICMPv6)
// 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! // 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 { func (r *Resolver) String() string {
@ -144,7 +176,6 @@ configuredServersLoop:
continue configuredServersLoop continue configuredServersLoop
} }
var lastFail int64
new := &Resolver{ new := &Resolver{
Server: server, Server: server,
ServerType: strings.ToLower(parts[0]), ServerType: strings.ToLower(parts[0]),
@ -152,9 +183,7 @@ configuredServersLoop:
ServerIP: ip, ServerIP: ip,
ServerIPScope: netutils.ClassifyIP(ip), ServerIPScope: netutils.ClassifyIP(ip),
ServerPort: port, ServerPort: port,
LastFail: &lastFail,
Source: "config", Source: "config",
Initialized: abool.NewBool(false),
} }
switch new.ServerType { switch new.ServerType {
@ -197,7 +226,6 @@ assignedServersLoop:
key = indexOfResolver(server, globalResolvers) key = indexOfResolver(server, globalResolvers)
if resetResolvers || key == -1 { if resetResolvers || key == -1 {
var lastFail int64
new := &Resolver{ new := &Resolver{
Server: server, Server: server,
ServerType: "dns", ServerType: "dns",
@ -205,9 +233,7 @@ assignedServersLoop:
ServerIP: nameserver.IP, ServerIP: nameserver.IP,
ServerIPScope: netutils.ClassifyIP(nameserver.IP), ServerIPScope: netutils.ClassifyIP(nameserver.IP),
ServerPort: 53, ServerPort: 53,
LastFail: &lastFail,
Source: "dhcp", Source: "dhcp",
Initialized: abool.NewBool(false),
} }
new.clientManager = newDNSClientManager(new) 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)
}