aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Miek Gieben <miek@miek.nl> 2016-08-20 23:03:36 +0100
committerGravatar GitHub <noreply@github.com> 2016-08-20 23:03:36 +0100
commit416603383dfe0f57ef480e4bf480bbab1bf780a9 (patch)
treec03871ed17a439df91d32b1cdc999fed0a15f434
parent9ac3cab1b7b1b1e78f86ce3c6a80fbee312162e6 (diff)
downloadcoredns-416603383dfe0f57ef480e4bf480bbab1bf780a9.tar.gz
coredns-416603383dfe0f57ef480e4bf480bbab1bf780a9.tar.zst
coredns-416603383dfe0f57ef480e4bf480bbab1bf780a9.zip
Cleanup and fixes (#223)
* Set version to 001 * Remove k8stest, test fails is k8s is not there: touch luck * Remove server directory: not used anymore * Disable k8s test (for now) * gometalinter changes
-rw-r--r--core/coredns.go16
-rw-r--r--core/dnsserver/directives.go2
-rw-r--r--core/dnsserver/register.go8
-rw-r--r--core/dnsserver/server.go23
-rw-r--r--coredns.go8
-rw-r--r--middleware/canonical.go2
-rw-r--r--middleware/commands.go120
-rw-r--r--middleware/commands_test.go291
-rw-r--r--middleware/kubernetes/k8stest/k8stest.go11
-rw-r--r--middleware/log/setup.go3
-rw-r--r--server/config.go76
-rw-r--r--server/config_test.go25
-rw-r--r--server/server.go467
-rw-r--r--server/zones.go28
-rw-r--r--test/etcd_test.go4
-rw-r--r--test/kubernetes_test.go16
-rw-r--r--test/proxy_test.go4
17 files changed, 33 insertions, 1071 deletions
diff --git a/core/coredns.go b/core/coredns.go
index e770a1a30..0ceb5d064 100644
--- a/core/coredns.go
+++ b/core/coredns.go
@@ -6,21 +6,19 @@ import (
// plug in the standard directives
_ "github.com/miekg/coredns/middleware/bind"
- _ "github.com/miekg/coredns/middleware/health"
- _ "github.com/miekg/coredns/middleware/pprof"
-
- _ "github.com/miekg/coredns/middleware/errors"
- _ "github.com/miekg/coredns/middleware/loadbalance"
- _ "github.com/miekg/coredns/middleware/log"
- _ "github.com/miekg/coredns/middleware/metrics"
- _ "github.com/miekg/coredns/middleware/rewrite"
-
_ "github.com/miekg/coredns/middleware/cache"
_ "github.com/miekg/coredns/middleware/chaos"
_ "github.com/miekg/coredns/middleware/dnssec"
+ _ "github.com/miekg/coredns/middleware/errors"
_ "github.com/miekg/coredns/middleware/etcd"
_ "github.com/miekg/coredns/middleware/file"
+ _ "github.com/miekg/coredns/middleware/health"
_ "github.com/miekg/coredns/middleware/kubernetes"
+ _ "github.com/miekg/coredns/middleware/loadbalance"
+ _ "github.com/miekg/coredns/middleware/log"
+ _ "github.com/miekg/coredns/middleware/metrics"
+ _ "github.com/miekg/coredns/middleware/pprof"
_ "github.com/miekg/coredns/middleware/proxy"
+ _ "github.com/miekg/coredns/middleware/rewrite"
_ "github.com/miekg/coredns/middleware/secondary"
)
diff --git a/core/dnsserver/directives.go b/core/dnsserver/directives.go
index 78a8a11f7..7f0c1a8f7 100644
--- a/core/dnsserver/directives.go
+++ b/core/dnsserver/directives.go
@@ -9,7 +9,7 @@ package dnsserver
// feel the effects of all other middleware below
// (after) them during a request, but they must not
// care what middleware above them are doing.
-var Directives = []string{
+var directives = []string{
"bind",
"health",
"pprof",
diff --git a/core/dnsserver/register.go b/core/dnsserver/register.go
index 3c12c019c..81bc710d0 100644
--- a/core/dnsserver/register.go
+++ b/core/dnsserver/register.go
@@ -13,7 +13,7 @@ const serverType = "dns"
func init() {
caddy.RegisterServerType(serverType, caddy.ServerType{
- Directives: Directives,
+ Directives: directives,
DefaultInput: func() caddy.Input {
if Port == DefaultPort && Zone != "" {
return caddy.CaddyfileInput{
@@ -32,8 +32,6 @@ func init() {
})
}
-var TestNewContext = newContext
-
func newContext() caddy.Context {
return &dnsContext{keysToConfigs: make(map[string]*Config)}
}
@@ -103,8 +101,8 @@ func (h *dnsContext) MakeServers() ([]caddy.Server, error) {
}
// AddMiddleware adds a middleware to a site's middleware stack.
-func (sc *Config) AddMiddleware(m Middleware) {
- sc.Middleware = append(sc.Middleware, m)
+func (c *Config) AddMiddleware(m Middleware) {
+ c.Middleware = append(c.Middleware, m)
}
// groupSiteConfigsByListenAddr groups site configs by their listen
diff --git a/core/dnsserver/server.go b/core/dnsserver/server.go
index 27c62312b..c29406f79 100644
--- a/core/dnsserver/server.go
+++ b/core/dnsserver/server.go
@@ -32,6 +32,7 @@ type Server struct {
connTimeout time.Duration // the maximum duration of a graceful shutdown
}
+// NewServer returns a new CoreDNS server and compiles all middleware in to it.
func NewServer(addr string, group []*Config) (*Server, error) {
s := &Server{
@@ -65,20 +66,6 @@ func NewServer(addr string, group []*Config) (*Server, error) {
return s, nil
}
-// LocalAddr return the addresses where the server is bound to.
-func (s *Server) LocalAddr() net.Addr {
- s.m.Lock()
- defer s.m.Unlock()
- return s.l.Addr()
-}
-
-// LocalAddrPacket return the net.PacketConn address where the server is bound to.
-func (s *Server) LocalAddrPacket() net.Addr {
- s.m.Lock()
- defer s.m.Unlock()
- return s.p.LocalAddr()
-}
-
// Serve starts the server with an existing listener. It blocks until the server stops.
func (s *Server) Serve(l net.Listener) error {
s.m.Lock()
@@ -97,6 +84,7 @@ func (s *Server) ServePacket(p net.PacketConn) error {
return s.server[udp].ActivateAndServe()
}
+// Listen implements caddy.TCPServer interface.
func (s *Server) Listen() (net.Listener, error) {
l, err := net.Listen("tcp", s.Addr)
if err != nil {
@@ -108,6 +96,7 @@ func (s *Server) Listen() (net.Listener, error) {
return l, nil
}
+// ListenPacket implements caddy.UDPServer interface.
func (s *Server) ListenPacket() (net.PacketConn, error) {
p, err := net.ListenPacket("udp", s.Addr)
if err != nil {
@@ -197,7 +186,7 @@ func (s *Server) ServeDNS(w dns.ResponseWriter, r *dns.Msg) {
if h, ok := s.zones[string(b[:l])]; ok {
if r.Question[0].Qtype != dns.TypeDS {
rcode, _ := h.middlewareChain.ServeDNS(ctx, w, r)
- if RcodeNoClientWrite(rcode) {
+ if rcodeNoClientWrite(rcode) {
DefaultErrorFunc(w, r, rcode)
}
return
@@ -211,7 +200,7 @@ func (s *Server) ServeDNS(w dns.ResponseWriter, r *dns.Msg) {
// Wildcard match, if we have found nothing try the root zone as a last resort.
if h, ok := s.zones["."]; ok {
rcode, _ := h.middlewareChain.ServeDNS(ctx, w, r)
- if RcodeNoClientWrite(rcode) {
+ if rcodeNoClientWrite(rcode) {
DefaultErrorFunc(w, r, rcode)
}
return
@@ -234,7 +223,7 @@ func DefaultErrorFunc(w dns.ResponseWriter, r *dns.Msg, rcode int) {
w.WriteMsg(answer)
}
-func RcodeNoClientWrite(rcode int) bool {
+func rcodeNoClientWrite(rcode int) bool {
switch rcode {
case dns.RcodeServerFailure:
fallthrough
diff --git a/coredns.go b/coredns.go
index e7e54dc60..ad8cd1c95 100644
--- a/coredns.go
+++ b/coredns.go
@@ -10,9 +10,15 @@ import (
//go:generate go run plugin_generate.go
func main() {
- // Set some flags/options specific for CoreDNS.
+ // Default values for flags for CoreDNS.
flag.Set("type", "dns")
+
+ // Values specific for CoreDNS.
caddy.DefaultConfigFile = "Corefile"
+ caddy.AppName = "coredns"
+ caddy.AppVersion = version
caddymain.Run()
}
+
+const version = "001"
diff --git a/middleware/canonical.go b/middleware/canonical.go
index e26e3c6c4..fd30946bf 100644
--- a/middleware/canonical.go
+++ b/middleware/canonical.go
@@ -56,4 +56,4 @@ func doDDD(b []byte) {
}
func isDigit(b byte) bool { return b >= '0' && b <= '9' }
-func dddToByte(s []byte) byte { return byte((s[1]-'0')*100 + (s[2]-'0')*10 + (s[3] - '0')) }
+func dddToByte(s []byte) byte { return (s[1]-'0')*100 + (s[2]-'0')*10 + (s[3] - '0') }
diff --git a/middleware/commands.go b/middleware/commands.go
deleted file mode 100644
index 5c241161e..000000000
--- a/middleware/commands.go
+++ /dev/null
@@ -1,120 +0,0 @@
-package middleware
-
-import (
- "errors"
- "runtime"
- "unicode"
-
- "github.com/flynn/go-shlex"
-)
-
-var runtimeGoos = runtime.GOOS
-
-// SplitCommandAndArgs takes a command string and parses it
-// shell-style into the command and its separate arguments.
-func SplitCommandAndArgs(command string) (cmd string, args []string, err error) {
- var parts []string
-
- if runtimeGoos == "windows" {
- parts = parseWindowsCommand(command) // parse it Windows-style
- } else {
- parts, err = parseUnixCommand(command) // parse it Unix-style
- if err != nil {
- err = errors.New("error parsing command: " + err.Error())
- return
- }
- }
-
- if len(parts) == 0 {
- err = errors.New("no command contained in '" + command + "'")
- return
- }
-
- cmd = parts[0]
- if len(parts) > 1 {
- args = parts[1:]
- }
-
- return
-}
-
-// parseUnixCommand parses a unix style command line and returns the
-// command and its arguments or an error
-func parseUnixCommand(cmd string) ([]string, error) {
- return shlex.Split(cmd)
-}
-
-// parseWindowsCommand parses windows command lines and
-// returns the command and the arguments as an array. It
-// should be able to parse commonly used command lines.
-// Only basic syntax is supported:
-// - spaces in double quotes are not token delimiters
-// - double quotes are escaped by either backspace or another double quote
-// - except for the above case backspaces are path separators (not special)
-//
-// Many sources point out that escaping quotes using backslash can be unsafe.
-// Use two double quotes when possible. (Source: http://stackoverflow.com/a/31413730/2616179 )
-//
-// This function has to be used on Windows instead
-// of the shlex package because this function treats backslash
-// characters properly.
-func parseWindowsCommand(cmd string) []string {
- const backslash = '\\'
- const quote = '"'
-
- var parts []string
- var part string
- var inQuotes bool
- var lastRune rune
-
- for i, ch := range cmd {
-
- if i != 0 {
- lastRune = rune(cmd[i-1])
- }
-
- if ch == backslash {
- // put it in the part - for now we don't know if it's an
- // escaping char or path separator
- part += string(ch)
- continue
- }
-
- if ch == quote {
- if lastRune == backslash {
- // remove the backslash from the part and add the escaped quote instead
- part = part[:len(part)-1]
- part += string(ch)
- continue
- }
-
- if lastRune == quote {
- // revert the last change of the inQuotes state
- // it was an escaping quote
- inQuotes = !inQuotes
- part += string(ch)
- continue
- }
-
- // normal escaping quotes
- inQuotes = !inQuotes
- continue
-
- }
-
- if unicode.IsSpace(ch) && !inQuotes && len(part) > 0 {
- parts = append(parts, part)
- part = ""
- continue
- }
-
- part += string(ch)
- }
-
- if len(part) > 0 {
- parts = append(parts, part)
- part = ""
- }
-
- return parts
-}
diff --git a/middleware/commands_test.go b/middleware/commands_test.go
deleted file mode 100644
index 3001e65a5..000000000
--- a/middleware/commands_test.go
+++ /dev/null
@@ -1,291 +0,0 @@
-package middleware
-
-import (
- "fmt"
- "runtime"
- "strings"
- "testing"
-)
-
-func TestParseUnixCommand(t *testing.T) {
- tests := []struct {
- input string
- expected []string
- }{
- // 0 - emtpy command
- {
- input: ``,
- expected: []string{},
- },
- // 1 - command without arguments
- {
- input: `command`,
- expected: []string{`command`},
- },
- // 2 - command with single argument
- {
- input: `command arg1`,
- expected: []string{`command`, `arg1`},
- },
- // 3 - command with multiple arguments
- {
- input: `command arg1 arg2`,
- expected: []string{`command`, `arg1`, `arg2`},
- },
- // 4 - command with single argument with space character - in quotes
- {
- input: `command "arg1 arg1"`,
- expected: []string{`command`, `arg1 arg1`},
- },
- // 5 - command with multiple spaces and tab character
- {
- input: "command arg1 arg2\targ3",
- expected: []string{`command`, `arg1`, `arg2`, `arg3`},
- },
- // 6 - command with single argument with space character - escaped with backspace
- {
- input: `command arg1\ arg2`,
- expected: []string{`command`, `arg1 arg2`},
- },
- // 7 - single quotes should escape special chars
- {
- input: `command 'arg1\ arg2'`,
- expected: []string{`command`, `arg1\ arg2`},
- },
- }
-
- for i, test := range tests {
- errorPrefix := fmt.Sprintf("Test [%d]: ", i)
- errorSuffix := fmt.Sprintf(" Command to parse: [%s]", test.input)
- actual, _ := parseUnixCommand(test.input)
- if len(actual) != len(test.expected) {
- t.Errorf(errorPrefix+"Expected %d parts, got %d: %#v."+errorSuffix, len(test.expected), len(actual), actual)
- continue
- }
- for j := 0; j < len(actual); j++ {
- if expectedPart, actualPart := test.expected[j], actual[j]; expectedPart != actualPart {
- t.Errorf(errorPrefix+"Expected: %v Actual: %v (index %d)."+errorSuffix, expectedPart, actualPart, j)
- }
- }
- }
-}
-
-func TestParseWindowsCommand(t *testing.T) {
- tests := []struct {
- input string
- expected []string
- }{
- { // 0 - empty command - do not fail
- input: ``,
- expected: []string{},
- },
- { // 1 - cmd without args
- input: `cmd`,
- expected: []string{`cmd`},
- },
- { // 2 - multiple args
- input: `cmd arg1 arg2`,
- expected: []string{`cmd`, `arg1`, `arg2`},
- },
- { // 3 - multiple args with space
- input: `cmd "combined arg" arg2`,
- expected: []string{`cmd`, `combined arg`, `arg2`},
- },
- { // 4 - path without spaces
- input: `mkdir C:\Windows\foo\bar`,
- expected: []string{`mkdir`, `C:\Windows\foo\bar`},
- },
- { // 5 - command with space in quotes
- input: `"command here"`,
- expected: []string{`command here`},
- },
- { // 6 - argument with escaped quotes (two quotes)
- input: `cmd ""arg""`,
- expected: []string{`cmd`, `"arg"`},
- },
- { // 7 - argument with escaped quotes (backslash)
- input: `cmd \"arg\"`,
- expected: []string{`cmd`, `"arg"`},
- },
- { // 8 - two quotes (escaped) inside an inQuote element
- input: `cmd "a ""quoted value"`,
- expected: []string{`cmd`, `a "quoted value`},
- },
- // TODO - see how many quotes are dislayed if we use "", """, """""""
- { // 9 - two quotes outside an inQuote element
- input: `cmd a ""quoted value`,
- expected: []string{`cmd`, `a`, `"quoted`, `value`},
- },
- { // 10 - path with space in quotes
- input: `mkdir "C:\directory name\foobar"`,
- expected: []string{`mkdir`, `C:\directory name\foobar`},
- },
- { // 11 - space without quotes
- input: `mkdir C:\ space`,
- expected: []string{`mkdir`, `C:\`, `space`},
- },
- { // 12 - space in quotes
- input: `mkdir "C:\ space"`,
- expected: []string{`mkdir`, `C:\ space`},
- },
- { // 13 - UNC
- input: `mkdir \\?\C:\Users`,
- expected: []string{`mkdir`, `\\?\C:\Users`},
- },
- { // 14 - UNC with space
- input: `mkdir "\\?\C:\Program Files"`,
- expected: []string{`mkdir`, `\\?\C:\Program Files`},
- },
-
- { // 15 - unclosed quotes - treat as if the path ends with quote
- input: `mkdir "c:\Program files`,
- expected: []string{`mkdir`, `c:\Program files`},
- },
- { // 16 - quotes used inside the argument
- input: `mkdir "c:\P"rogra"m f"iles`,
- expected: []string{`mkdir`, `c:\Program files`},
- },
- }
-
- for i, test := range tests {
- errorPrefix := fmt.Sprintf("Test [%d]: ", i)
- errorSuffix := fmt.Sprintf(" Command to parse: [%s]", test.input)
-
- actual := parseWindowsCommand(test.input)
- if len(actual) != len(test.expected) {
- t.Errorf(errorPrefix+"Expected %d parts, got %d: %#v."+errorSuffix, len(test.expected), len(actual), actual)
- continue
- }
- for j := 0; j < len(actual); j++ {
- if expectedPart, actualPart := test.expected[j], actual[j]; expectedPart != actualPart {
- t.Errorf(errorPrefix+"Expected: %v Actual: %v (index %d)."+errorSuffix, expectedPart, actualPart, j)
- }
- }
- }
-}
-
-func TestSplitCommandAndArgs(t *testing.T) {
-
- // force linux parsing. It's more robust and covers error cases
- runtimeGoos = "linux"
- defer func() {
- runtimeGoos = runtime.GOOS
- }()
-
- var parseErrorContent = "error parsing command:"
- var noCommandErrContent = "no command contained in"
-
- tests := []struct {
- input string
- expectedCommand string
- expectedArgs []string
- expectedErrContent string
- }{
- // 0 - emtpy command
- {
- input: ``,
- expectedCommand: ``,
- expectedArgs: nil,
- expectedErrContent: noCommandErrContent,
- },
- // 1 - command without arguments
- {
- input: `command`,
- expectedCommand: `command`,
- expectedArgs: nil,
- expectedErrContent: ``,
- },
- // 2 - command with single argument
- {
- input: `command arg1`,
- expectedCommand: `command`,
- expectedArgs: []string{`arg1`},
- expectedErrContent: ``,
- },
- // 3 - command with multiple arguments
- {
- input: `command arg1 arg2`,
- expectedCommand: `command`,
- expectedArgs: []string{`arg1`, `arg2`},
- expectedErrContent: ``,
- },
- // 4 - command with unclosed quotes
- {
- input: `command "arg1 arg2`,
- expectedCommand: "",
- expectedArgs: nil,
- expectedErrContent: parseErrorContent,
- },
- // 5 - command with unclosed quotes
- {
- input: `command 'arg1 arg2"`,
- expectedCommand: "",
- expectedArgs: nil,
- expectedErrContent: parseErrorContent,
- },
- }
-
- for i, test := range tests {
- errorPrefix := fmt.Sprintf("Test [%d]: ", i)
- errorSuffix := fmt.Sprintf(" Command to parse: [%s]", test.input)
- actualCommand, actualArgs, actualErr := SplitCommandAndArgs(test.input)
-
- // test if error matches expectation
- if test.expectedErrContent != "" {
- if actualErr == nil {
- t.Errorf(errorPrefix+"Expected error with content [%s], found no error."+errorSuffix, test.expectedErrContent)
- } else if !strings.Contains(actualErr.Error(), test.expectedErrContent) {
- t.Errorf(errorPrefix+"Expected error with content [%s], found [%v]."+errorSuffix, test.expectedErrContent, actualErr)
- }
- } else if actualErr != nil {
- t.Errorf(errorPrefix+"Expected no error, found [%v]."+errorSuffix, actualErr)
- }
-
- // test if command matches
- if test.expectedCommand != actualCommand {
- t.Errorf(errorPrefix+"Expected command: [%s], actual: [%s]."+errorSuffix, test.expectedCommand, actualCommand)
- }
-
- // test if arguments match
- if len(test.expectedArgs) != len(actualArgs) {
- t.Errorf(errorPrefix+"Wrong number of arguments! Expected [%v], actual [%v]."+errorSuffix, test.expectedArgs, actualArgs)
- } else {
- // test args only if the count matches.
- for j, actualArg := range actualArgs {
- expectedArg := test.expectedArgs[j]
- if actualArg != expectedArg {
- t.Errorf(errorPrefix+"Argument at position [%d] differ! Expected [%s], actual [%s]"+errorSuffix, j, expectedArg, actualArg)
- }
- }
- }
- }
-}
-
-func ExampleSplitCommandAndArgs() {
- var commandLine string
- var command string
- var args []string
-
- // just for the test - change GOOS and reset it at the end of the test
- runtimeGoos = "windows"
- defer func() {
- runtimeGoos = runtime.GOOS
- }()
-
- commandLine = `mkdir /P "C:\Program Files"`
- command, args, _ = SplitCommandAndArgs(commandLine)
-
- fmt.Printf("Windows: %s: %s [%s]\n", commandLine, command, strings.Join(args, ","))
-
- // set GOOS to linux
- runtimeGoos = "linux"
-
- commandLine = `mkdir -p /path/with\ space`
- command, args, _ = SplitCommandAndArgs(commandLine)
-
- fmt.Printf("Linux: %s: %s [%s]\n", commandLine, command, strings.Join(args, ","))
-
- // Output:
- // Windows: mkdir /P "C:\Program Files": mkdir [/P,C:\Program Files]
- // Linux: mkdir -p /path/with\ space: mkdir [-p,/path/with space]
-}
diff --git a/middleware/kubernetes/k8stest/k8stest.go b/middleware/kubernetes/k8stest/k8stest.go
deleted file mode 100644
index f846b0d92..000000000
--- a/middleware/kubernetes/k8stest/k8stest.go
+++ /dev/null
@@ -1,11 +0,0 @@
-package k8stest
-
-import (
- "net/http"
-)
-
-// checkKubernetesRunning performs a basic
-func CheckKubernetesRunning() bool {
- _, err := http.Get("http://localhost:8080/api/v1")
- return err == nil
-}
diff --git a/middleware/log/setup.go b/middleware/log/setup.go
index a80e69ac3..a1e143e6b 100644
--- a/middleware/log/setup.go
+++ b/middleware/log/setup.go
@@ -7,7 +7,6 @@ import (
"github.com/miekg/coredns/core/dnsserver"
"github.com/miekg/coredns/middleware"
- "github.com/miekg/coredns/server"
"github.com/hashicorp/go-syslog"
"github.com/mholt/caddy"
@@ -64,7 +63,7 @@ func setup(c *caddy.Controller) error {
})
dnsserver.GetConfig(c).AddMiddleware(func(next dnsserver.Handler) dnsserver.Handler {
- return Logger{Next: next, Rules: rules, ErrorFunc: server.DefaultErrorFunc}
+ return Logger{Next: next, Rules: rules, ErrorFunc: dnsserver.DefaultErrorFunc}
})
return nil
diff --git a/server/config.go b/server/config.go
deleted file mode 100644
index de64ed7f2..000000000
--- a/server/config.go
+++ /dev/null
@@ -1,76 +0,0 @@
-package server
-
-import (
- "net"
-
- "github.com/miekg/coredns/middleware"
-)
-
-// Config configuration for a single server.
-type Config struct {
- // The hostname or IP on which to serve
- Host string
-
- // The host address to bind on - defaults to (virtual) Host if empty
- BindHost string
-
- // The port to listen on
- Port string
-
- // The directory from which to parse db files
- Root string
-
- // HTTPS configuration
- TLS TLSConfig
-
- // Middleware stack
- Middleware []middleware.Middleware
-
- // Startup is a list of functions (or methods) to execute at
- // server startup and restart; these are executed before any
- // parts of the server are configured, and the functions are
- // blocking. These are good for setting up middlewares and
- // starting goroutines.
- Startup []func() error
-
- // FirstStartup is like Startup but these functions only execute
- // during the initial startup, not on subsequent restarts.
- //
- // (Note: The server does not ever run these on its own; it is up
- // to the calling application to do so, and do so only once, as the
- // server itself has no notion whether it's a restart or not.)
- FirstStartup []func() error
-
- // Functions (or methods) to execute when the server quits;
- // these are executed in response to SIGINT and are blocking. These
- // function are *also* called when we are restarting.
- Shutdown []func() error
-
- // The path to the configuration file from which this was loaded
- ConfigFile string
-
- // The name of the application
- AppName string
-
- // The application's version
- AppVersion string
-}
-
-// Address returns the host:port of c as a string.
-func (c Config) Address() string {
- return net.JoinHostPort(c.Host, c.Port)
-}
-
-// TLSConfig describes how TLS should be configured and used.
-type TLSConfig struct {
- Enabled bool // will be set to true if TLS is enabled
- LetsEncryptEmail string
- Manual bool // will be set to true if user provides own certs and keys
- Managed bool // will be set to true if config qualifies for implicit automatic/managed HTTPS
- OnDemand bool // will be set to true if user enables on-demand TLS (obtain certs during handshakes)
- Ciphers []uint16
- ProtocolMinVersion uint16
- ProtocolMaxVersion uint16
- PreferServerCipherSuites bool
- ClientCerts []string
-}
diff --git a/server/config_test.go b/server/config_test.go
deleted file mode 100644
index 8787e467b..000000000
--- a/server/config_test.go
+++ /dev/null
@@ -1,25 +0,0 @@
-package server
-
-import "testing"
-
-func TestConfigAddress(t *testing.T) {
- cfg := Config{Host: "foobar", Port: "1234"}
- if actual, expected := cfg.Address(), "foobar:1234"; expected != actual {
- t.Errorf("Expected '%s' but got '%s'", expected, actual)
- }
-
- cfg = Config{Host: "", Port: "1234"}
- if actual, expected := cfg.Address(), ":1234"; expected != actual {
- t.Errorf("Expected '%s' but got '%s'", expected, actual)
- }
-
- cfg = Config{Host: "foobar", Port: ""}
- if actual, expected := cfg.Address(), "foobar:"; expected != actual {
- t.Errorf("Expected '%s' but got '%s'", expected, actual)
- }
-
- cfg = Config{Host: "::1", Port: "443"}
- if actual, expected := cfg.Address(), "[::1]:443"; expected != actual {
- t.Errorf("Expected '%s' but got '%s'", expected, actual)
- }
-}
diff --git a/server/server.go b/server/server.go
deleted file mode 100644
index b0f89468e..000000000
--- a/server/server.go
+++ /dev/null
@@ -1,467 +0,0 @@
-// Package server implements a configurable, general-purpose web server.
-// It relies on configurations obtained from the adjacent config package
-// and can execute middleware as defined by the adjacent middleware package.
-package server
-
-import (
- "crypto/tls"
- "crypto/x509"
- "fmt"
- "io/ioutil"
- "log"
- "net"
- "os"
- "runtime"
- "sync"
- "time"
-
- "github.com/miekg/coredns/middleware"
- "github.com/miekg/coredns/middleware/metrics"
-
- "github.com/miekg/dns"
- "golang.org/x/net/context"
-)
-
-// Server represents an instance of a server, which serves
-// DNS requests at a particular address (host and port). A
-// server is capable of serving numerous zones on
-// the same address and the listener may be stopped for
-// graceful termination (POSIX only).
-type Server struct {
- Addr string // Address we listen on
- mux *dns.ServeMux
- server [2]*dns.Server // by convention 0 is tcp and 1 is udp
- listenerMu sync.Mutex // protects listener and packetconn inside server
-
- tls bool // whether this server is serving all HTTPS hosts or not
- TLSConfig *tls.Config
- OnDemandTLS bool // whether this server supports on-demand TLS (load certs at handshake-time)
- zones map[string]zone // zones keyed by their address
- dnsWg sync.WaitGroup // used to wait on outstanding connections
- startChan chan struct{} // used to block until server is finished starting
- connTimeout time.Duration // the maximum duration of a graceful shutdown
- ReqCallback OptionalCallback // if non-nil, is executed at the beginning of every request
- SNICallback func(clientHello *tls.ClientHelloInfo) (*tls.Certificate, error)
-}
-
-// OptionalCallback is a function that may or may not handle a request.
-// It returns whether or not it handled the request. If it handled the
-// request, it is presumed that no further request handling should occur.
-type OptionalCallback func(dns.ResponseWriter, *dns.Msg) bool
-
-// New creates a new Server which will bind to addr and serve
-// the sites/hosts configured in configs. Its listener will
-// gracefully close when the server is stopped which will take
-// no longer than gracefulTimeout.
-//
-// This function does not start serving.
-//
-// Do not re-use a server (start, stop, then start again). We
-// could probably add more locking to make this possible, but
-// as it stands, you should dispose of a server after stopping it.
-// The behavior of serving with a spent server is undefined.
-func New(addr string, configs []Config, gracefulTimeout time.Duration) (*Server, error) {
- var useTLS, useOnDemandTLS bool
- if len(configs) > 0 {
- useTLS = configs[0].TLS.Enabled
- useOnDemandTLS = configs[0].TLS.OnDemand
- }
-
- s := &Server{
- Addr: addr,
- TLSConfig: new(tls.Config),
- // TODO: Make these values configurable?
- // ReadTimeout: 2 * time.Minute,
- // WriteTimeout: 2 * time.Minute,
- // MaxHeaderBytes: 1 << 16,
- tls: useTLS,
- OnDemandTLS: useOnDemandTLS,
- zones: make(map[string]zone),
- startChan: make(chan struct{}),
- connTimeout: gracefulTimeout,
- }
- mux := dns.NewServeMux()
- mux.Handle(".", s) // wildcard handler, everything will go through here
- s.mux = mux
-
- // We have to bound our wg with one increment
- // to prevent a "race condition" that is hard-coded
- // into sync.WaitGroup.Wait() - basically, an add
- // with a positive delta must be guaranteed to
- // occur before Wait() is called on the wg.
- // In a way, this kind of acts as a safety barrier.
- s.dnsWg.Add(1)
-
- // Set up each zone
- for _, conf := range configs {
- if _, exists := s.zones[conf.Host]; exists {
- return nil, fmt.Errorf("cannot serve %s - host already defined for address %s", conf.Address(), s.Addr)
- }
-
- z := zone{config: conf}
-
- // Build middleware stack
- err := z.buildStack()
- if err != nil {
- return nil, err
- }
-
- s.zones[conf.Host] = z
- }
-
- return s, nil
-}
-
-// Serve starts the server with an existing listener. It blocks until the server stops.
-func (s *Server) Serve(ln net.Listener, pc net.PacketConn) error {
- err := s.setup()
- if err != nil {
- close(s.startChan) // MUST defer so error is properly reported, same with all cases in this file
- return err
- }
- s.listenerMu.Lock()
- s.server[0] = &dns.Server{Listener: ln, Net: "tcp", Handler: s.mux}
- s.server[1] = &dns.Server{PacketConn: pc, Net: "udp", Handler: s.mux}
- s.listenerMu.Unlock()
-
- go func() {
- s.server[0].ActivateAndServe()
- }()
- close(s.startChan)
- return s.server[1].ActivateAndServe()
-}
-
-// ListenAndServe starts the server with a new listener. It blocks until the server stops.
-func (s *Server) ListenAndServe() error {
- err := s.setup()
- // defer close(s.startChan) // Don't understand why defer wouldn't actually work in this method (prolly cause the last ActivateAndServe does not actually return?
- if err != nil {
- close(s.startChan)
- return err
- }
-
- l, err := net.Listen("tcp", s.Addr)
- if err != nil {
- close(s.startChan)
- return err
- }
- pc, err := net.ListenPacket("udp", s.Addr)
- if err != nil {
- close(s.startChan)
- return err
- }
-
- s.listenerMu.Lock()
- s.server[0] = &dns.Server{Listener: l, Net: "tcp", Handler: s.mux}
- s.server[1] = &dns.Server{PacketConn: pc, Net: "udp", Handler: s.mux}
- s.listenerMu.Unlock()
-
- go func() {
- s.server[0].ActivateAndServe()
- }()
- close(s.startChan)
- return s.server[1].ActivateAndServe()
-}
-
-// setup prepares the server s to begin listening; it should be
-// called just before the listener announces itself on the network
-// and should only be called when the server is just starting up.
-func (s *Server) setup() error {
- // Execute startup functions now
- for _, z := range s.zones {
- for _, startupFunc := range z.config.Startup {
- err := startupFunc()
- if err != nil {
- return err
- }
- }
- }
-
- return nil
-}
-
-/*
-TODO(miek): no such thing in the glorious Go DNS.
-// serveTLS serves TLS with SNI and client auth support if s has them enabled. It
-// blocks until s quits.
-func serveTLS(s *Server, ln net.Listener, tlsConfigs []TLSConfig) error {
- // Customize our TLS configuration
- s.TLSConfig.MinVersion = tlsConfigs[0].ProtocolMinVersion
- s.TLSConfig.MaxVersion = tlsConfigs[0].ProtocolMaxVersion
- s.TLSConfig.CipherSuites = tlsConfigs[0].Ciphers
- s.TLSConfig.PreferServerCipherSuites = tlsConfigs[0].PreferServerCipherSuites
-
- // TLS client authentication, if user enabled it
- err := setupClientAuth(tlsConfigs, s.TLSConfig)
- if err != nil {
- defer close(s.startChan)
- return err
- }
-
- // Create TLS listener - note that we do not replace s.listener
- // with this TLS listener; tls.listener is unexported and does
- // not implement the File() method we need for graceful restarts
- // on POSIX systems.
- ln = tls.NewListener(ln, s.TLSConfig)
-
- close(s.startChan) // unblock anyone waiting for this to start listening
- return s.Serve(ln)
-}
-*/
-
-// Stop stops the server. It blocks until the server is
-// totally stopped. On POSIX systems, it will wait for
-// connections to close (up to a max timeout of a few
-// seconds); on Windows it will close the listener
-// immediately.
-func (s *Server) Stop() (err error) {
-
- if runtime.GOOS != "windows" {
- // force connections to close after timeout
- done := make(chan struct{})
- go func() {
- s.dnsWg.Done() // decrement our initial increment used as a barrier
- s.dnsWg.Wait()
- close(done)
- }()
-
- // Wait for remaining connections to finish or
- // force them all to close after timeout
- select {
- case <-time.After(s.connTimeout):
- case <-done:
- }
- }
-
- // Close the listener now; this stops the server without delay
- s.listenerMu.Lock()
- defer s.listenerMu.Unlock()
-
- for _, s1 := range s.server {
- if s1.Listener != nil {
- err = s1.Listener.Close()
- }
- if s1.PacketConn != nil {
- err = s1.PacketConn.Close()
- }
- err = s1.Shutdown()
- }
-
- return
-}
-
-// WaitUntilStarted blocks until the server s is started, meaning
-// that practically the next instruction is to start the server loop.
-// It also unblocks if the server encounters an error during startup.
-func (s *Server) WaitUntilStarted() {
- <-s.startChan
-}
-
-// ListenerFd gets a dup'ed file of the listener. If there
-// is no underlying file, the return value will be nil. It
-// is the caller's responsibility to close the file.
-func (s *Server) ListenerFd() *os.File {
- s.listenerMu.Lock()
- defer s.listenerMu.Unlock()
- if s.server[0].Listener != nil {
- file, _ := s.server[0].Listener.(*net.TCPListener).File()
- return file
- }
- return nil
-}
-
-// PacketConnFd gets a dup'ed file of the packetconn. If there
-// is no underlying file, the return value will be nil. It
-// is the caller's responsibility to close the file.
-func (s *Server) PacketConnFd() *os.File {
- s.listenerMu.Lock()
- defer s.listenerMu.Unlock()
- if s.server[1].PacketConn != nil {
- file, _ := s.server[1].PacketConn.(*net.UDPConn).File()
- return file
- }
- return nil
-}
-
-// ServeDNS is the entry point for every request to the address that s
-// is bound to. It acts as a multiplexer for the requests zonename as
-// defined in the request so that the correct zone
-// (configuration and middleware stack) will handle the request.
-func (s *Server) ServeDNS(w dns.ResponseWriter, r *dns.Msg) {
- defer func() {
- // In case the user doesn't enable error middleware, we still
- // need to make sure that we stay alive up here
- if rec := recover(); rec != nil {
- DefaultErrorFunc(w, r, dns.RcodeServerFailure)
- }
- }()
-
- if m, err := middleware.Edns0Version(r); err != nil { // Wrong EDNS version, return at once.
- rc := middleware.RcodeToString(dns.RcodeBadVers)
- state := middleware.State{W: w, Req: r}
-
- metrics.Report(state, metrics.Dropped, rc, m.Len(), time.Now())
- w.WriteMsg(m)
- return
- }
-
- // Execute the optional request callback if it exists
- if s.ReqCallback != nil && s.ReqCallback(w, r) {
- return
- }
-
- q := r.Question[0].Name
- b := make([]byte, len(q))
- off, end := 0, false
- ctx := context.Background()
-
- for {
- l := len(q[off:])
- for i := 0; i < l; i++ {
- b[i] = q[off+i]
- // normalize the name for the lookup
- if b[i] >= 'A' && b[i] <= 'Z' {
- b[i] |= ('a' - 'A')
- }
- }
-
- if h, ok := s.zones[string(b[:l])]; ok {
- if r.Question[0].Qtype != dns.TypeDS {
- rcode, _ := h.stack.ServeDNS(ctx, w, r)
- if RcodeNoClientWrite(rcode) {
- DefaultErrorFunc(w, r, rcode)
- }
- return
- }
- }
- off, end = dns.NextLabel(q, off)
- if end {
- break
- }
- }
- // Wildcard match, if we have found nothing try the root zone as a last resort.
- if h, ok := s.zones["."]; ok {
- rcode, _ := h.stack.ServeDNS(ctx, w, r)
- if RcodeNoClientWrite(rcode) {
- DefaultErrorFunc(w, r, rcode)
- }
- return
- }
-
- // Still here? Error out with REFUSED and some logging
- remoteHost := w.RemoteAddr().String()
- DefaultErrorFunc(w, r, dns.RcodeRefused)
- log.Printf("[INFO] \"%s %s %s\" - No such zone at %s (Remote: %s)", dns.Type(r.Question[0].Qtype), dns.Class(r.Question[0].Qclass), q, s.Addr, remoteHost)
-}
-
-// DefaultErrorFunc responds to an DNS request with an error.
-func DefaultErrorFunc(w dns.ResponseWriter, r *dns.Msg, rcode int) {
- state := middleware.State{W: w, Req: r}
- rc := middleware.RcodeToString(rcode)
-
- answer := new(dns.Msg)
- answer.SetRcode(r, rcode)
- state.SizeAndDo(answer)
-
- metrics.Report(state, metrics.Dropped, rc, answer.Len(), time.Now())
- w.WriteMsg(answer)
-}
-
-// setupClientAuth sets up TLS client authentication only if
-// any of the TLS configs specified at least one cert file.
-func setupClientAuth(tlsConfigs []TLSConfig, config *tls.Config) error {
- var clientAuth bool
- for _, cfg := range tlsConfigs {
- if len(cfg.ClientCerts) > 0 {
- clientAuth = true
- break
- }
- }
-
- if clientAuth {
- pool := x509.NewCertPool()
- for _, cfg := range tlsConfigs {
- for _, caFile := range cfg.ClientCerts {
- caCrt, err := ioutil.ReadFile(caFile) // Anyone that gets a cert from this CA can connect
- if err != nil {
- return err
- }
- if !pool.AppendCertsFromPEM(caCrt) {
- return fmt.Errorf("error loading client certificate '%s': no certificates were successfully parsed", caFile)
- }
- }
- }
- config.ClientCAs = pool
- config.ClientAuth = tls.RequireAndVerifyClientCert
- }
-
- return nil
-}
-
-// RunFirstStartupFuncs runs all of the server's FirstStartup
-// callback functions unless one of them returns an error first.
-// It is the caller's responsibility to call this only once and
-// at the correct time. The functions here should not be executed
-// at restarts or where the user does not explicitly start a new
-// instance of the server.
-func (s *Server) RunFirstStartupFuncs() error {
- for _, z := range s.zones {
- for _, f := range z.config.FirstStartup {
- if err := f(); err != nil {
- return err
- }
- }
- }
- return nil
-}
-
-// ShutdownCallbacks executes all the shutdown callbacks
-// for all the virtualhosts in servers, and returns all the
-// errors generated during their execution. In other words,
-// an error executing one shutdown callback does not stop
-// execution of others. Only one shutdown callback is executed
-// at a time. You must protect the servers that are passed in
-// if they are shared across threads.
-func ShutdownCallbacks(servers []*Server) []error {
- var errs []error
- for _, s := range servers {
- for _, zone := range s.zones {
- for _, shutdownFunc := range zone.config.Shutdown {
- err := shutdownFunc()
- if err != nil {
- errs = append(errs, err)
- }
- }
- }
- }
- return errs
-}
-
-func StartupCallbacks(servers []*Server) []error {
- var errs []error
- for _, s := range servers {
- for _, zone := range s.zones {
- for _, startupFunc := range zone.config.Startup {
- err := startupFunc()
- if err != nil {
- errs = append(errs, err)
- }
- }
- }
- }
- return errs
-}
-
-func RcodeNoClientWrite(rcode int) bool {
- switch rcode {
- case dns.RcodeServerFailure:
- fallthrough
- case dns.RcodeRefused:
- fallthrough
- case dns.RcodeFormatError:
- fallthrough
- case dns.RcodeNotImplemented:
- return true
- }
- return false
-}
diff --git a/server/zones.go b/server/zones.go
deleted file mode 100644
index 6a5a7a938..000000000
--- a/server/zones.go
+++ /dev/null
@@ -1,28 +0,0 @@
-package server
-
-import "github.com/miekg/coredns/middleware"
-
-// zone represents a DNS zone. While a Server
-// is what actually binds to the address, a user may want to serve
-// multiple zones on a single address, and this is what a
-// zone allows us to do.
-type zone struct {
- config Config
- stack middleware.Handler
-}
-
-// buildStack builds the server's middleware stack based
-// on its config. This method should be called last before
-// ListenAndServe begins.
-func (z *zone) buildStack() error {
- z.compile(z.config.Middleware)
- return nil
-}
-
-// compile is an elegant alternative to nesting middleware function
-// calls like handler1(handler2(handler3(finalHandler))).
-func (z *zone) compile(layers []middleware.Middleware) {
- for i := len(layers) - 1; i >= 0; i-- {
- z.stack = layers[i](z.stack)
- }
-}
diff --git a/test/etcd_test.go b/test/etcd_test.go
index 6beb4cff2..6645e9931 100644
--- a/test/etcd_test.go
+++ b/test/etcd_test.go
@@ -77,10 +77,10 @@ func TestEtcdStubAndProxyLookup(t *testing.T) {
t.Error("Expected to at least one RR in the answer section, got none")
}
if resp.Answer[0].Header().Rrtype != dns.TypeA {
- t.Error("Expected RR to A, got: %d", resp.Answer[0].Header().Rrtype)
+ t.Errorf("Expected RR to A, got: %d", resp.Answer[0].Header().Rrtype)
}
if resp.Answer[0].(*dns.A).A.String() != "93.184.216.34" {
- t.Error("Expected 93.184.216.34, got: %d", resp.Answer[0].(*dns.A).A.String())
+ t.Errorf("Expected 93.184.216.34, got: %d", resp.Answer[0].(*dns.A).A.String())
}
}
diff --git a/test/kubernetes_test.go b/test/kubernetes_test.go
index 009b50657..c8dce32e4 100644
--- a/test/kubernetes_test.go
+++ b/test/kubernetes_test.go
@@ -7,8 +7,6 @@ import (
"log"
"testing"
- "github.com/miekg/coredns/middleware/kubernetes/k8stest"
-
"github.com/miekg/dns"
)
@@ -63,17 +61,13 @@ var testdataLookupSRV = []struct {
{"*.*.coredns.local.", 1, 1}, // One SRV record, via namespace and service wildcard
}
-func TestK8sIntegration(t *testing.T) {
+func testK8sIntegration(t *testing.T) {
// subtests here (Go 1.7 feature).
testLookupA(t)
testLookupSRV(t)
}
func testLookupA(t *testing.T) {
- if !k8stest.CheckKubernetesRunning() {
- t.Skip("Skipping Kubernetes Integration tests. Kubernetes is not running")
- }
-
corefile :=
`.:0 {
kubernetes coredns.local {
@@ -104,7 +98,7 @@ func testLookupA(t *testing.T) {
res, _, err := dnsClient.Exchange(dnsMessage, udp)
if err != nil {
- t.Fatal("Could not send query: %s", err)
+ t.Fatalf("Could not send query: %s", err)
}
// Count A records in the answer section
ARecordCount := 0
@@ -124,10 +118,6 @@ func testLookupA(t *testing.T) {
}
func testLookupSRV(t *testing.T) {
- if !k8stest.CheckKubernetesRunning() {
- t.Skip("Skipping Kubernetes Integration tests. Kubernetes is not running")
- }
-
corefile :=
`.:0 {
kubernetes coredns.local {
@@ -159,7 +149,7 @@ func testLookupSRV(t *testing.T) {
res, _, err := dnsClient.Exchange(dnsMessage, udp)
if err != nil {
- t.Fatal("Could not send query: %s", err)
+ t.Fatalf("Could not send query: %s", err)
}
// Count SRV records in the answer section
srvRecordCount := 0
diff --git a/test/proxy_test.go b/test/proxy_test.go
index ca04e1ae8..96a2c4c0d 100644
--- a/test/proxy_test.go
+++ b/test/proxy_test.go
@@ -56,9 +56,9 @@ func TestLookupProxy(t *testing.T) {
t.Error("Expected to at least one RR in the answer section, got none")
}
if resp.Answer[0].Header().Rrtype != dns.TypeA {
- t.Error("Expected RR to A, got: %d", resp.Answer[0].Header().Rrtype)
+ t.Errorf("Expected RR to A, got: %d", resp.Answer[0].Header().Rrtype)
}
if resp.Answer[0].(*dns.A).A.String() != "127.0.0.1" {
- t.Error("Expected 127.0.0.1, got: %d", resp.Answer[0].(*dns.A).A.String())
+ t.Errorf("Expected 127.0.0.1, got: %d", resp.Answer[0].(*dns.A).A.String())
}
}