package dnsserver import ( "fmt" "net" "time" "github.com/mholt/caddy" "github.com/mholt/caddy/caddyfile" ) const serverType = "dns" func init() { caddy.RegisterServerType(serverType, caddy.ServerType{ Directives: Directives, DefaultInput: func() caddy.Input { if Port == DefaultPort && Zone != "" { return caddy.CaddyfileInput{ Filepath: "Corefile", Contents: nil, ServerTypeName: serverType, } } return caddy.CaddyfileInput{ Filepath: "Corefile", Contents: nil, ServerTypeName: serverType, } }, NewContext: newContext, }) } var TestNewContext = newContext func newContext() 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 } // 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 dups := map[string]string{} for _, s := range serverBlocks { for i, k := range s.Keys { za, err := normalizeZone(k) if err != nil { return nil, err } s.Keys[i] = za.String() if v, ok := dups[za.Zone]; ok { return nil, fmt.Errorf("cannot serve %s - zone already defined for %v", za, v) } dups[za.Zone] = za.String() // Save the config to our master list, and key it for lookups cfg := &Config{ Zone: za.Zone, Port: za.Port, // TODO(miek): more? } h.saveConfig(za.String(), 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) { // 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 { s, err := NewServer(addr, group) if err != nil { return nil, err } servers = append(servers, s) } return servers, nil } // AddMiddleware adds a middleware to a site's middleware stack. func (sc *Config) AddMiddleware(m Middleware) { sc.Middleware = append(sc.Middleware, m) } // 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 { if conf.Port == "" { conf.Port = Port } addr, err := net.ResolveTCPAddr("tcp", net.JoinHostPort(conf.ListenHost, conf.Port)) if err != nil { return nil, err } addrstr := addr.String() groups[addrstr] = append(groups[addrstr], conf) } return groups, nil } const ( // DefaultZone is the default zone. DefaultZone = "." // DefaultPort is the default port. DefaultPort = "2053" // DefaultRoot is the default root folder. DefaultRoot = "." ) // These "soft defaults" are configurable by // command line flags, etc. var ( // Root is the site root Root = DefaultRoot // Host is the site host Zone = DefaultZone // Port is the site port Port = DefaultPort // GracefulTimeout is the maximum duration of a graceful shutdown. GracefulTimeout time.Duration )