-
Notifications
You must be signed in to change notification settings - Fork 9
/
blocklist.go
129 lines (101 loc) · 3.21 KB
/
blocklist.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
package blocklist
import (
"context"
"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"
"strings"
)
var log = clog.NewWithPlugin("blocklist")
type Blocklist struct {
blockDomains map[string]bool
allowDomains map[string]bool
Next plugin.Handler
domainMetrics bool
blockResponse int
}
func NewBlocklistPlugin(next plugin.Handler, blockDomains []string, allowDomains []string, domainMetrics bool, blockResponse int) Blocklist {
log.Debugf(
"Creating blocklist plugin with %d blocks, %d allows, domain metrics set to %v, and a block response code of %d",
len(blockDomains),
len(allowDomains),
domainMetrics,
blockResponse,
)
return Blocklist{
blockDomains: toMap(blockDomains),
allowDomains: toMap(allowDomains),
Next: next,
domainMetrics: domainMetrics,
blockResponse: blockResponse,
}
}
// ServeDNS handles processing the DNS query in relation to the blocklist
// A count of metrics around the blocking and allowing status is maintained
// It returns the DNS RCODE
func (b Blocklist) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) {
state := request.Request{W: w, Req: r}
shouldBlock, shouldAllow := b.shouldBlock(state.Name())
if shouldBlock {
// If an RR should be both blocked and allowed,
// then allow it and update appropriate metrics
if shouldAllow {
allowCount.WithLabelValues(metrics.WithServer(ctx)).Inc()
if b.domainMetrics {
allowWithDomainsCount.WithLabelValues(metrics.WithServer(ctx), state.Name()).Inc()
}
} else {
// Handle the blocking of the RR
resp := new(dns.Msg)
resp.SetRcode(r, b.blockResponse)
err := w.WriteMsg(resp)
if err != nil {
log.Errorf("failed to write block for %s, %v+", state.Name(), err)
}
blockCount.WithLabelValues(metrics.WithServer(ctx)).Inc()
if b.domainMetrics {
blockWithDomainsCount.WithLabelValues(metrics.WithServer(ctx), state.Name()).Inc()
}
log.Debugf(
"blocked \"%s IN %s %s\" from %s",
state.Type(),
state.Name(),
state.Proto(),
state.RemoteAddr(),
)
return b.blockResponse, nil
}
}
return plugin.NextOrFailure(b.Name(), b.Next, ctx, w, r)
}
// shouldBlock checks for the presence of a DNS name in the block and allow lists
// It returns the blockList and allowList status for that RR
func (b Blocklist) shouldBlock(name string) (isBlocked bool, isAllowed bool) {
log.Debugf("shouldBlock(%s)", name)
if name == "localhost." {
return false, false
}
isBlocked = inList(name, b.blockDomains)
isAllowed = inList(name, b.allowDomains)
return isBlocked, isAllowed
}
func inList(name string, domainList map[string]bool) bool {
inList := false
nameParts := strings.Split(name, ".")
for i := range nameParts {
n := strings.Join(nameParts[i:], ".")
// Because of how domains are passed through, the final iteration
// of the joined array will be a zero-length string
// Manually override that to be the DNS root RR
if len(n) == 0 {
n = "."
}
if _, inList = domainList[n]; inList {
break
}
}
return inList
}
func (b Blocklist) Name() string { return "blocklist" }