Files
coredns/core/dnsserver/register.go
Miek Gieben e42614edc5 reverse zones (#4538)
* core: fix v4 non-octet reverse zones

This fixes the reverse zones handling. Add expanstion of the reverse
notation to all octet boundary subnets and add those to the config - just as if
they were directly typed in the config.

This takes inspiration from #4501, but that (even with DCO!!) seems to
be just using https://github.com/apparentlymart/go-cidr/ so use that
instead - I think a minor function is still needed that one is copied
from #4501.

Also sort the zones we are listing on startup - caught in this PR
because of the expanded zones being not listed next to each other.

This also removes the need for FilterFunc from the config, so this is
now gone as well, making the whole thing slightly more efficient.

Add couple of reverse unit tests and a e2e test that queries for the
correct (and incorrect) reverse zones and checks the reply.

Closes: #4501
Fixes: #2779

Signed-off-by: Miek Gieben <miek@miek.nl>

* Add more test cases

Add test from origin bug report: #2779

Signed-off-by: Miek Gieben <miek@miek.nl>

* Rebase and fix conflicts

Signed-off-by: Miek Gieben <miek@miek.nl>
2021-05-11 09:50:18 +02:00

258 lines
7.5 KiB
Go

package dnsserver
import (
"flag"
"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"
)
const serverType = "dns"
// Any flags defined here, need to be namespaced to the serverType other
// wise they potentially clash with other server types.
func init() {
flag.StringVar(&Port, serverType+".port", DefaultPort, "Default port")
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.
for ik, k := range s.Keys {
_, k1 := parse.Transport(k) // get rid of any dns:// or other scheme.
_, port, ipnet, err := plugin.SplitHostPort(k1)
if ipnet == nil || err != nil { // err will be caught below
continue
}
if port != "" {
port = ":" + port
}
nets := classFromCIDR(ipnet)
if len(nets) > 1 {
s.Keys[ik] = nets[0] + port // replace for the first
for _, n := range nets[1:] { // add the rest
s.Keys = append(s.Keys, n+port)
}
}
}
serverBlocks[ib].Keys = s.Keys // important to save back the new keys that are potentially created here.
for ik, k := range s.Keys {
za, err := normalizeZone(k)
if err != nil {
return nil, err
}
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,
}
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) {
// Now that all Keys and Directives are parsed and initialized
// lets verify that there is no overlap on the zones and addresses to listen for
errValid := h.validateZonesAndListeningAddresses()
if errValid != nil {
return nil, errValid
}
// 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)
}
}
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}
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
}
// groupSiteConfigsByListenAddr 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)