/
register.go
324 lines (285 loc) · 9.95 KB
/
register.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
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
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
package dnsserver
import (
"fmt"
"net"
"time"
"github.com/coredns/caddy"
"github.com/coredns/caddy/caddyfile"
"github.com/coredns/coredns/plugin"
"github.com/coredns/coredns/plugin/pkg/parse"
"github.com/coredns/coredns/plugin/pkg/transport"
"github.com/miekg/dns"
)
const serverType = "dns"
func init() {
caddy.RegisterServerType(serverType, caddy.ServerType{
Directives: func() []string { return Directives },
DefaultInput: func() caddy.Input {
return caddy.CaddyfileInput{
Filepath: "Corefile",
Contents: []byte(".:" + Port + " {\nwhoami\nlog\n}\n"),
ServerTypeName: serverType,
}
},
NewContext: newContext,
})
}
func newContext(i *caddy.Instance) caddy.Context {
return &dnsContext{keysToConfigs: make(map[string]*Config)}
}
type dnsContext struct {
keysToConfigs map[string]*Config
// configs is the master list of all site configs.
configs []*Config
}
func (h *dnsContext) saveConfig(key string, cfg *Config) {
h.configs = append(h.configs, cfg)
h.keysToConfigs[key] = cfg
}
// Compile-time check to ensure dnsContext implements the caddy.Context interface
var _ caddy.Context = &dnsContext{}
// InspectServerBlocks make sure that everything checks out before
// executing directives and otherwise prepares the directives to
// be parsed and executed.
func (h *dnsContext) InspectServerBlocks(sourceFile string, serverBlocks []caddyfile.ServerBlock) ([]caddyfile.ServerBlock, error) {
// Normalize and check all the zone names and check for duplicates
for ib, s := range serverBlocks {
// Walk the s.Keys and expand any reverse address in their proper DNS in-addr zones. If the expansions leads for
// more than one reverse zone, replace the current value and add the rest to s.Keys.
zoneAddrs := []zoneAddr{}
for ik, k := range s.Keys {
trans, k1 := parse.Transport(k) // get rid of any dns:// or other scheme.
hosts, port, err := plugin.SplitHostPort(k1)
// We need to make this a fully qualified domain name to catch all errors here and not later when
// plugin.Normalize is called again on these strings, with the prime difference being that the domain
// name is fully qualified. This was found by fuzzing where "ȶ" is deemed OK, but "ȶ." is not (might be a
// bug in miekg/dns actually). But here we were checking ȶ, which is OK, and later we barf in ȶ. leading to
// "index out of range".
for ih := range hosts {
_, _, err := plugin.SplitHostPort(dns.Fqdn(hosts[ih]))
if err != nil {
return nil, err
}
}
if err != nil {
return nil, err
}
if port == "" {
switch trans {
case transport.DNS:
port = Port
case transport.TLS:
port = transport.TLSPort
case transport.GRPC:
port = transport.GRPCPort
case transport.HTTPS:
port = transport.HTTPSPort
}
}
if len(hosts) > 1 {
s.Keys[ik] = hosts[0] + ":" + port // replace for the first
for _, h := range hosts[1:] { // add the rest
s.Keys = append(s.Keys, h+":"+port)
}
}
for i := range hosts {
zoneAddrs = append(zoneAddrs, zoneAddr{Zone: dns.Fqdn(hosts[i]), Port: port, Transport: trans})
}
}
serverBlocks[ib].Keys = s.Keys // important to save back the new keys that are potentially created here.
var firstConfigInBlock *Config
for ik := range s.Keys {
za := zoneAddrs[ik]
s.Keys[ik] = za.String()
// Save the config to our master list, and key it for lookups.
cfg := &Config{
Zone: za.Zone,
ListenHosts: []string{""},
Port: za.Port,
Transport: za.Transport,
}
// Set reference to the first config in the current block.
// This is used later by MakeServers to share a single plugin list
// for all zones in a server block.
if ik == 0 {
firstConfigInBlock = cfg
}
cfg.firstConfigInBlock = firstConfigInBlock
keyConfig := keyForConfig(ib, ik)
h.saveConfig(keyConfig, cfg)
}
}
return serverBlocks, nil
}
// MakeServers uses the newly-created siteConfigs to create and return a list of server instances.
func (h *dnsContext) MakeServers() ([]caddy.Server, error) {
// Copy the Plugin, ListenHosts and Debug from first config in the block
// to all other config in the same block . Doing this results in zones
// sharing the same plugin instances and settings as other zones in
// the same block.
for _, c := range h.configs {
c.Plugin = c.firstConfigInBlock.Plugin
c.ListenHosts = c.firstConfigInBlock.ListenHosts
c.Debug = c.firstConfigInBlock.Debug
c.Stacktrace = c.firstConfigInBlock.Stacktrace
// Fork TLSConfig for each encrypted connection
c.TLSConfig = c.firstConfigInBlock.TLSConfig.Clone()
c.ReadTimeout = c.firstConfigInBlock.ReadTimeout
c.WriteTimeout = c.firstConfigInBlock.WriteTimeout
c.IdleTimeout = c.firstConfigInBlock.IdleTimeout
c.TsigSecret = c.firstConfigInBlock.TsigSecret
}
// we must map (group) each config to a bind address
groups, err := groupConfigsByListenAddr(h.configs)
if err != nil {
return nil, err
}
// then we create a server for each group
var servers []caddy.Server
for addr, group := range groups {
// switch on addr
switch tr, _ := parse.Transport(addr); tr {
case transport.DNS:
s, err := NewServer(addr, group)
if err != nil {
return nil, err
}
servers = append(servers, s)
case transport.TLS:
s, err := NewServerTLS(addr, group)
if err != nil {
return nil, err
}
servers = append(servers, s)
case transport.GRPC:
s, err := NewServergRPC(addr, group)
if err != nil {
return nil, err
}
servers = append(servers, s)
case transport.HTTPS:
s, err := NewServerHTTPS(addr, group)
if err != nil {
return nil, err
}
servers = append(servers, s)
}
}
// For each server config, check for View Filter plugins
for _, c := range h.configs {
// Add filters in the plugin.cfg order for consistent filter func evaluation order.
for _, d := range Directives {
if vf, ok := c.registry[d].(Viewer); ok {
if c.ViewName != "" {
return nil, fmt.Errorf("multiple views defined in server block")
}
c.ViewName = vf.ViewName()
c.FilterFuncs = append(c.FilterFuncs, vf.Filter)
}
}
}
// Verify that there is no overlap on the zones and listen addresses
// for unfiltered server configs
errValid := h.validateZonesAndListeningAddresses()
if errValid != nil {
return nil, errValid
}
return servers, nil
}
// AddPlugin adds a plugin to a site's plugin stack.
func (c *Config) AddPlugin(m plugin.Plugin) {
c.Plugin = append(c.Plugin, m)
}
// registerHandler adds a handler to a site's handler registration. Handlers
//
// use this to announce that they exist to other plugin.
func (c *Config) registerHandler(h plugin.Handler) {
if c.registry == nil {
c.registry = make(map[string]plugin.Handler)
}
// Just overwrite...
c.registry[h.Name()] = h
}
// Handler returns the plugin handler that has been added to the config under its name.
// This is useful to inspect if a certain plugin is active in this server.
// Note that this is order dependent and the order is defined in directives.go, i.e. if your plugin
// comes before the plugin you are checking; it will not be there (yet).
func (c *Config) Handler(name string) plugin.Handler {
if c.registry == nil {
return nil
}
if h, ok := c.registry[name]; ok {
return h
}
return nil
}
// Handlers returns a slice of plugins that have been registered. This can be used to
// inspect and interact with registered plugins but cannot be used to remove or add plugins.
// Note that this is order dependent and the order is defined in directives.go, i.e. if your plugin
// comes before the plugin you are checking; it will not be there (yet).
func (c *Config) Handlers() []plugin.Handler {
if c.registry == nil {
return nil
}
hs := make([]plugin.Handler, 0, len(c.registry))
for k := range c.registry {
hs = append(hs, c.registry[k])
}
return hs
}
func (h *dnsContext) validateZonesAndListeningAddresses() error {
//Validate Zone and addresses
checker := newOverlapZone()
for _, conf := range h.configs {
for _, h := range conf.ListenHosts {
// Validate the overlapping of ZoneAddr
akey := zoneAddr{Transport: conf.Transport, Zone: conf.Zone, Address: h, Port: conf.Port}
var existZone, overlapZone *zoneAddr
if len(conf.FilterFuncs) > 0 {
// This config has filters. Check for overlap with other (unfiltered) configs.
existZone, overlapZone = checker.check(akey)
} else {
// This config has no filters. Check for overlap with other (unfiltered) configs,
// and register the zone to prevent subsequent zones from overlapping with it.
existZone, overlapZone = checker.registerAndCheck(akey)
}
if existZone != nil {
return fmt.Errorf("cannot serve %s - it is already defined", akey.String())
}
if overlapZone != nil {
return fmt.Errorf("cannot serve %s - zone overlap listener capacity with %v", akey.String(), overlapZone.String())
}
}
}
return nil
}
// groupConfigsByListenAddr groups site configs by their listen
// (bind) address, so sites that use the same listener can be served
// on the same server instance. The return value maps the listen
// address (what you pass into net.Listen) to the list of site configs.
// This function does NOT vet the configs to ensure they are compatible.
func groupConfigsByListenAddr(configs []*Config) (map[string][]*Config, error) {
groups := make(map[string][]*Config)
for _, conf := range configs {
for _, h := range conf.ListenHosts {
addr, err := net.ResolveTCPAddr("tcp", net.JoinHostPort(h, conf.Port))
if err != nil {
return nil, err
}
addrstr := conf.Transport + "://" + addr.String()
groups[addrstr] = append(groups[addrstr], conf)
}
}
return groups, nil
}
// DefaultPort is the default port.
const DefaultPort = transport.Port
// These "soft defaults" are configurable by
// command line flags, etc.
var (
// Port is the port we listen on by default.
Port = DefaultPort
// GracefulTimeout is the maximum duration of a graceful shutdown.
GracefulTimeout time.Duration
)
var _ caddy.GracefulServer = new(Server)