mirror of
https://github.com/coredns/coredns.git
synced 2025-11-02 10:13:14 -05:00
* 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>
258 lines
7.5 KiB
Go
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)
|