-
Notifications
You must be signed in to change notification settings - Fork 62
/
fastest.go
68 lines (57 loc) · 1.87 KB
/
fastest.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
package rdns
import (
"github.com/miekg/dns"
)
// Fastest is a resolver group that queries all resolvers concurrently for
// the same query, then returns the fastest response only.
type Fastest struct {
id string
resolvers []Resolver
}
var _ Resolver = &FailRotate{}
// NewFastest returns a new instance of a resolver group that returns the fastest
// response from all its resolvers.
func NewFastest(id string, resolvers ...Resolver) *Fastest {
return &Fastest{
id: id,
resolvers: resolvers,
}
}
// Resolve a DNS query by sending it to all resolvers and returning the fastest
// non-error response
func (r *Fastest) Resolve(q *dns.Msg, ci ClientInfo) (*dns.Msg, error) {
log := logger(r.id, q, ci)
type response struct {
r Resolver
a *dns.Msg
err error
}
responseCh := make(chan response, len(r.resolvers))
// Send the query to all resolvers. The responses are collected in a buffered channel
for _, resolver := range r.resolvers {
resolver := resolver
go func() {
a, err := resolver.Resolve(q, ci)
responseCh <- response{resolver, a, err}
}()
}
// Wait for responses, the first one that is successful is returned while the remaining open requests
// are abandoned.
var i int
for resolverResponse := range responseCh {
resolver, a, err := resolverResponse.r, resolverResponse.a, resolverResponse.err
if err == nil && (a == nil || a.Rcode != dns.RcodeServerFailure) { // Return immediately if successful
log.WithField("resolver", resolver.String()).Trace("using response from resolver")
return a, err
}
log.WithField("resolver", resolver.String()).WithError(err).Debug("resolver returned failure, waiting for next response")
// If all responses were bad, return the last one
if i++; i >= len(r.resolvers) {
return a, err
}
}
return nil, nil // should never be reached
}
func (r *Fastest) String() string {
return r.id
}