-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathfile.go
159 lines (133 loc) · 3.91 KB
/
file.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
// Package file implements a file backend.
// Modified for https://github.com/charlesportwoodii/coredns-partial`
package file
import (
"context"
"fmt"
"io"
"github.com/coredns/coredns/plugin"
clog "github.com/coredns/coredns/plugin/pkg/log"
"github.com/coredns/coredns/plugin/transfer"
"github.com/coredns/coredns/request"
"github.com/miekg/dns"
)
var log = clog.NewWithPlugin("file")
type (
// File is the plugin that reads zone data from disk.
File struct {
Next plugin.Handler
Zones
transfer *transfer.Transfer
}
// Zones maps zone names to a *Zone.
Zones struct {
Z map[string]*Zone // A map mapping zone (origin) to the Zone's data
Names []string // All the keys from the map Z as a string slice.
Fallthrough map[string]bool
}
)
// ServeDNS implements the plugin.Handle interface.
func (f File) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) {
state := request.Request{W: w, Req: r}
qname := state.Name()
// TODO(miek): match the qname better in the map
zone := plugin.Zones(f.Zones.Names).Matches(qname)
if zone == "" {
return plugin.NextOrFailure(f.Name(), f.Next, ctx, w, r)
}
z, ok := f.Zones.Z[zone]
if !ok || z == nil {
return dns.RcodeServerFailure, nil
}
// This is only for when we are a secondary zones.
if r.Opcode == dns.OpcodeNotify {
if z.isNotify(state) {
m := new(dns.Msg)
m.SetReply(r)
m.Authoritative = true
w.WriteMsg(m)
log.Infof("Notify from %s for %s: checking transfer", state.IP(), zone)
ok, err := z.shouldTransfer()
if ok {
z.TransferIn()
} else {
log.Infof("Notify from %s for %s: no SOA serial increase seen", state.IP(), zone)
}
if err != nil {
log.Warningf("Notify from %s for %s: failed primary check: %s", state.IP(), zone, err)
}
return dns.RcodeSuccess, nil
}
log.Infof("Dropping notify from %s for %s", state.IP(), zone)
return dns.RcodeSuccess, nil
}
z.RLock()
exp := z.Expired
z.RUnlock()
if exp {
log.Errorf("Zone %s is expired", zone)
return dns.RcodeServerFailure, nil
}
answer, ns, extra, result := z.Lookup(ctx, state, qname)
m := new(dns.Msg)
m.SetReply(r)
m.Authoritative = true
m.Answer, m.Ns, m.Extra = answer, ns, extra
switch result {
case Success:
case NoData:
case NameError:
m.Rcode = dns.RcodeNameError
if f.Zones.Fallthrough[zone] {
log.Infof("Forwarding request to upstream")
return plugin.NextOrFailure(f.Name(), f.Next, ctx, w, r)
}
case Delegation:
m.Authoritative = false
case ServerFailure:
return dns.RcodeServerFailure, nil
}
w.WriteMsg(m)
return dns.RcodeSuccess, nil
}
// Name implements the Handler interface.
func (f File) Name() string { return "file" }
type serialErr struct {
err string
zone string
origin string
serial int64
}
func (s *serialErr) Error() string {
return fmt.Sprintf("%s for origin %s in file %s, with %d SOA serial", s.err, s.origin, s.zone, s.serial)
}
// Parse parses the zone in filename and returns a new Zone or an error.
// If serial >= 0 it will reload the zone, if the SOA hasn't changed
// it returns an error indicating nothing was read.
func Parse(f io.Reader, origin, fileName string, serial int64) (*Zone, error) {
zp := dns.NewZoneParser(f, dns.Fqdn(origin), fileName)
zp.SetIncludeAllowed(true)
z := NewZone(origin, fileName)
seenSOA := false
for rr, ok := zp.Next(); ok; rr, ok = zp.Next() {
if err := zp.Err(); err != nil {
return nil, err
}
if !seenSOA {
if s, ok := rr.(*dns.SOA); ok {
seenSOA = true
// -1 is valid serial is we failed to load the file on startup.
if serial >= 0 && s.Serial == uint32(serial) { // same serial
return nil, &serialErr{err: "no change in SOA serial", origin: origin, zone: fileName, serial: serial}
}
}
}
if err := z.Insert(rr); err != nil {
return nil, err
}
}
if !seenSOA {
return nil, fmt.Errorf("file %q has no SOA record for origin %s", fileName, origin)
}
return z, nil
}