forked from 0xERR0R/blocky
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathstrict_resolver.go
99 lines (76 loc) · 2.46 KB
/
strict_resolver.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
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
package resolver
import (
"context"
"errors"
"fmt"
"strings"
"sync/atomic"
"github.com/0xERR0R/blocky/config"
"github.com/0xERR0R/blocky/model"
"github.com/0xERR0R/blocky/util"
"github.com/sirupsen/logrus"
)
const (
strictResolverType = "strict"
)
// StrictResolver delegates the DNS message strictly to the first configured upstream resolver
// if it can't provide the answer in time the next resolver is used
type StrictResolver struct {
configurable[*config.UpstreamGroup]
typed
resolvers atomic.Pointer[[]*upstreamResolverStatus]
}
// NewStrictResolver creates a new strict resolver instance
func NewStrictResolver(
ctx context.Context, cfg config.UpstreamGroup, bootstrap *Bootstrap,
) (*StrictResolver, error) {
r := newStrictResolver(
cfg,
[]Resolver{bootstrap}, // if init strategy is fast, use bootstrap until init finishes
)
return initGroupResolvers(ctx, r, cfg, bootstrap)
}
func newStrictResolver(
cfg config.UpstreamGroup, resolvers []Resolver,
) *StrictResolver {
r := StrictResolver{
configurable: withConfig(&cfg),
typed: withType(strictResolverType),
}
r.setResolvers(newUpstreamResolverStatuses(resolvers))
return &r
}
func (r *StrictResolver) setResolvers(resolvers []*upstreamResolverStatus) {
r.resolvers.Store(&resolvers)
}
func (r *StrictResolver) Name() string {
return r.String()
}
func (r *StrictResolver) String() string {
resolvers := *r.resolvers.Load()
upstreams := make([]string, len(resolvers))
for i, s := range resolvers {
upstreams[i] = s.resolver.String()
}
return fmt.Sprintf("%s upstreams '%s (%s)'", strictResolverType, r.cfg.Name, strings.Join(upstreams, ","))
}
// Resolve sends the query request in a strict order to the upstream resolvers
func (r *StrictResolver) Resolve(ctx context.Context, request *model.Request) (*model.Response, error) {
ctx, logger := r.log(ctx)
// start with first resolver
for _, resolver := range *r.resolvers.Load() {
logger.Debugf("using %s as resolver", resolver.resolver)
resp, err := resolver.resolve(ctx, request)
if err != nil {
// log error and try next upstream
logger.WithField("resolver", resolver.resolver).Debug("resolution failed from resolver, cause: ", err)
continue
}
logger.WithFields(logrus.Fields{
"resolver": *resolver,
"answer": util.AnswerToString(resp.Res.Answer),
}).Debug("using response from resolver")
return resp, nil
}
return nil, errors.New("resolution was not successful, no resolver returned an answer in time")
}