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"
"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
}

View file

@ -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
// 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)
}