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
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
|
package dnsserver
import (
"log"
"net"
"runtime"
"sync"
"time"
"github.com/miekg/coredns/middleware/pkg/edns"
"github.com/miekg/coredns/request"
"github.com/miekg/dns"
"golang.org/x/net/context"
)
// Server represents an instance of a server, which serves
// DNS requests at a particular address (host and port). A
// server is capable of serving numerous zones on
// the same address and the listener may be stopped for
// graceful termination (POSIX only).
type Server struct {
Addr string // Address we listen on
mux *dns.ServeMux
server [2]*dns.Server // 0 is a net.Listener, 1 is a net.PacketConn (a *UDPConn) in our case.
l net.Listener
p net.PacketConn
m sync.Mutex // protects listener and packetconn
zones map[string]*Config // zones keyed by their address
dnsWg sync.WaitGroup // used to wait on outstanding connections
connTimeout time.Duration // the maximum duration of a graceful shutdown
}
// NewServer returns a new CoreDNS server and compiles all middleware in to it.
func NewServer(addr string, group []*Config) (*Server, error) {
s := &Server{
Addr: addr,
zones: make(map[string]*Config),
connTimeout: 5 * time.Second, // TODO(miek): was configurable
}
mux := dns.NewServeMux()
mux.Handle(".", s) // wildcard handler, everything will go through here
s.mux = mux
// We have to bound our wg with one increment
// to prevent a "race condition" that is hard-coded
// into sync.WaitGroup.Wait() - basically, an add
// with a positive delta must be guaranteed to
// occur before Wait() is called on the wg.
// In a way, this kind of acts as a safety barrier.
s.dnsWg.Add(1)
for _, site := range group {
// set the config per zone
s.zones[site.Zone] = site
// compile custom middleware for everything
var stack Handler
for i := len(site.Middleware) - 1; i >= 0; i-- {
stack = site.Middleware[i](stack)
}
site.middlewareChain = stack
}
return s, nil
}
// Serve starts the server with an existing listener. It blocks until the server stops.
func (s *Server) Serve(l net.Listener) error {
s.m.Lock()
s.server[tcp] = &dns.Server{Listener: l, Net: "tcp", Handler: s.mux}
s.m.Unlock()
return s.server[tcp].ActivateAndServe()
}
// ServePacket starts the server with an existing packetconn. It blocks until the server stops.
func (s *Server) ServePacket(p net.PacketConn) error {
s.m.Lock()
s.server[udp] = &dns.Server{PacketConn: p, Net: "udp", Handler: s.mux}
s.m.Unlock()
return s.server[udp].ActivateAndServe()
}
// Listen implements caddy.TCPServer interface.
func (s *Server) Listen() (net.Listener, error) {
l, err := net.Listen("tcp", s.Addr)
if err != nil {
return nil, err
}
s.m.Lock()
s.l = l
s.m.Unlock()
return l, nil
}
// ListenPacket implements caddy.UDPServer interface.
func (s *Server) ListenPacket() (net.PacketConn, error) {
p, err := net.ListenPacket("udp", s.Addr)
if err != nil {
return nil, err
}
s.m.Lock()
s.p = p
s.m.Unlock()
return p, nil
}
// Stop stops the server. It blocks until the server is
// totally stopped. On POSIX systems, it will wait for
// connections to close (up to a max timeout of a few
// seconds); on Windows it will close the listener
// immediately.
func (s *Server) Stop() (err error) {
if runtime.GOOS != "windows" {
// force connections to close after timeout
done := make(chan struct{})
go func() {
s.dnsWg.Done() // decrement our initial increment used as a barrier
s.dnsWg.Wait()
close(done)
}()
// Wait for remaining connections to finish or
// force them all to close after timeout
select {
case <-time.After(s.connTimeout):
case <-done:
}
}
// Close the listener now; this stops the server without delay
s.m.Lock()
if s.l != nil {
err = s.l.Close()
}
if s.p != nil {
err = s.p.Close()
}
for _, s1 := range s.server {
err = s1.Shutdown()
}
s.m.Unlock()
return
}
// ServeDNS is the entry point for every request to the address that s
// is bound to. It acts as a multiplexer for the requests zonename as
// defined in the request so that the correct zone
// (configuration and middleware stack) will handle the request.
func (s *Server) ServeDNS(w dns.ResponseWriter, r *dns.Msg) {
// TODO(miek): expensive to use defer
defer func() {
// In case the user doesn't enable error middleware, we still
// need to make sure that we stay alive up here
if rec := recover(); rec != nil {
DefaultErrorFunc(w, r, dns.RcodeServerFailure)
}
}()
if m, err := edns.Version(r); err != nil { // Wrong EDNS version, return at once.
w.WriteMsg(m)
return
}
q := r.Question[0].Name
b := make([]byte, len(q))
off, end := 0, false
ctx := context.Background()
for {
l := len(q[off:])
for i := 0; i < l; i++ {
b[i] = q[off+i]
// normalize the name for the lookup
if b[i] >= 'A' && b[i] <= 'Z' {
b[i] |= ('a' - 'A')
}
}
if h, ok := s.zones[string(b[:l])]; ok {
if r.Question[0].Qtype != dns.TypeDS {
rcode, _ := h.middlewareChain.ServeDNS(ctx, w, r)
if rcodeNoClientWrite(rcode) {
DefaultErrorFunc(w, r, rcode)
}
return
}
}
off, end = dns.NextLabel(q, off)
if end {
break
}
}
// Wildcard match, if we have found nothing try the root zone as a last resort.
if h, ok := s.zones["."]; ok {
rcode, _ := h.middlewareChain.ServeDNS(ctx, w, r)
if rcodeNoClientWrite(rcode) {
DefaultErrorFunc(w, r, rcode)
}
return
}
// Still here? Error out with REFUSED and some logging
remoteHost := w.RemoteAddr().String()
DefaultErrorFunc(w, r, dns.RcodeRefused)
log.Printf("[INFO] \"%s %s %s\" - No such zone at %s (Remote: %s)", dns.Type(r.Question[0].Qtype), dns.Class(r.Question[0].Qclass), q, s.Addr, remoteHost)
}
// DefaultErrorFunc responds to an DNS request with an error.
func DefaultErrorFunc(w dns.ResponseWriter, r *dns.Msg, rcode int) {
state := request.Request{W: w, Req: r}
answer := new(dns.Msg)
answer.SetRcode(r, rcode)
state.SizeAndDo(answer)
w.WriteMsg(answer)
}
func rcodeNoClientWrite(rcode int) bool {
switch rcode {
case dns.RcodeServerFailure:
fallthrough
case dns.RcodeRefused:
fallthrough
case dns.RcodeFormatError:
fallthrough
case dns.RcodeNotImplemented:
return true
}
return false
}
const (
tcp = 0
udp = 1
)
|