mirror of
				https://github.com/coredns/coredns.git
				synced 2025-10-31 10:13:14 -04:00 
			
		
		
		
	* Changed reference to Caddy over to CoreDNS * Removing references to caddy * Fixed misleading error message to reference coredns * Cleaning up references to caddy * Adding clean and deps targets Muscle memory is resulting in "make clean" commands. * Adding test target to makefile * More "Caddy" cleanup
		
			
				
	
	
		
			341 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			341 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package core
 | |
| 
 | |
| import (
 | |
| 	"bytes"
 | |
| 	"fmt"
 | |
| 	"io"
 | |
| 	"log"
 | |
| 	"net"
 | |
| 	"sync"
 | |
| 
 | |
| 	"github.com/miekg/coredns/core/parse"
 | |
| 	"github.com/miekg/coredns/core/setup"
 | |
| 	"github.com/miekg/coredns/server"
 | |
| )
 | |
| 
 | |
| const (
 | |
| 	// DefaultConfigFile is the name of the configuration file that is loaded
 | |
| 	// by default if no other file is specified.
 | |
| 	DefaultConfigFile = "Corefile"
 | |
| )
 | |
| 
 | |
| func loadConfigsUpToIncludingTLS(filename string, input io.Reader) ([]server.Config, []parse.ServerBlock, int, error) {
 | |
| 	var configs []server.Config
 | |
| 
 | |
| 	// Each server block represents similar hosts/addresses, since they
 | |
| 	// were grouped together in the Corefile.
 | |
| 	serverBlocks, err := parse.ServerBlocks(filename, input, true)
 | |
| 	if err != nil {
 | |
| 		return nil, nil, 0, err
 | |
| 	}
 | |
| 	if len(serverBlocks) == 0 {
 | |
| 		newInput := DefaultInput()
 | |
| 		serverBlocks, err = parse.ServerBlocks(newInput.Path(), bytes.NewReader(newInput.Body()), true)
 | |
| 		if err != nil {
 | |
| 			return nil, nil, 0, err
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	var lastDirectiveIndex int // we set up directives in two parts; this stores where we left off
 | |
| 
 | |
| 	// Iterate each server block and make a config for each one,
 | |
| 	// executing the directives that were parsed in order up to the tls
 | |
| 	// directive; this is because we must activate Let's Encrypt.
 | |
| 	for i, sb := range serverBlocks {
 | |
| 		onces := makeOnces()
 | |
| 		storages := makeStorages()
 | |
| 
 | |
| 		for j, addr := range sb.Addresses {
 | |
| 			config := server.Config{
 | |
| 				Host:       addr.Host,
 | |
| 				Port:       addr.Port,
 | |
| 				Root:       Root,
 | |
| 				ConfigFile: filename,
 | |
| 				AppName:    AppName,
 | |
| 				AppVersion: AppVersion,
 | |
| 			}
 | |
| 
 | |
| 			// It is crucial that directives are executed in the proper order.
 | |
| 			for k, dir := range directiveOrder {
 | |
| 				// Execute directive if it is in the server block
 | |
| 				if tokens, ok := sb.Tokens[dir.name]; ok {
 | |
| 					// Each setup function gets a controller, from which setup functions
 | |
| 					// get access to the config, tokens, and other state information useful
 | |
| 					// to set up its own host only.
 | |
| 					controller := &setup.Controller{
 | |
| 						Config:    &config,
 | |
| 						Dispenser: parse.NewDispenserTokens(filename, tokens),
 | |
| 						OncePerServerBlock: func(f func() error) error {
 | |
| 							var err error
 | |
| 							onces[dir.name].Do(func() {
 | |
| 								err = f()
 | |
| 							})
 | |
| 							return err
 | |
| 						},
 | |
| 						ServerBlockIndex:     i,
 | |
| 						ServerBlockHostIndex: j,
 | |
| 						ServerBlockHosts:     sb.HostList(),
 | |
| 						ServerBlockStorage:   storages[dir.name],
 | |
| 					}
 | |
| 					// execute setup function and append middleware handler, if any
 | |
| 					midware, err := dir.setup(controller)
 | |
| 					if err != nil {
 | |
| 						return nil, nil, lastDirectiveIndex, err
 | |
| 					}
 | |
| 					if midware != nil {
 | |
| 						config.Middleware = append(config.Middleware, midware)
 | |
| 					}
 | |
| 					storages[dir.name] = controller.ServerBlockStorage // persist for this server block
 | |
| 				}
 | |
| 
 | |
| 				// Stop after TLS setup, since we need to activate Let's Encrypt before continuing;
 | |
| 				// it makes some changes to the configs that middlewares might want to know about.
 | |
| 				if dir.name == "tls" {
 | |
| 					lastDirectiveIndex = k
 | |
| 					break
 | |
| 				}
 | |
| 			}
 | |
| 
 | |
| 			configs = append(configs, config)
 | |
| 		}
 | |
| 	}
 | |
| 	return configs, serverBlocks, lastDirectiveIndex, nil
 | |
| }
 | |
| 
 | |
| // loadConfigs reads input (named filename) and parses it, returning the
 | |
| // server configurations in the order they appeared in the input. As part
 | |
| // of this, it activates Let's Encrypt for the configs that are produced.
 | |
| // Thus, the returned configs are already optimally configured for HTTPS.
 | |
| func loadConfigs(filename string, input io.Reader) ([]server.Config, error) {
 | |
| 	configs, serverBlocks, lastDirectiveIndex, err := loadConfigsUpToIncludingTLS(filename, input)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 
 | |
| 	// Now we have all the configs, but they have only been set up to the
 | |
| 	// point of tls. We need to activate Let's Encrypt before setting up
 | |
| 	// the rest of the middlewares so they have correct information regarding
 | |
| 	// TLS configuration, if necessary. (this only appends, so our iterations
 | |
| 	// over server blocks below shouldn't be affected)
 | |
| 	if !IsRestart() && !Quiet {
 | |
| 		fmt.Println("Activating privacy features...")
 | |
| 	}
 | |
| 	/* TODO(miek): stopped for now
 | |
| 	configs, err = https.Activate(configs)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	} else if !IsRestart() && !Quiet {
 | |
| 		fmt.Println(" done.")
 | |
| 	}
 | |
| 	*/
 | |
| 
 | |
| 	// Finish setting up the rest of the directives, now that TLS is
 | |
| 	// optimally configured. These loops are similar to above except
 | |
| 	// we don't iterate all the directives from the beginning and we
 | |
| 	// don't create new configs.
 | |
| 	configIndex := -1
 | |
| 	for i, sb := range serverBlocks {
 | |
| 		onces := makeOnces()
 | |
| 		storages := makeStorages()
 | |
| 
 | |
| 		for j := range sb.Addresses {
 | |
| 			configIndex++
 | |
| 
 | |
| 			for k := lastDirectiveIndex + 1; k < len(directiveOrder); k++ {
 | |
| 				dir := directiveOrder[k]
 | |
| 
 | |
| 				if tokens, ok := sb.Tokens[dir.name]; ok {
 | |
| 					controller := &setup.Controller{
 | |
| 						Config:    &configs[configIndex],
 | |
| 						Dispenser: parse.NewDispenserTokens(filename, tokens),
 | |
| 						OncePerServerBlock: func(f func() error) error {
 | |
| 							var err error
 | |
| 							onces[dir.name].Do(func() {
 | |
| 								err = f()
 | |
| 							})
 | |
| 							return err
 | |
| 						},
 | |
| 						ServerBlockIndex:     i,
 | |
| 						ServerBlockHostIndex: j,
 | |
| 						ServerBlockHosts:     sb.HostList(),
 | |
| 						ServerBlockStorage:   storages[dir.name],
 | |
| 					}
 | |
| 					midware, err := dir.setup(controller)
 | |
| 					if err != nil {
 | |
| 						return nil, err
 | |
| 					}
 | |
| 					if midware != nil {
 | |
| 						configs[configIndex].Middleware = append(configs[configIndex].Middleware, midware)
 | |
| 					}
 | |
| 					storages[dir.name] = controller.ServerBlockStorage // persist for this server block
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return configs, nil
 | |
| }
 | |
| 
 | |
| // makeOnces makes a map of directive name to sync.Once
 | |
| // instance. This is intended to be called once per server
 | |
| // block when setting up configs so that Setup functions
 | |
| // for each directive can perform a task just once per
 | |
| // server block, even if there are multiple hosts on the block.
 | |
| //
 | |
| // We need one Once per directive, otherwise the first
 | |
| // directive to use it would exclude other directives from
 | |
| // using it at all, which would be a bug.
 | |
| func makeOnces() map[string]*sync.Once {
 | |
| 	onces := make(map[string]*sync.Once)
 | |
| 	for _, dir := range directiveOrder {
 | |
| 		onces[dir.name] = new(sync.Once)
 | |
| 	}
 | |
| 	return onces
 | |
| }
 | |
| 
 | |
| // makeStorages makes a map of directive name to interface{}
 | |
| // so that directives' setup functions can persist state
 | |
| // between different hosts on the same server block during the
 | |
| // setup phase.
 | |
| func makeStorages() map[string]interface{} {
 | |
| 	storages := make(map[string]interface{})
 | |
| 	for _, dir := range directiveOrder {
 | |
| 		storages[dir.name] = nil
 | |
| 	}
 | |
| 	return storages
 | |
| }
 | |
| 
 | |
| // arrangeBindings groups configurations by their bind address. For example,
 | |
| // a server that should listen on localhost and another on 127.0.0.1 will
 | |
| // be grouped into the same address: 127.0.0.1. It will return an error
 | |
| // if an address is malformed or a TLS listener is configured on the
 | |
| // same address as a plaintext HTTP listener. The return value is a map of
 | |
| // bind address to list of configs that would become VirtualHosts on that
 | |
| // server. Use the keys of the returned map to create listeners, and use
 | |
| // the associated values to set up the virtualhosts.
 | |
| func arrangeBindings(allConfigs []server.Config) (bindingGroup, error) {
 | |
| 	var groupings bindingGroup
 | |
| 
 | |
| 	// Group configs by bind address
 | |
| 	for _, conf := range allConfigs {
 | |
| 		// use default port if none is specified
 | |
| 		if conf.Port == "" {
 | |
| 			conf.Port = Port
 | |
| 		}
 | |
| 
 | |
| 		bindAddr, warnErr, fatalErr := resolveAddr(conf)
 | |
| 		if fatalErr != nil {
 | |
| 			return groupings, fatalErr
 | |
| 		}
 | |
| 		if warnErr != nil {
 | |
| 			log.Printf("[WARNING] Resolving bind address for %s: %v", conf.Address(), warnErr)
 | |
| 		}
 | |
| 
 | |
| 		// Make sure to compare the string representation of the address,
 | |
| 		// not the pointer, since a new *TCPAddr is created each time.
 | |
| 		var existing bool
 | |
| 		for i := 0; i < len(groupings); i++ {
 | |
| 			if groupings[i].BindAddr.String() == bindAddr.String() {
 | |
| 				groupings[i].Configs = append(groupings[i].Configs, conf)
 | |
| 				existing = true
 | |
| 				break
 | |
| 			}
 | |
| 		}
 | |
| 		if !existing {
 | |
| 			groupings = append(groupings, bindingMapping{
 | |
| 				BindAddr: bindAddr,
 | |
| 				Configs:  []server.Config{conf},
 | |
| 			})
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	// Don't allow HTTP and HTTPS to be served on the same address
 | |
| 	for _, group := range groupings {
 | |
| 		isTLS := group.Configs[0].TLS.Enabled
 | |
| 		for _, config := range group.Configs {
 | |
| 			if config.TLS.Enabled != isTLS {
 | |
| 				thisConfigProto, otherConfigProto := "HTTP", "HTTP"
 | |
| 				if config.TLS.Enabled {
 | |
| 					thisConfigProto = "HTTPS"
 | |
| 				}
 | |
| 				if group.Configs[0].TLS.Enabled {
 | |
| 					otherConfigProto = "HTTPS"
 | |
| 				}
 | |
| 				return groupings, fmt.Errorf("configuration error: Cannot multiplex %s (%s) and %s (%s) on same address",
 | |
| 					group.Configs[0].Address(), otherConfigProto, config.Address(), thisConfigProto)
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return groupings, nil
 | |
| }
 | |
| 
 | |
| // resolveAddr determines the address (host and port) that a config will
 | |
| // bind to. The returned address, resolvAddr, should be used to bind the
 | |
| // listener or group the config with other configs using the same address.
 | |
| // The first error, if not nil, is just a warning and should be reported
 | |
| // but execution may continue. The second error, if not nil, is a real
 | |
| // problem and the server should not be started.
 | |
| //
 | |
| // This function does not handle edge cases like port "http" or "https" if
 | |
| // they are not known to the system. It does, however, serve on the wildcard
 | |
| // host if resolving the address of the specific hostname fails.
 | |
| func resolveAddr(conf server.Config) (resolvAddr *net.TCPAddr, warnErr, fatalErr error) {
 | |
| 	resolvAddr, warnErr = net.ResolveTCPAddr("tcp", net.JoinHostPort(conf.BindHost, conf.Port))
 | |
| 	if warnErr != nil {
 | |
| 		// the hostname probably couldn't be resolved, just bind to wildcard then
 | |
| 		resolvAddr, fatalErr = net.ResolveTCPAddr("tcp", net.JoinHostPort("", conf.Port))
 | |
| 		if fatalErr != nil {
 | |
| 			return
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return
 | |
| }
 | |
| 
 | |
| // validDirective returns true if d is a valid
 | |
| // directive; false otherwise.
 | |
| func validDirective(d string) bool {
 | |
| 	for _, dir := range directiveOrder {
 | |
| 		if dir.name == d {
 | |
| 			return true
 | |
| 		}
 | |
| 	}
 | |
| 	return false
 | |
| }
 | |
| 
 | |
| // DefaultInput returns the default Corefile input
 | |
| // to use when it is otherwise empty or missing.
 | |
| // It uses the default host and port and root.
 | |
| func DefaultInput() CorefileInput {
 | |
| 	port := Port
 | |
| 	return CorefileInput{
 | |
| 		Contents: []byte(fmt.Sprintf("%s:%s\nroot %s", Host, port, Root)),
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // These defaults are configurable through the command line
 | |
| var (
 | |
| 	// Root is the site root
 | |
| 	Root = DefaultRoot
 | |
| 
 | |
| 	// Host is the site host
 | |
| 	Host = DefaultHost
 | |
| 
 | |
| 	// Port is the site port
 | |
| 	Port = DefaultPort
 | |
| )
 | |
| 
 | |
| // bindingMapping maps a network address to configurations
 | |
| // that will bind to it. The order of the configs is important.
 | |
| type bindingMapping struct {
 | |
| 	BindAddr *net.TCPAddr
 | |
| 	Configs  []server.Config
 | |
| }
 | |
| 
 | |
| // bindingGroup maps network addresses to their configurations.
 | |
| // Preserving the order of the groupings is important
 | |
| // (related to graceful shutdown and restart)
 | |
| // so this is a slice, not a literal map.
 | |
| type bindingGroup []bindingMapping
 |