diff options
Diffstat (limited to 'plugin/pkg/parse')
-rw-r--r-- | plugin/pkg/parse/host.go | 98 | ||||
-rw-r--r-- | plugin/pkg/parse/host_test.go | 87 | ||||
-rw-r--r-- | plugin/pkg/parse/parse.go | 7 | ||||
-rw-r--r-- | plugin/pkg/parse/transport.go | 33 | ||||
-rw-r--r-- | plugin/pkg/parse/transport_test.go | 25 |
5 files changed, 247 insertions, 3 deletions
diff --git a/plugin/pkg/parse/host.go b/plugin/pkg/parse/host.go new file mode 100644 index 000000000..87177125f --- /dev/null +++ b/plugin/pkg/parse/host.go @@ -0,0 +1,98 @@ +package parse + +import ( + "fmt" + "net" + "os" + + "github.com/coredns/coredns/plugin/pkg/transport" + + "github.com/miekg/dns" +) + +// HostPortOrFile parses the strings in s, each string can either be a +// address, [scheme://]address:port or a filename. The address part is checked +// and in case of filename a resolv.conf like file is (assumed) and parsed and +// the nameservers found are returned. +func HostPortOrFile(s ...string) ([]string, error) { + var servers []string + for _, h := range s { + + trans, host := Transport(h) + + addr, _, err := net.SplitHostPort(host) + if err != nil { + // Parse didn't work, it is not a addr:port combo + if net.ParseIP(host) == nil { + // Not an IP address. + ss, err := tryFile(host) + if err == nil { + servers = append(servers, ss...) + continue + } + return servers, fmt.Errorf("not an IP address or file: %q", host) + } + var ss string + switch trans { + case transport.DNS: + ss = net.JoinHostPort(host, transport.Port) + case transport.TLS: + ss = transport.TLS + "://" + net.JoinHostPort(host, transport.TLSPort) + case transport.GRPC: + ss = transport.GRPC + "://" + net.JoinHostPort(host, transport.GRPCPort) + case transport.HTTPS: + ss = transport.HTTPS + "://" + net.JoinHostPort(host, transport.HTTPSPort) + } + servers = append(servers, ss) + continue + } + + if net.ParseIP(addr) == nil { + // Not an IP address. + ss, err := tryFile(host) + if err == nil { + servers = append(servers, ss...) + continue + } + return servers, fmt.Errorf("not an IP address or file: %q", host) + } + servers = append(servers, h) + } + return servers, nil +} + +// Try to open this is a file first. +func tryFile(s string) ([]string, error) { + c, err := dns.ClientConfigFromFile(s) + if err == os.ErrNotExist { + return nil, fmt.Errorf("failed to open file %q: %q", s, err) + } else if err != nil { + return nil, err + } + + servers := []string{} + for _, s := range c.Servers { + servers = append(servers, net.JoinHostPort(s, c.Port)) + } + return servers, nil +} + +// HostPort will check if the host part is a valid IP address, if the +// IP address is valid, but no port is found, defaultPort is added. +func HostPort(s, defaultPort string) (string, error) { + addr, port, err := net.SplitHostPort(s) + if port == "" { + port = defaultPort + } + if err != nil { + if net.ParseIP(s) == nil { + return "", fmt.Errorf("must specify an IP address: `%s'", s) + } + return net.JoinHostPort(s, port), nil + } + + if net.ParseIP(addr) == nil { + return "", fmt.Errorf("must specify an IP address: `%s'", addr) + } + return net.JoinHostPort(addr, port), nil +} diff --git a/plugin/pkg/parse/host_test.go b/plugin/pkg/parse/host_test.go new file mode 100644 index 000000000..f6e771f29 --- /dev/null +++ b/plugin/pkg/parse/host_test.go @@ -0,0 +1,87 @@ +package parse + +import ( + "io/ioutil" + "os" + "testing" + + "github.com/coredns/coredns/plugin/pkg/transport" +) + +func TestHostPortOrFile(t *testing.T) { + tests := []struct { + in string + expected string + shouldErr bool + }{ + { + "8.8.8.8", + "8.8.8.8:53", + false, + }, + { + "8.8.8.8:153", + "8.8.8.8:153", + false, + }, + { + "/etc/resolv.conf:53", + "", + true, + }, + { + "resolv.conf", + "127.0.0.1:53", + false, + }, + } + + err := ioutil.WriteFile("resolv.conf", []byte("nameserver 127.0.0.1\n"), 0600) + if err != nil { + t.Fatalf("Failed to write test resolv.conf") + } + defer os.Remove("resolv.conf") + + for i, tc := range tests { + got, err := HostPortOrFile(tc.in) + if err == nil && tc.shouldErr { + t.Errorf("Test %d, expected error, got nil", i) + continue + } + if err != nil && tc.shouldErr { + continue + } + if got[0] != tc.expected { + t.Errorf("Test %d, expected %q, got %q", i, tc.expected, got[0]) + } + } +} + +func TestParseHostPort(t *testing.T) { + tests := []struct { + in string + expected string + shouldErr bool + }{ + {"8.8.8.8:53", "8.8.8.8:53", false}, + {"a.a.a.a:153", "", true}, + {"8.8.8.8", "8.8.8.8:53", false}, + {"8.8.8.8:", "8.8.8.8:53", false}, + {"8.8.8.8::53", "", true}, + {"resolv.conf", "", true}, + } + + for i, tc := range tests { + got, err := HostPort(tc.in, transport.Port) + if err == nil && tc.shouldErr { + t.Errorf("Test %d, expected error, got nil", i) + continue + } + if err != nil && !tc.shouldErr { + t.Errorf("Test %d, expected no error, got %q", i, err) + } + if got != tc.expected { + t.Errorf("Test %d, expected %q, got %q", i, tc.expected, got) + } + } +} diff --git a/plugin/pkg/parse/parse.go b/plugin/pkg/parse/parse.go index 17d2641ef..17fe75fb0 100644 --- a/plugin/pkg/parse/parse.go +++ b/plugin/pkg/parse/parse.go @@ -4,7 +4,8 @@ package parse import ( "fmt" - "github.com/coredns/coredns/plugin/pkg/dnsutil" + "github.com/coredns/coredns/plugin/pkg/transport" + "github.com/mholt/caddy" ) @@ -19,7 +20,7 @@ func Transfer(c *caddy.Controller, secondary bool) (tos, froms []string, err err tos = c.RemainingArgs() for i := range tos { if tos[i] != "*" { - normalized, err := dnsutil.ParseHostPort(tos[i], "53") + normalized, err := HostPort(tos[i], transport.Port) if err != nil { return nil, nil, err } @@ -34,7 +35,7 @@ func Transfer(c *caddy.Controller, secondary bool) (tos, froms []string, err err froms = c.RemainingArgs() for i := range froms { if froms[i] != "*" { - normalized, err := dnsutil.ParseHostPort(froms[i], "53") + normalized, err := HostPort(froms[i], transport.Port) if err != nil { return nil, nil, err } diff --git a/plugin/pkg/parse/transport.go b/plugin/pkg/parse/transport.go new file mode 100644 index 000000000..d632120d7 --- /dev/null +++ b/plugin/pkg/parse/transport.go @@ -0,0 +1,33 @@ +package parse + +import ( + "strings" + + "github.com/coredns/coredns/plugin/pkg/transport" +) + +// Transport returns the transport defined in s and a string where the +// transport prefix is removed (if there was any). If no transport is defined +// we default to TransportDNS +func Transport(s string) (trans string, addr string) { + switch { + case strings.HasPrefix(s, transport.TLS+"://"): + s = s[len(transport.TLS+"://"):] + return transport.TLS, s + + case strings.HasPrefix(s, transport.DNS+"://"): + s = s[len(transport.DNS+"://"):] + return transport.DNS, s + + case strings.HasPrefix(s, transport.GRPC+"://"): + s = s[len(transport.GRPC+"://"):] + return transport.GRPC, s + + case strings.HasPrefix(s, transport.HTTPS+"://"): + s = s[len(transport.HTTPS+"://"):] + + return transport.HTTPS, s + } + + return transport.DNS, s +} diff --git a/plugin/pkg/parse/transport_test.go b/plugin/pkg/parse/transport_test.go new file mode 100644 index 000000000..d0e0fcddd --- /dev/null +++ b/plugin/pkg/parse/transport_test.go @@ -0,0 +1,25 @@ +package parse + +import ( + "testing" + + "github.com/coredns/coredns/plugin/pkg/transport" +) + +func TestTransport(t *testing.T) { + for i, test := range []struct { + input string + expected string + }{ + {"dns://.:53", transport.DNS}, + {"2003::1/64.:53", transport.DNS}, + {"grpc://example.org:1443 ", transport.GRPC}, + {"tls://example.org ", transport.TLS}, + {"https://example.org ", transport.HTTPS}, + } { + actual, _ := Transport(test.input) + if actual != test.expected { + t.Errorf("Test %d: Expected %s but got %s", i, test.expected, actual) + } + } +} |