forked from coredns/unbound
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathunbound.go
165 lines (137 loc) · 3.9 KB
/
unbound.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
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
package unbound
import (
"context"
"fmt"
"errors"
"strconv"
"github.com/coredns/coredns/plugin"
"github.com/coredns/coredns/plugin/metrics"
clog "github.com/coredns/coredns/plugin/pkg/log"
"github.com/coredns/coredns/request"
"github.com/miekg/dns"
"github.com/miekg/unbound"
)
var log = clog.NewWithPlugin("unbound")
// Unbound is a plugin that resolves requests using libunbound.
type Unbound struct {
u *unbound.Unbound
t *unbound.Unbound
from []string
except []string
strict bool
Next plugin.Handler
}
// options for unbound, see unbound.conf(5).
var options = map[string]string{
"msg-cache-size": "0",
"rrset-cache-size": "0",
}
// New returns a pointer to an initialzed Unbound.
func New() *Unbound {
udp := unbound.New()
tcp := unbound.New()
tcp.SetOption("tcp-upstream:", "yes")
u := &Unbound{u: udp, t: tcp}
for k, v := range options {
if err := u.setOption(k, v); err != nil {
log.Warningf("Could not set option: %s", err)
}
}
return u
}
// Stop stops unbound and cleans up the memory used.
func (u *Unbound) Stop() error {
u.u.Destroy()
u.t.Destroy()
return nil
}
// setOption sets option k to value v in u.
func (u *Unbound) setOption(k, v string) error {
// Add ":" as unbound expects it
k += ":"
// Set for both udp and tcp handlers, return the error from the latter.
u.u.SetOption(k, v)
err := u.t.SetOption(k, v)
if err != nil {
return fmt.Errorf("failed to set option %q with value %q: %s", k, v, err)
}
return nil
}
// config reads the file f and sets unbound configuration
func (u *Unbound) config(f string) error {
var err error
err = u.u.Config(f)
if err != nil {
return fmt.Errorf("failed to read config file (%s) UDP context: %s", f, err)
}
err = u.t.Config(f)
if err != nil {
return fmt.Errorf("failed to read config file (%s) TCP context: %s", f, err)
}
return nil
}
// anchor reads the file f and sets it as anchor
func (u *Unbound) setAnchor(f string) error {
var err error
err = u.u.AddTaFile(f)
if err != nil {
return fmt.Errorf("failed to read trust anchor file (%s) UDP context: %s", f, err)
}
err = u.t.AddTaFile(f)
if err != nil {
return fmt.Errorf("failed to read trust anchor file (%s) TCP context: %s", f, err)
}
return nil
}
// ServeDNS implements the plugin.Handler interface.
func (u *Unbound) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) {
state := request.Request{W: w, Req: r}
if !u.match(state) {
return plugin.NextOrFailure(u.Name(), u.Next, ctx, w, r)
}
var (
res *unbound.Result
err error
)
switch state.Proto() {
case "tcp":
res, err = u.t.Resolve(state.QName(), state.QType(), state.QClass())
case "udp":
res, err = u.u.Resolve(state.QName(), state.QType(), state.QClass())
}
rcode := dns.RcodeServerFailure
if err == nil {
rcode = res.AnswerPacket.Rcode
}
rc, ok := dns.RcodeToString[rcode]
if !ok {
rc = strconv.Itoa(rcode)
}
server := metrics.WithServer(ctx)
RcodeCount.WithLabelValues(server, rc).Add(1)
RequestDuration.WithLabelValues(server).Observe(res.Rtt.Seconds())
if err != nil {
return dns.RcodeServerFailure, err
}
if u.strict && !res.Secure {
return dns.RcodeServerFailure, errors.New("dnssec validation failed")
}
// If the client *didn't* set the opt record, and specifically not the DO bit,
// strip this from the reply (unbound default to setting DO).
if !state.Do() {
// technically we can still set bufsize and fluff, for now remove the entire OPT record.
for i := 0; i < len(res.AnswerPacket.Extra); i++ {
rr := res.AnswerPacket.Extra[i]
if _, ok := rr.(*dns.OPT); ok {
res.AnswerPacket.Extra = append(res.AnswerPacket.Extra[:i], res.AnswerPacket.Extra[i+1:]...)
break // TODO(miek): more than one? Think TSIG?
}
}
filter(res.AnswerPacket, dnssec)
}
res.AnswerPacket.Id = r.Id
w.WriteMsg(res.AnswerPacket)
return 0, nil
}
// Name implements the Handler interface.
func (u *Unbound) Name() string { return "unbound" }