123 lines
2.6 KiB
Go
123 lines
2.6 KiB
Go
package checker
|
|
|
|
import (
|
|
"sort"
|
|
)
|
|
|
|
// Idempotent: rules and report both call it; both must see the same grouping.
|
|
func deriveView(data *ResolverPropagationData) {
|
|
if data == nil {
|
|
return
|
|
}
|
|
|
|
for key, view := range data.RRsets {
|
|
// Reset derived fields so repeated calls stay idempotent.
|
|
view.Groups = nil
|
|
view.ConsensusSig = ""
|
|
view.Agreeing = nil
|
|
view.Dissenting = nil
|
|
view.MatchesExpected = false
|
|
|
|
voteCount := map[string]int{}
|
|
type group struct {
|
|
rcode string
|
|
records []string
|
|
resolvers []string
|
|
}
|
|
groups := map[string]*group{}
|
|
|
|
for _, rv := range data.Resolvers {
|
|
p := rv.Probes[key]
|
|
if p == nil || p.Error != "" {
|
|
continue
|
|
}
|
|
g := groups[p.Signature]
|
|
if g == nil {
|
|
g = &group{rcode: p.Rcode, records: p.Records}
|
|
groups[p.Signature] = g
|
|
}
|
|
g.resolvers = append(g.resolvers, rv.ID)
|
|
if !rv.Filtered {
|
|
voteCount[p.Signature]++
|
|
}
|
|
}
|
|
|
|
// Pick the winning signature, preferring NOERROR responses.
|
|
var winSig string
|
|
var winVotes int
|
|
for sig, g := range groups {
|
|
if g.rcode != "NOERROR" && winSig != "" {
|
|
continue
|
|
}
|
|
if voteCount[sig] > winVotes {
|
|
winSig = sig
|
|
winVotes = voteCount[sig]
|
|
}
|
|
}
|
|
if winSig == "" {
|
|
for sig := range groups {
|
|
winSig = sig
|
|
break
|
|
}
|
|
}
|
|
view.ConsensusSig = winSig
|
|
|
|
type gEntry struct {
|
|
sig string
|
|
g *group
|
|
}
|
|
var entries []gEntry
|
|
for s, g := range groups {
|
|
sort.Strings(g.resolvers)
|
|
entries = append(entries, gEntry{sig: s, g: g})
|
|
}
|
|
sort.Slice(entries, func(i, j int) bool {
|
|
return len(entries[i].g.resolvers) > len(entries[j].g.resolvers)
|
|
})
|
|
for _, e := range entries {
|
|
view.Groups = append(view.Groups, SignatureGroup{
|
|
Signature: e.sig,
|
|
Records: e.g.records,
|
|
Resolvers: e.g.resolvers,
|
|
Rcode: e.g.rcode,
|
|
})
|
|
if e.sig == winSig {
|
|
view.Agreeing = append(view.Agreeing, e.g.resolvers...)
|
|
} else {
|
|
view.Dissenting = append(view.Dissenting, e.g.resolvers...)
|
|
}
|
|
}
|
|
sort.Strings(view.Agreeing)
|
|
sort.Strings(view.Dissenting)
|
|
|
|
if view.Expected != "" {
|
|
view.MatchesExpected = view.ConsensusSig == view.Expected
|
|
}
|
|
}
|
|
|
|
// Recompute UnfilteredAgreeing from the consensus we just built.
|
|
agree := 0
|
|
for _, rv := range data.Resolvers {
|
|
if rv.Filtered || !rv.Reachable {
|
|
continue
|
|
}
|
|
ok := true
|
|
for key, p := range rv.Probes {
|
|
if p == nil || p.Error != "" {
|
|
continue
|
|
}
|
|
v := data.RRsets[key]
|
|
if v == nil || v.ConsensusSig == "" {
|
|
continue
|
|
}
|
|
if p.Signature != v.ConsensusSig {
|
|
ok = false
|
|
break
|
|
}
|
|
}
|
|
if ok {
|
|
agree++
|
|
}
|
|
}
|
|
data.Stats.UnfilteredAgreeing = agree
|
|
}
|